From 96355df4552fb1ba8b96231b8320c2876707801f Mon Sep 17 00:00:00 2001 From: Atalay Kutlay Date: Sat, 1 Jun 2024 23:50:59 -0400 Subject: [PATCH 1/2] bug: Sort indices before sending to solver --- pyomo/contrib/appsi/solvers/highs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyomo/contrib/appsi/solvers/highs.py b/pyomo/contrib/appsi/solvers/highs.py index c948444839d..57a7b1eac72 100644 --- a/pyomo/contrib/appsi/solvers/highs.py +++ b/pyomo/contrib/appsi/solvers/highs.py @@ -481,7 +481,7 @@ def _remove_constraints(self, cons: List[ConstraintData]): indices_to_remove.append(con_ndx) self._mutable_helpers.pop(con, None) self._solver_model.deleteRows( - len(indices_to_remove), np.array(indices_to_remove) + len(indices_to_remove), np.sort(np.array(indices_to_remove)) ) con_ndx = 0 new_con_map = dict() From 9c73e8f8d0a7a6acc6c0041220c709bf6dae96c6 Mon Sep 17 00:00:00 2001 From: Atalay Kutlay Date: Sat, 1 Jun 2024 23:52:21 -0400 Subject: [PATCH 2/2] Add test for variable fix and unfix --- .../solvers/tests/test_highs_persistent.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/pyomo/contrib/appsi/solvers/tests/test_highs_persistent.py b/pyomo/contrib/appsi/solvers/tests/test_highs_persistent.py index b26f45ff2cc..4d8251e0de9 100644 --- a/pyomo/contrib/appsi/solvers/tests/test_highs_persistent.py +++ b/pyomo/contrib/appsi/solvers/tests/test_highs_persistent.py @@ -80,6 +80,43 @@ def test_mutable_params_with_remove_vars(self): res = opt.solve(m) self.assertAlmostEqual(res.best_feasible_objective, -9) + def test_fix_and_unfix(self): + # Tests issue https://github.com/Pyomo/pyomo/issues/3127 + + m = pe.ConcreteModel() + m.x = pe.Var(domain=pe.Binary) + m.y = pe.Var(domain=pe.Binary) + m.fx = pe.Var(domain=pe.NonNegativeReals) + m.fy = pe.Var(domain=pe.NonNegativeReals) + m.c1 = pe.Constraint(expr=m.fx <= m.x) + m.c2 = pe.Constraint(expr=m.fy <= m.y) + m.c3 = pe.Constraint(expr=m.x + m.y <= 1) + + m.obj = pe.Objective(expr=m.fx * 0.5 + m.fy * 0.4, sense=pe.maximize) + + opt = Highs() + + # solution 1 has m.x == 1 and m.y == 0 + r = opt.solve(m) + self.assertAlmostEqual(m.fx.value, 1, places=5) + self.assertAlmostEqual(m.fy.value, 0, places=5) + self.assertAlmostEqual(r.best_feasible_objective, 0.5, places=5) + + # solution 2 has m.x == 0 and m.y == 1 + m.y.fix(1) + r = opt.solve(m) + self.assertAlmostEqual(m.fx.value, 0, places=5) + self.assertAlmostEqual(m.fy.value, 1, places=5) + self.assertAlmostEqual(r.best_feasible_objective, 0.4, places=5) + + # solution 3 should be equal solution 1 + m.y.unfix() + m.x.fix(1) + r = opt.solve(m) + self.assertAlmostEqual(m.fx.value, 1, places=5) + self.assertAlmostEqual(m.fy.value, 0, places=5) + self.assertAlmostEqual(r.best_feasible_objective, 0.5, places=5) + def test_capture_highs_output(self): # tests issue #3003 #