Skip to content

Commit 5eecd95

Browse files
Add getConsVals (#1033)
* Add getConsVals * fix memleak * remove useless declarations * Update src/pyscipopt/scip.pxi Co-authored-by: DominikKamp <[email protected]> * Update CHANGELOG.md Co-authored-by: DominikKamp <[email protected]> * generalize getRhs, getLhs * Apply suggestions from code review Co-authored-by: DominikKamp <[email protected]> * Change name and better test * Correct memory allocation in getConsVars * return None at not success * Apply suggestions from code review Co-authored-by: DominikKamp <[email protected]> * Update src/pyscipopt/scip.pxi Co-authored-by: DominikKamp <[email protected]> * Update src/pyscipopt/scip.pxi Co-authored-by: DominikKamp <[email protected]> --------- Co-authored-by: DominikKamp <[email protected]>
1 parent 258f9bf commit 5eecd95

File tree

4 files changed

+88
-12
lines changed

4 files changed

+88
-12
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
- Added addMatrixConsIndicator(), and tests
1010
- Added SCIPvarMarkRelaxationOnly, SCIPvarIsRelaxationOnly, SCIPvarMarkDeletable, SCIPvarIsDeletable, and tests
1111
- Wrapped SCIPgetNLPBranchCands
12+
- Added getConsVals() to get coefficients of any linear type constraint
13+
- Generalized getLhs() and getRhs() to additionally support any linear type constraint
1214
### Fixed
1315
- Raised an error when an expression is used when a variable is required
1416
### Changed

src/pyscipopt/scip.pxd

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,7 @@ cdef extern from "scip/scip.h":
858858
SCIP_RETCODE SCIPtransformCons(SCIP* scip, SCIP_CONS* cons, SCIP_CONS** transcons)
859859
SCIP_RETCODE SCIPgetTransformedCons(SCIP* scip, SCIP_CONS* cons, SCIP_CONS** transcons)
860860
SCIP_RETCODE SCIPgetConsVars(SCIP* scip, SCIP_CONS* cons, SCIP_VAR** vars, int varssize, SCIP_Bool* success)
861+
SCIP_RETCODE SCIPgetConsVals(SCIP* scip, SCIP_CONS* cons, SCIP_Real* vals, int valssize, SCIP_Bool* success)
861862
SCIP_RETCODE SCIPgetConsNVars(SCIP* scip, SCIP_CONS* cons, int* nvars, SCIP_Bool* success)
862863
SCIP_CONS** SCIPgetConss(SCIP* scip)
863864
const char* SCIPconsGetName(SCIP_CONS* cons)
@@ -1477,6 +1478,8 @@ cdef extern from "scip/cons_linear.h":
14771478
SCIP_RETCODE SCIPchgRhsLinear(SCIP* scip, SCIP_CONS* cons, SCIP_Real rhs)
14781479
SCIP_Real SCIPgetLhsLinear(SCIP* scip, SCIP_CONS* cons)
14791480
SCIP_Real SCIPgetRhsLinear(SCIP* scip, SCIP_CONS* cons)
1481+
SCIP_Real SCIPconsGetLhs(SCIP* scip, SCIP_CONS* cons, SCIP_Bool* success)
1482+
SCIP_Real SCIPconsGetRhs(SCIP* scip, SCIP_CONS* cons, SCIP_Bool* success)
14801483
SCIP_RETCODE SCIPchgCoefLinear(SCIP* scip, SCIP_CONS* cons, SCIP_VAR* var, SCIP_Real newval)
14811484
SCIP_RETCODE SCIPdelCoefLinear(SCIP* scip, SCIP_CONS* cons, SCIP_VAR*)
14821485
SCIP_RETCODE SCIPaddCoefLinear(SCIP* scip, SCIP_CONS* cons, SCIP_VAR*, SCIP_Real val)

src/pyscipopt/scip.pxi

Lines changed: 64 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2196,6 +2196,18 @@ cdef class Constraint:
21962196
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
21972197
return constype == 'knapsack'
21982198

2199+
def isLinearType(self):
2200+
"""
2201+
Returns True if constraint can be represented as a linear constraint.
2202+
2203+
Returns
2204+
-------
2205+
bool
2206+
2207+
"""
2208+
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
2209+
return constype in ('linear', 'knapsack', 'setppc', 'logicor', 'varbound')
2210+
21992211
def isNonlinear(self):
22002212
"""
22012213
Returns True if constraint is nonlinear.
@@ -6019,7 +6031,11 @@ cdef class Model:
60196031

60206032
SCIPgetConsNVars(self._scip, constraint.scip_cons, &nvars, &success)
60216033
_vars = <SCIP_VAR**> malloc(nvars * sizeof(SCIP_VAR*))
6022-
SCIPgetConsVars(self._scip, constraint.scip_cons, _vars, nvars*sizeof(SCIP_VAR*), &success)
6034+
SCIPgetConsVars(self._scip, constraint.scip_cons, _vars, nvars, &success)
6035+
6036+
if not success:
6037+
free(_vars)
6038+
return None
60236039

60246040
vars = []
60256041
for i in range(nvars):
@@ -6034,7 +6050,39 @@ cdef class Model:
60346050
self._modelvars[ptr] = var
60356051
vars.append(var)
60366052

6053+
free(_vars)
60376054
return vars
6055+
6056+
def getConsVals(self, Constraint constraint):
6057+
"""
6058+
Returns the value array of an arbitrary SCIP constraint that can be represented as a single linear constraint.
6059+
6060+
Parameters
6061+
----------
6062+
constraint : Constraint
6063+
Constraint to get the values from.
6064+
6065+
Returns
6066+
-------
6067+
list of float
6068+
6069+
"""
6070+
cdef SCIP_Real* _vals
6071+
cdef int nvars
6072+
cdef SCIP_Bool success
6073+
cdef int i
6074+
6075+
nvars = self.getConsNVars(constraint)
6076+
_vals = <SCIP_Real*> malloc(nvars * sizeof(SCIP_Real))
6077+
PY_SCIP_CALL(SCIPgetConsVals(self._scip, constraint.scip_cons, _vals, nvars, &success))
6078+
6079+
if not success:
6080+
free(_vals)
6081+
return None
6082+
6083+
vals = [_vals[i] for i in range(nvars)]
6084+
free(_vals)
6085+
return vals
60386086

60396087
def getNVarsAnd(self, Constraint and_cons):
60406088
"""
@@ -6050,8 +6098,6 @@ cdef class Model:
60506098
int
60516099
60526100
"""
6053-
cdef int nvars
6054-
cdef SCIP_Bool success
60556101

60566102
return SCIPgetNVarsAnd(self._scip, and_cons.scip_cons)
60576103

@@ -6069,9 +6115,9 @@ cdef class Model:
60696115
list of Variable
60706116
60716117
"""
6118+
60726119
cdef SCIP_VAR** _vars
60736120
cdef int nvars
6074-
cdef SCIP_Bool success
60756121
cdef int i
60766122

60776123
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(and_cons.scip_cons))).decode('UTF-8')
@@ -6109,8 +6155,8 @@ cdef class Model:
61096155
Variable
61106156
61116157
"""
6158+
61126159
cdef SCIP_VAR* _resultant
6113-
cdef SCIP_Bool success
61146160

61156161
_resultant = SCIPgetResultantAnd(self._scip, and_cons.scip_cons)
61166162

@@ -6140,8 +6186,7 @@ cdef class Model:
61406186
bool
61416187
61426188
"""
6143-
cdef SCIP_Bool success
6144-
6189+
61456190
return SCIPisAndConsSorted(self._scip, and_cons.scip_cons)
61466191

