Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
- MatrixVariable comparisons (<=, >=, ==) now support numpy's broadcast feature.
- Added methods: getMaxDepth(), getPlungeDepth(), getLowerbound(), getCutoffbound(), getNNodeLPIterations(), getNStrongbranchLPIterations().
- setup.py now automatically detects conda environments when SCIPOPTDIR is not defined.
- Added function getStatus() to get variable status in variable class
- Added function isActive() to get whether a variable is active in variable class
- Added function markDoNotAggrVar() to prevent a variable from being aggregated
- Added function markDoNotMultaggrVar() to prevent a variable from being multi-aggregated
### Fixed
- Implemented all binary operations between MatrixExpr and GenExpr
- Fixed the type of @ matrix operation result from MatrixVariable to MatrixExpr.
Expand Down
14 changes: 14 additions & 0 deletions src/pyscipopt/scip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,16 @@ cdef extern from "scip/scip.h":
SCIP_VARTYPE SCIP_VARTYPE_IMPLINT
SCIP_VARTYPE SCIP_VARTYPE_CONTINUOUS

ctypedef int SCIP_VARSTATUS
cdef extern from "scip/type_var.h":
SCIP_VARSTATUS SCIP_VARSTATUS_ORIGINAL
SCIP_VARSTATUS SCIP_VARSTATUS_LOOSE
SCIP_VARSTATUS SCIP_VARSTATUS_COLUMN
SCIP_VARSTATUS SCIP_VARSTATUS_FIXED
SCIP_VARSTATUS SCIP_VARSTATUS_AGGREGATED
SCIP_VARSTATUS SCIP_VARSTATUS_MULTAGGR
SCIP_VARSTATUS SCIP_VARSTATUS_NEGATED

ctypedef int SCIP_OBJSENSE
cdef extern from "scip/type_prob.h":
SCIP_OBJSENSE SCIP_OBJSENSE_MAXIMIZE
Expand Down Expand Up @@ -768,6 +778,8 @@ cdef extern from "scip/scip.h":
SCIP_RETCODE SCIPdelVar(SCIP* scip, SCIP_VAR* var, SCIP_Bool* deleted)

SCIP_RETCODE SCIPchgVarType(SCIP* scip, SCIP_VAR* var, SCIP_VARTYPE vartype, SCIP_Bool* infeasible)
SCIP_RETCODE SCIPmarkDoNotAggrVar(SCIP* scip, SCIP_VAR* var)
SCIP_RETCODE SCIPmarkDoNotMultaggrVar(SCIP* scip, SCIP_VAR* var)
SCIP_RETCODE SCIPcaptureVar(SCIP* scip, SCIP_VAR* var)
SCIP_RETCODE SCIPaddPricedVar(SCIP* scip, SCIP_VAR* var, SCIP_Real score)
SCIP_RETCODE SCIPreleaseVar(SCIP* scip, SCIP_VAR** var)
Expand All @@ -790,8 +802,10 @@ cdef extern from "scip/scip.h":
int SCIPgetNImplVars(SCIP* scip)
int SCIPgetNContVars(SCIP* scip)
SCIP_VARTYPE SCIPvarGetType(SCIP_VAR* var)
SCIP_VARSTATUS SCIPvarGetStatus(SCIP_VAR* var)
SCIP_Bool SCIPvarIsOriginal(SCIP_VAR* var)
SCIP_Bool SCIPvarIsTransformed(SCIP_VAR* var)
SCIP_Bool SCIPvarIsActive(SCIP_VAR* var)
SCIP_COL* SCIPvarGetCol(SCIP_VAR* var)
SCIP_Bool SCIPvarIsInLP(SCIP_VAR* var)
SCIP_Real SCIPvarGetLbOriginal(SCIP_VAR* var)
Expand Down
83 changes: 62 additions & 21 deletions src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@
if rc == SCIP_OKAY:
pass
elif rc == SCIP_ERROR:
raise Exception('SCIP: unspecified error!')

Check failure on line 311 in src/pyscipopt/scip.pxi

View workflow job for this annotation

GitHub Actions / test-coverage (3.11)

SCIP: unspecified error!
elif rc == SCIP_NOMEMORY:
raise MemoryError('SCIP: insufficient memory error!')
elif rc == SCIP_READERROR:
Expand Down Expand Up @@ -1120,7 +1120,6 @@
PY_SCIP_CALL(SCIPtranslateSubSol(target._scip, self.scip, self.sol, NULL, source_vars, &(targetSol.sol)))
return targetSol


