diff --git a/miplearn/components/steps/drop_redundant.py b/miplearn/components/steps/drop_redundant.py index 1eadfff..f22cb6c 100644 --- a/miplearn/components/steps/drop_redundant.py +++ b/miplearn/components/steps/drop_redundant.py @@ -45,6 +45,10 @@ class DropRedundantInequalitiesStep(Component): self.violation_tolerance = violation_tolerance self.max_iterations = max_iterations self.current_iteration = 0 + self.total_dropped = 0 + self.total_restored = 0 + self.total_kept = 0 + self.total_iterations = 0 def before_solve(self, solver, instance, _): self.current_iteration = 0 @@ -62,7 +66,7 @@ class DropRedundantInequalitiesStep(Component): self.total_iterations = 0 for category in y.keys(): for i in range(len(y[category])): - if y[category][i][0] == 1: + if y[category][i][1] == 1: cid = constraints[category][i] c = LazyConstraint( cid=cid, @@ -101,7 +105,7 @@ class DropRedundantInequalitiesStep(Component): for category in tqdm(x.keys(), desc="Fit (rlx:drop_ineq)"): if category not in self.classifiers: self.classifiers[category] = deepcopy(self.classifier_prototype) - self.classifiers[category].fit(x[category], y[category]) + self.classifiers[category].fit(x[category], np.array(y[category])) @staticmethod def _x_test(instance, constraint_ids): @@ -160,9 +164,9 @@ class DropRedundantInequalitiesStep(Component): if category not in y: y[category] = [] if slack > self.slack_tolerance: - y[category] += [[1]] + y[category] += [[False, True]] else: - y[category] += [[0]] + y[category] += [[True, False]] return y def predict(self, x): @@ -175,9 +179,9 @@ class DropRedundantInequalitiesStep(Component): proba = self.classifiers[category].predict_proba(x_cat) for i in range(len(proba)): if proba[i][1] >= self.threshold: - y[category] += [[1]] + y[category] += [[False, True]] else: - y[category] += [[0]] + y[category] += [[True, False]] return y def evaluate(self, instance): @@ -187,13 +191,13 @@ class DropRedundantInequalitiesStep(Component): tp, tn, fp, fn = 0, 0, 0, 0 for category in y_true.keys(): for i in range(len(y_true[category])): - if y_pred[category][i][0] == 1: - if y_true[category][i][0] == 1: + if y_pred[category][i][1] == 1: + if y_true[category][i][1] == 1: tp += 1 else: fp += 1 else: - if y_true[category][i][0] == 1: + if y_true[category][i][1] == 1: fn += 1 else: tn += 1 diff --git a/tests/components/steps/test_drop_redundant.py b/tests/components/steps/test_drop_redundant.py index 8e4c329..d673b1a 100644 --- a/tests/components/steps/test_drop_redundant.py +++ b/tests/components/steps/test_drop_redundant.py @@ -6,11 +6,13 @@ from unittest.mock import Mock, call import numpy as np +from miplearn import RelaxIntegralityStep, BasePyomoSolver 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 def _setup(): @@ -268,8 +270,20 @@ def test_x_y_fit_predict_evaluate(): ), } expected_y = { - "type-a": np.array([[0], [0], [1]]), - "type-b": np.array([[1], [0], [0]]), + "type-a": np.array( + [ + [True, False], + [True, False], + [False, True], + ] + ), + "type-b": np.array( + [ + [False, True], + [True, False], + [True, False], + ] + ), } # Should build X and Y matrices correctly @@ -287,7 +301,15 @@ def test_x_y_fit_predict_evaluate(): np.testing.assert_array_equal(actual_x, expected_x[category]) np.testing.assert_array_equal(actual_y, expected_y[category]) - assert component.predict(expected_x) == {"type-a": [[1]], "type-b": [[0], [1]]} + assert component.predict(expected_x) == { + "type-a": [ + [False, True], + ], + "type-b": [ + [True, False], + [False, True], + ], + } ev = component.evaluate(instances[1]) assert ev["True positive"] == 1 @@ -350,8 +372,20 @@ def test_x_multiple_solves(): } expected_y = { - "type-a": np.array([[1], [0], [0], [1]]), - "type-b": np.array([[1], [0]]), + "type-a": np.array( + [ + [False, True], + [True, False], + [True, False], + [False, True], + ] + ), + "type-b": np.array( + [ + [False, True], + [True, False], + ] + ), } # Should build X and Y matrices correctly @@ -362,3 +396,17 @@ def test_x_multiple_solves(): for category in ["type-a", "type-b"]: np.testing.assert_array_equal(actual_x[category], expected_x[category]) np.testing.assert_array_equal(actual_y[category], expected_y[category]) + + +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)