Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add COPT solver #190

Merged
merged 4 commits into from
Oct 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ Fri 0 4
* [Gurobi](https://www.gurobi.com/)
* [Xpress](https://www.fico.com/en/products/fico-xpress-solver)
* [Cplex](https://www.ibm.com/de-de/analytics/cplex-optimizer)
* [COPT](https://www.shanshu.ai/copt)

Note that these do have to be installed by the user separately.

Expand Down
1 change: 1 addition & 0 deletions doc/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ Solvers
solvers.run_cplex
solvers.run_gurobi
solvers.run_xpress
solvers.run_copt

Solving
=======
Expand Down
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ flexible data-handling features:
- `Gurobi <https://www.gurobi.com/>`__
- `Xpress <https://www.fico.com/en/products/fico-xpress-solver>`__
- `Cplex <https://www.ibm.com/de-de/analytics/cplex-optimizer>`__
- `COPT <https://www.shanshu.ai/copt>`__



Expand Down
1 change: 1 addition & 0 deletions doc/prerequisites.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ Linopy won't work without a solver. Currently, the following solvers are support
- `Gurobi <https://www.gurobi.com/>`__ - closed source, commercial, very fast
- `Xpress <https://www.fico.com/en/products/fico-xpress-solver>`__ - closed source, commercial, very fast
- `Cplex <https://www.ibm.com/de-de/analytics/cplex-optimizer>`__ - closed source, commercial, very fast
- `COPT <https://www.shanshu.ai/copt>`__ - closed source, commercial, very fast

For a subset of the solvers, Linopy provides a wrapper.

Expand Down
6 changes: 4 additions & 2 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
Release Notes
=============

.. Upcoming Release
.. ----------------
Upcoming Release
----------------

* Added solver interface for COPT by Cardinal Optimizer.

Version 0.3.0
-------------
Expand Down
103 changes: 103 additions & 0 deletions linopy/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@
import xpress

available_solvers.append("xpress")
with contextlib.suppress(ImportError):
import coptpy

available_solvers.append("copt")
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -705,6 +709,105 @@
return Result(status, solution, m)


def run_copt(
model,
io_api=None,
problem_fn=None,
solution_fn=None,
log_fn=None,
warmstart_fn=None,
basis_fn=None,
keep_files=False,
env=None,
**solver_options,
):
"""
Solve a linear problem using the COPT solver.

https://guide.coap.online/copt/en-doc/index.html

For more information on solver options, see
https://guide.coap.online/copt/en-doc/parameter.html
"""
# conditions: https://guide.coap.online/copt/en-doc/constant.html#chapconst-solstatus
CONDITION_MAP = {
0: "unstarted",
1: "optimal",
2: "infeasible",
3: "unbounded",
4: "infeasible_or_unbounded",
5: "numerical",
6: "node_limit",
7: "imprecise",
8: "time_limit",
9: "unfinished",
10: "interrupted",
}

if io_api is not None and io_api not in ["lp", "mps"]:
logger.warning(

Check warning on line 748 in linopy/solvers.py

View check run for this annotation

Codecov / codecov/patch

linopy/solvers.py#L748

Added line #L748 was not covered by tests
f"IO setting '{io_api}' not available for COPT solver. "
"Falling back to `lp`."
)

problem_fn = model.to_file(problem_fn)

problem_fn = maybe_convert_path(problem_fn)
log_fn = maybe_convert_path(log_fn)
warmstart_fn = maybe_convert_path(warmstart_fn)
basis_fn = maybe_convert_path(basis_fn)

env = coptpy.Envr()

m = env.createModel()

m.read(str(problem_fn))

if log_fn:
m.setLogFile(log_fn)

for k, v in solver_options.items():
m.setParam(k, v)

if warmstart_fn:
m.readBasis(warmstart_fn)

m.solve()

if basis_fn and m.HasBasis:
try:
m.write(basis_fn)
except Exception as err:
logger.info("No model basis stored. Raised error: ", err)

Check warning on line 781 in linopy/solvers.py

View check run for this annotation

Codecov / codecov/patch

linopy/solvers.py#L780-L781

Added lines #L780 - L781 were not covered by tests

condition = m.LpStatus if model.type == "LP" else m.MipStatus
termination_condition = CONDITION_MAP.get(condition, condition)
status = Status.from_termination_condition(termination_condition)
status.legacy_status = condition

def get_solver_solution() -> Solution:
objective = m.LpObjval if model.type == "LP" else m.BestObj

sol = pd.Series({v.name: v.x for v in m.getVars()}, dtype=float)
sol = set_int_index(sol)

try:
dual = pd.Series({v.name: v.pi for v in m.getConstrs()}, dtype=float)
dual = set_int_index(dual)
except coptpy.CoptError:
logger.warning("Dual values of MILP couldn't be parsed")
dual = pd.Series(dtype=float)

return Solution(sol, dual, objective)

solution = safe_get_solution(status, get_solver_solution)
maybe_adjust_objective_sign(solution, model.objective.sense, io_api, "copt")

env.close()

return Result(status, solution, m)


def run_pips(
model,
io_api=None,
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@
"highspy",
"cplex",
"xpress",
"coptpy",
],
},
classifiers=[
Expand Down
1 change: 1 addition & 0 deletions test/test_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@ def test_solver_options(model, solver, io_api):
"scip": {"time_limit": 1},
"xpress": {"maxtime": 1},
"highs": {"time_limit": 1},
"copt": {"TimeLimit": 1},
}
status, condition = model.solve(solver, io_api=io_api, **time_limit_option[solver])
assert status == "ok"
Expand Down
Loading