diff --git a/miplearn/solvers/gurobi.py b/miplearn/solvers/gurobi.py index 167a456..e76f738 100644 --- a/miplearn/solvers/gurobi.py +++ b/miplearn/solvers/gurobi.py @@ -274,6 +274,14 @@ class GurobiSolver(InternalSolver): def get_constraint_slacks(self): return {c.ConstrName: c.Slack for c in self.model.getConstrs()} + def set_constraint_sense(self, cid, sense): + c = self.model.getConstrByName(cid) + c.Sense = sense + + def set_constraint_rhs(self, cid, rhs): + c = self.model.getConstrByName(cid) + c.RHS = rhs + def relax(self): self.model = self.model.relax() self._update_vars() diff --git a/miplearn/solvers/internal.py b/miplearn/solvers/internal.py index 540c870..84c946b 100644 --- a/miplearn/solvers/internal.py +++ b/miplearn/solvers/internal.py @@ -195,6 +195,14 @@ class InternalSolver(ABC): def is_constraint_satisfied(self, cobj): pass + @abstractmethod + def set_constraint_sense(self, cid, sense): + pass + + @abstractmethod + def set_constraint_rhs(self, cid, rhs): + pass + @abstractmethod def set_threads(self, threads): pass diff --git a/miplearn/solvers/pyomo/base.py b/miplearn/solvers/pyomo/base.py index 7c22801..d44de7e 100644 --- a/miplearn/solvers/pyomo/base.py +++ b/miplearn/solvers/pyomo/base.py @@ -219,12 +219,6 @@ class BasePyomoSolver(InternalSolver): def get_constraint_ids(self): return list(self._cname_to_constr.keys()) - def extract_constraint(self, cid): - raise Exception("Not implemented") - - def is_constraint_satisfied(self, cobj): - raise Exception("Not implemented") - @abstractmethod def _get_warm_start_regexp(self): pass @@ -249,8 +243,20 @@ class BasePyomoSolver(InternalSolver): def _get_gap_tolerance_option_name(self): pass + def extract_constraint(self, cid): + raise Exception("Not implemented") + + def is_constraint_satisfied(self, cobj): + raise Exception("Not implemented") + def relax(self): raise Exception("not implemented") def get_constraint_slacks(self): raise Exception("not implemented") + + def set_constraint_sense(self, cid, sense): + raise Exception("Not implemented") + + def set_constraint_rhs(self, cid, rhs): + raise Exception("Not implemented") diff --git a/miplearn/solvers/tests/test_internal_solver.py b/miplearn/solvers/tests/test_internal_solver.py index 4d68394..bde466d 100644 --- a/miplearn/solvers/tests/test_internal_solver.py +++ b/miplearn/solvers/tests/test_internal_solver.py @@ -122,7 +122,7 @@ def test_internal_solver(): assert stats["Lower bound"] == 1030.0 if isinstance(solver, GurobiSolver): - # Extract new constraint + # Extract the new constraint cobj = solver.extract_constraint("cut") # New constraint should no longer affect solution and should no longer @@ -145,6 +145,13 @@ def test_internal_solver(): # New constraint should now be satisfied assert solver.is_constraint_satisfied(cobj) + # Relax problem and make cut into an equality constraint + solver.relax() + solver.set_constraint_rhs("cut", 0.5) + solver.set_constraint_sense("cut", "=") + stats = solver.solve() + assert round(stats["Lower bound"]) == 1179.0 + def test_iteration_cb(): for solver_class in _get_internal_solvers():