cdef class BoundChange:
"""Bound change."""

Expand Down Expand Up @@ -1538,6 +1537,32 @@
elif vartype == SCIP_VARTYPE_IMPLINT:
return "IMPLINT"

def getStatus(self):
"""
Retrieve the variable status (ORIGINAL, LOOSE, COLUMN, FIXED, AGGREGATED, MULTAGGR, NEGATED)

Returns
-------
str
"ORIGINAL", "LOOSE", "COLUMN", "FIXED", "AGGREGATED", "MULTAGGR", "NEGATED"

"""
varstatus = SCIPvarGetStatus(self.scip_var)
if varstatus == SCIP_VARSTATUS_ORIGINAL:
return "ORIGINAL"
elif varstatus == SCIP_VARSTATUS_LOOSE:
return "LOOSE"
elif varstatus == SCIP_VARSTATUS_COLUMN:
return "COLUMN"
elif varstatus == SCIP_VARSTATUS_FIXED:
return "FIXED"
elif varstatus == SCIP_VARSTATUS_AGGREGATED:
return "AGGREGATED"
elif varstatus == SCIP_VARSTATUS_MULTAGGR:
return "MULTAGGR"
elif varstatus == SCIP_VARSTATUS_NEGATED:
return "NEGATED"

def isOriginal(self):
"""
Retrieve whether the variable belongs to the original problem
Expand All @@ -1549,6 +1574,17 @@
"""
return SCIPvarIsOriginal(self.scip_var)

def isActive(self):
"""
Retrieve whether the variable is active

Returns
-------
bool

"""
return SCIPvarIsActive(self.scip_var)

def isInLP(self):
"""
Retrieve whether the variable is a COLUMN variable that is member of the current LP.
Expand All @@ -1560,7 +1596,6 @@
"""
return SCIPvarIsInLP(self.scip_var)


def getIndex(self):
"""
Retrieve the unique index of the variable.
Expand Down Expand Up @@ -2043,7 +2078,6 @@
mayround[idx] = self[idx].varMayRound()
return mayround


cdef class Constraint:
"""Base class holding a pointer to corresponding SCIP_CONS"""

Expand Down Expand Up @@ -2584,7 +2618,6 @@
else:
PY_SCIP_CALL(SCIPcopy(sourceModel._scip, self._scip, NULL, NULL, n, globalcopy, enablepricing, threadsafe, True, self._valid))


def attachEventHandlerCallback(self,
callback,
events,
Expand Down Expand Up @@ -2634,7 +2667,6 @@

self.includeEventhdlr(event_handler, name, description)


def __dealloc__(self):
# call C function directly, because we can no longer call this object's methods, according to
# http://docs.cython.org/src/reference/extension_types.html#finalization-dealloc
Expand Down Expand Up @@ -2817,7 +2849,6 @@
"""
return SCIPminorVersion()


def getTechVersion(self):
"""
Retrieve SCIP technical version.
Expand Down Expand Up @@ -3559,7 +3590,6 @@
return iters

# Objective function

def setMinimize(self):
"""Set the objective sense to minimization."""
PY_SCIP_CALL(SCIPsetObjsense(self._scip, SCIP_OBJSENSE_MINIMIZE))
Expand Down Expand Up @@ -3905,7 +3935,6 @@
locale.setlocale(locale.LC_NUMERIC,user_locale)

# Variable Functions

def addVar(self, name='', vtype='C', lb=0.0, ub=None, obj=0.0, pricedVar=False, pricedVarScore=1.0, deletable=False):
"""
Create a new variable. Default variable is non-negative and continuous.
Expand Down Expand Up @@ -4385,7 +4414,6 @@
ub = SCIPinfinity(self._scip)
PY_SCIP_CALL(SCIPchgVarUbNode(self._scip, node.scip_node, var.scip_var, ub))


def chgVarType(self, Variable var, vtype):
"""
Changes the type of a variable.
Expand Down Expand Up @@ -4413,6 +4441,28 @@
if infeasible:
print('could not change variable type of variable %s' % var)

def markDoNotAggrVar(self, Variable var):
"""
Marks a variable preventing it from being aggregated.

Parameters
----------
var : Variable
variable to mark
"""
PY_SCIP_CALL(SCIPmarkDoNotAggrVar(self._scip, var.scip_var))

def markDoNotMultaggrVar(self, Variable var):
"""
Marks a variable preventing it from being multi-aggregated.

