Move instance fixtures into the main source; remove duplication

master
Alinson S. Xavier 5 years ago
parent f3fd1e0cda
commit a8224b5a38
No known key found for this signature in database
GPG Key ID: DCA0DAD4D2F58624

@ -289,35 +289,3 @@ class KnapsackInstance(Instance):
self.weights[item],
self.prices[item],
]
class GurobiKnapsackInstance(KnapsackInstance):
"""
Simpler (one-dimensional) knapsack instance, implemented directly in Gurobi
instead of Pyomo, used for testing.
"""
def __init__(
self,
weights: List[float],
prices: List[float],
capacity: float,
) -> None:
super().__init__(weights, prices, capacity)
@overrides
def to_model(self) -> Any:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model("Knapsack")
n = len(self.weights)
x = model.addVars(n, vtype=GRB.BINARY, name="x")
model.addConstr(
gp.quicksum(x[i] * self.weights[i] for i in range(n)) <= self.capacity,
"eq_capacity",
)
model.setObjective(
gp.quicksum(x[i] * self.prices[i] for i in range(n)), GRB.MAXIMIZE
)
return model

@ -19,6 +19,7 @@ from miplearn.solvers.internal import (
LazyCallback,
MIPSolveStats,
)
from miplearn.solvers.pyomo.base import PyomoTestInstanceKnapsack
from miplearn.types import (
SolverParams,
UserCutCallback,
@ -442,3 +443,77 @@ class GurobiSolver(InternalSolver):
params=self.params,
lazy_cb_frequency=self.lazy_cb_frequency,
)
@overrides
def build_test_instance_infeasible(self) -> Instance:
return GurobiTestInstanceInfeasible()
@overrides
def build_test_instance_redundancy(self) -> Instance:
return GurobiTestInstanceRedundancy()
@overrides
def build_test_instance_knapsack(self) -> Instance:
return GurobiTestInstanceKnapsack(
weights=[23.0, 26.0, 20.0, 18.0],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
)
class GurobiTestInstanceInfeasible(Instance):
@overrides
def to_model(self) -> Any:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model()
x = model.addVars(1, vtype=GRB.BINARY, name="x")
model.addConstr(x[0] >= 2)
model.setObjective(x[0])
return model
class GurobiTestInstanceRedundancy(Instance):
def to_model(self) -> Any:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model()
x = model.addVars(2, vtype=GRB.BINARY, name="x")
model.addConstr(x[0] + x[1] <= 1)
model.addConstr(x[0] + x[1] <= 2)
model.setObjective(x[0] + x[1], GRB.MAXIMIZE)
return model
class GurobiTestInstanceKnapsack(PyomoTestInstanceKnapsack):
"""
Simpler (one-dimensional) knapsack instance, implemented directly in Gurobi
instead of Pyomo, used for testing.
"""
def __init__(
self,
weights: List[float],
prices: List[float],
capacity: float,
) -> None:
super().__init__(weights, prices, capacity)
@overrides
def to_model(self) -> Any:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model("Knapsack")
n = len(self.weights)
x = model.addVars(n, vtype=GRB.BINARY, name="x")
model.addConstr(
gp.quicksum(x[i] * self.weights[i] for i in range(n)) <= self.capacity,
"eq_capacity",
)
model.setObjective(
gp.quicksum(x[i] * self.prices[i] for i in range(n)), GRB.MAXIMIZE
)
return model

