Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
2 changes: 1 addition & 1 deletion doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Release Notes
=============

.. Upcoming Version

* Add ``mock_solve`` option to model.solve for quick testing without actual solving
* Add support for SOS1 and SOS2 (Special Ordered Sets) constraints via ``Model.add_sos_constraints()`` and ``Model.remove_sos_constraints()``
* Add simplify method to LinearExpression to combine duplicate terms
* Add convenience function to create LinearExpression from constant
Expand Down
42 changes: 42 additions & 0 deletions linopy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1124,6 +1124,7 @@ def solve(
slice_size: int = 2_000_000,
remote: RemoteHandler | OetcHandler = None, # type: ignore
progress: bool | None = None,
mock_solve: bool = False,
**solver_options: Any,
) -> tuple[str, str]:
"""
Expand Down Expand Up @@ -1191,6 +1192,8 @@ def solve(
Whether to show a progress bar of writing the lp file. The default is
None, which means that the progress bar is shown if the model has more
than 10000 variables and constraints.
mock_solve : bool, optional
Whether to run a mock solve. This will skip the actual solving. Variables will be set to have dummy values
**solver_options : kwargs
Options passed to the solver.

Expand All @@ -1200,6 +1203,11 @@ def solve(
Tuple containing the status and termination condition of the
optimization process.
"""
if mock_solve:
return self._mock_solve(
sanitize_zeros=sanitize_zeros, sanitize_infinities=sanitize_infinities
)

# clear cached matrix properties potentially present from previous solve commands
self.matrices.clean_cached_properties()

Expand Down Expand Up @@ -1375,6 +1383,40 @@ def solve(

return result.status.status.value, result.status.termination_condition.value

def _mock_solve(
self,
sanitize_zeros: bool = True,
sanitize_infinities: bool = True,
) -> tuple[str, str]:
solver_name = "mock"

# clear cached matrix properties potentially present from previous solve commands
self.matrices.clean_cached_properties()

logger.info(f" Solve problem using {solver_name.title()} solver")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FabianHofmann There is this line here which will evaluate to " Solve problem using Mock solver" in line with the other solver log statements. Is this sufficient or do you think we need to be more explicit?

# reset result
self.reset_solution()

if sanitize_zeros:
self.constraints.sanitize_zeros()

if sanitize_infinities:
self.constraints.sanitize_infinities()

self.objective._value = 0.0
self.status = "ok"
self.termination_condition = TerminationCondition.optimal.value
self.solver_model = None
self.solver_name = solver_name

for name, var in self.variables.items():
var.solution = xr.DataArray(0.0, var.coords)

for name, con in self.constraints.items():
con.dual = xr.DataArray(0.0, con.labels.coords)

return "ok", "none"

def compute_infeasibilities(self) -> list[int]:
"""
Compute a set of infeasible constraints.
Expand Down
13 changes: 13 additions & 0 deletions test/test_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,19 @@ def test_model_maximization(
assert np.isclose(m.objective.value or 0, 3.3)


def test_mock_solve(model_maximization: Model) -> None:
m = model_maximization
assert m.objective.sense == "max"
assert m.objective.value is None

status, condition = m.solve(solver="some_non_existant_solver", mock_solve=True)
assert status == "ok"
assert m.objective.value == 0
x_solution = m.variables["x"].solution
assert x_solution.coords == m.variables["x"].coords
assert (x_solution == 0).all()


@pytest.mark.parametrize("solver,io_api,explicit_coordinate_names", params)
def test_default_settings_chunked(
model_chunked: Model, solver: str, io_api: str, explicit_coordinate_names: bool
Expand Down