mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 09:28:51 -06:00
Add types to remaining files; activate mypy's disallow_untyped_defs
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
from inspect import isclass
|
||||
from typing import List, Callable, Any
|
||||
|
||||
from miplearn.instance.base import Instance
|
||||
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
@@ -19,7 +20,7 @@ def _is_subclass_or_instance(obj: Any, parent_class: Any) -> bool:
|
||||
)
|
||||
|
||||
|
||||
def _get_knapsack_instance(solver):
|
||||
def _get_knapsack_instance(solver: Any) -> Instance:
|
||||
if _is_subclass_or_instance(solver, BasePyomoSolver):
|
||||
return KnapsackInstance(
|
||||
weights=[23.0, 26.0, 20.0, 18.0],
|
||||
|
||||
@@ -20,7 +20,7 @@ from ..fixtures.infeasible import get_infeasible_instance
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def test_redirect_output():
|
||||
def test_redirect_output() -> None:
|
||||
import sys
|
||||
|
||||
original_stdout = sys.stdout
|
||||
@@ -31,7 +31,7 @@ def test_redirect_output():
|
||||
assert io.getvalue() == "Hello world\n"
|
||||
|
||||
|
||||
def test_internal_solver_warm_starts():
|
||||
def test_internal_solver_warm_starts() -> None:
|
||||
for solver in get_internal_solvers():
|
||||
logger.info("Solver: %s" % solver)
|
||||
instance = _get_knapsack_instance(solver)
|
||||
@@ -54,7 +54,7 @@ def test_internal_solver_warm_starts():
|
||||
assert stats["Upper bound"] == 725.0
|
||||
|
||||
|
||||
def test_internal_solver():
|
||||
def test_internal_solver() -> None:
|
||||
for solver in get_internal_solvers():
|
||||
logger.info("Solver: %s" % solver)
|
||||
|
||||
@@ -64,26 +64,37 @@ def test_internal_solver():
|
||||
|
||||
assert solver.get_variable_names() == ["x[0]", "x[1]", "x[2]", "x[3]"]
|
||||
|
||||
stats = solver.solve_lp()
|
||||
lp_stats = solver.solve_lp()
|
||||
assert not solver.is_infeasible()
|
||||
assert round(stats["LP value"], 3) == 1287.923
|
||||
assert len(stats["LP log"]) > 100
|
||||
assert lp_stats["LP value"] is not None
|
||||
assert round(lp_stats["LP value"], 3) == 1287.923
|
||||
assert len(lp_stats["LP log"]) > 100
|
||||
|
||||
solution = solver.get_solution()
|
||||
assert solution is not None
|
||||
assert solution["x[0]"] is not None
|
||||
assert solution["x[1]"] is not None
|
||||
assert solution["x[2]"] is not None
|
||||
assert solution["x[3]"] is not None
|
||||
assert round(solution["x[0]"], 3) == 1.000
|
||||
assert round(solution["x[1]"], 3) == 0.923
|
||||
assert round(solution["x[2]"], 3) == 1.000
|
||||
assert round(solution["x[3]"], 3) == 0.000
|
||||
|
||||
stats = solver.solve(tee=True)
|
||||
mip_stats = solver.solve(tee=True)
|
||||
assert not solver.is_infeasible()
|
||||
assert len(stats["MIP log"]) > 100
|
||||
assert stats["Lower bound"] == 1183.0
|
||||
assert stats["Upper bound"] == 1183.0
|
||||
assert stats["Sense"] == "max"
|
||||
assert isinstance(stats["Wallclock time"], float)
|
||||
assert len(mip_stats["MIP log"]) > 100
|
||||
assert mip_stats["Lower bound"] == 1183.0
|
||||
assert mip_stats["Upper bound"] == 1183.0
|
||||
assert mip_stats["Sense"] == "max"
|
||||
assert isinstance(mip_stats["Wallclock time"], float)
|
||||
|
||||
solution = solver.get_solution()
|
||||
assert solution is not None
|
||||
assert solution["x[0]"] is not None
|
||||
assert solution["x[1]"] is not None
|
||||
assert solution["x[2]"] is not None
|
||||
assert solution["x[3]"] is not None
|
||||
assert solution["x[0]"] == 1.0
|
||||
assert solution["x[1]"] == 0.0
|
||||
assert solution["x[2]"] == 1.0
|
||||
@@ -143,43 +154,45 @@ def test_internal_solver():
|
||||
solver.relax()
|
||||
solver.set_constraint_sense("cut", "=")
|
||||
stats = solver.solve()
|
||||
assert stats["Lower bound"] is not None
|
||||
assert round(stats["Lower bound"]) == 1030.0
|
||||
assert round(solver.get_dual("eq_capacity")) == 0.0
|
||||
|
||||
|
||||
def test_relax():
|
||||
def test_relax() -> None:
|
||||
for solver in get_internal_solvers():
|
||||
instance = _get_knapsack_instance(solver)
|
||||
solver.set_instance(instance)
|
||||
solver.relax()
|
||||
stats = solver.solve()
|
||||
assert stats["Lower bound"] is not None
|
||||
assert round(stats["Lower bound"]) == 1288.0
|
||||
|
||||
|
||||
def test_infeasible_instance():
|
||||
def test_infeasible_instance() -> None:
|
||||
for solver in get_internal_solvers():
|
||||
instance = get_infeasible_instance(solver)
|
||||
solver.set_instance(instance)
|
||||
stats = solver.solve()
|
||||
mip_stats = solver.solve()
|
||||
|
||||
assert solver.is_infeasible()
|
||||
assert solver.get_solution() is None
|
||||
assert stats["Upper bound"] is None
|
||||
assert stats["Lower bound"] is None
|
||||
assert mip_stats["Upper bound"] is None
|
||||
assert mip_stats["Lower bound"] is None
|
||||
|
||||
stats = solver.solve_lp()
|
||||
lp_stats = solver.solve_lp()
|
||||
assert solver.get_solution() is None
|
||||
assert stats["LP value"] is None
|
||||
assert lp_stats["LP value"] is None
|
||||
|
||||
|
||||
def test_iteration_cb():
|
||||
def test_iteration_cb() -> None:
|
||||
for solver in get_internal_solvers():
|
||||
logger.info("Solver: %s" % solver)
|
||||
instance = _get_knapsack_instance(solver)
|
||||
solver.set_instance(instance)
|
||||
count = 0
|
||||
|
||||
def custom_iteration_cb():
|
||||
def custom_iteration_cb() -> bool:
|
||||
nonlocal count
|
||||
count += 1
|
||||
return count < 5
|
||||
|
||||
@@ -3,19 +3,21 @@
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from miplearn import InternalSolver
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from . import _get_knapsack_instance
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def test_lazy_cb():
|
||||
def test_lazy_cb() -> None:
|
||||
solver = GurobiSolver()
|
||||
instance = _get_knapsack_instance(solver)
|
||||
model = instance.to_model()
|
||||
|
||||
def lazy_cb(cb_solver, cb_model):
|
||||
def lazy_cb(cb_solver: InternalSolver, cb_model: Any) -> None:
|
||||
cobj = (cb_model.getVarByName("x[0]") * 1.0, "<", 0.0, "cut")
|
||||
if not cb_solver.is_constraint_satisfied(cobj):
|
||||
cb_solver.add_constraint(cobj)
|
||||
|
||||
@@ -16,7 +16,7 @@ from . import _get_knapsack_instance, get_internal_solvers
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def test_learning_solver():
|
||||
def test_learning_solver() -> None:
|
||||
for mode in ["exact", "heuristic"]:
|
||||
for internal_solver in get_internal_solvers():
|
||||
logger.info("Solver: %s" % internal_solver)
|
||||
@@ -30,17 +30,21 @@ def test_learning_solver():
|
||||
assert hasattr(instance, "features")
|
||||
|
||||
sample = instance.training_data[0]
|
||||
assert sample.solution is not None
|
||||
assert sample.solution["x[0]"] == 1.0
|
||||
assert sample.solution["x[1]"] == 0.0
|
||||
assert sample.solution["x[2]"] == 1.0
|
||||
assert sample.solution["x[3]"] == 1.0
|
||||
assert sample.lower_bound == 1183.0
|
||||
assert sample.upper_bound == 1183.0
|
||||
assert sample.lp_solution is not None
|
||||
assert round(sample.lp_solution["x[0]"], 3) == 1.000
|
||||
assert round(sample.lp_solution["x[1]"], 3) == 0.923
|
||||
assert round(sample.lp_solution["x[2]"], 3) == 1.000
|
||||
assert round(sample.lp_solution["x[3]"], 3) == 0.000
|
||||
assert sample.lp_value is not None
|
||||
assert round(sample.lp_value, 3) == 1287.923
|
||||
assert sample.mip_log is not None
|
||||
assert len(sample.mip_log) > 100
|
||||
|
||||
solver.fit([instance])
|
||||
@@ -51,7 +55,7 @@ def test_learning_solver():
|
||||
dill.dump(solver, file)
|
||||
|
||||
|
||||
def test_solve_without_lp():
|
||||
def test_solve_without_lp() -> None:
|
||||
for internal_solver in get_internal_solvers():
|
||||
logger.info("Solver: %s" % internal_solver)
|
||||
instance = _get_knapsack_instance(internal_solver)
|
||||
@@ -64,7 +68,7 @@ def test_solve_without_lp():
|
||||
solver.solve(instance)
|
||||
|
||||
|
||||
def test_parallel_solve():
|
||||
def test_parallel_solve() -> None:
|
||||
for internal_solver in get_internal_solvers():
|
||||
instances = [_get_knapsack_instance(internal_solver) for _ in range(10)]
|
||||
solver = LearningSolver(solver=internal_solver)
|
||||
@@ -72,10 +76,11 @@ def test_parallel_solve():
|
||||
assert len(results) == 10
|
||||
for instance in instances:
|
||||
data = instance.training_data[0]
|
||||
assert data.solution is not None
|
||||
assert len(data.solution.keys()) == 4
|
||||
|
||||
|
||||
def test_solve_fit_from_disk():
|
||||
def test_solve_fit_from_disk() -> None:
|
||||
for internal_solver in get_internal_solvers():
|
||||
# Create instances and pickle them
|
||||
instances = []
|
||||
@@ -108,7 +113,7 @@ def test_solve_fit_from_disk():
|
||||
os.remove(instance.filename)
|
||||
|
||||
|
||||
def test_simulate_perfect():
|
||||
def test_simulate_perfect() -> None:
|
||||
internal_solver = GurobiSolver()
|
||||
instance = _get_knapsack_instance(internal_solver)
|
||||
with tempfile.NamedTemporaryFile(suffix=".pkl", delete=False) as tmp:
|
||||
@@ -121,7 +126,7 @@ def test_simulate_perfect():
|
||||
assert stats["Lower bound"] == stats["Objective: Predicted lower bound"]
|
||||
|
||||
|
||||
def test_gap():
|
||||
def test_gap() -> None:
|
||||
assert LearningSolver._compute_gap(ub=0.0, lb=0.0) == 0.0
|
||||
assert LearningSolver._compute_gap(ub=1.0, lb=0.5) == 0.5
|
||||
assert LearningSolver._compute_gap(ub=1.0, lb=1.0) == 0.0
|
||||
|
||||
Reference in New Issue
Block a user