Parameters
----------
var : Variable
variable to mark
"""
PY_SCIP_CALL(SCIPmarkDoNotMultaggrVar(self._scip, var.scip_var))

def getVars(self, transformed=False):
"""
Retrieve all variables.
Expand Down Expand Up @@ -4754,7 +4804,6 @@
"""Marks the given node to be propagated again the next time a node of its subtree is processed."""
PY_SCIP_CALL(SCIPrepropagateNode(self._scip, node.scip_node))


# LP Methods
def getLPSolstat(self):
"""
Expand All @@ -4767,7 +4816,6 @@
"""
return SCIPgetLPSolstat(self._scip)


def constructLP(self):
"""
Makes sure that the LP of the current node is loaded and
Expand Down Expand Up @@ -7044,7 +7092,6 @@

return pyCons


def addMatrixConsIndicator(self, cons: Union[ExprCons, MatrixExprCons], binvar: Union[Variable, MatrixVariable] = None,
activeone: Union[bool, np.ndarray] = True, name: Union[str, np.ndarray] = "",
initial: Union[bool, np.ndarray] = True, separate: Union[bool, np.ndarray] = True,
Expand Down Expand Up @@ -7627,7 +7674,6 @@

return activity


def getSlack(self, Constraint cons, Solution sol = None, side = None):
"""
Retrieve slack of given constraint.
Expand Down Expand Up @@ -8648,7 +8694,6 @@
"""
PY_SCIP_CALL( SCIPincludeBendersDefaultCuts(self._scip, benders._benders) )


def includeEventhdlr(self, Eventhdlr eventhdlr, name, desc):
"""
Include an event handler.
Expand Down Expand Up @@ -9285,7 +9330,6 @@
# TODO: It might be necessary in increment the reference to benders i.e Py_INCREF(benders)
Py_INCREF(benderscut)


def getLPBranchCands(self):
"""
Gets branching candidates for LP solution branching (fractional variables) along with solution values,
Expand Down Expand Up @@ -9329,9 +9373,9 @@
"""
Gets number of branching candidates for LP solution branching (number of fractional variables)

Returns
-------
int
Returns
-------
int
number of LP branching candidates

"""
Expand Down Expand Up @@ -9388,7 +9432,6 @@
PY_SCIP_CALL(SCIPbranchVar(self._scip, wrapper.ptr[0], &downchild, &eqchild, &upchild))
return Node.create(downchild), Node.create(eqchild), Node.create(upchild)


def branchVarVal(self, Variable variable, value):
"""
Branches on variable using a value which separates the domain of the variable.
Expand Down Expand Up @@ -10992,7 +11035,6 @@
v = str_conversion(value)
PY_SCIP_CALL(SCIPsetStringParam(self._scip, n, v))


def getParam(self, name):
"""
Get the value of a parameter of type
Expand Down Expand Up @@ -11482,7 +11524,6 @@
"""
return SCIPgetTreesizeEstimation(self._scip)


def getBipartiteGraphRepresentation(self, prev_col_features=None, prev_edge_features=None, prev_row_features=None,
static_only=False, suppress_warnings=False):
"""
Expand Down
33 changes: 33 additions & 0 deletions tests/test_vars.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,3 +111,36 @@ def test_getNBranchingsCurrentRun():
n_branchings += var.getNBranchingsCurrentRun(SCIP_BRANCHDIR.DOWNWARDS)

assert n_branchings == m.getNNodes() - 1

def test_markDoNotAggrVar_and_getStatus():
model = Model()
x = model.addVar("x", obj=2, lb=0, ub=10)
y = model.addVar("y", obj=3, lb=0, ub=20)
z = model.addVar("z", obj=1, lb=0, ub=10)
w = model.addVar("w", obj=4, lb=0, ub=15)

model.addCons(y - 2*x == 0)
model.addCons(x + z + w == 10)
model.addCons(x*y*z >= 21) # to prevent presolve from removing all variables
model.presolve()

assert z.getStatus() == "ORIGINAL"
assert model.getTransformedVar(z).getStatus() == "AGGREGATED"
assert model.getTransformedVar(w).getStatus() == "MULTAGGR"

assert model.getNVars(True) == 1

model.freeTransform()
model.markDoNotMultaggrVar(w)
model.presolve()

assert model.getTransformedVar(w).getStatus() != "MULTAGGR"
assert model.getNVars(True) == 3

model.freeTransform()
model.markDoNotAggrVar(y)
model.presolve()
assert model.getTransformedVar(z).getStatus() != "AGGREGATED"
assert model.getNVars(True) == 4

assert x.getStatus() == "ORIGINAL"