Skip to content

Commit f83e2a4

Browse files
Add three functions for variables (#1107)
* add function to get variable status * add isActive to get whether variable is active * add markDoNotMultaggrVar to mark a variable * Update src/pyscipopt/scip.pxi Co-authored-by: João Dionísio <[email protected]> * Update src/pyscipopt/scip.pxi Co-authored-by: João Dionísio <[email protected]> * Add markDoNotAggrVar and minor readme changes * formatting in scip.pxi * add test for variable marking * test getStatus as well --------- Co-authored-by: João Dionísio <[email protected]> Co-authored-by: Joao-Dionisio <[email protected]>
1 parent f6536f0 commit f83e2a4

File tree

4 files changed

+113
-21
lines changed

4 files changed

+113
-21
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
- MatrixVariable comparisons (<=, >=, ==) now support numpy's broadcast feature.
88
- Added methods: getMaxDepth(), getPlungeDepth(), getLowerbound(), getCutoffbound(), getNNodeLPIterations(), getNStrongbranchLPIterations().
99
- setup.py now automatically detects conda environments when SCIPOPTDIR is not defined.
10+
- Added function getStatus() to get variable status in variable class
11+
- Added function isActive() to get whether a variable is active in variable class
12+
- Added function markDoNotAggrVar() to prevent a variable from being aggregated
13+
- Added function markDoNotMultaggrVar() to prevent a variable from being multi-aggregated
1014
### Fixed
1115
- Implemented all binary operations between MatrixExpr and GenExpr
1216
- Fixed the type of @ matrix operation result from MatrixVariable to MatrixExpr.

src/pyscipopt/scip.pxd

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ cdef extern from "scip/scip.h":
3030
SCIP_VARTYPE SCIP_VARTYPE_IMPLINT
3131
SCIP_VARTYPE SCIP_VARTYPE_CONTINUOUS
3232

33+
ctypedef int SCIP_VARSTATUS
34+
cdef extern from "scip/type_var.h":
35+
SCIP_VARSTATUS SCIP_VARSTATUS_ORIGINAL
36+
SCIP_VARSTATUS SCIP_VARSTATUS_LOOSE
37+
SCIP_VARSTATUS SCIP_VARSTATUS_COLUMN
38+
SCIP_VARSTATUS SCIP_VARSTATUS_FIXED
39+
SCIP_VARSTATUS SCIP_VARSTATUS_AGGREGATED
40+
SCIP_VARSTATUS SCIP_VARSTATUS_MULTAGGR
41+
SCIP_VARSTATUS SCIP_VARSTATUS_NEGATED
42+
3343
ctypedef int SCIP_OBJSENSE
3444
cdef extern from "scip/type_prob.h":
3545
SCIP_OBJSENSE SCIP_OBJSENSE_MAXIMIZE
@@ -768,6 +778,8 @@ cdef extern from "scip/scip.h":
768778
SCIP_RETCODE SCIPdelVar(SCIP* scip, SCIP_VAR* var, SCIP_Bool* deleted)
769779

770780
SCIP_RETCODE SCIPchgVarType(SCIP* scip, SCIP_VAR* var, SCIP_VARTYPE vartype, SCIP_Bool* infeasible)
781+
SCIP_RETCODE SCIPmarkDoNotAggrVar(SCIP* scip, SCIP_VAR* var)
782+
SCIP_RETCODE SCIPmarkDoNotMultaggrVar(SCIP* scip, SCIP_VAR* var)
771783
SCIP_RETCODE SCIPcaptureVar(SCIP* scip, SCIP_VAR* var)
772784
SCIP_RETCODE SCIPaddPricedVar(SCIP* scip, SCIP_VAR* var, SCIP_Real score)
773785
SCIP_RETCODE SCIPreleaseVar(SCIP* scip, SCIP_VAR** var)
@@ -790,8 +802,10 @@ cdef extern from "scip/scip.h":
790802
int SCIPgetNImplVars(SCIP* scip)
791803
int SCIPgetNContVars(SCIP* scip)
792804
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR* var)
805+
SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR* var)
793806
SCIP_Bool SCIPvarIsOriginal(SCIP_VAR* var)
794807
SCIP_Bool SCIPvarIsTransformed(SCIP_VAR* var)
808+
SCIP_Bool SCIPvarIsActive(SCIP_VAR* var)
795809
SCIP_COL* SCIPvarGetCol(SCIP_VAR* var)
796810
SCIP_Bool SCIPvarIsInLP(SCIP_VAR* var)
797811
SCIP_Real SCIPvarGetLbOriginal(SCIP_VAR* var)

