Skip to content

Commit

Permalink
Refactoring of linopy solvers to object oriented architecture (#349)
Browse files Browse the repository at this point in the history
* Refactor solvers.py to object-oriented implementation of Solvers which also changes the call of solvers in model.py

* Adjustments to execution via direct API for highs and gurobi solvers

* add unit tests for solver classes and solving from lp file and direct

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* fix of path_to_str type of Path

* fix of path_to_str type of Path back to before

* read sense and io_api from problem file

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* solve typing issues and move util methods to functions in solvers.py

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* Changed architecture of direct execution and execution from problem file. Also renaming of Solver class solve method to 'solve_problem()'.

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* get rid of unused type: ignore comment in solvers.py

* refactor #349

* fix bug in maybe_adjust_objective_function and adjust docstrings

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* pin coptpy version away from 7.2.1 due to bug

* add pytest for not direct solvers NotImplementedError

---------

Co-authored-by: daniel.rdt <daniel.ruedt@campus.tu-berlin.de>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: lkstrp <lkstrp@pm.me>
  • Loading branch information
4 people authored Oct 18, 2024
1 parent 07be103 commit 03f3cc7
Show file tree
Hide file tree
Showing 5 changed files with 1,852 additions and 996 deletions.
2 changes: 1 addition & 1 deletion linopy/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ class Result:

status: Status
solution: Union[Solution, None] = None
solver_model: Union[Any, None] = None
solver_model: Any = None

def __repr__(self) -> str:
solver_model_string = (
Expand Down
46 changes: 34 additions & 12 deletions linopy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@
)
from linopy.matrices import MatrixAccessor
from linopy.objective import Objective
from linopy.solvers import available_solvers, quadratic_solvers
from linopy.solvers import IO_APIS, available_solvers, quadratic_solvers
from linopy.types import (
ConstantLike,
ConstraintLike,
Expand Down Expand Up @@ -79,6 +79,9 @@ class Model:
the optimization process.
"""

solver_model: Any
solver_name: str

__slots__ = (
# containers
"_variables",
Expand Down Expand Up @@ -1015,6 +1018,12 @@ def solve(
# clear cached matrix properties potentially present from previous solve commands
self.matrices.clean_cached_properties()

# check io_api
if io_api is not None and io_api not in IO_APIS:
raise ValueError(
f"Keyword argument `io_api` has to be one of {IO_APIS} or None"
)

if remote:
solved = remote.solve_on_remote(
self,
Expand Down Expand Up @@ -1075,19 +1084,32 @@ def solve(
)

try:
func = getattr(solvers, f"run_{solver_name}")
result = func(
self,
io_api=io_api,
problem_fn=to_path(problem_fn),
solution_fn=to_path(solution_fn),
log_fn=to_path(log_fn),
warmstart_fn=to_path(warmstart_fn),
basis_fn=to_path(basis_fn),
keep_files=keep_files,
env=env,
solver_class = getattr(solvers, f"{solvers.SolverName(solver_name).name}")
# initialize the solver as object of solver subclass <solver_class>
solver = solver_class(
**solver_options,
)
if io_api == "direct":
# no problem file written and direct model is set for solver
result = solver.solve_problem_from_model(
model=self,
solution_fn=to_path(solution_fn),
log_fn=to_path(log_fn),
warmstart_fn=to_path(warmstart_fn),
basis_fn=to_path(basis_fn),
env=env,
)
else:
problem_fn = self.to_file(to_path(problem_fn), io_api)
result = solver.solve_problem_from_file(
problem_fn=to_path(problem_fn),
solution_fn=to_path(solution_fn),
log_fn=to_path(log_fn),
warmstart_fn=to_path(warmstart_fn),
basis_fn=to_path(basis_fn),
env=env,
)

finally:
for fn in (problem_fn, solution_fn):
if fn is not None and (os.path.exists(fn) and not keep_files):
Expand Down
Loading

0 comments on commit 03f3cc7

Please sign in to comment.