Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
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