mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Organize test fixtures; handle infeasibility in DropRedundant
This commit is contained in:
@@ -6,13 +6,14 @@ from unittest.mock import Mock, call
|
||||
|
||||
import numpy as np
|
||||
|
||||
from miplearn import RelaxIntegralityStep, BasePyomoSolver
|
||||
from miplearn import RelaxIntegralityStep, GurobiSolver
|
||||
from miplearn.classifiers import Classifier
|
||||
from miplearn.components.steps.drop_redundant import DropRedundantInequalitiesStep
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
from tests.solvers import _get_knapsack_instance
|
||||
from tests.fixtures.infeasible import get_infeasible_instance
|
||||
from tests.fixtures.redundant import get_instance_with_redundancy
|
||||
|
||||
|
||||
def _setup():
|
||||
@@ -30,6 +31,7 @@ def _setup():
|
||||
)
|
||||
internal.extract_constraint = Mock(side_effect=lambda cid: "<%s>" % cid)
|
||||
internal.is_constraint_satisfied = Mock(return_value=False)
|
||||
internal.is_infeasible = Mock(return_value=False)
|
||||
|
||||
instance = Mock(spec=Instance)
|
||||
instance.get_constraint_features = Mock(
|
||||
@@ -399,14 +401,19 @@ def test_x_multiple_solves():
|
||||
|
||||
|
||||
def test_usage():
|
||||
solver = LearningSolver(
|
||||
components=[
|
||||
RelaxIntegralityStep(),
|
||||
DropRedundantInequalitiesStep(),
|
||||
]
|
||||
)
|
||||
instance = _get_knapsack_instance(BasePyomoSolver)
|
||||
# The following should not crash
|
||||
solver.solve(instance)
|
||||
solver.fit([instance])
|
||||
solver.solve(instance)
|
||||
for internal_solver in [GurobiSolver]:
|
||||
for instance in [
|
||||
get_instance_with_redundancy(internal_solver),
|
||||
get_infeasible_instance(internal_solver),
|
||||
]:
|
||||
solver = LearningSolver(
|
||||
solver=internal_solver,
|
||||
components=[
|
||||
RelaxIntegralityStep(),
|
||||
DropRedundantInequalitiesStep(),
|
||||
],
|
||||
)
|
||||
# The following should not crash
|
||||
solver.solve(instance)
|
||||
solver.fit([instance])
|
||||
solver.solve(instance)
|
||||
|
||||
3
tests/fixtures/__init__.py
vendored
Normal file
3
tests/fixtures/__init__.py
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
40
tests/fixtures/infeasible.py
vendored
Normal file
40
tests/fixtures/infeasible.py
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
from typing import Any
|
||||
|
||||
from pyomo import environ as pe
|
||||
|
||||
from miplearn.instance 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):
|
||||
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):
|
||||
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):
|
||||
if _is_subclass_or_instance(solver, BasePyomoSolver):
|
||||
return InfeasiblePyomoInstance()
|
||||
if _is_subclass_or_instance(solver, GurobiSolver):
|
||||
return InfeasibleGurobiInstance()
|
||||
39
tests/fixtures/redundant.py
vendored
Normal file
39
tests/fixtures/redundant.py
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
# MIPLearn: Extensible Framework for Learning-Enhanced Mixed-Integer Optimization
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
from typing import Any
|
||||
|
||||
from miplearn import Instance, BasePyomoSolver, GurobiSolver
|
||||
import pyomo.environ as pe
|
||||
|
||||
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):
|
||||
if _is_subclass_or_instance(solver, BasePyomoSolver):
|
||||
return PyomoInstanceWithRedundancy()
|
||||
if _is_subclass_or_instance(solver, GurobiSolver):
|
||||
return GurobiInstanceWithRedundancy()
|
||||
@@ -3,11 +3,8 @@
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
from inspect import isclass
|
||||
from typing import List, Callable, Any
|
||||
from typing import List, Callable
|
||||
|
||||
from pyomo import environ as pe
|
||||
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
@@ -16,27 +13,6 @@ from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver
|
||||
from miplearn.solvers.pyomo.xpress import XpressPyomoSolver
|
||||
|
||||
|
||||
class InfeasiblePyomoInstance(Instance):
|
||||
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):
|
||||
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 _is_subclass_or_instance(obj, parent_class):
|
||||
return isinstance(obj, parent_class) or (
|
||||
isclass(obj) and issubclass(obj, parent_class)
|
||||
@@ -59,12 +35,5 @@ def _get_knapsack_instance(solver):
|
||||
assert False
|
||||
|
||||
|
||||
def _get_infeasible_instance(solver):
|
||||
if _is_subclass_or_instance(solver, BasePyomoSolver):
|
||||
return InfeasiblePyomoInstance()
|
||||
if _is_subclass_or_instance(solver, GurobiSolver):
|
||||
return InfeasibleGurobiInstance()
|
||||
|
||||
|
||||
def _get_internal_solvers() -> List[Callable[[], InternalSolver]]:
|
||||
return [GurobiPyomoSolver, GurobiSolver, XpressPyomoSolver]
|
||||
|
||||
@@ -14,8 +14,8 @@ from miplearn.solvers.pyomo.base import BasePyomoSolver
|
||||
from . import (
|
||||
_get_knapsack_instance,
|
||||
_get_internal_solvers,
|
||||
_get_infeasible_instance,
|
||||
)
|
||||
from ..fixtures.infeasible import get_infeasible_instance
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -186,7 +186,7 @@ def test_relax():
|
||||
|
||||
def test_infeasible_instance():
|
||||
for solver_class in _get_internal_solvers():
|
||||
instance = _get_infeasible_instance(solver_class)
|
||||
instance = get_infeasible_instance(solver_class)
|
||||
solver = solver_class()
|
||||
solver.set_instance(instance)
|
||||
stats = solver.solve()
|
||||
|
||||
Reference in New Issue
Block a user