mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Finish rewrite of user cuts component
This commit is contained in:
@@ -5,8 +5,8 @@
|
||||
from numpy.linalg import norm
|
||||
from sklearn.svm import SVC
|
||||
|
||||
from miplearn import AdaptiveClassifier, ScikitLearnClassifier
|
||||
from miplearn.classifiers.adaptive import CandidateClassifierSpecs
|
||||
from miplearn.classifiers.adaptive import CandidateClassifierSpecs, AdaptiveClassifier
|
||||
from miplearn.classifiers.sklearn import ScikitLearnClassifier
|
||||
from tests.classifiers import _build_circle_training_data
|
||||
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@ from unittest.mock import Mock, call
|
||||
|
||||
import numpy as np
|
||||
|
||||
from miplearn import RelaxIntegralityStep, GurobiSolver
|
||||
from miplearn.classifiers import Classifier
|
||||
from miplearn.components.steps.drop_redundant import DropRedundantInequalitiesStep
|
||||
from miplearn.components.steps.relax_integrality import RelaxIntegralityStep
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
from miplearn.features import TrainingSample, Features
|
||||
|
||||
@@ -3,7 +3,8 @@
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
from unittest.mock import Mock
|
||||
|
||||
from miplearn import Component, Instance
|
||||
from miplearn.components.component import Component
|
||||
from miplearn.instance import Instance
|
||||
|
||||
|
||||
def test_xy_instance():
|
||||
|
||||
@@ -8,16 +8,16 @@ import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from miplearn import Instance
|
||||
from miplearn.classifiers import Classifier
|
||||
from miplearn.classifiers.threshold import MinProbabilityThreshold
|
||||
from miplearn.components import classifier_evaluation_dict
|
||||
from miplearn.components.lazy_dynamic import DynamicLazyConstraintsComponent
|
||||
from miplearn.components.dynamic_lazy import DynamicLazyConstraintsComponent
|
||||
from miplearn.features import (
|
||||
TrainingSample,
|
||||
Features,
|
||||
InstanceFeatures,
|
||||
)
|
||||
from miplearn.instance import Instance
|
||||
|
||||
E = 0.1
|
||||
|
||||
@@ -144,7 +144,7 @@ def test_fit(training_instances: List[Instance]) -> None:
|
||||
|
||||
def test_sample_predict_evaluate(training_instances: List[Instance]) -> None:
|
||||
comp = DynamicLazyConstraintsComponent()
|
||||
comp.known_cids = ["c1", "c2", "c3", "c4"]
|
||||
comp.known_cids.extend(["c1", "c2", "c3", "c4"])
|
||||
comp.thresholds["type-a"] = MinProbabilityThreshold([0.5, 0.5])
|
||||
comp.thresholds["type-b"] = MinProbabilityThreshold([0.5, 0.5])
|
||||
comp.classifiers["type-a"] = Mock(spec=Classifier)
|
||||
@@ -11,8 +11,10 @@ import pytest
|
||||
from gurobipy import GRB
|
||||
from networkx import Graph
|
||||
|
||||
from miplearn import Instance, LearningSolver, GurobiSolver
|
||||
from miplearn.components.user_cuts import UserCutsComponentNG
|
||||
from miplearn.components.dynamic_user_cuts import UserCutsComponent
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -20,12 +22,11 @@ logger = logging.getLogger(__name__)
|
||||
class GurobiStableSetProblem(Instance):
|
||||
def __init__(self, graph: Graph) -> None:
|
||||
super().__init__()
|
||||
self.graph = graph
|
||||
self.nodes = list(self.graph.nodes)
|
||||
self.graph: Graph = graph
|
||||
|
||||
def to_model(self) -> Any:
|
||||
model = gp.Model()
|
||||
x = [model.addVar(vtype=GRB.BINARY) for _ in range(len(self.nodes))]
|
||||
x = [model.addVar(vtype=GRB.BINARY) for _ in range(len(self.graph.nodes))]
|
||||
model.setObjective(gp.quicksum(x), GRB.MAXIMIZE)
|
||||
for e in list(self.graph.edges):
|
||||
model.addConstr(x[e[0]] + x[e[1]] <= 1)
|
||||
@@ -39,16 +40,14 @@ class GurobiStableSetProblem(Instance):
|
||||
vals = model.cbGetNodeRel(model.getVars())
|
||||
violations = []
|
||||
for clique in nx.find_cliques(self.graph):
|
||||
lhs = sum(vals[i] for i in clique)
|
||||
if lhs > 1:
|
||||
if sum(vals[i] for i in clique) > 1:
|
||||
violations += [frozenset(clique)]
|
||||
return violations
|
||||
|
||||
def build_user_cut(self, model: Any, violation: Hashable) -> Any:
|
||||
assert isinstance(violation, FrozenSet)
|
||||
def build_user_cut(self, model: Any, cid: Hashable) -> Any:
|
||||
assert isinstance(cid, FrozenSet)
|
||||
x = model.getVars()
|
||||
cut = gp.quicksum([x[i] for i in violation]) <= 1
|
||||
return cut
|
||||
return gp.quicksum([x[i] for i in cid]) <= 1
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -62,7 +61,7 @@ def solver() -> LearningSolver:
|
||||
return LearningSolver(
|
||||
solver=lambda: GurobiSolver(),
|
||||
components=[
|
||||
UserCutsComponentNG(),
|
||||
UserCutsComponent(),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -71,7 +70,17 @@ def test_usage(
|
||||
stab_instance: Instance,
|
||||
solver: LearningSolver,
|
||||
) -> None:
|
||||
solver.solve(stab_instance)
|
||||
stats_before = solver.solve(stab_instance)
|
||||
sample = stab_instance.training_data[0]
|
||||
assert sample.user_cuts_enforced is not None
|
||||
assert len(sample.user_cuts_enforced) > 0
|
||||
print(stats_before)
|
||||
assert stats_before["UserCuts: Added ahead-of-time"] == 0
|
||||
assert stats_before["UserCuts: Added in callback"] > 0
|
||||
|
||||
solver.fit([stab_instance])
|
||||
stats_after = solver.solve(stab_instance)
|
||||
assert (
|
||||
stats_after["UserCuts: Added ahead-of-time"]
|
||||
== stats_before["UserCuts: Added in callback"]
|
||||
)
|
||||
@@ -7,9 +7,12 @@ from unittest.mock import Mock
|
||||
import pytest
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from miplearn import GurobiPyomoSolver, LearningSolver, Regressor, Instance
|
||||
from miplearn.classifiers import Regressor
|
||||
from miplearn.components.objective import ObjectiveValueComponent
|
||||
from miplearn.features import TrainingSample, InstanceFeatures, Features
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
from miplearn.solvers.pyomo.gurobi import GurobiPyomoSolver
|
||||
from tests.fixtures.knapsack import get_knapsack_instance
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -8,12 +8,14 @@ import numpy as np
|
||||
from numpy.testing import assert_array_equal
|
||||
from scipy.stats import randint
|
||||
|
||||
from miplearn import Classifier, LearningSolver, Instance
|
||||
from miplearn.classifiers import Classifier
|
||||
from miplearn.classifiers.threshold import Threshold
|
||||
from miplearn.components import classifier_evaluation_dict
|
||||
from miplearn.components.primal import PrimalSolutionComponent
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.problems.tsp import TravelingSalesmanGenerator
|
||||
from miplearn.features import TrainingSample, VariableFeatures, Features
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
|
||||
|
||||
def test_xy() -> None:
|
||||
|
||||
@@ -8,10 +8,12 @@ import numpy as np
|
||||
import pytest
|
||||
from numpy.testing import assert_array_equal
|
||||
|
||||
from miplearn import LearningSolver, InternalSolver, Instance
|
||||
from miplearn.classifiers import Classifier
|
||||
from miplearn.classifiers.threshold import Threshold, MinProbabilityThreshold
|
||||
from miplearn.components.lazy_static import StaticLazyConstraintsComponent
|
||||
from miplearn.components.static_lazy import StaticLazyConstraintsComponent
|
||||
from miplearn.instance import Instance
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
from miplearn.solvers.learning import LearningSolver
|
||||
from miplearn.types import (
|
||||
LearningSolveStats,
|
||||
)
|
||||
6
tests/fixtures/knapsack.py
vendored
6
tests/fixtures/knapsack.py
vendored
@@ -1,10 +1,12 @@
|
||||
# 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.instance 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
|
||||
|
||||
|
||||
|
||||
4
tests/fixtures/redundant.py
vendored
4
tests/fixtures/redundant.py
vendored
@@ -3,9 +3,11 @@
|
||||
# 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 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
|
||||
|
||||
|
||||
|
||||
@@ -67,7 +67,6 @@ def test_subtour():
|
||||
solver = LearningSolver()
|
||||
solver.solve(instance)
|
||||
assert len(instance.training_data[0].lazy_enforced) > 0
|
||||
assert hasattr(instance, "found_violated_user_cuts")
|
||||
x = instance.training_data[0].solution["x"]
|
||||
assert x[0, 1] == 1.0
|
||||
assert x[0, 4] == 1.0
|
||||
|
||||
@@ -30,7 +30,7 @@ def test_benchmark():
|
||||
benchmark = BenchmarkRunner(test_solvers)
|
||||
benchmark.fit(train_instances)
|
||||
benchmark.parallel_solve(test_instances, n_jobs=n_jobs, n_trials=2)
|
||||
assert benchmark.results.values.shape == (12, 18)
|
||||
assert benchmark.results.values.shape == (12, 20)
|
||||
|
||||
benchmark.write_csv("/tmp/benchmark.csv")
|
||||
assert os.path.isfile("/tmp/benchmark.csv")
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
# Copyright (C) 2020, UChicago Argonne, LLC. All rights reserved.
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
|
||||
from miplearn import GurobiSolver
|
||||
from miplearn.features import (
|
||||
FeaturesExtractor,
|
||||
InstanceFeatures,
|
||||
VariableFeatures,
|
||||
ConstraintFeatures,
|
||||
)
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from tests.fixtures.knapsack import get_knapsack_instance
|
||||
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
# Released under the modified BSD license. See COPYING.md for more details.
|
||||
import tempfile
|
||||
|
||||
from miplearn import GurobiSolver
|
||||
from miplearn.instance import write_pickle_gz, PickleGzInstance
|
||||
from miplearn.solvers.gurobi import GurobiSolver
|
||||
from tests.fixtures.knapsack import get_knapsack_instance
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user