src/pyscipopt/scip.pxi

Lines changed: 62 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,7 +1120,6 @@ cdef class Solution:
11201120
PY_SCIP_CALL(SCIPtranslateSubSol(target._scip, self.scip, self.sol, NULL, source_vars, &(targetSol.sol)))
11211121
return targetSol
11221122

1123-
11241123
cdef class BoundChange:
11251124
"""Bound change."""
11261125

@@ -1538,6 +1537,32 @@ cdef class Variable(Expr):
15381537
elif vartype == SCIP_VARTYPE_IMPLINT:
15391538
return "IMPLINT"
15401539

1540+
def getStatus(self):
1541+
"""
1542+
Retrieve the variable status (ORIGINAL, LOOSE, COLUMN, FIXED, AGGREGATED, MULTAGGR, NEGATED)
1543+
1544+
Returns
1545+
-------
1546+
str
1547+
"ORIGINAL", "LOOSE", "COLUMN", "FIXED", "AGGREGATED", "MULTAGGR", "NEGATED"
1548+
1549+
"""
1550+
varstatus = SCIPvarGetStatus(self.scip_var)
1551+
if varstatus == SCIP_VARSTATUS_ORIGINAL:
1552+
return "ORIGINAL"
1553+
elif varstatus == SCIP_VARSTATUS_LOOSE:
1554+
return "LOOSE"
1555+
elif varstatus == SCIP_VARSTATUS_COLUMN:
1556+
return "COLUMN"
1557+
elif varstatus == SCIP_VARSTATUS_FIXED:
1558+
return "FIXED"
1559+
elif varstatus == SCIP_VARSTATUS_AGGREGATED:
1560+
return "AGGREGATED"
1561+
elif varstatus == SCIP_VARSTATUS_MULTAGGR:
1562+
return "MULTAGGR"
1563+
elif varstatus == SCIP_VARSTATUS_NEGATED:
1564+
return "NEGATED"
1565+
15411566
def isOriginal(self):
15421567
"""
15431568
Retrieve whether the variable belongs to the original problem
@@ -1549,6 +1574,17 @@ cdef class Variable(Expr):
15491574
"""
15501575
return SCIPvarIsOriginal(self.scip_var)
15511576

1577+
def isActive(self):
1578+
"""
1579+
Retrieve whether the variable is active
1580+
1581+
Returns
1582+
-------
1583+
bool
1584+
1585+
"""
1586+
return SCIPvarIsActive(self.scip_var)
1587+
15521588
def isInLP(self):
15531589
"""
15541590
Retrieve whether the variable is a COLUMN variable that is member of the current LP.
@@ -1560,7 +1596,6 @@ cdef class Variable(Expr):
15601596
"""
15611597
return SCIPvarIsInLP(self.scip_var)
15621598

1563-
15641599
def getIndex(self):
15651600
"""
15661601
Retrieve the unique index of the variable.
@@ -2043,7 +2078,6 @@ class MatrixVariable(MatrixExpr):
20432078
mayround[idx] = self[idx].varMayRound()
20442079
return mayround
20452080

2046-
20472081
cdef class Constraint:
20482082
"""Base class holding a pointer to corresponding SCIP_CONS"""
20492083

@@ -2584,7 +2618,6 @@ cdef class Model:
25842618
else:
25852619
PY_SCIP_CALL(SCIPcopy(sourceModel._scip, self._scip, NULL, NULL, n, globalcopy, enablepricing, threadsafe, True, self._valid))
25862620

2587-
25882621
def attachEventHandlerCallback(self,
25892622
callback,
25902623
events,
@@ -2634,7 +2667,6 @@ cdef class Model:
26342667

26352668
self.includeEventhdlr(event_handler, name, description)
26362669

2637-
26382670
def __dealloc__(self):
26392671
# call C function directly, because we can no longer call this object's methods, according to
26402672
# http://docs.cython.org/src/reference/extension_types.html#finalization-dealloc
@@ -2817,7 +2849,6 @@ cdef class Model:
28172849
"""
28182850
return SCIPminorVersion()
28192851

