mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Fix mypy errors
This commit is contained in:
@@ -21,7 +21,7 @@ class BasicCollector:
|
|||||||
n_jobs: int = 1,
|
n_jobs: int = 1,
|
||||||
progress: bool = False,
|
progress: bool = False,
|
||||||
) -> None:
|
) -> None:
|
||||||
def _collect(data_filename):
|
def _collect(data_filename: str) -> None:
|
||||||
h5_filename = _to_h5_filename(data_filename)
|
h5_filename = _to_h5_filename(data_filename)
|
||||||
mps_filename = h5_filename.replace(".h5", ".mps")
|
mps_filename = h5_filename.replace(".h5", ".mps")
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ class AlvLouWeh2017Extractor(FeaturesExtractor):
|
|||||||
self.with_m3 = with_m3
|
self.with_m3 = with_m3
|
||||||
|
|
||||||
def get_instance_features(self, h5: H5File) -> np.ndarray:
|
def get_instance_features(self, h5: H5File) -> np.ndarray:
|
||||||
raise NotImplemented()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def get_var_features(self, h5: H5File) -> np.ndarray:
|
def get_var_features(self, h5: H5File) -> np.ndarray:
|
||||||
"""
|
"""
|
||||||
@@ -197,7 +197,7 @@ class AlvLouWeh2017Extractor(FeaturesExtractor):
|
|||||||
return features
|
return features
|
||||||
|
|
||||||
def get_constr_features(self, h5: H5File) -> np.ndarray:
|
def get_constr_features(self, h5: H5File) -> np.ndarray:
|
||||||
raise NotImplemented()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
|
||||||
def _fix_infinity(m: Optional[np.ndarray]) -> None:
|
def _fix_infinity(m: Optional[np.ndarray]) -> None:
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ class H5FieldsExtractor(FeaturesExtractor):
|
|||||||
data = h5.get_scalar(field)
|
data = h5.get_scalar(field)
|
||||||
assert data is not None
|
assert data is not None
|
||||||
x.append(data)
|
x.append(data)
|
||||||
x = np.hstack(x)
|
x_np = np.hstack(x)
|
||||||
assert len(x.shape) == 1
|
assert len(x_np.shape) == 1
|
||||||
return x
|
return x_np
|
||||||
|
|
||||||
def get_var_features(self, h5: H5File) -> np.ndarray:
|
def get_var_features(self, h5: H5File) -> np.ndarray:
|
||||||
var_types = h5.get_array("static_var_types")
|
var_types = h5.get_array("static_var_types")
|
||||||
@@ -51,13 +51,14 @@ class H5FieldsExtractor(FeaturesExtractor):
|
|||||||
raise Exception("No constr fields provided")
|
raise Exception("No constr fields provided")
|
||||||
return self._extract(h5, self.constr_fields, n_constr)
|
return self._extract(h5, self.constr_fields, n_constr)
|
||||||
|
|
||||||
def _extract(self, h5, fields, n_expected):
|
def _extract(self, h5: H5File, fields: List[str], n_expected: int) -> np.ndarray:
|
||||||
x = []
|
x = []
|
||||||
for field in fields:
|
for field in fields:
|
||||||
try:
|
try:
|
||||||
data = h5.get_array(field)
|
data = h5.get_array(field)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
v = h5.get_scalar(field)
|
v = h5.get_scalar(field)
|
||||||
|
assert v is not None
|
||||||
data = np.repeat(v, n_expected)
|
data = np.repeat(v, n_expected)
|
||||||
assert data is not None
|
assert data is not None
|
||||||
assert len(data.shape) == 1
|
assert len(data.shape) == 1
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ class H5File:
|
|||||||
), f"bytes expected; found: {value.__class__}" # type: ignore
|
), f"bytes expected; found: {value.__class__}" # type: ignore
|
||||||
self.put_array(key, np.frombuffer(value, dtype="uint8"))
|
self.put_array(key, np.frombuffer(value, dtype="uint8"))
|
||||||
|
|
||||||
def close(self):
|
def close(self) -> None:
|
||||||
self.file.close()
|
self.file.close()
|
||||||
|
|
||||||
def __enter__(self) -> "H5File":
|
def __enter__(self) -> "H5File":
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ def build_setcover_model_gurobipy(data: Union[str, SetCoverData]) -> GurobiModel
|
|||||||
|
|
||||||
def build_setcover_model_pyomo(
|
def build_setcover_model_pyomo(
|
||||||
data: Union[str, SetCoverData],
|
data: Union[str, SetCoverData],
|
||||||
solver="gurobi_persistent",
|
solver: str = "gurobi_persistent",
|
||||||
) -> PyomoModel:
|
) -> PyomoModel:
|
||||||
data = _read_setcover_data(data)
|
data = _read_setcover_data(data)
|
||||||
(n_elements, n_sets) = data.incidence_matrix.shape
|
(n_elements, n_sets) = data.incidence_matrix.shape
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ def build_stab_model_gurobipy(data: MaxWeightStableSetData) -> GurobiModel:
|
|||||||
|
|
||||||
def build_stab_model_pyomo(
|
def build_stab_model_pyomo(
|
||||||
data: MaxWeightStableSetData,
|
data: MaxWeightStableSetData,
|
||||||
solver="gurobi_persistent",
|
solver: str = "gurobi_persistent",
|
||||||
) -> PyomoModel:
|
) -> PyomoModel:
|
||||||
data = _read_stab_data(data)
|
data = _read_stab_data(data)
|
||||||
model = pe.ConcreteModel()
|
model = pe.ConcreteModel()
|
||||||
|
|||||||
@@ -9,9 +9,10 @@ import numpy as np
|
|||||||
from scipy.sparse import lil_matrix
|
from scipy.sparse import lil_matrix
|
||||||
|
|
||||||
from miplearn.h5 import H5File
|
from miplearn.h5 import H5File
|
||||||
|
from miplearn.solvers.abstract import AbstractModel
|
||||||
|
|
||||||
|
|
||||||
class GurobiModel:
|
class GurobiModel(AbstractModel):
|
||||||
_supports_basis_status = True
|
_supports_basis_status = True
|
||||||
_supports_sensitivity_analysis = True
|
_supports_sensitivity_analysis = True
|
||||||
_supports_node_count = True
|
_supports_node_count = True
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
from os.path import exists
|
from os.path import exists
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
from typing import List, Any, Union
|
from typing import List, Any, Union, Dict, Callable, Optional
|
||||||
|
|
||||||
from miplearn.h5 import H5File
|
from miplearn.h5 import H5File
|
||||||
from miplearn.io import _to_h5_filename
|
from miplearn.io import _to_h5_filename
|
||||||
@@ -11,23 +11,28 @@ from miplearn.solvers.abstract import AbstractModel
|
|||||||
|
|
||||||
|
|
||||||
class LearningSolver:
|
class LearningSolver:
|
||||||
def __init__(self, components: List[Any], skip_lp=False):
|
def __init__(self, components: List[Any], skip_lp: bool = False) -> None:
|
||||||
self.components = components
|
self.components = components
|
||||||
self.skip_lp = skip_lp
|
self.skip_lp = skip_lp
|
||||||
|
|
||||||
def fit(self, data_filenames):
|
def fit(self, data_filenames: List[str]) -> None:
|
||||||
h5_filenames = [_to_h5_filename(f) for f in data_filenames]
|
h5_filenames = [_to_h5_filename(f) for f in data_filenames]
|
||||||
for comp in self.components:
|
for comp in self.components:
|
||||||
comp.fit(h5_filenames)
|
comp.fit(h5_filenames)
|
||||||
|
|
||||||
def optimize(self, model: Union[str, AbstractModel], build_model=None):
|
def optimize(
|
||||||
|
self,
|
||||||
|
model: Union[str, AbstractModel],
|
||||||
|
build_model: Optional[Callable] = None,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
if isinstance(model, str):
|
if isinstance(model, str):
|
||||||
h5_filename = _to_h5_filename(model)
|
h5_filename = _to_h5_filename(model)
|
||||||
assert build_model is not None
|
assert build_model is not None
|
||||||
model = build_model(model)
|
model = build_model(model)
|
||||||
|
assert isinstance(model, AbstractModel)
|
||||||
else:
|
else:
|
||||||
h5_filename = NamedTemporaryFile().name
|
h5_filename = NamedTemporaryFile().name
|
||||||
stats = {}
|
stats: Dict[str, Any] = {}
|
||||||
mode = "r+" if exists(h5_filename) else "w"
|
mode = "r+" if exists(h5_filename) else "w"
|
||||||
with H5File(h5_filename, mode) as h5:
|
with H5File(h5_filename, mode) as h5:
|
||||||
model.extract_after_load(h5)
|
model.extract_after_load(h5)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
|
# Copyright (C) 2020-2022, UChicago Argonne, LLC. All rights reserved.
|
||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
from numbers import Number
|
from numbers import Number
|
||||||
from typing import Optional, Dict, List, Any
|
from typing import Optional, Dict, List, Any, Tuple, Union
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pyomo
|
import pyomo
|
||||||
@@ -24,7 +24,7 @@ class PyomoModel(AbstractModel):
|
|||||||
self.is_persistent = hasattr(self.solver, "set_instance")
|
self.is_persistent = hasattr(self.solver, "set_instance")
|
||||||
if self.is_persistent:
|
if self.is_persistent:
|
||||||
self.solver.set_instance(model)
|
self.solver.set_instance(model)
|
||||||
self.results = None
|
self.results: Optional[Dict] = None
|
||||||
self._is_warm_start_available = False
|
self._is_warm_start_available = False
|
||||||
if not hasattr(self.inner, "dual"):
|
if not hasattr(self.inner, "dual"):
|
||||||
self.inner.dual = Suffix(direction=Suffix.IMPORT)
|
self.inner.dual = Suffix(direction=Suffix.IMPORT)
|
||||||
@@ -56,7 +56,7 @@ class PyomoModel(AbstractModel):
|
|||||||
raise Exception(f"Unknown sense: {sense}")
|
raise Exception(f"Unknown sense: {sense}")
|
||||||
self.solver.add_constraint(eq)
|
self.solver.add_constraint(eq)
|
||||||
|
|
||||||
def _var_names_to_vars(self, var_names):
|
def _var_names_to_vars(self, var_names: np.ndarray) -> List[Any]:
|
||||||
varname_to_var = {}
|
varname_to_var = {}
|
||||||
for var in self.inner.component_objects(Var):
|
for var in self.inner.component_objects(Var):
|
||||||
for idx in var:
|
for idx in var:
|
||||||
@@ -70,12 +70,14 @@ class PyomoModel(AbstractModel):
|
|||||||
h5.put_scalar("static_sense", self._get_sense())
|
h5.put_scalar("static_sense", self._get_sense())
|
||||||
|
|
||||||
def extract_after_lp(self, h5: H5File) -> None:
|
def extract_after_lp(self, h5: H5File) -> None:
|
||||||
|
assert self.results is not None
|
||||||
self._extract_after_lp_vars(h5)
|
self._extract_after_lp_vars(h5)
|
||||||
self._extract_after_lp_constrs(h5)
|
self._extract_after_lp_constrs(h5)
|
||||||
h5.put_scalar("lp_obj_value", self.results["Problem"][0]["Lower bound"])
|
h5.put_scalar("lp_obj_value", self.results["Problem"][0]["Lower bound"])
|
||||||
h5.put_scalar("lp_wallclock_time", self._get_runtime())
|
h5.put_scalar("lp_wallclock_time", self._get_runtime())
|
||||||
|
|
||||||
def _get_runtime(self):
|
def _get_runtime(self) -> float:
|
||||||
|
assert self.results is not None
|
||||||
solver_dict = self.results["Solver"][0]
|
solver_dict = self.results["Solver"][0]
|
||||||
for key in ["Wallclock time", "User time"]:
|
for key in ["Wallclock time", "User time"]:
|
||||||
if isinstance(solver_dict[key], Number):
|
if isinstance(solver_dict[key], Number):
|
||||||
@@ -83,6 +85,7 @@ class PyomoModel(AbstractModel):
|
|||||||
raise Exception("Time unavailable")
|
raise Exception("Time unavailable")
|
||||||
|
|
||||||
def extract_after_mip(self, h5: H5File) -> None:
|
def extract_after_mip(self, h5: H5File) -> None:
|
||||||
|
assert self.results is not None
|
||||||
h5.put_scalar("mip_wallclock_time", self._get_runtime())
|
h5.put_scalar("mip_wallclock_time", self._get_runtime())
|
||||||
if self.results["Solver"][0]["Termination condition"] == "infeasible":
|
if self.results["Solver"][0]["Termination condition"] == "infeasible":
|
||||||
return
|
return
|
||||||
@@ -150,7 +153,7 @@ class PyomoModel(AbstractModel):
|
|||||||
var.value = val
|
var.value = val
|
||||||
self._is_warm_start_available = True
|
self._is_warm_start_available = True
|
||||||
|
|
||||||
def _extract_after_load_vars(self, h5):
|
def _extract_after_load_vars(self, h5: H5File) -> None:
|
||||||
names: List[str] = []
|
names: List[str] = []
|
||||||
types: List[str] = []
|
types: List[str] = []
|
||||||
upper_bounds: List[float] = []
|
upper_bounds: List[float] = []
|
||||||
@@ -211,7 +214,7 @@ class PyomoModel(AbstractModel):
|
|||||||
h5.put_array("static_var_obj_coeffs", np.array(obj_coeffs))
|
h5.put_array("static_var_obj_coeffs", np.array(obj_coeffs))
|
||||||
h5.put_scalar("static_obj_offset", obj_offset)
|
h5.put_scalar("static_obj_offset", obj_offset)
|
||||||
|
|
||||||
def _extract_after_load_constrs(self, h5):
|
def _extract_after_load_constrs(self, h5: H5File) -> None:
|
||||||
names: List[str] = []
|
names: List[str] = []
|
||||||
rhs: List[float] = []
|
rhs: List[float] = []
|
||||||
senses: List[str] = []
|
senses: List[str] = []
|
||||||
@@ -219,7 +222,7 @@ class PyomoModel(AbstractModel):
|
|||||||
lhs_col: List[int] = []
|
lhs_col: List[int] = []
|
||||||
lhs_data: List[float] = []
|
lhs_data: List[float] = []
|
||||||
|
|
||||||
varname_to_idx = {}
|
varname_to_idx: Dict[str, int] = {}
|
||||||
for var in self.inner.component_objects(Var):
|
for var in self.inner.component_objects(Var):
|
||||||
for idx in var:
|
for idx in var:
|
||||||
varname = var.name
|
varname = var.name
|
||||||
@@ -285,7 +288,7 @@ class PyomoModel(AbstractModel):
|
|||||||
h5.put_array("static_constr_rhs", np.array(rhs))
|
h5.put_array("static_constr_rhs", np.array(rhs))
|
||||||
h5.put_array("static_constr_sense", np.array(senses, dtype="S"))
|
h5.put_array("static_constr_sense", np.array(senses, dtype="S"))
|
||||||
|
|
||||||
def _extract_after_lp_vars(self, h5):
|
def _extract_after_lp_vars(self, h5: H5File) -> None:
|
||||||
rc = []
|
rc = []
|
||||||
values = []
|
values = []
|
||||||
for var in self.inner.component_objects(Var):
|
for var in self.inner.component_objects(Var):
|
||||||
@@ -296,7 +299,7 @@ class PyomoModel(AbstractModel):
|
|||||||
h5.put_array("lp_var_reduced_costs", np.array(rc))
|
h5.put_array("lp_var_reduced_costs", np.array(rc))
|
||||||
h5.put_array("lp_var_values", np.array(values))
|
h5.put_array("lp_var_values", np.array(values))
|
||||||
|
|
||||||
def _extract_after_lp_constrs(self, h5):
|
def _extract_after_lp_constrs(self, h5: H5File) -> None:
|
||||||
dual = []
|
dual = []
|
||||||
slacks = []
|
slacks = []
|
||||||
for constr in self.inner.component_objects(pyomo.core.Constraint):
|
for constr in self.inner.component_objects(pyomo.core.Constraint):
|
||||||
@@ -307,7 +310,7 @@ class PyomoModel(AbstractModel):
|
|||||||
h5.put_array("lp_constr_dual_values", np.array(dual))
|
h5.put_array("lp_constr_dual_values", np.array(dual))
|
||||||
h5.put_array("lp_constr_slacks", np.array(slacks))
|
h5.put_array("lp_constr_slacks", np.array(slacks))
|
||||||
|
|
||||||
def _extract_after_mip_vars(self, h5):
|
def _extract_after_mip_vars(self, h5: H5File) -> None:
|
||||||
values = []
|
values = []
|
||||||
for var in self.inner.component_objects(Var):
|
for var in self.inner.component_objects(Var):
|
||||||
for idx in var:
|
for idx in var:
|
||||||
@@ -315,7 +318,7 @@ class PyomoModel(AbstractModel):
|
|||||||
values.append(v.value)
|
values.append(v.value)
|
||||||
h5.put_array("mip_var_values", np.array(values))
|
h5.put_array("mip_var_values", np.array(values))
|
||||||
|
|
||||||
def _extract_after_mip_constrs(self, h5):
|
def _extract_after_mip_constrs(self, h5: H5File) -> None:
|
||||||
slacks = []
|
slacks = []
|
||||||
for constr in self.inner.component_objects(pyomo.core.Constraint):
|
for constr in self.inner.component_objects(pyomo.core.Constraint):
|
||||||
for idx in constr:
|
for idx in constr:
|
||||||
@@ -323,7 +326,7 @@ class PyomoModel(AbstractModel):
|
|||||||
slacks.append(abs(self.inner.slack[c]))
|
slacks.append(abs(self.inner.slack[c]))
|
||||||
h5.put_array("mip_constr_slacks", np.array(slacks))
|
h5.put_array("mip_constr_slacks", np.array(slacks))
|
||||||
|
|
||||||
def _parse_pyomo_expr(self, expr: Any):
|
def _parse_pyomo_expr(self, expr: Any) -> Tuple[Dict[str, float], float]:
|
||||||
lhs = {}
|
lhs = {}
|
||||||
offset = 0.0
|
offset = 0.0
|
||||||
if isinstance(expr, SumExpression):
|
if isinstance(expr, SumExpression):
|
||||||
@@ -332,7 +335,7 @@ class PyomoModel(AbstractModel):
|
|||||||
lhs[term._args_[1].name] = float(term._args_[0])
|
lhs[term._args_[1].name] = float(term._args_[0])
|
||||||
elif isinstance(term, _GeneralVarData):
|
elif isinstance(term, _GeneralVarData):
|
||||||
lhs[term.name] = 1.0
|
lhs[term.name] = 1.0
|
||||||
elif isinstance(term, Number):
|
elif isinstance(term, float):
|
||||||
offset += term
|
offset += term
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown term type: {term.__class__.__name__}")
|
raise Exception(f"Unknown term type: {term.__class__.__name__}")
|
||||||
@@ -342,7 +345,7 @@ class PyomoModel(AbstractModel):
|
|||||||
raise Exception(f"Unknown expression type: {expr.__class__.__name__}")
|
raise Exception(f"Unknown expression type: {expr.__class__.__name__}")
|
||||||
return lhs, offset
|
return lhs, offset
|
||||||
|
|
||||||
def _gap(self, zp, zd, tol=1e-6):
|
def _gap(self, zp: float, zd: float, tol: float = 1e-6) -> float:
|
||||||
# Reference: https://www.gurobi.com/documentation/9.5/refman/mipgap2.html
|
# Reference: https://www.gurobi.com/documentation/9.5/refman/mipgap2.html
|
||||||
if abs(zp) < tol:
|
if abs(zp) < tol:
|
||||||
if abs(zd) < tol:
|
if abs(zd) < tol:
|
||||||
@@ -352,7 +355,7 @@ class PyomoModel(AbstractModel):
|
|||||||
else:
|
else:
|
||||||
return abs(zp - zd) / abs(zp)
|
return abs(zp - zd) / abs(zp)
|
||||||
|
|
||||||
def _get_sense(self):
|
def _get_sense(self) -> str:
|
||||||
for obj in self.inner.component_objects(Objective):
|
for obj in self.inner.component_objects(Objective):
|
||||||
sense = obj.sense
|
sense = obj.sense
|
||||||
if sense == pyomo.core.kernel.objective.minimize:
|
if sense == pyomo.core.kernel.objective.minimize:
|
||||||
@@ -361,6 +364,7 @@ class PyomoModel(AbstractModel):
|
|||||||
return "max"
|
return "max"
|
||||||
else:
|
else:
|
||||||
raise Exception(f"Unknown sense: ${sense}")
|
raise Exception(f"Unknown sense: ${sense}")
|
||||||
|
raise Exception(f"No objective")
|
||||||
|
|
||||||
def write(self, filename: str) -> None:
|
def write(self, filename: str) -> None:
|
||||||
self.inner.write(filename, io_options={"symbolic_solver_labels": True})
|
self.inner.write(filename, io_options={"symbolic_solver_labels": True})
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from miplearn.problems.setcover import (
|
|||||||
SetCoverGenerator,
|
SetCoverGenerator,
|
||||||
build_setcover_model_pyomo,
|
build_setcover_model_pyomo,
|
||||||
)
|
)
|
||||||
|
from miplearn.solvers.abstract import AbstractModel
|
||||||
|
|
||||||
|
|
||||||
def test_set_cover_generator() -> None:
|
def test_set_cover_generator() -> None:
|
||||||
@@ -84,6 +85,7 @@ def test_set_cover() -> None:
|
|||||||
build_setcover_model_pyomo(data),
|
build_setcover_model_pyomo(data),
|
||||||
build_setcover_model_gurobipy(data),
|
build_setcover_model_gurobipy(data),
|
||||||
]:
|
]:
|
||||||
|
assert isinstance(model, AbstractModel)
|
||||||
with NamedTemporaryFile() as tempfile:
|
with NamedTemporaryFile() as tempfile:
|
||||||
with H5File(tempfile.name) as h5:
|
with H5File(tempfile.name) as h5:
|
||||||
model.optimize()
|
model.optimize()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from miplearn.problems.stab import (
|
|||||||
build_stab_model_pyomo,
|
build_stab_model_pyomo,
|
||||||
build_stab_model_gurobipy,
|
build_stab_model_gurobipy,
|
||||||
)
|
)
|
||||||
|
from miplearn.solvers.abstract import AbstractModel
|
||||||
|
|
||||||
|
|
||||||
def test_stab() -> None:
|
def test_stab() -> None:
|
||||||
@@ -23,6 +24,7 @@ def test_stab() -> None:
|
|||||||
build_stab_model_pyomo(data),
|
build_stab_model_pyomo(data),
|
||||||
build_stab_model_gurobipy(data),
|
build_stab_model_gurobipy(data),
|
||||||
]:
|
]:
|
||||||
|
assert isinstance(model, AbstractModel)
|
||||||
with NamedTemporaryFile() as tempfile:
|
with NamedTemporaryFile() as tempfile:
|
||||||
with H5File(tempfile.name) as h5:
|
with H5File(tempfile.name) as h5:
|
||||||
model.optimize()
|
model.optimize()
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
# Released under the modified BSD license. See COPYING.md for more details.
|
# Released under the modified BSD license. See COPYING.md for more details.
|
||||||
|
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
from typing import Callable, Any
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
import pytest
|
import pytest
|
||||||
@@ -40,28 +41,28 @@ def test_pyomo_persistent(data: SetCoverData) -> None:
|
|||||||
_test_solver(lambda d: build_setcover_model_pyomo(d, "gurobi_persistent"), data)
|
_test_solver(lambda d: build_setcover_model_pyomo(d, "gurobi_persistent"), data)
|
||||||
|
|
||||||
|
|
||||||
def _test_solver(build_model, data):
|
def _test_solver(build_model: Callable, data: Any) -> None:
|
||||||
_test_extract(build_model(data))
|
_test_extract(build_model(data))
|
||||||
_test_add_constr(build_model(data))
|
_test_add_constr(build_model(data))
|
||||||
_test_fix_vars(build_model(data))
|
_test_fix_vars(build_model(data))
|
||||||
_test_infeasible(build_model(data))
|
_test_infeasible(build_model(data))
|
||||||
|
|
||||||
|
|
||||||
def _test_extract(model):
|
def _test_extract(model: AbstractModel) -> None:
|
||||||
with NamedTemporaryFile() as tempfile:
|
with NamedTemporaryFile() as tempfile:
|
||||||
with H5File(tempfile.name) as h5:
|
with H5File(tempfile.name) as h5:
|
||||||
|
|
||||||
def test_scalar(key, expected_value):
|
def test_scalar(key: str, expected_value: Any) -> None:
|
||||||
actual_value = h5.get_scalar(key)
|
actual_value = h5.get_scalar(key)
|
||||||
assert actual_value is not None
|
assert actual_value is not None
|
||||||
assert actual_value == expected_value
|
assert actual_value == expected_value
|
||||||
|
|
||||||
def test_array(key, expected_value):
|
def test_array(key: str, expected_value: Any) -> None:
|
||||||
actual_value = h5.get_array(key)
|
actual_value = h5.get_array(key)
|
||||||
assert actual_value is not None
|
assert actual_value is not None
|
||||||
assert actual_value.tolist() == expected_value
|
assert actual_value.tolist() == expected_value
|
||||||
|
|
||||||
def test_sparse(key, expected_value):
|
def test_sparse(key: str, expected_value: Any) -> None:
|
||||||
actual_value = h5.get_sparse(key)
|
actual_value = h5.get_sparse(key)
|
||||||
assert actual_value is not None
|
assert actual_value is not None
|
||||||
assert actual_value.todense().tolist() == expected_value
|
assert actual_value.todense().tolist() == expected_value
|
||||||
@@ -143,7 +144,7 @@ def _test_extract(model):
|
|||||||
assert pool_var_values.shape == (n_sols, 5)
|
assert pool_var_values.shape == (n_sols, 5)
|
||||||
|
|
||||||
|
|
||||||
def _test_add_constr(model: AbstractModel):
|
def _test_add_constr(model: AbstractModel) -> None:
|
||||||
with NamedTemporaryFile() as tempfile:
|
with NamedTemporaryFile() as tempfile:
|
||||||
with H5File(tempfile.name) as h5:
|
with H5File(tempfile.name) as h5:
|
||||||
model.add_constrs(
|
model.add_constrs(
|
||||||
@@ -154,10 +155,12 @@ def _test_add_constr(model: AbstractModel):
|
|||||||
)
|
)
|
||||||
model.optimize()
|
model.optimize()
|
||||||
model.extract_after_mip(h5)
|
model.extract_after_mip(h5)
|
||||||
assert h5.get_array("mip_var_values").tolist() == [1, 0, 0, 0, 1]
|
mip_var_values = h5.get_array("mip_var_values")
|
||||||
|
assert mip_var_values is not None
|
||||||
|
assert mip_var_values.tolist() == [1, 0, 0, 0, 1]
|
||||||
|
|
||||||
|
|
||||||
def _test_fix_vars(model: AbstractModel):
|
def _test_fix_vars(model: AbstractModel) -> None:
|
||||||
with NamedTemporaryFile() as tempfile:
|
with NamedTemporaryFile() as tempfile:
|
||||||
with H5File(tempfile.name) as h5:
|
with H5File(tempfile.name) as h5:
|
||||||
model.fix_variables(
|
model.fix_variables(
|
||||||
@@ -166,10 +169,12 @@ def _test_fix_vars(model: AbstractModel):
|
|||||||
)
|
)
|
||||||
model.optimize()
|
model.optimize()
|
||||||
model.extract_after_mip(h5)
|
model.extract_after_mip(h5)
|
||||||
assert h5.get_array("mip_var_values").tolist() == [1, 0, 0, 0, 1]
|
mip_var_values = h5.get_array("mip_var_values")
|
||||||
|
assert mip_var_values is not None
|
||||||
|
assert mip_var_values.tolist() == [1, 0, 0, 0, 1]
|
||||||
|
|
||||||
|
|
||||||
def _test_infeasible(model: AbstractModel):
|
def _test_infeasible(model: AbstractModel) -> None:
|
||||||
with NamedTemporaryFile() as tempfile:
|
with NamedTemporaryFile() as tempfile:
|
||||||
with H5File(tempfile.name) as h5:
|
with H5File(tempfile.name) as h5:
|
||||||
model.fix_variables(
|
model.fix_variables(
|
||||||
|
|||||||
Reference in New Issue
Block a user