mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-06 01:18:52 -06:00
Refactor StaticLazy; remove old constraint methods
This commit is contained in:
@@ -16,6 +16,7 @@ from miplearn.features import (
|
||||
Features,
|
||||
Constraint,
|
||||
Sample,
|
||||
ConstraintFeatures,
|
||||
)
|
||||
from miplearn.instance.base import Instance
|
||||
from miplearn.solvers.internal import InternalSolver
|
||||
@@ -32,23 +33,21 @@ def sample() -> Sample:
|
||||
instance=InstanceFeatures(
|
||||
lazy_constraint_count=4,
|
||||
),
|
||||
constraints_old={
|
||||
"c1": Constraint(category="type-a", lazy=True),
|
||||
"c2": Constraint(category="type-a", lazy=True),
|
||||
"c3": Constraint(category="type-a", lazy=True),
|
||||
"c4": Constraint(category="type-b", lazy=True),
|
||||
"c5": Constraint(category="type-b", lazy=False),
|
||||
},
|
||||
constraints=ConstraintFeatures(
|
||||
names=("c1", "c2", "c3", "c4", "c5"),
|
||||
categories=(
|
||||
"type-a",
|
||||
"type-a",
|
||||
"type-a",
|
||||
"type-b",
|
||||
"type-b",
|
||||
),
|
||||
lazy=(True, True, True, True, False),
|
||||
),
|
||||
),
|
||||
after_lp=Features(
|
||||
instance=InstanceFeatures(),
|
||||
constraints_old={
|
||||
"c1": Constraint(),
|
||||
"c2": Constraint(),
|
||||
"c3": Constraint(),
|
||||
"c4": Constraint(),
|
||||
"c5": Constraint(),
|
||||
},
|
||||
constraints=ConstraintFeatures(names=("c1", "c2", "c3", "c4", "c5")),
|
||||
),
|
||||
after_mip=Features(
|
||||
extra={
|
||||
@@ -57,17 +56,14 @@ def sample() -> Sample:
|
||||
),
|
||||
)
|
||||
sample.after_lp.instance.to_list = Mock(return_value=[5.0]) # type: ignore
|
||||
sample.after_lp.constraints_old["c1"].to_list = Mock( # type: ignore
|
||||
return_value=[1.0, 1.0]
|
||||
)
|
||||
sample.after_lp.constraints_old["c2"].to_list = Mock( # type: ignore
|
||||
return_value=[1.0, 2.0]
|
||||
)
|
||||
sample.after_lp.constraints_old["c3"].to_list = Mock( # type: ignore
|
||||
return_value=[1.0, 3.0]
|
||||
)
|
||||
sample.after_lp.constraints_old["c4"].to_list = Mock( # type: ignore
|
||||
return_value=[1.0, 4.0, 0.0]
|
||||
sample.after_lp.constraints.to_list = Mock( # type: ignore
|
||||
side_effect=lambda idx: {
|
||||
0: [1.0, 1.0],
|
||||
1: [1.0, 2.0],
|
||||
2: [1.0, 3.0],
|
||||
3: [1.0, 4.0, 0.0],
|
||||
4: None,
|
||||
}[idx]
|
||||
)
|
||||
return sample
|
||||
|
||||
@@ -87,6 +83,9 @@ def test_usage_with_solver(instance: Instance) -> None:
|
||||
|
||||
internal = solver.internal_solver = Mock(spec=InternalSolver)
|
||||
internal.is_constraint_satisfied_old = Mock(return_value=False)
|
||||
internal.are_constraints_satisfied = Mock(
|
||||
side_effect=lambda cf, tol=1.0: [False for i in range(len(cf.names))]
|
||||
)
|
||||
|
||||
component = StaticLazyConstraintsComponent(violation_tolerance=1.0)
|
||||
component.thresholds["type-a"] = MinProbabilityThreshold([0.5, 0.5])
|
||||
@@ -115,7 +114,6 @@ def test_usage_with_solver(instance: Instance) -> None:
|
||||
stats: LearningSolveStats = {}
|
||||
sample = instance.samples[0]
|
||||
assert sample.after_load is not None
|
||||
assert sample.after_load.constraints_old is not None
|
||||
assert sample.after_mip is not None
|
||||
assert sample.after_mip.extra is not None
|
||||
del sample.after_mip.extra["lazy_enforced"]
|
||||
@@ -134,8 +132,8 @@ def test_usage_with_solver(instance: Instance) -> None:
|
||||
component.classifiers["type-b"].predict_proba.assert_called_once()
|
||||
|
||||
# Should ask internal solver to remove some constraints
|
||||
assert internal.remove_constraint.call_count == 1
|
||||
internal.remove_constraint.assert_has_calls([call("c3")])
|
||||
assert internal.remove_constraints.call_count == 1
|
||||
internal.remove_constraints.assert_has_calls([call(("c3",))])
|
||||
|
||||
# LearningSolver calls after_iteration (first time)
|
||||
should_repeat = component.iteration_cb(solver, instance, None)
|
||||
@@ -143,19 +141,20 @@ def test_usage_with_solver(instance: Instance) -> None:
|
||||
|
||||
# Should ask internal solver to verify if constraints in the pool are
|
||||
# satisfied and add the ones that are not
|
||||
c3 = sample.after_load.constraints_old["c3"]
|
||||
internal.is_constraint_satisfied_old.assert_called_once_with(c3, tol=1.0)
|
||||
internal.is_constraint_satisfied_old.reset_mock()
|
||||
internal.add_constraint.assert_called_once_with(c3, name="c3")
|
||||
internal.add_constraint.reset_mock()
|
||||
assert sample.after_load.constraints is not None
|
||||
c = sample.after_load.constraints[False, False, True, False, False]
|
||||
internal.are_constraints_satisfied.assert_called_once_with(c, tol=1.0)
|
||||
internal.are_constraints_satisfied.reset_mock()
|
||||
internal.add_constraints.assert_called_once_with(c)
|
||||
internal.add_constraints.reset_mock()
|
||||
|
||||
# LearningSolver calls after_iteration (second time)
|
||||
should_repeat = component.iteration_cb(solver, instance, None)
|
||||
assert not should_repeat
|
||||
|
||||
# The lazy constraint pool should be empty by now, so no calls should be made
|
||||
internal.is_constraint_satisfied_old.assert_not_called()
|
||||
internal.add_constraint.assert_not_called()
|
||||
internal.are_constraints_satisfied.assert_not_called()
|
||||
internal.add_constraints.assert_not_called()
|
||||
|
||||
# LearningSolver calls after_solve_mip
|
||||
component.after_solve_mip(
|
||||
|
||||
@@ -50,7 +50,6 @@ def test_redundancy() -> None:
|
||||
solver.relax_constraints(["c1"])
|
||||
stats = solver.solve_lp()
|
||||
assert stats.lp_value == 2.0
|
||||
assert solver.is_constraint_satisfied(["c1"]) == [False]
|
||||
|
||||
solver.enforce_constraints(["c1"])
|
||||
stats = solver.solve_lp()
|
||||
|
||||
@@ -28,7 +28,6 @@ def test_knapsack() -> None:
|
||||
|
||||
features = FeaturesExtractor().extract(instance, solver)
|
||||
assert features.variables is not None
|
||||
assert features.constraints_old is not None
|
||||
assert features.instance is not None
|
||||
|
||||
assert_equals(
|
||||
@@ -66,22 +65,29 @@ def test_knapsack() -> None:
|
||||
),
|
||||
)
|
||||
assert_equals(
|
||||
_round_constraints(features.constraints_old),
|
||||
{
|
||||
"eq_capacity": Constraint(
|
||||
basis_status="N",
|
||||
category="eq_capacity",
|
||||
dual_value=13.538462,
|
||||
lazy=False,
|
||||
lhs={"x[0]": 23.0, "x[1]": 26.0, "x[2]": 20.0, "x[3]": 18.0, "z": -1.0},
|
||||
rhs=0.0,
|
||||
sa_rhs_down=-24.0,
|
||||
sa_rhs_up=1.9999999999999987,
|
||||
sense="=",
|
||||
slack=0.0,
|
||||
user_features=[0.0],
|
||||
)
|
||||
},
|
||||
_round(features.constraints),
|
||||
ConstraintFeatures(
|
||||
basis_status=("N",),
|
||||
categories=("eq_capacity",),
|
||||
dual_values=(13.538462,),
|
||||
names=("eq_capacity",),
|
||||
lazy=(False,),
|
||||
lhs=(
|
||||
(
|
||||
("x[0]", 23.0),
|
||||
("x[1]", 26.0),
|
||||
("x[2]", 20.0),
|
||||
("x[3]", 18.0),
|
||||
("z", -1.0),
|
||||
),
|
||||
),
|
||||
rhs=(0.0,),
|
||||
sa_rhs_down=(-24.0,),
|
||||
sa_rhs_up=(2.0,),
|
||||
senses=("=",),
|
||||
slacks=(0.0,),
|
||||
user_features=((0.0,),),
|
||||
),
|
||||
)
|
||||
assert_equals(
|
||||
features.instance,
|
||||
|
||||
Reference in New Issue
Block a user