2820-
28212852
def getTechVersion(self):
28222853
"""
28232854
Retrieve SCIP technical version.
@@ -3559,7 +3590,6 @@ cdef class Model:
35593590
return iters
35603591

35613592
# Objective function
3562-
35633593
def setMinimize(self):
35643594
"""Set the objective sense to minimization."""
35653595
PY_SCIP_CALL(SCIPsetObjsense(self._scip, SCIP_OBJSENSE_MINIMIZE))
@@ -3905,7 +3935,6 @@ cdef class Model:
39053935
locale.setlocale(locale.LC_NUMERIC,user_locale)
39063936

39073937
# Variable Functions
3908-
39093938
def addVar(self, name='', vtype='C', lb=0.0, ub=None, obj=0.0, pricedVar=False, pricedVarScore=1.0, deletable=False):
39103939
"""
39113940
Create a new variable. Default variable is non-negative and continuous.
@@ -4385,7 +4414,6 @@ cdef class Model:
43854414
ub = SCIPinfinity(self._scip)
43864415
PY_SCIP_CALL(SCIPchgVarUbNode(self._scip, node.scip_node, var.scip_var, ub))
43874416

4388-
43894417
def chgVarType(self, Variable var, vtype):
43904418
"""
43914419
Changes the type of a variable.
@@ -4413,6 +4441,28 @@ cdef class Model:
44134441
if infeasible:
44144442
print('could not change variable type of variable %s' % var)
44154443

4444+
def markDoNotAggrVar(self, Variable var):
4445+
"""
4446+
Marks a variable preventing it from being aggregated.
4447+
4448+
Parameters
4449+
----------
4450+
var : Variable
4451+
variable to mark
4452+
"""
4453+
PY_SCIP_CALL(SCIPmarkDoNotAggrVar(self._scip, var.scip_var))
4454+
4455+
def markDoNotMultaggrVar(self, Variable var):
4456+
"""
4457+
Marks a variable preventing it from being multi-aggregated.
4458+
4459+
Parameters
4460+
----------
4461+
var : Variable
4462+
variable to mark
4463+
"""
4464+
PY_SCIP_CALL(SCIPmarkDoNotMultaggrVar(self._scip, var.scip_var))
4465+
44164466
def getVars(self, transformed=False):
44174467
"""
44184468
Retrieve all variables.
@@ -4754,7 +4804,6 @@ cdef class Model:
47544804
"""Marks the given node to be propagated again the next time a node of its subtree is processed."""
47554805
PY_SCIP_CALL(SCIPrepropagateNode(self._scip, node.scip_node))
47564806

4757-
47584807
# LP Methods
47594808
def getLPSolstat(self):
47604809
"""
@@ -4767,7 +4816,6 @@ cdef class Model:
47674816
"""
47684817
return SCIPgetLPSolstat(self._scip)
47694818

4770-
47714819
def constructLP(self):
47724820
"""
47734821
Makes sure that the LP of the current node is loaded and
@@ -7044,7 +7092,6 @@ cdef class Model:
70447092

70457093
return pyCons
70467094

7047-
70487095
def addMatrixConsIndicator(self, cons: Union[ExprCons, MatrixExprCons], binvar: Union[Variable, MatrixVariable] = None,
70497096
activeone: Union[bool, np.ndarray] = True, name: Union[str, np.ndarray] = "",
70507097
initial: Union[bool, np.ndarray] = True, separate: Union[bool, np.ndarray] = True,
@@ -7627,7 +7674,6 @@ cdef class Model:
76277674

76287675
return activity
76297676

7630-
76317677
def getSlack(self, Constraint cons, Solution sol = None, side = None):
76327678
"""
76337679
Retrieve slack of given constraint.
@@ -8648,7 +8694,6 @@ cdef class Model:
86488694
"""
86498695
PY_SCIP_CALL( SCIPincludeBendersDefaultCuts(self._scip, benders._benders) )
86508696

8651-
86528697
def includeEventhdlr(self, Eventhdlr eventhdlr, name, desc):
86538698
"""
86548699
Include an event handler.
@@ -9285,7 +9330,6 @@ cdef class Model:
92859330
# TODO: It might be necessary in increment the reference to benders i.e Py_INCREF(benders)
92869331
Py_INCREF(benderscut)
92879332