@ -292,3 +292,15 @@ class InternalSolver(ABC):
completely unitialized.
"""
pass
@abstractmethod
def build_test_instance_infeasible(self) -> Instance:
pass
@abstractmethod
def build_test_instance_redundancy(self) -> Instance:
pass
@abstractmethod
def build_test_instance_knapsack(self) -> Instance:
pass

@ -29,7 +29,9 @@ from miplearn.types import (
UserCutCallback,
Solution,
VariableName,
Category,
)
import numpy as np
logger = logging.getLogger(__name__)
@ -338,3 +340,88 @@ class BasePyomoSolver(InternalSolver):
@overrides
def get_sense(self) -> str:
return self._obj_sense
@overrides
def build_test_instance_infeasible(self) -> Instance:
return PyomoTestInstanceInfeasible()
@overrides
def build_test_instance_redundancy(self) -> Instance:
return PyomoTestInstanceRedundancy()
@overrides
def build_test_instance_knapsack(self) -> Instance:
return PyomoTestInstanceKnapsack(
weights=[23.0, 26.0, 20.0, 18.0],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
)
class PyomoTestInstanceInfeasible(Instance):
@overrides
def to_model(self) -> pe.ConcreteModel:
model = pe.ConcreteModel()
model.x = pe.Var([0], domain=pe.Binary)
model.OBJ = pe.Objective(expr=model.x[0], sense=pe.maximize)
model.eq = pe.Constraint(expr=model.x[0] >= 2)
return model
class PyomoTestInstanceRedundancy(Instance):
def to_model(self) -> pe.ConcreteModel:
model = pe.ConcreteModel()
model.x = pe.Var([0, 1], domain=pe.Binary)
model.OBJ = pe.Objective(expr=model.x[0] + model.x[1], sense=pe.maximize)
model.eq1 = pe.Constraint(expr=model.x[0] + model.x[1] <= 1)
model.eq2 = pe.Constraint(expr=model.x[0] + model.x[1] <= 2)
return model
class PyomoTestInstanceKnapsack(Instance):
"""
Simpler (one-dimensional) Knapsack Problem, used for testing.
"""
def __init__(
self,
weights: List[float],
prices: List[float],
capacity: float,
) -> None:
super().__init__()
self.weights = weights
self.prices = prices
self.capacity = capacity
self.varname_to_item: Dict[VariableName, int] = {
f"x[{i}]": i for i in range(len(self.weights))
}
@overrides
def to_model(self) -> pe.ConcreteModel:
model = pe.ConcreteModel()
items = range(len(self.weights))
model.x = pe.Var(items, domain=pe.Binary)
model.OBJ = pe.Objective(
expr=sum(model.x[v] * self.prices[v] for v in items),
sense=pe.maximize,
)
model.eq_capacity = pe.Constraint(
expr=sum(model.x[v] * self.weights[v] for v in items) <= self.capacity
)
return model
@overrides
def get_instance_features(self) -> List[float]:
return [
self.capacity,
np.average(self.weights),
]
@overrides
def get_variable_features(self, var_name: VariableName) -> List[Category]:
item = self.varname_to_item[var_name]
return [
self.weights[item],
self.prices[item],
]

@ -14,7 +14,6 @@ from miplearn.features import TrainingSample, InstanceFeatures, Features
from miplearn.instance.base import Instance
from miplearn.solvers.learning import LearningSolver
from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver
from tests.fixtures.knapsack import get_knapsack_instance
@pytest.fixture
@ -252,7 +251,7 @@ def test_sample_evaluate(instance: Instance, sample: TrainingSample) -> None:
def test_usage() -> None:
solver = LearningSolver(components=[ObjectiveValueComponent()])
instance = get_knapsack_instance(GurobiPyomoSolver())
instance = GurobiPyomoSolver().build_test_instance_knapsack()
solver.solve(instance)
solver.fit([instance])
stats = solver.solve(instance)

@ -1,3 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.

@ -1,44 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from typing import Any
from overrides import overrides
from pyomo import environ as pe
from miplearn.instance.base import Instance
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
from tests.solvers import _is_subclass_or_instance
class InfeasiblePyomoInstance(Instance):
@overrides
def to_model(self) -> pe.ConcreteModel:
model = pe.ConcreteModel()
model.x = pe.Var([0], domain=pe.Binary)
model.OBJ = pe.Objective(expr=model.x[0], sense=pe.maximize)
model.eq = pe.Constraint(expr=model.x[0] >= 2)
return model
class InfeasibleGurobiInstance(Instance):
@overrides
def to_model(self) -> Any:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model()
x = model.addVars(1, vtype=GRB.BINARY, name="x")
model.addConstr(x[0] >= 2)
model.setObjective(x[0])
return model
def get_infeasible_instance(solver: Any) -> Instance:
if _is_subclass_or_instance(solver, BasePyomoSolver):
return InfeasiblePyomoInstance()
if _is_subclass_or_instance(solver, GurobiSolver):
return InfeasibleGurobiInstance()
assert False

@ -1,50 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from typing import List, Any, Tuple
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
from miplearn.solvers.learning import LearningSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
from tests.solvers import _is_subclass_or_instance
def get_test_pyomo_instances() -> Tuple[List[Instance], List[Any]]:
instances: List[Instance] = [
KnapsackInstance(
weights=[23.0, 26.0, 20.0, 18.0],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
),
KnapsackInstance(
weights=[25.0, 30.0, 22.0, 18.0],
prices=[500.0, 365.0, 420.0, 150.0],
capacity=70.0,
),
]
models = [instance.to_model() for instance in instances]
solver = LearningSolver()
for i in range(len(instances)):
solver.solve(instances[i], models[i])
return instances, models
def get_knapsack_instance(solver: InternalSolver) -> Instance:
if _is_subclass_or_instance(solver, BasePyomoSolver):
return KnapsackInstance(
weights=[23.0, 26.0, 20.0, 18.0],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
)
elif _is_subclass_or_instance(solver, GurobiSolver):
return GurobiKnapsackInstance(
weights=[23.0, 26.0, 20.0, 18.0],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
)
else:
assert False

@ -1,42 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from typing import Any
import pyomo.environ as pe
from miplearn.instance.base import Instance
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
from tests.solvers import _is_subclass_or_instance
class PyomoInstanceWithRedundancy(Instance):
def to_model(self) -> pe.ConcreteModel:
model = pe.ConcreteModel()
model.x = pe.Var([0, 1], domain=pe.Binary)
model.OBJ = pe.Objective(expr=model.x[0] + model.x[1], sense=pe.maximize)
model.eq1 = pe.Constraint(expr=model.x[0] + model.x[1] <= 1)
model.eq2 = pe.Constraint(expr=model.x[0] + model.x[1] <= 2)
return model
class GurobiInstanceWithRedundancy(Instance):
def to_model(self) -> Any:
import gurobipy as gp
from gurobipy import GRB
model = gp.Model()
x = model.addVars(2, vtype=GRB.BINARY, name="x")
model.addConstr(x[0] + x[1] <= 1)
model.addConstr(x[0] + x[1] <= 2)
model.setObjective(x[0] + x[1], GRB.MAXIMIZE)
return model
def get_instance_with_redundancy(solver: Any) -> Instance:
if _is_subclass_or_instance(solver, BasePyomoSolver):
return PyomoInstanceWithRedundancy()
if _is_subclass_or_instance(solver, GurobiSolver):
return GurobiInstanceWithRedundancy()
assert False

@ -5,11 +5,10 @@ import tempfile
from miplearn.instance.picklegz import write_pickle_gz, PickleGzInstance
from miplearn.solvers.gurobi import GurobiSolver
from tests.fixtures.knapsack import get_knapsack_instance
def test_usage() -> None:
original = get_knapsack_instance(GurobiSolver())
original = GurobiSolver().build_test_instance_knapsack()
file = tempfile.NamedTemporaryFile()
write_pickle_gz(original, file.name)
pickled = PickleGzInstance(file.name)

@ -1,33 +0,0 @@
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
# Copyright (C) 2020-2021, UChicago Argonne, LLC. All rights reserved.
# Released under the modified BSD license. See COPYING.md for more details.
from inspect import isclass
from typing import Any
from miplearn.instance.base import Instance
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
def _is_subclass_or_instance(obj: Any, parent_class: Any) -> bool:
return isinstance(obj, parent_class) or (
isclass(obj) and issubclass(obj, parent_class)
)
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],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
)
if _is_subclass_or_instance(solver, GurobiSolver):
return GurobiKnapsackInstance(
weights=[23.0, 26.0, 20.0, 18.0],
prices=[505.0, 352.0, 458.0, 220.0],
capacity=67.0,
)
assert False

@ -13,11 +13,9 @@ from miplearn import InternalSolver
from miplearn.solvers import _RedirectOutput
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
from . import _get_knapsack_instance
# noinspection PyUnresolvedReferences
from .. import internal_solvers
from ..fixtures.infeasible import get_infeasible_instance
logger = logging.getLogger(__name__)
@ -38,7 +36,7 @@ def test_internal_solver_warm_starts(
) -> None:
for solver in internal_solvers:
logger.info("Solver: %s" % solver)
instance = _get_knapsack_instance(solver)
instance = solver.build_test_instance_knapsack()
model = instance.to_model()
solver.set_instance(instance, model)
solver.set_warm_start({"x[0]": 1.0, "x[1]": 0.0, "x[2]": 0.0, "x[3]": 1.0})
@ -64,7 +62,7 @@ def test_internal_solver(
for solver in internal_solvers:
logger.info("Solver: %s" % solver)
instance = _get_knapsack_instance(solver)
instance = solver.build_test_instance_knapsack()
model = instance.to_model()
solver.set_instance(instance, model)
@ -169,7 +167,7 @@ def test_relax(
internal_solvers: List[InternalSolver],
) -> None:
for solver in internal_solvers:
instance = _get_knapsack_instance(solver)
instance = solver.build_test_instance_knapsack()
solver.set_instance(instance)
solver.relax()
stats = solver.solve()
@ -181,7 +179,7 @@ def test_infeasible_instance(
internal_solvers: List[InternalSolver],
) -> None:
for solver in internal_solvers:
instance = get_infeasible_instance(solver)
instance = solver.build_test_instance_infeasible()
solver.set_instance(instance)
mip_stats = solver.solve()
@ -200,7 +198,7 @@ def test_iteration_cb(
) -> None:
for solver in internal_solvers:
logger.info("Solver: %s" % solver)
instance = _get_knapsack_instance(solver)
instance = solver.build_test_instance_knapsack()
solver.set_instance(instance)
count = 0

@ -7,14 +7,13 @@ 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() -> None:
solver = GurobiSolver()
instance = _get_knapsack_instance(solver)
instance = solver.build_test_instance_knapsack()
model = instance.to_model()
def lazy_cb(cb_solver: InternalSolver, cb_model: Any) -> None:

@ -13,7 +13,6 @@ from miplearn import Instance, InternalSolver
from miplearn.instance.picklegz import PickleGzInstance, write_pickle_gz, read_pickle_gz
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.learning import LearningSolver
from . import _get_knapsack_instance
# noinspection PyUnresolvedReferences
from tests import internal_solvers
@ -27,7 +26,7 @@ def test_learning_solver(
for mode in ["exact", "heuristic"]:
for internal_solver in internal_solvers:
logger.info("Solver: %s" % internal_solver)
instance = _get_knapsack_instance(internal_solver)
instance = internal_solver.build_test_instance_knapsack()
solver = LearningSolver(
solver=internal_solver,
mode=mode,
@ -71,7 +70,7 @@ def test_solve_without_lp(
) -> None:
for internal_solver in internal_solvers:
logger.info("Solver: %s" % internal_solver)
instance = _get_knapsack_instance(internal_solver)
instance = internal_solver.build_test_instance_knapsack()
solver = LearningSolver(
solver=internal_solver,
solve_lp=False,
@ -85,7 +84,7 @@ def test_parallel_solve(
internal_solvers: List[InternalSolver],
) -> None:
for internal_solver in internal_solvers:
instances = [_get_knapsack_instance(internal_solver) for _ in range(10)]
instances = [internal_solver.build_test_instance_knapsack() for _ in range(10)]
solver = LearningSolver(solver=internal_solver)
results = solver.parallel_solve(instances, n_jobs=3)
assert len(results) == 10
@ -102,7 +101,7 @@ def test_solve_fit_from_disk(
# Create instances and pickle them
instances: List[Instance] = []
for k in range(3):
instance = _get_knapsack_instance(internal_solver)
instance = internal_solver.build_test_instance_knapsack()
with tempfile.NamedTemporaryFile(suffix=".pkl", delete=False) as file:
instances += [PickleGzInstance(file.name)]
write_pickle_gz(instance, file.name)
@ -132,7 +131,7 @@ def test_solve_fit_from_disk(
def test_simulate_perfect() -> None:
internal_solver = GurobiSolver()
instance = _get_knapsack_instance(internal_solver)
instance = internal_solver.build_test_instance_knapsack()
with tempfile.NamedTemporaryFile(suffix=".pkl", delete=False) as tmp:
write_pickle_gz(instance, tmp.name)
solver = LearningSolver(

@ -9,13 +9,12 @@ from miplearn.features import (
ConstraintFeatures,
)
from miplearn.solvers.gurobi import GurobiSolver
from tests.fixtures.knapsack import get_knapsack_instance
def test_knapsack() -> None:
for solver_factory in [GurobiSolver]:
solver = solver_factory()
instance = get_knapsack_instance(solver)
instance = solver.build_test_instance_knapsack()
model = instance.to_model()
solver.set_instance(instance, model)
FeaturesExtractor(solver).extract(instance)

Loading…
Cancel
Save