You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Model.solve(): inlined result-to-model mapping logic (normal, remote, and mock paths each have independent mapping)
solver_capabilities.py: global capability lookup
direct conversion entry points live on Model (delegating to linopy/io.py functions)
We want to refactor to better encapsulate needed information.
Changes to Solver Class
The following new attributes:
options: dict[str, Any]
status: Status | None
solution: Solution | None
report: SolverReport | None -> new object containing solver times and other meta data from solving (potentially come up with a different name
solver_model: Any
io_api: str | None
capability: SolverInfo | None
env: gurobipy.Env | None (and other solver envs)
solver_name: str (current)
and the following functions:
to_solver_model(model, explicit_coordinate_names=False, env=None): thin wrapper around existing linopy/io.py conversion functions (model.to_gurobipy(), model.to_highspy(), raises NotImplementedError for solver wo interface
update_solver_model(model, explicit_coordinate_names=False, env=None): synchronize native solver model after linopy model changes. Not implemented now
resolve(sense) -> Result: solve or re-solve self.solver_model. Stores result on the solver instance (self.status, self.solution, self.solver_model) and returns Result. Raises if self.solver_model is None.
(Note currently the Gurobi Env is created inside a contextlib.ExitStack scoped to solve_problem_from_model() — if solver_model persists beyond solve(), the env must also persist)
Assign the Solver instance to the linopy model at model.solver (add "solver" to __slots__), None at initialization
Changes to Result Class
Add solver_name: str to the Result dataclass. Currently Result holds status, solution, and solver_model. Adding solver_name makes Result fully self-describing — no need to pass solver identity out-of-band.
Changes to Model class
Add apply_result(result: Result | None = None) -> tuple[str, str] as proposed in Solver Refactor and Extension #628 but witht the difference when result is None, pull the result from self.solver (reads self.solver.status, self.solver.solution, self.solver.solver_model). This enables a clean post-resolve() flow where the solver already has the result. When result is provided explicitly, use it directly (for remote solve, mock, or detached workflows). Used for status and termination updates, objective assignment, primal and dual value assignments.
Model.solver_model should point to Model.solver.solver_model.
Model.solver_name should point to Model.solver.solver_name.
Currently information about solver state and meta info is split across multiple places:
Model:solver_model,solver_name,status,termination_conditionResult:status,solution,solver_modelSolver: onlysolver_optionsModel.solve(): inlined result-to-model mapping logic (normal, remote, and mock paths each have independent mapping)solver_capabilities.py: global capability lookupModel(delegating tolinopy/io.pyfunctions)We want to refactor to better encapsulate needed information.
Changes to Solver Class
The following new attributes:
options: dict[str, Any]status: Status | Nonesolution: Solution | Nonereport: SolverReport | None-> new object containing solver times and other meta data from solving (potentially come up with a different namesolver_model: Anyio_api: str | Nonecapability: SolverInfo | Noneenv: gurobipy.Env | None(and other solver envs)solver_name: str(current)and the following functions:
to_solver_model(model, explicit_coordinate_names=False, env=None): thin wrapper around existinglinopy/io.pyconversion functions (model.to_gurobipy(),model.to_highspy(), raisesNotImplementedErrorfor solver wo interfaceupdate_solver_model(model, explicit_coordinate_names=False, env=None): synchronize native solver model after linopy model changes. Not implemented nowresolve(sense) -> Result: solve or re-solveself.solver_model. Stores result on the solver instance (self.status,self.solution,self.solver_model) and returnsResult. Raises ifself.solver_model is None.(Note currently the Gurobi
Envis created inside acontextlib.ExitStackscoped tosolve_problem_from_model()— ifsolver_modelpersists beyondsolve(), the env must also persist)Assign the
Solverinstance to the linopy model atmodel.solver(add"solver"to__slots__),Noneat initializationChanges to Result Class
Add
solver_name: strto theResultdataclass. CurrentlyResultholdsstatus,solution, andsolver_model. Addingsolver_namemakesResultfully self-describing — no need to pass solver identity out-of-band.Changes to Model class
Add
apply_result(result: Result | None = None) -> tuple[str, str]as proposed in Solver Refactor and Extension #628 but witht the difference whenresultisNone, pull the result fromself.solver(readsself.solver.status,self.solver.solution,self.solver.solver_model). This enables a clean post-resolve()flow where the solver already has the result. Whenresultis provided explicitly, use it directly (for remote solve, mock, or detached workflows). Used for status and termination updates, objective assignment, primal and dual value assignments.Model.solver_modelshould point toModel.solver.solver_model.Model.solver_nameshould point toModel.solver.solver_name.