Comments (4)
Oh, I like this idea! Some thoughts (hopefully later more):
- I thought about having a
Solver
class as a skeleton which is then inherited by the individual solvers, like
class Solver:
def __init__(self, **kwargs):
....
def solve(self):
raise NotImplementedError
class SolverA(Solver):
....
def solve(self):
# Implement the solve method for SolverA
pass
class SolverB(Solver):
....
def solve(self):
# Implement the solve method for SolverB
pass
- I would be in favor of moving the
assign_solution
to the Model class, following a composition approach (ie the model pull from the solution)
from linopy.
Hey @dannyopts, you are totally right. I find this is indeed a missing feature in linopy. It would not require too much work, but I am a little bit constrained atm. The solve function would be needed to be subdivided, ideally converted into classes "Solver" which has underlying functions like, "get_solution", "assign_parameters", etc. If you want to propose something, you are more than welcome, otherwise this will lay on my todo list for some time still.
from linopy.
I will take a look and see how I get on
from linopy.
@FabianHofmann I am thinking something like:
We replace the f"run_{solver_name}" function with an abstract Solver class which is implemented for each solver.
Roughly I see this looking something like this:
class Solver:
"""A persistent instance of a solver model which can be updated and resolved
Concrete versions of this class replace f"run_{solver_name}"
"""
def __init__(self, model, sanitize_zeros=True, problem_fn=None, log_fn=None, **solver_options):
self.sanitize_zeros = sanitize_zeros
self.problem_fn = problem_fn
self.log_fn = log_fn
self.solver_model = self.create_solver_model(model)
self.solver_options = solver_options
@staticmethod
def get_solver(solver_name, **kwargs):
if solver_name == "highs":
return HighsSolver(**kwargs)
if solver_name == "gurobi":
return GurobiSolver(**kwargs)
...
def run(self) -> SolverStatus:
"""Solver the solver_model"""
raise NotImplementedError()
def compute_IIS(self):
raise NotImplementedError()
def update_cost_coeffs(self, c) -> None:
"""Set the cost vector on the solver_model"""
raise NotImplementedError()
def update_constraint(self, constraint_name: str, values: xr.DataArray) -> None:
"""Set the cost vector on the solver_model"""
pass
def update_rhs(self, b) -> None:
"""Set the b vector"""
pass
def get_result(self) -> Result:
pass
def close(self) -> None:
"""Free any resources (eg release the license if using gurobi)""""
pass
(possibly making it a context manager would be a nice touch?)
In the result class we would then add the responsibility of writing the result back to the model:
@dataclass
class Result:
...
def update_model(self, model):
model.objective._value = self.solution.objective
model.status = self.status.status.value
model.termination_condition = self.status.termination_condition.value
model.solver_model = self.solver_model
if not self.status.is_ok:
return
self.assign_solution(self, model)
self.assign_dual(self, model)
def assign_solution(self, model):
# map solution and dual to original shape which includes missing values
sol = self.solution.primal.copy()
sol.loc[-1] = nan
for name, var in model.variables.items():
idx = np.ravel(var.labels)
try:
vals = sol[idx].values.reshape(var.labels.shape)
except KeyError:
vals = sol.reindex(idx).values.reshape(var.labels.shape)
var.solution = xr.DataArray(vals, var.coords)
def assign_dual(self, model):
# map solution and dual to original shape which includes missing values
if not self.solution.dual.empty:
dual = self.solution.dual.copy()
dual.loc[-1] = nan
for name, con in model.constraints.items():
idx = np.ravel(con.labels)
try:
vals = dual[idx].values.reshape(con.labels.shape)
except KeyError:
vals = dual.reindex(idx).values.reshape(con.labels.shape)
con.dual = xr.DataArray(vals, con.labels.coords)
And in the Model class solve would become something like:
def solve(
self,
...
):
if remote:
...
return self.status, self.termination_condition
solver = Solver.get_solver(
solver_name=solver_name,
io_api=io_api,
env=env,
sanitize_zeros=sanitize_zeros,
problem_fn=problem_fn,
solution_fn=solution_fn,
log_fn=log_fn,
basis_fn=basis_fn,
warmstart_fn=warmstart_fn,
**solver_options
)
with solver:
result = solver.run()
result.info()
result.update_model(self)
return result
But a user could also create a Solver instance outside of solve and have a persistent handle to the same underlying model instance and update at their leisure.
Note: this would also resolve the issue which was mentioned in #192 about using a remote gurobi server and wanting to compute an IIS.
Does this sound like a reasonable path forward @FabianHofmann? Any feedback?
from linopy.
Related Issues (20)
- [Feature] Support early stopping for solving such as allowing a relative optimality gap HOT 1
- Add solution values for linear expressions HOT 3
- Enable broadcasting when multiplying with pandas objects HOT 1
- How to add multi-objectives? HOT 6
- Support constant values in objective HOT 1
- Indicate applied masking in string representation of constraints HOT 3
- Change `dims` keyword of sum functions to `dim` HOT 1
- Support for sparse xarrays HOT 23
- show benchmark in readme
- Missing dimension when initializing variable with boundaries of lower dimensions HOT 2
- Improve documentation on overriding coordinates feature HOT 8
- Inconsistent coordinate override in arithmetic expressions HOT 3
- Docstring of `Model.solve()` has wrong return type
- `io_api="mps"` leads to memory overhead
- Error using scip
- Licensed CPLEX not detected HOT 3
- MPS IO changes with highspy >= 1.7
- Re-solving a changed model with "direct" api results in wrong model being solved HOT 1
- add_constraints lhs eats constant HOT 2
- Explore PytOptInferface for writing matrix over native C API HOT 9
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from linopy.