From 35991590f684fa1eaf222697e045b535e645a432 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Sat, 26 Apr 2025 18:04:22 +0100 Subject: [PATCH 1/2] Support for AND-constraints --- CHANGELOG.md | 1 + src/pyscipopt/scip.pxd | 5 ++ src/pyscipopt/scip.pxi | 118 +++++++++++++++++++++++++++++++++++++++++ tests/test_cons.py | 20 +++++++ 4 files changed, 144 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dadd7965..5b48cb751 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- More support for AND-Constraints - Added getLinearConsIndicator - Added SCIP_LPPARAM, setIntParam, setRealParam, getIntParam, getRealParam, isOptimal, getObjVal, getRedcost for lpi - Added isFeasPositive diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index f53421164..16c9a5488 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1612,6 +1612,11 @@ cdef extern from "scip/cons_and.h": SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode) + int SCIPgetNVarsAnd(SCIP* scip, SCIP_CONS* cons) + SCIP_VAR** SCIPgetVarsAnd(SCIP* scip, SCIP_CONS* cons) + SCIP_VAR* SCIPgetResultantAnd(SCIP* scip, SCIP_CONS* cons) + SCIP_Bool SCIPisAndConsSorted(SCIP* scip, SCIP_CONS* cons) + SCIP_RETCODE SCIPsortAndCons(SCIP* scip, SCIP_CONS* cons) cdef extern from "scip/cons_or.h": SCIP_RETCODE SCIPcreateConsOr(SCIP* scip, diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 3b847ef60..32909bcaf 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -5689,6 +5689,124 @@ cdef class Model: return vars + def getNVarsAnd(self, Constraint constraint): + """ + Gets number of variables in and constraint. + + Parameters + ---------- + constraint : Constraint + Constraint to get the number of variables from. + + Returns + ------- + int + + """ + cdef int nvars + cdef SCIP_Bool success + + return SCIPgetConsNVarsAnd(self._scip, constraint.scip_cons) + + def getConsVarsAnd(self, Constraint constraint): + """ + Gets variables in and constraint. + + Parameters + ---------- + constraint : Constraint + Constraint to get the variables from. + + Returns + ------- + list of Variable + + """ + cdef SCIP_VAR** _vars + cdef int nvars + cdef SCIP_Bool success + cdef int i + + SCIPgetConsNVarsAnd(self._scip, constraint.scip_cons, &nvars, &success) + _vars = malloc(nvars * sizeof(SCIP_VAR*)) + _vars = SCIPgetConsVarsAnd(self._scip, constraint.scip_cons) + + vars = [] + for i in range(nvars): + ptr = (_vars[i]) + # check whether the corresponding variable exists already + if ptr in self._modelvars: + vars.append(self._modelvars[ptr]) + else: + # create a new variable + var = Variable.create(_vars[i]) + assert var.ptr() == ptr + self._modelvars[ptr] = var + vars.append(var) + + return vars + + def getResultantAnd(self, Constraint constraint): + """ + Gets the resultant variable in And constraint. + + Parameters + ---------- + constraint : Constraint + Constraint to get the resultant variable from. + + Returns + ------- + Variable + + """ + cdef SCIP_VAR* _resultant + cdef SCIP_Bool success + + _resultant = SCIPgetResultantAnd(self._scip, constraint.scip_cons) + + ptr = (_resultant) + # check whether the corresponding variable exists already + if ptr not in self._modelvars: + # create a new variable + var = Variable.create(_resultant) + assert var.ptr() == ptr + self._modelvars[ptr] = var + + return resultant + + def isAndConsSorted(self, Constraint constraint): + """ + Returns if the variables of the AND-constraint are sorted with respect to their indices. + + Parameters + ---------- + constraint : Constraint + Constraint to check. + + Returns + ------- + bool + + """ + cdef SCIP_Bool success + + return SCIPisAndConsSorted(self._scip, constraint.scip_cons) + + def sortAndCons(self, Constraint constraint): + """ + Sorts the variables of the AND-constraint with respect to their indices. + + Parameters + ---------- + constraint : Constraint + Constraint to sort. + + """ + cdef SCIP_Bool success + + PY_SCIP_CALL(SCIPsortAndCons(self._scip, constraint.scip_cons)) + def printCons(self, Constraint constraint): """ Print the constraint diff --git a/tests/test_cons.py b/tests/test_cons.py index ab53e9a24..d9e026608 100644 --- a/tests/test_cons.py +++ b/tests/test_cons.py @@ -72,6 +72,26 @@ def test_cons_logical(): assert m.isEQ(m.getVal(result1), 1) assert m.isEQ(m.getVal(result2), 0) +def test_cons_and(): + m = Model() + x1 = m.addVar(vtype="B") + x2 = m.addVar(vtype="B") + result = m.addVar(vtype="B") + + and_cons = m.addConsAnd([x1, x2], result) + + assert m.getNConsAnd(and_cons) == 1 + vars = m.getVarsAnd(and_cons) + assert len(vars) == 2 + assert vars[0] == x1 + assert vars[1] == x2 + resultant_var = m.getResultAnd(and_cons) + assert resultant_var == result + m.optimize() + + m.sortAndCons(and_cons) + assert m.isAndConsSorted(and_cons) + def test_SOScons(): m = Model() x = {} From 155d786a7ce20b83020d72bc73679dfc225c6980 Mon Sep 17 00:00:00 2001 From: Joao-Dionisio Date: Sat, 26 Apr 2025 19:23:36 +0100 Subject: [PATCH 2/2] better spacing in pxd --- src/pyscipopt/scip.pxd | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index 16c9a5488..c42cb6ae6 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1612,10 +1612,15 @@ cdef extern from "scip/cons_and.h": SCIP_Bool dynamic, SCIP_Bool removable, SCIP_Bool stickingatnode) + int SCIPgetNVarsAnd(SCIP* scip, SCIP_CONS* cons) + SCIP_VAR** SCIPgetVarsAnd(SCIP* scip, SCIP_CONS* cons) + SCIP_VAR* SCIPgetResultantAnd(SCIP* scip, SCIP_CONS* cons) + SCIP_Bool SCIPisAndConsSorted(SCIP* scip, SCIP_CONS* cons) + SCIP_RETCODE SCIPsortAndCons(SCIP* scip, SCIP_CONS* cons) cdef extern from "scip/cons_or.h":