mirror of
https://github.com/ANL-CEEESA/MIPLearn.git
synced 2025-12-08 10:28:52 -06:00
Reformat source code with Black; add pre-commit hooks and CI checks
This commit is contained in:
@@ -4,10 +4,12 @@
|
||||
|
||||
from unittest.mock import Mock, call
|
||||
|
||||
from miplearn import (StaticLazyConstraintsComponent,
|
||||
LearningSolver,
|
||||
Instance,
|
||||
InternalSolver)
|
||||
from miplearn import (
|
||||
StaticLazyConstraintsComponent,
|
||||
LearningSolver,
|
||||
Instance,
|
||||
InternalSolver,
|
||||
)
|
||||
from miplearn.classifiers import Classifier
|
||||
|
||||
|
||||
@@ -23,39 +25,47 @@ def test_usage_with_solver():
|
||||
|
||||
instance = Mock(spec=Instance)
|
||||
instance.has_static_lazy_constraints = Mock(return_value=True)
|
||||
instance.is_constraint_lazy = Mock(side_effect=lambda cid: {
|
||||
"c1": False,
|
||||
"c2": True,
|
||||
"c3": True,
|
||||
"c4": True,
|
||||
}[cid])
|
||||
instance.get_constraint_features = Mock(side_effect=lambda cid: {
|
||||
"c2": [1.0, 0.0],
|
||||
"c3": [0.5, 0.5],
|
||||
"c4": [1.0],
|
||||
}[cid])
|
||||
instance.get_constraint_category = Mock(side_effect=lambda cid: {
|
||||
"c2": "type-a",
|
||||
"c3": "type-a",
|
||||
"c4": "type-b",
|
||||
}[cid])
|
||||
instance.is_constraint_lazy = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c1": False,
|
||||
"c2": True,
|
||||
"c3": True,
|
||||
"c4": True,
|
||||
}[cid]
|
||||
)
|
||||
instance.get_constraint_features = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c2": [1.0, 0.0],
|
||||
"c3": [0.5, 0.5],
|
||||
"c4": [1.0],
|
||||
}[cid]
|
||||
)
|
||||
instance.get_constraint_category = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c2": "type-a",
|
||||
"c3": "type-a",
|
||||
"c4": "type-b",
|
||||
}[cid]
|
||||
)
|
||||
|
||||
component = StaticLazyConstraintsComponent(threshold=0.90,
|
||||
use_two_phase_gap=False,
|
||||
violation_tolerance=1.0)
|
||||
component = StaticLazyConstraintsComponent(
|
||||
threshold=0.90, use_two_phase_gap=False, violation_tolerance=1.0
|
||||
)
|
||||
component.classifiers = {
|
||||
"type-a": Mock(spec=Classifier),
|
||||
"type-b": Mock(spec=Classifier),
|
||||
}
|
||||
component.classifiers["type-a"].predict_proba = \
|
||||
Mock(return_value=[
|
||||
component.classifiers["type-a"].predict_proba = Mock(
|
||||
return_value=[
|
||||
[0.20, 0.80],
|
||||
[0.05, 0.95],
|
||||
])
|
||||
component.classifiers["type-b"].predict_proba = \
|
||||
Mock(return_value=[
|
||||
]
|
||||
)
|
||||
component.classifiers["type-b"].predict_proba = Mock(
|
||||
return_value=[
|
||||
[0.02, 0.98],
|
||||
])
|
||||
]
|
||||
)
|
||||
|
||||
# LearningSolver calls before_solve
|
||||
component.before_solve(solver, instance, None)
|
||||
@@ -67,37 +77,59 @@ def test_usage_with_solver():
|
||||
internal.get_constraint_ids.assert_called_once()
|
||||
|
||||
# Should ask if each constraint in the model is lazy
|
||||
instance.is_constraint_lazy.assert_has_calls([
|
||||
call("c1"), call("c2"), call("c3"), call("c4"),
|
||||
])
|
||||
instance.is_constraint_lazy.assert_has_calls(
|
||||
[
|
||||
call("c1"),
|
||||
call("c2"),
|
||||
call("c3"),
|
||||
call("c4"),
|
||||
]
|
||||
)
|
||||
|
||||
# For the lazy ones, should ask for features
|
||||
instance.get_constraint_features.assert_has_calls([
|
||||
call("c2"), call("c3"), call("c4"),
|
||||
])
|
||||
instance.get_constraint_features.assert_has_calls(
|
||||
[
|
||||
call("c2"),
|
||||
call("c3"),
|
||||
call("c4"),
|
||||
]
|
||||
)
|
||||
|
||||
# Should also ask for categories
|
||||
assert instance.get_constraint_category.call_count == 3
|
||||
instance.get_constraint_category.assert_has_calls([
|
||||
call("c2"), call("c3"), call("c4"),
|
||||
])
|
||||
instance.get_constraint_category.assert_has_calls(
|
||||
[
|
||||
call("c2"),
|
||||
call("c3"),
|
||||
call("c4"),
|
||||
]
|
||||
)
|
||||
|
||||
# Should ask internal solver to remove constraints identified as lazy
|
||||
assert internal.extract_constraint.call_count == 3
|
||||
internal.extract_constraint.assert_has_calls([
|
||||
call("c2"), call("c3"), call("c4"),
|
||||
])
|
||||
internal.extract_constraint.assert_has_calls(
|
||||
[
|
||||
call("c2"),
|
||||
call("c3"),
|
||||
call("c4"),
|
||||
]
|
||||
)
|
||||
|
||||
# Should ask ML to predict whether each lazy constraint should be enforced
|
||||
component.classifiers["type-a"].predict_proba.assert_called_once_with([[1.0, 0.0], [0.5, 0.5]])
|
||||
component.classifiers["type-a"].predict_proba.assert_called_once_with(
|
||||
[[1.0, 0.0], [0.5, 0.5]]
|
||||
)
|
||||
component.classifiers["type-b"].predict_proba.assert_called_once_with([[1.0]])
|
||||
|
||||
# For the ones that should be enforced, should ask solver to re-add them
|
||||
# to the formulation. The remaining ones should remain in the pool.
|
||||
assert internal.add_constraint.call_count == 2
|
||||
internal.add_constraint.assert_has_calls([
|
||||
call("<c3>"), call("<c4>"),
|
||||
])
|
||||
internal.add_constraint.assert_has_calls(
|
||||
[
|
||||
call("<c3>"),
|
||||
call("<c4>"),
|
||||
]
|
||||
)
|
||||
internal.add_constraint.reset_mock()
|
||||
|
||||
# LearningSolver calls after_iteration (first time)
|
||||
@@ -126,37 +158,45 @@ def test_usage_with_solver():
|
||||
def test_fit():
|
||||
instance_1 = Mock(spec=Instance)
|
||||
instance_1.found_violated_lazy_constraints = ["c1", "c2", "c4", "c5"]
|
||||
instance_1.get_constraint_category = Mock(side_effect=lambda cid: {
|
||||
"c1": "type-a",
|
||||
"c2": "type-a",
|
||||
"c3": "type-a",
|
||||
"c4": "type-b",
|
||||
"c5": "type-b",
|
||||
}[cid])
|
||||
instance_1.get_constraint_features = Mock(side_effect=lambda cid: {
|
||||
"c1": [1, 1],
|
||||
"c2": [1, 2],
|
||||
"c3": [1, 3],
|
||||
"c4": [1, 4, 0],
|
||||
"c5": [1, 5, 0],
|
||||
}[cid])
|
||||
instance_1.get_constraint_category = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c1": "type-a",
|
||||
"c2": "type-a",
|
||||
"c3": "type-a",
|
||||
"c4": "type-b",
|
||||
"c5": "type-b",
|
||||
}[cid]
|
||||
)
|
||||
instance_1.get_constraint_features = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c1": [1, 1],
|
||||
"c2": [1, 2],
|
||||
"c3": [1, 3],
|
||||
"c4": [1, 4, 0],
|
||||
"c5": [1, 5, 0],
|
||||
}[cid]
|
||||
)
|
||||
|
||||
instance_2 = Mock(spec=Instance)
|
||||
instance_2.found_violated_lazy_constraints = ["c2", "c3", "c4"]
|
||||
instance_2.get_constraint_category = Mock(side_effect=lambda cid: {
|
||||
"c1": "type-a",
|
||||
"c2": "type-a",
|
||||
"c3": "type-a",
|
||||
"c4": "type-b",
|
||||
"c5": "type-b",
|
||||
}[cid])
|
||||
instance_2.get_constraint_features = Mock(side_effect=lambda cid: {
|
||||
"c1": [2, 1],
|
||||
"c2": [2, 2],
|
||||
"c3": [2, 3],
|
||||
"c4": [2, 4, 0],
|
||||
"c5": [2, 5, 0],
|
||||
}[cid])
|
||||
instance_2.get_constraint_category = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c1": "type-a",
|
||||
"c2": "type-a",
|
||||
"c3": "type-a",
|
||||
"c4": "type-b",
|
||||
"c5": "type-b",
|
||||
}[cid]
|
||||
)
|
||||
instance_2.get_constraint_features = Mock(
|
||||
side_effect=lambda cid: {
|
||||
"c1": [2, 1],
|
||||
"c2": [2, 2],
|
||||
"c3": [2, 3],
|
||||
"c4": [2, 4, 0],
|
||||
"c5": [2, 5, 0],
|
||||
}[cid]
|
||||
)
|
||||
|
||||
instances = [instance_1, instance_2]
|
||||
component = StaticLazyConstraintsComponent()
|
||||
@@ -171,18 +211,22 @@ def test_fit():
|
||||
}
|
||||
expected_x = {
|
||||
"type-a": [[1, 1], [1, 2], [1, 3], [2, 1], [2, 2], [2, 3]],
|
||||
"type-b": [[1, 4, 0], [1, 5, 0], [2, 4, 0], [2, 5, 0]]
|
||||
"type-b": [[1, 4, 0], [1, 5, 0], [2, 4, 0], [2, 5, 0]],
|
||||
}
|
||||
expected_y = {
|
||||
"type-a": [[0, 1], [0, 1], [1, 0], [1, 0], [0, 1], [0, 1]],
|
||||
"type-b": [[0, 1], [0, 1], [0, 1], [1, 0]]
|
||||
"type-b": [[0, 1], [0, 1], [0, 1], [1, 0]],
|
||||
}
|
||||
assert component._collect_constraints(instances) == expected_constraints
|
||||
assert component.x(instances) == expected_x
|
||||
assert component.y(instances) == expected_y
|
||||
|
||||
component.fit(instances)
|
||||
component.classifiers["type-a"].fit.assert_called_once_with(expected_x["type-a"],
|
||||
expected_y["type-a"])
|
||||
component.classifiers["type-b"].fit.assert_called_once_with(expected_x["type-b"],
|
||||
expected_y["type-b"])
|
||||
component.classifiers["type-a"].fit.assert_called_once_with(
|
||||
expected_x["type-a"],
|
||||
expected_y["type-a"],
|
||||
)
|
||||
component.classifiers["type-b"].fit.assert_called_once_with(
|
||||
expected_x["type-b"],
|
||||
expected_y["type-b"],
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user