Skip to content

Add useful error message for missing highspy. Allow variable objective #453

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ benchmark/notebooks/.ipynb_checkpoints
benchmark/scripts/__pycache__
benchmark/scripts/benchmarks-pypsa-eur/__pycache__
benchmark/scripts/leftovers/

# IDE
.idea
1 change: 1 addition & 0 deletions doc/release_notes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Upcoming Version
gap tolerance.
* Improve the mapping of termination conditions for the SCIP solver
* Treat GLPK's `integer undefined` status as not-OK
* Allow the objective to be set by a variable instead of an expression

Version 0.5.3
--------------
Expand Down
5 changes: 4 additions & 1 deletion linopy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -683,7 +683,8 @@ def add_constraints(

def add_objective(
self,
expr: LinearExpression
expr: Variable
| LinearExpression
| QuadraticExpression
| Sequence[tuple[ConstantLike, VariableLike]],
overwrite: bool = False,
Expand All @@ -709,6 +710,8 @@ def add_objective(
"Objective already defined."
" Set `overwrite` to True to force overwriting."
)
if isinstance(expr, Variable):
expr = expr * 1.0 # Convert to expression
self.objective.expression = expr # type: ignore[assignment]
self.objective.sense = sense

Expand Down
5 changes: 5 additions & 0 deletions linopy/solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -455,6 +455,11 @@ def solve_problem_from_file(
status.legacy_status = first_line

# Use HiGHS to parse the problem file and find the set of variable names, needed to parse solution
if "highs" not in available_solvers:
raise ModuleNotFoundError(
f"highspy is not installed. Please install it to use {self.solver_name.name} solver."
)

h = highspy.Highs()
h.readModel(path_to_string(problem_fn))
variables = {v.name for v in h.getVariables()}
Expand Down
8 changes: 8 additions & 0 deletions test/test_objective.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ def quadratic_objective() -> Objective:
return m.objective


def test_set_objective_from_variable() -> None:
m = Model()
v = m.add_variables(coords=[[1, 2, 3]])
m.add_objective(v)
assert isinstance(m.objective, Objective)
assert isinstance(m.objective.expression, LinearExpression)


def test_model(linear_objective: Objective, quadratic_objective: Objective) -> None:
assert isinstance(linear_objective.model, Model)
assert isinstance(quadratic_objective.model, Model)
Expand Down
23 changes: 23 additions & 0 deletions test/test_solvers.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@
@author: sid
"""

import importlib
from pathlib import Path

import pytest
from _pytest.monkeypatch import MonkeyPatch

from linopy import solvers

Expand Down Expand Up @@ -64,3 +66,24 @@ def test_free_mps_solution_parsing(solver: str, tmp_path: Path) -> None:

assert result.status.is_ok
assert result.solution.objective == 30.0


def test_highs_missing(monkeypatch: MonkeyPatch) -> None:
monkeypatch.setattr("linopy.solvers.available_solvers", ["cbc"])

import linopy.model

importlib.reload(linopy.model)

from linopy.model import Model

model = Model()
x = model.add_variables(lower=0.0, name="x")
model.add_constraints(x >= 0.0)
model.add_objective(x, sense="min")

with pytest.raises(
ModuleNotFoundError,
match="highspy is not installed. Please install it to use CBC solve",
):
model.solve(solver_name="cbc")
Loading