Add first model feature (constraint RHS)

This commit is contained in:
2021-03-02 17:21:05 -06:00
parent 31ca45036a
commit 1397937f03
17 changed files with 167 additions and 47 deletions

View File

@@ -1,26 +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.
from miplearn.problems.knapsack import KnapsackInstance
from miplearn.solvers.learning import LearningSolver
def get_test_pyomo_instances():
instances = [
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

View File

@@ -12,7 +12,7 @@ from miplearn.classifiers import Classifier
from miplearn.components.lazy_dynamic import DynamicLazyConstraintsComponent
from miplearn.solvers.internal import InternalSolver
from miplearn.solvers.learning import LearningSolver
from .. import get_test_pyomo_instances
from tests.fixtures.knapsack import get_test_pyomo_instances
E = 0.1

View File

@@ -1,6 +1,7 @@
# 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 cast
from unittest.mock import Mock
@@ -10,7 +11,7 @@ from numpy.testing import assert_array_equal
from miplearn.instance import Instance
from miplearn.classifiers import Regressor
from miplearn.components.objective import ObjectiveValueComponent
from .. import get_test_pyomo_instances
from tests.fixtures.knapsack import get_test_pyomo_instances
def test_x_y_predict() -> None:

View File

@@ -11,7 +11,6 @@ from miplearn import Classifier
from miplearn.classifiers.threshold import Threshold, MinPrecisionThreshold
from miplearn.components.primal import PrimalSolutionComponent
from miplearn.instance import Instance
from tests import get_test_pyomo_instances
def test_x_y_fit() -> None:

45
tests/fixtures/knapsack.py vendored Normal file
View File

@@ -0,0 +1,45 @@
# 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 miplearn import BasePyomoSolver, GurobiSolver, InternalSolver, Instance
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
from miplearn.solvers.learning import LearningSolver
from tests.solvers import _is_subclass_or_instance
def get_test_pyomo_instances():
instances = [
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

View File

@@ -3,7 +3,7 @@
# Released under the modified BSD license. See COPYING.md for more details.
from inspect import isclass
from typing import List, Callable
from typing import List, Callable, Any
from miplearn.problems.knapsack import KnapsackInstance, GurobiKnapsackInstance
from miplearn.solvers.gurobi import GurobiSolver
@@ -13,7 +13,7 @@ from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver
from miplearn.solvers.pyomo.xpress import XpressPyomoSolver
def _is_subclass_or_instance(obj, parent_class):
def _is_subclass_or_instance(obj: Any, parent_class: Any) -> bool:
return isinstance(obj, parent_class) or (
isclass(obj) and issubclass(obj, parent_class)
)
@@ -35,5 +35,5 @@ def _get_knapsack_instance(solver):
assert False
def _get_internal_solvers() -> List[Callable[[], InternalSolver]]:
def get_internal_solvers() -> List[Callable[[], InternalSolver]]:
return [GurobiPyomoSolver, GurobiSolver, XpressPyomoSolver]

View File

@@ -13,7 +13,7 @@ from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.pyomo.base import BasePyomoSolver
from . import (
_get_knapsack_instance,
_get_internal_solvers,
get_internal_solvers,
)
from ..fixtures.infeasible import get_infeasible_instance
@@ -32,7 +32,7 @@ def test_redirect_output():
def test_internal_solver_warm_starts():
for solver_class in _get_internal_solvers():
for solver_class in get_internal_solvers():
logger.info("Solver: %s" % solver_class)
instance = _get_knapsack_instance(solver_class)
model = instance.to_model()
@@ -83,7 +83,7 @@ def test_internal_solver_warm_starts():
def test_internal_solver():
for solver_class in _get_internal_solvers():
for solver_class in get_internal_solvers():
logger.info("Solver: %s" % solver_class)
instance = _get_knapsack_instance(solver_class)
@@ -175,7 +175,7 @@ def test_internal_solver():
def test_relax():
for solver_class in _get_internal_solvers():
for solver_class in get_internal_solvers():
instance = _get_knapsack_instance(solver_class)
solver = solver_class()
solver.set_instance(instance)
@@ -185,7 +185,7 @@ def test_relax():
def test_infeasible_instance():
for solver_class in _get_internal_solvers():
for solver_class in get_internal_solvers():
instance = get_infeasible_instance(solver_class)
solver = solver_class()
solver.set_instance(instance)
@@ -203,7 +203,7 @@ def test_infeasible_instance():
def test_iteration_cb():
for solver_class in _get_internal_solvers():
for solver_class in get_internal_solvers():
logger.info("Solver: %s" % solver_class)
instance = _get_knapsack_instance(solver_class)
solver = solver_class()

View File

@@ -10,14 +10,14 @@ import os
from miplearn.solvers.gurobi import GurobiSolver
from miplearn.solvers.learning import LearningSolver
from . import _get_knapsack_instance, _get_internal_solvers
from . import _get_knapsack_instance, get_internal_solvers
logger = logging.getLogger(__name__)
def test_learning_solver():
for mode in ["exact", "heuristic"]:
for internal_solver in _get_internal_solvers():
for internal_solver in get_internal_solvers():
logger.info("Solver: %s" % internal_solver)
instance = _get_knapsack_instance(internal_solver)
solver = LearningSolver(
@@ -26,6 +26,9 @@ def test_learning_solver():
)
solver.solve(instance)
assert hasattr(instance, "model_features")
data = instance.training_data[0]
assert data["Solution"]["x"][0] == 1.0
assert data["Solution"]["x"][1] == 0.0
@@ -49,7 +52,7 @@ def test_learning_solver():
def test_solve_without_lp():
for internal_solver in _get_internal_solvers():
for internal_solver in get_internal_solvers():
logger.info("Solver: %s" % internal_solver)
instance = _get_knapsack_instance(internal_solver)
solver = LearningSolver(
@@ -62,7 +65,7 @@ def test_solve_without_lp():
def test_parallel_solve():
for internal_solver in _get_internal_solvers():
for internal_solver in get_internal_solvers():
instances = [_get_knapsack_instance(internal_solver) for _ in range(10)]
solver = LearningSolver(solver=internal_solver)
results = solver.parallel_solve(instances, n_jobs=3)
@@ -73,7 +76,7 @@ def test_parallel_solve():
def test_solve_fit_from_disk():
for internal_solver in _get_internal_solvers():
for internal_solver in get_internal_solvers():
# Create instances and pickle them
filenames = []
for k in range(3):

23
tests/test_features.py Normal file
View File

@@ -0,0 +1,23 @@
# 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 miplearn.features import ModelFeaturesExtractor
from tests.fixtures.knapsack import get_knapsack_instance
from tests.solvers import get_internal_solvers
def test_knapsack() -> None:
for solver_factory in get_internal_solvers():
# Initialize model, instance and internal solver
solver = solver_factory()
instance = get_knapsack_instance(solver)
model = instance.to_model()
solver.set_instance(instance, model)
# Extract all model features
extractor = ModelFeaturesExtractor(solver)
features = extractor.extract()
# Test constraint features
print(solver, features)
assert features["ConstraintRHS"]["eq_capacity"] == 67.0