Skip to content

Commit

Permalink
[pre-commit.ci] auto fixes from pre-commit.com hooks
Browse files Browse the repository at this point in the history
for more information, see https://pre-commit.ci
  • Loading branch information
pre-commit-ci[bot] committed Dec 13, 2024
1 parent 63485af commit 7778c6d
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 44 deletions.
48 changes: 37 additions & 11 deletions linopy/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ def print_coord(coord):

def get_printers(m: Model, for_polars: bool = False, with_names: bool = False):
if with_names:

def print_variable(var):
name, coord = m.variables.get_label_position(var)
name = clean_name(name)
Expand All @@ -82,16 +83,21 @@ def print_constraint(cons):
return f"{name}{print_coord(coord)}#{cons}"

def print_variable_polars(series):
return pl.lit(" "), series.map_elements(print_variable, return_dtype=pl.String)
return pl.lit(" "), series.map_elements(
print_variable, return_dtype=pl.String
)

def print_constraint_polars(series):
return pl.lit(None), series.map_elements(print_constraint, return_dtype=pl.String)
return pl.lit(None), series.map_elements(
print_constraint, return_dtype=pl.String
)

if for_polars:
return print_variable_polars, print_constraint_polars
else:
return print_variable, print_constraint
else:

def print_variable(var):
return f"x{var}"

Expand Down Expand Up @@ -408,7 +414,7 @@ def to_lp_file(
integer_label: str,
slice_size: int = 10_000_000,
progress: bool = True,
printers = None,
printers=None,
) -> None:
batch_size = 5000

