diff --git a/doc/release_notes.rst b/doc/release_notes.rst index 6a69bdfc..26cfb3ca 100644 --- a/doc/release_notes.rst +++ b/doc/release_notes.rst @@ -6,7 +6,7 @@ Upcoming Version * Support for warmstart in HiGHS using basis or solution files, including support for writing basis and solution files of a solved model. * Linopy now uses mypy for type checking allowing for a more secure and stable code base. - +* The creation of solution files with gurobi, scip and mindopt is now supported. Version 0.3.11 -------------- diff --git a/linopy/solvers.py b/linopy/solvers.py index e468278c..8d0ef679 100644 --- a/linopy/solvers.py +++ b/linopy/solvers.py @@ -577,6 +577,13 @@ def run_cplex( with contextlib.suppress(cplex.exceptions.errors.CplexSolverError): m.solve() + + if solution_fn is not None: + try: + m.solution.write(path_to_string(solution_fn)) + except cplex.exceptions.errors.CplexSolverError as err: + logger.info("Unable to save solution file. Raised error: %s", err) + condition = m.solution.get_status_string() termination_condition = CONDITION_MAP.get(condition, condition) status = Status.from_termination_condition(termination_condition) @@ -686,6 +693,12 @@ def run_gurobi( except gurobipy.GurobiError as err: logger.info("No model basis stored. Raised error: %s", err) + if solution_fn is not None and solution_fn.suffix == ".sol": + try: + m.write(path_to_string(solution_fn)) + except gurobipy.GurobiError as err: + logger.info("Unable to save solution file. Raised error: %s", err) + condition = m.status termination_condition = CONDITION_MAP.get(condition, condition) status = Status.from_termination_condition(termination_condition) @@ -773,6 +786,12 @@ def run_scip( if basis_fn: logger.warning("Basis not implemented for SCIP") + if solution_fn: + try: + m.writeSol(m.getBestSol(), filename=path_to_string(solution_fn)) + except FileNotFoundError as err: + logger.warning("Unable to save solution file. Raised error: %s", err) + condition = m.getStatus() termination_condition = CONDITION_MAP.get(condition, condition) status = Status.from_termination_condition(termination_condition) @@ -864,6 +883,12 @@ def run_xpress( except Exception as err: logger.info("No model basis stored. Raised error: %s", err) + if solution_fn is not None: + try: + m.writeSolution(path_to_string(solution_fn)) + except Exception as err: + logger.info("Unable to save solution file. Raised error: %s", err) + condition = m.getProbStatusString() termination_condition = CONDITION_MAP.get(condition, condition) status = Status.from_termination_condition(termination_condition) @@ -1164,6 +1189,12 @@ def run_copt( except coptpy.CoptError as err: logger.info("No model basis stored. Raised error: %s", err) + if solution_fn: + try: + m.write(path_to_string(solution_fn)) + except coptpy.CoptError as err: + logger.info("No model solution stored. Raised error: %s", err) + condition = m.LpStatus if model.type in ["LP", "QP"] else m.MipStatus termination_condition = CONDITION_MAP.get(condition, condition) status = Status.from_termination_condition(termination_condition) @@ -1256,6 +1287,12 @@ def run_mindopt( except mindoptpy.MindoptError as err: # type: ignore logger.info("No model basis stored. Raised error: %s", err) + if solution_fn: + try: + m.write(path_to_string(solution_fn)) + except mindoptpy.MindoptError as err: # type: ignore + logger.info("No model solution stored. Raised error: %s", err) + condition = m.status termination_condition = CONDITION_MAP.get(condition, condition) status = Status.from_termination_condition(termination_condition) diff --git a/test/test_optimization.py b/test/test_optimization.py index 2b5124f1..4f39ac2f 100644 --- a/test/test_optimization.py +++ b/test/test_optimization.py @@ -459,12 +459,17 @@ def test_set_files(tmp_path, model, solver, io_api): def test_set_files_and_keep_files(tmp_path, model, solver, io_api): status, condition = model.solve( solver, + io_api=io_api, problem_fn=tmp_path / "problem.lp", solution_fn=tmp_path / "solution.sol", log_fn=tmp_path / "logging.log", keep_files=True, ) assert status == "ok" + if io_api != "direct": + assert (tmp_path / "problem.lp").exists() + assert (tmp_path / "solution.sol").exists() + assert (tmp_path / "logging.log").exists() @pytest.mark.parametrize("solver,io_api", params)