9288-
92899333
def getLPBranchCands(self):
92909334
"""
92919335
Gets branching candidates for LP solution branching (fractional variables) along with solution values,
@@ -9329,9 +9373,9 @@ cdef class Model:
93299373
"""
93309374
Gets number of branching candidates for LP solution branching (number of fractional variables)
93319375
9332-
Returns
9333-
-------
9334-
int
9376+
Returns
9377+
-------
9378+
int
93359379
number of LP branching candidates
93369380
93379381
"""
@@ -9388,7 +9432,6 @@ cdef class Model:
93889432
PY_SCIP_CALL(SCIPbranchVar(self._scip, wrapper.ptr[0], &downchild, &eqchild, &upchild))
93899433
return Node.create(downchild), Node.create(eqchild), Node.create(upchild)
93909434

9391-
93929435
def branchVarVal(self, Variable variable, value):
93939436
"""
93949437
Branches on variable using a value which separates the domain of the variable.
@@ -10992,7 +11035,6 @@ cdef class Model:
1099211035
v = str_conversion(value)
1099311036
PY_SCIP_CALL(SCIPsetStringParam(self._scip, n, v))
1099411037

10995-
1099611038
def getParam(self, name):
1099711039
"""
1099811040
Get the value of a parameter of type
@@ -11482,7 +11524,6 @@ cdef class Model:
1148211524
"""
1148311525
return SCIPgetTreesizeEstimation(self._scip)
1148411526

11485-
1148611527
def getBipartiteGraphRepresentation(self, prev_col_features=None, prev_edge_features=None, prev_row_features=None,
1148711528
static_only=False, suppress_warnings=False):
1148811529
"""

tests/test_vars.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,36 @@ def test_getNBranchingsCurrentRun():
111111
n_branchings += var.getNBranchingsCurrentRun(SCIP_BRANCHDIR.DOWNWARDS)
112112

113113
assert n_branchings == m.getNNodes() - 1
114+
115+
def test_markDoNotAggrVar_and_getStatus():
116+
model = Model()
117+
x = model.addVar("x", obj=2, lb=0, ub=10)
118+
y = model.addVar("y", obj=3, lb=0, ub=20)
119+
z = model.addVar("z", obj=1, lb=0, ub=10)
120+
w = model.addVar("w", obj=4, lb=0, ub=15)
121+
122+
model.addCons(y - 2*x == 0)
123+
model.addCons(x + z + w == 10)
124+
model.addCons(x*y*z >= 21) # to prevent presolve from removing all variables
125+
model.presolve()
126+
127+
assert z.getStatus() == "ORIGINAL"
128+
assert model.getTransformedVar(z).getStatus() == "AGGREGATED"
129+
assert model.getTransformedVar(w).getStatus() == "MULTAGGR"
130+
131+
assert model.getNVars(True) == 1
132+
133+
model.freeTransform()
134+
model.markDoNotMultaggrVar(w)
135+
model.presolve()
136+
137+
assert model.getTransformedVar(w).getStatus() != "MULTAGGR"
138+
assert model.getNVars(True) == 3
139+
140+
model.freeTransform()
141+
model.markDoNotAggrVar(y)
142+
model.presolve()
143+
assert model.getTransformedVar(z).getStatus() != "AGGREGATED"
144+
assert model.getNVars(True) == 4
145+
146+
assert x.getStatus() == "ORIGINAL"

0 commit comments

Comments
 (0)