Expand Down Expand Up @@ -625,7 +631,9 @@ def integers_to_file_polars(
formatted.write_csv(f, **kwargs)


def constraints_to_file_polars(m, f, progress=False, lazy=False, slice_size=2_000_000, printers=None):
def constraints_to_file_polars(
m, f, progress=False, lazy=False, slice_size=2_000_000, printers=None
):
if not len(m.constraints):
return

Expand Down Expand Up @@ -695,16 +703,22 @@ def to_lp_file_polars(
start = time.time()

objective_to_file_polars(m, f, progress=progress, printers=printers)
constraints_to_file_polars(m, f=f, progress=progress, slice_size=slice_size, printers=printers)
bounds_to_file_polars(m, f=f, progress=progress, slice_size=slice_size, printers=printers)
binaries_to_file_polars(m, f=f, progress=progress, slice_size=slice_size, printers=printers)
constraints_to_file_polars(
m, f=f, progress=progress, slice_size=slice_size, printers=printers
)
bounds_to_file_polars(
m, f=f, progress=progress, slice_size=slice_size, printers=printers
)
binaries_to_file_polars(
m, f=f, progress=progress, slice_size=slice_size, printers=printers
)
integers_to_file_polars(
m,
integer_label=integer_label,
f=f,
progress=progress,
slice_size=slice_size,
printers = printers,
printers=printers,
)
f.write(b"end\n")

Expand Down Expand Up @@ -736,13 +750,25 @@ def to_file(
if progress is None:
progress = m._xCounter > 10_000

printers = get_printers(m, for_polars=io_api=="lp-polars", with_names=with_names)
printers = get_printers(m, for_polars=io_api == "lp-polars", with_names=with_names)

if io_api == "lp":
to_lp_file(m, fn, integer_label, slice_size=slice_size, progress=progress, printers=printers)
to_lp_file(
m,
fn,
integer_label,
slice_size=slice_size,
progress=progress,
printers=printers,
)
elif io_api == "lp-polars":
to_lp_file_polars(
m, fn, integer_label, slice_size=slice_size, progress=progress, printers=printers
m,
fn,
integer_label,
slice_size=slice_size,
progress=progress,
printers=printers,
)

elif io_api == "mps":
Expand Down
4 changes: 3 additions & 1 deletion linopy/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -1137,7 +1137,9 @@ def solve(
)
else:
if solver_name in ["glpk", "cbc"] and with_names:
logger.warning(f"{solver_name} does not support writing names to lp files, disabling it.")
logger.warning(
f"{solver_name} does not support writing names to lp files, disabling it."
)
with_names = False
problem_fn = self.to_file(
to_path(problem_fn),
Expand Down
133 changes: 101 additions & 32 deletions test/test_optimization.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
@author: fabian
"""

import itertools
import logging
import platform
Expand All @@ -30,9 +31,11 @@

params = [
(name, io_api, with_names)
for (name, io_api, with_names) in list(itertools.product(available_solvers, io_apis, with_names))
if "lp" in io_api or with_names == False
]
for (name, io_api, with_names) in list(
itertools.product(available_solvers, io_apis, with_names)
)
if "lp" in io_api or with_names == False
]

direct_solvers = ["gurobi", "highs", "mosek"]
for solver in direct_solvers:
Expand Down Expand Up @@ -376,8 +379,12 @@ def test_default_setting_expression_sol_accessor(model, solver, io_api, with_nam


@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_anonymous_constraint(model, model_anonymous_constraint, solver, io_api, with_names):
status, condition = model_anonymous_constraint.solve(solver, io_api=io_api, with_names=with_names)
def test_anonymous_constraint(
model, model_anonymous_constraint, solver, io_api, with_names
):
status, condition = model_anonymous_constraint.solve(
solver, io_api=io_api, with_names=with_names
)
assert status == "ok"
assert np.isclose(model_anonymous_constraint.objective.value, 3.3)

Expand All @@ -402,15 +409,19 @@ def test_model_maximization(model_maximization, solver, io_api, with_names):

@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_default_settings_chunked(model_chunked, solver, io_api, with_names):
status, condition = model_chunked.solve(solver, io_api=io_api, with_names=with_names)
status, condition = model_chunked.solve(
solver, io_api=io_api, with_names=with_names
)
assert status == "ok"
assert np.isclose(model_chunked.objective.value, 3.3)


@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_default_settings_small_slices(model, solver, io_api, with_names):
assert model.objective.sense == "min"
status, condition = model.solve(solver, io_api=io_api, with_names=with_names, slice_size=2)
status, condition = model.solve(
solver, io_api=io_api, with_names=with_names, slice_size=2
)
assert status == "ok"
assert np.isclose(model.objective.value, 3.3)
assert model.solver_name == solver
Expand All @@ -430,20 +441,30 @@ def test_solver_options(model, solver, io_api, with_names):
"mindopt": {"MaxTime": 1},
"copt": {"TimeLimit": 1},
}
status, condition = model.solve(solver, io_api=io_api, with_names=with_names, **time_limit_option[solver])
status, condition = model.solve(
solver, io_api=io_api, with_names=with_names, **time_limit_option[solver]
)
assert status == "ok"


@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_duplicated_variables(model_with_duplicated_variables, solver, io_api, with_names):
status, condition = model_with_duplicated_variables.solve(solver, io_api=io_api, with_names=with_names)
def test_duplicated_variables(
model_with_duplicated_variables, solver, io_api, with_names
):
status, condition = model_with_duplicated_variables.solve(
solver, io_api=io_api, with_names=with_names
)
assert status == "ok"
assert all(model_with_duplicated_variables.solution["x"] == 5)


@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_non_aligned_variables(model_with_non_aligned_variables, solver, io_api, with_names):
status, condition = model_with_non_aligned_variables.solve(solver, io_api=io_api, with_names=with_names)
def test_non_aligned_variables(
model_with_non_aligned_variables, solver, io_api, with_names
):
status, condition = model_with_non_aligned_variables.solve(
solver, io_api=io_api, with_names=with_names
)
assert status == "ok"
with pytest.warns(UserWarning):
assert model_with_non_aligned_variables.solution["x"][0] == 0
Expand Down Expand Up @@ -509,7 +530,9 @@ def test_infeasible_model(model, solver, io_api, with_names):

@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_model_with_inf(model_with_inf, solver, io_api, with_names):
status, condition = model_with_inf.solve(solver, io_api=io_api, with_names=with_names)
status, condition = model_with_inf.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (model_with_inf.solution.x == 0).all()
assert (model_with_inf.solution.y == 10).all()
Expand All @@ -519,7 +542,9 @@ def test_model_with_inf(model_with_inf, solver, io_api, with_names):
"solver,io_api,with_names", [p for p in params if p[0] not in ["mindopt"]]
)
def test_milp_binary_model(milp_binary_model, solver, io_api, with_names):
status, condition = milp_binary_model.solve(solver, io_api=io_api, with_names=with_names)
status, condition = milp_binary_model.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (
(milp_binary_model.solution.y == 1) | (milp_binary_model.solution.y == 0)
Expand All @@ -530,7 +555,9 @@ def test_milp_binary_model(milp_binary_model, solver, io_api, with_names):
"solver,io_api,with_names", [p for p in params if p[0] not in ["mindopt"]]
)
def test_milp_binary_model_r(milp_binary_model_r, solver, io_api, with_names):
status, condition = milp_binary_model_r.solve(solver, io_api=io_api, with_names=with_names)
status, condition = milp_binary_model_r.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (
(milp_binary_model_r.solution.x == 1) | (milp_binary_model_r.solution.x == 0)
Expand All @@ -553,18 +580,26 @@ def test_milp_model_r(milp_model_r, solver, io_api, with_names):
# MPS format by Highs wrong, see https://github.com/ERGO-Code/HiGHS/issues/1325
# skip it
if io_api != "mps":
status, condition = milp_model_r.solve(solver, io_api=io_api, with_names=with_names)
status, condition = milp_model_r.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert ((milp_model_r.solution.x == 11) | (milp_model_r.solution.y == 0)).all()


@pytest.mark.parametrize(
"solver,io_api,with_names",
[p for p in params if (p[0],p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]],
[
p
for p in params
if (p[0], p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]
],
)
def test_quadratic_model(quadratic_model, solver, io_api, with_names):
if solver in feasible_quadratic_solvers:
status, condition = quadratic_model.solve(solver, io_api=io_api, with_names=with_names)
status, condition = quadratic_model.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (quadratic_model.solution.x.round(3) == 0).all()
assert (quadratic_model.solution.y.round(3) >= 10).all()
Expand All @@ -576,28 +611,44 @@ def test_quadratic_model(quadratic_model, solver, io_api, with_names):

@pytest.mark.parametrize(
"solver,io_api,with_names",
[p for p in params if (p[0],p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]],
[
p
for p in params
if (p[0], p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]
],
)
def test_quadratic_model_cross_terms(quadratic_model_cross_terms, solver, io_api, with_names):
def test_quadratic_model_cross_terms(
quadratic_model_cross_terms, solver, io_api, with_names
):
if solver in feasible_quadratic_solvers:
status, condition = quadratic_model_cross_terms.solve(solver, io_api=io_api, with_names=with_names)
status, condition = quadratic_model_cross_terms.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (quadratic_model_cross_terms.solution.x.round(3) == 1.5).all()
assert (quadratic_model_cross_terms.solution.y.round(3) == 8.5).all()
assert round(quadratic_model_cross_terms.objective.value, 3) == 77.5
else:
with pytest.raises(ValueError):
quadratic_model_cross_terms.solve(solver, io_api=io_api, with_names=with_names)
quadratic_model_cross_terms.solve(
solver, io_api=io_api, with_names=with_names
)


@pytest.mark.parametrize(
"solver,io_api,with_names",
[p for p in params if (p[0],p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]],
[
p
for p in params
if (p[0], p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]
],
)
def test_quadratic_model_wo_constraint(quadratic_model, solver, io_api, with_names):
quadratic_model.constraints.remove("con0")
if solver in feasible_quadratic_solvers:
status, condition = quadratic_model.solve(solver, io_api=io_api, with_names=with_names)
status, condition = quadratic_model.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (quadratic_model.solution.x.round(3) == 0).all()
assert round(quadratic_model.objective.value, 3) == 0
Expand All @@ -608,22 +659,34 @@ def test_quadratic_model_wo_constraint(quadratic_model, solver, io_api, with_nam

@pytest.mark.parametrize(
"solver,io_api,with_names",
[p for p in params if (p[0],p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]],
[
p
for p in params
if (p[0], p[1]) not in [("mindopt", "lp"), ("mindopt", "lp-polars")]
],
)
def test_quadratic_model_unbounded(quadratic_model_unbounded, solver, io_api, with_names):
def test_quadratic_model_unbounded(
quadratic_model_unbounded, solver, io_api, with_names
):
if solver in feasible_quadratic_solvers:
status, condition = quadratic_model_unbounded.solve(solver, io_api=io_api, with_names=with_names)
status, condition = quadratic_model_unbounded.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition in ["unbounded", "unknown", "infeasible_or_unbounded"]
else:
with pytest.raises(ValueError):
quadratic_model_unbounded.solve(solver, io_api=io_api, with_names=with_names)
quadratic_model_unbounded.solve(
solver, io_api=io_api, with_names=with_names
)


@pytest.mark.parametrize(
"solver,io_api,with_names", [p for p in params if p[0] not in ["mindopt"]]
)
def test_modified_model(modified_model, solver, io_api, with_names):
status, condition = modified_model.solve(solver, io_api=io_api, with_names=with_names)
status, condition = modified_model.solve(
solver, io_api=io_api, with_names=with_names
)
assert condition == "optimal"
assert (modified_model.solution.x == 0).all()
assert (modified_model.solution.y == 10).all()
Expand Down Expand Up @@ -657,9 +720,13 @@ def test_basis_and_warmstart(tmp_path, model, solver, io_api, with_names):


@pytest.mark.parametrize("solver,io_api,with_names", params)
def test_solution_fn_parent_dir_doesnt_exist(model, solver, io_api, with_names, tmp_path):
def test_solution_fn_parent_dir_doesnt_exist(
model, solver, io_api, with_names, tmp_path
):
solution_fn = tmp_path / "non_existent_dir" / "non_existent_file"
status, condition = model.solve(solver, io_api=io_api, with_names=with_names, solution_fn=solution_fn)
status, condition = model.solve(
solver, io_api=io_api, with_names=with_names, solution_fn=solution_fn
)
assert status == "ok"


Expand Down Expand Up @@ -697,7 +764,9 @@ def test_model_resolve(model, solver, io_api, with_names):
assert np.isclose(model.objective.value, 5.25)


@pytest.mark.parametrize("solver,io_api,with_names", [p for p in params if "direct" not in p])
@pytest.mark.parametrize(
"solver,io_api,with_names", [p for p in params if "direct" not in p]
)
def test_solver_classes_from_problem_file(model, solver, io_api, with_names):
# first test initialization of super class. Should not be possible to initialize
with pytest.raises(TypeError):
Expand Down

0 comments on commit 7778c6d

Please sign in to comment.