Skip to content

Commit 05f8b0d

Browse files
author
xueyuan
committed
Support trivial constraints in SCIP interfaces
1 parent b953cf9 commit 05f8b0d

3 files changed

Lines changed: 49 additions & 0 deletions

File tree

pyomo/contrib/solver/solvers/scip/base.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
EqualityExpression,
4747
InequalityExpression,
4848
RangedExpression,
49+
TrivialRelationalExpression,
4950
)
5051
from pyomo.core.expr.visitor import StreamBasedExpressionVisitor
5152
from pyomo.gdp.disjunct import AutoLinkedBinaryVar
@@ -218,6 +219,12 @@ def _handle_inequality(node, data, opt, visitor):
218219
return data[0] <= data[1]
219220

220221

222+
def _handle_trivial_inequality(node, data, opt, visitor):
223+
# Keep the constant relation symbolic so that PySCIPOpt creates a
224+
# constraint instead of Python evaluating it to a bool.
225+
return scip.Expr() + data[0] <= data[1]
226+
227+
221228
def _handle_named_expression(node, data, opt, visitor):
222229
return data[0]
223230

@@ -244,6 +251,7 @@ def _handle_unit(node, data, opt, visitor):
244251
EqualityExpression: _handle_equality,
245252
RangedExpression: _handle_ranged,
246253
InequalityExpression: _handle_inequality,
254+
TrivialRelationalExpression: _handle_trivial_inequality,
247255
ScalarExpression: _handle_named_expression,
248256
ExpressionData: _handle_named_expression,
249257
VarData: _handle_var,

pyomo/contrib/solver/tests/solvers/test_scip_direct.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,26 @@ def test_infeasible_raises_no_feasible_solution_error(self):
243243
m, load_solutions=True, raise_exception_on_nonoptimal_result=False
244244
)
245245

246+
def test_trivial_constraints(self):
247+
m = pyo.ConcreteModel()
248+
m.x = pyo.Var(bounds=(0, 1))
249+
m.feasible = pyo.Constraint(expr=pyo.Constraint.Feasible)
250+
m.obj = pyo.Objective(expr=m.x)
251+
252+
opt = ScipDirect()
253+
res = opt.solve(m)
254+
self.assertEqual(res.solution_status, SolutionStatus.optimal)
255+
self.assertAlmostEqual(m.x.value, 0)
256+
257+
m.infeasible = pyo.Constraint(expr=pyo.Constraint.Infeasible)
258+
res = opt.solve(
259+
m, load_solutions=False, raise_exception_on_nonoptimal_result=False
260+
)
261+
self.assertEqual(
262+
res.termination_condition, TerminationCondition.provenInfeasible
263+
)
264+
self.assertEqual(res.solution_status, SolutionStatus.noSolution)
265+
246266
def test_timer(self):
247267
m = self.create_lp_model()
248268
timer = HierarchicalTimer()

pyomo/contrib/solver/tests/solvers/test_scip_persistent.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,3 +289,24 @@ def test_infeasible_raises(self):
289289
opt.solve(
290290
m, load_solutions=True, raise_exception_on_nonoptimal_result=False
291291
)
292+
293+
def test_trivial_constraints(self):
294+
m = pyo.ConcreteModel()
295+
m.x = pyo.Var(bounds=(0, 1))
296+
m.feasible = pyo.Constraint(expr=pyo.Constraint.Feasible)
297+
m.obj = pyo.Objective(expr=m.x)
298+
299+
opt = ScipPersistent()
300+
res = opt.solve(m)
301+
self.assertEqual(res.solution_status, SolutionStatus.optimal)
302+
self.assertAlmostEqual(m.x.value, 0)
303+
304+
m.infeasible = pyo.Constraint(expr=pyo.Constraint.Infeasible)
305+
opt.add_constraints([m.infeasible])
306+
res = opt.solve(
307+
m, load_solutions=False, raise_exception_on_nonoptimal_result=False
308+
)
309+
self.assertEqual(
310+
res.termination_condition, TerminationCondition.provenInfeasible
311+
)
312+
self.assertEqual(res.solution_status, SolutionStatus.noSolution)

0 commit comments

Comments
 (0)