61476192
def sortAndCons(self, Constraint and_cons):
@@ -6154,7 +6199,6 @@ cdef class Model:
61546199
Constraint to sort.
61556200
61566201
"""
6157-
cdef SCIP_Bool success
61586202

61596203
PY_SCIP_CALL(SCIPsortAndCons(self._scip, and_cons.scip_cons))
61606204

@@ -7270,9 +7314,13 @@ cdef class Model:
72707314
float
72717315
72727316
"""
7317+
cdef SCIP_Bool success
72737318
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
7274-
if constype == 'linear':
7275-
return SCIPgetRhsLinear(self._scip, cons.scip_cons)
7319+
7320+
if cons.isLinearType():
7321+
rhs = SCIPconsGetRhs(self._scip, cons.scip_cons, &success)
7322+
assert(success)
7323+
return rhs
72767324
elif constype == 'nonlinear':
72777325
return SCIPgetRhsNonlinear(cons.scip_cons)
72787326
else:
@@ -7311,9 +7359,13 @@ cdef class Model:
73117359
float
73127360
73137361
"""
7362+
cdef SCIP_Bool success
73147363
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
7315-
if constype == 'linear':
7316-
return SCIPgetLhsLinear(self._scip, cons.scip_cons)
7364+
7365+
if cons.isLinearType():
7366+
lhs = SCIPconsGetLhs(self._scip, cons.scip_cons, &success)
7367+
assert(success)
7368+
return lhs
73177369
elif constype == 'nonlinear':
73187370
return SCIPgetLhsNonlinear(cons.scip_cons)
73197371
else:

tests/test_cons.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,23 @@ def test_getConsVars():
2727
c = m.addCons(quicksum(x[i] for i in x) <= 1)
2828
assert m.getConsVars(c) == [x[i] for i in x]
2929

30+
def test_getConsVals():
31+
n_vars = 100
32+
m = Model()
33+
x = {}
34+
for i in range(n_vars):
35+
x[i] = m.addVar("%i" % i, vtype="B")
36+
37+
c1 = m.addCons(quicksum(x[i] for i in x) <= 1)
38+
c2 = m.addConsKnapsack([x[i] for i in x], [i for i in range(1, n_vars+1)], 10)
39+
vals1 = m.getConsVals(c1)
40+
vals2 = m.getConsVals(c2)
41+
42+
assert len(vals1) == n_vars
43+
assert all(isinstance(v, float) for v in vals1)
44+
assert len(vals2) == n_vars
45+
assert all(isinstance(v, float) for v in vals2)
46+
assert m.getConsVals(c2) == [i for i in range(1, n_vars+1)]
3047

3148
def test_constraint_option_setting():
3249
m = Model()
@@ -266,6 +283,8 @@ def test_cons_knapsack():
266283
m.chgCapacityKnapsack(knapsack_cons, 5)
267284

268285
assert m.getCapacityKnapsack(knapsack_cons) == 5
286+
assert m.getRhs(knapsack_cons) == 5
287+
assert m.getLhs(knapsack_cons) == -m.infinity()
269288

270289
m.addCoefKnapsack(knapsack_cons, z, 3)
271290
weights = m.getWeightsKnapsack(knapsack_cons)

0 commit comments

Comments
 (0)