Skip to content

add constraint cumulative #1002

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
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
41 changes: 41 additions & 0 deletions src/pyscipopt/scip.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -1513,6 +1513,47 @@ cdef extern from "scip/cons_knapsack.h":
int SCIPgetNVarsKnapsack(SCIP* scip, SCIP_CONS* cons)
SCIP_Longint* SCIPgetWeightsKnapsack(SCIP* scip, SCIP_CONS* cons)

cdef extern from "scip/cons_cumulative.h":
SCIP_RETCODE SCIPcreateConsCumulative(SCIP* scip,
SCIP_CONS** cons,
char* name,
int nvars,
SCIP_VAR** vars,
int* durations,
int* demands,
int capacity,
SCIP_Bool initial,
SCIP_Bool separate,
SCIP_Bool enforce,
SCIP_Bool check,
SCIP_Bool propagate,
SCIP_Bool local,
SCIP_Bool modifiable,
SCIP_Bool dynamic,
SCIP_Bool removable,
SCIP_Bool stickingatnode)

SCIP_RETCODE SCIPcreateConsBasicCumulative(SCIP* scip, SCIP_CONS** cons,
char* name,
int nvars,
SCIP_VAR** vars,
int* durations,
int* demands,
int capacity)

SCIP_RETCODE SCIPsetHminCumulative(SCIP* scip, SCIP_CONS* cons, int hmin)
SCIP_RETCODE SCIPsetHmaxCumulative(SCIP* scip, SCIP_CONS* cons, int hmax)

int SCIPgetHminCumulative(SCIP* scip, SCIP_CONS* cons)
int SCIPgetHmaxCumulative(SCIP* scip, SCIP_CONS* cons)

SCIP_VAR** SCIPgetVarsCumulative(SCIP* scip, SCIP_CONS* cons)
int SCIPgetNVarsCumulative(SCIP* scip, SCIP_CONS* cons)
int* SCIPgetDurationsCumulative(SCIP* scip, SCIP_CONS* cons)
int* SCIPgetDemandsCumulative(SCIP* scip, SCIP_CONS* cons)
int SCIPgetCapacityCumulative(SCIP* scip, SCIP_CONS* cons)


cdef extern from "scip/cons_nonlinear.h":
SCIP_EXPR* SCIPgetExprNonlinear(SCIP_CONS* cons)
SCIP_RETCODE SCIPcreateConsNonlinear(SCIP* scip,
Expand Down
148 changes: 147 additions & 1 deletion src/pyscipopt/scip.pxi
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@
if rc == SCIP_OKAY:
pass
elif rc == SCIP_ERROR:
raise Exception('SCIP: unspecified error!')

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

View workflow job for this annotation

GitHub Actions / test-coverage (3.11)

SCIP: unspecified error!

Check failure on line 304 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 @@ -2137,6 +2137,18 @@
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
return constype == 'linear'

def isCumulative(self):
"""
Returns True if constraint is cumulative

Returns
-------
bool

"""
constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(self.scip_cons))).decode('UTF-8')
return constype == 'cumulative'

def isKnapsack(self):
"""
Returns True if constraint is a knapsack constraint.
Expand Down Expand Up @@ -6057,7 +6069,50 @@
else:
PY_SCIP_CALL(SCIPaddConsLocal(self._scip, cons.scip_cons, NULL))
Py_INCREF(cons)


def addConsCumulative(self, vars, durations, demands, capacity, name="",
initial=True, separate=True, enforce=True, check=True,
propagate=True, local=False, modifiable=False,
dynamic=False, removable=False, stickingatnode=False):

cdef int n = len(vars)
assert n == len(durations) == len(demands)

if name == "":
name = "c" + str(SCIPgetNConss(self._scip) + 1)

cdef SCIP_VAR** c_vars = <SCIP_VAR**> malloc(n * sizeof(SCIP_VAR*))
cdef int* c_durs = <int*> malloc(n * sizeof(int))
cdef int* c_dem = <int*> malloc(n * sizeof(int))
cdef SCIP_CONS* cons
cdef int i

for i in range(n):
c_vars[i] = (<Variable> vars[i]).scip_var
c_durs[i] = <int> durations[i]
c_dem[i] = <int> demands[i]

# --- Constraint erzeugen
PY_SCIP_CALL(SCIPcreateConsCumulative(
self._scip, &cons, str_conversion(name),
n, c_vars, c_durs, c_dem, <int>capacity,
initial, separate, enforce, check, propagate,
local, modifiable, dynamic, removable, stickingatnode))

# --- Constraint dem Modell hinzufügen (verhindert spätere Segfaults!)
PY_SCIP_CALL(SCIPaddCons(self._scip, cons))

# --- Hilfs‑Arrays freigeben
free(c_vars)
free(c_durs)
free(c_dem)

# --- Python‑Wrapper erstellen und zurückgeben
pyCons = Constraint.create(cons)
PY_SCIP_CALL(SCIPreleaseCons(self._scip, &cons))
return pyCons


def addConsKnapsack(self, vars, weights, capacity, name="",
initial=True, separate=True, enforce=True, check=True,
modifiable=False, propagate=True, local=False, dynamic=False,
Expand Down Expand Up @@ -6817,6 +6872,97 @@
else:
raise Warning("method cannot be called for constraints of type " + constype)

def getCapacityCumulative(self, Constraint cons):
"""
Liefert die Kapazität einer cumulative‑Constraint.
"""
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative', \
"Methode nur für cumulative‑Constraints geeignet"
return SCIPgetCapacityCumulative(self._scip, cons.scip_cons)

def getNVarsCumulative(self, Constraint cons):
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
return SCIPgetNVarsCumulative(self._scip, cons.scip_cons)

def getVarsCumulative(self, Constraint cons):
"""
Gibt die Startzeit‑Variablen als Python‑Liste zurück.
"""
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
cdef int nvars = SCIPgetNVarsCumulative(self._scip, cons.scip_cons)
cdef SCIP_VAR** _vars = SCIPgetVarsCumulative(self._scip,
cons.scip_cons)

return [ Variable.create(_vars[i]) for i in range(nvars) ]

def getDurationsCumulative(self, Constraint cons):
"""
Dict {varname: duration}
"""
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
cdef int nvars = SCIPgetNVarsCumulative(self._scip, cons.scip_cons)
cdef SCIP_VAR** _vars = SCIPgetVarsCumulative(self._scip,
cons.scip_cons)
cdef int* _durs = SCIPgetDurationsCumulative(self._scip,
cons.scip_cons)

cdef dict durs = {}
cdef int i
for i in range(nvars):
durs[ bytes(SCIPvarGetName(_vars[i])).decode('utf-8') ] = _durs[i]
return durs

def getDemandsCumulative(self, Constraint cons):
"""
Dict {varname: demand}
"""
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
cdef int nvars = SCIPgetNVarsCumulative(self._scip, cons.scip_cons)
cdef SCIP_VAR** _vars = SCIPgetVarsCumulative(self._scip,
cons.scip_cons)
cdef int* _dem = SCIPgetDemandsCumulative(self._scip,
cons.scip_cons)

cdef dict demands = {}
cdef int i
for i in range(nvars):
demands[ bytes(SCIPvarGetName(_vars[i])).decode('utf-8') ] = _dem[i]
return demands

def getHminCumulative(self, Constraint cons):
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
return SCIPgetHminCumulative(self._scip, cons.scip_cons)

def setHminCumulative(self, Constraint cons, hmin):
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
PY_SCIP_CALL(SCIPsetHminCumulative(self._scip, cons.scip_cons, hmin))

def getHmaxCumulative(self, Constraint cons):
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
return SCIPgetHmaxCumulative(self._scip, cons.scip_cons)

def setHmaxCumulative(self, Constraint cons, hmax):
constype = bytes(SCIPconshdlrGetName(
SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8')
assert constype == 'cumulative'
PY_SCIP_CALL(SCIPsetHmaxCumulative(self._scip, cons.scip_cons, hmax))

def chgCapacityKnapsack(self, Constraint cons, capacity):
"""
Change capacity of a knapsack constraint.
Expand Down
15 changes: 15 additions & 0 deletions tests/test_cons.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ def test_SOScons():
assert c1.getConshdlrName() == "SOS1"
assert c2.getConshdlrName() == "SOS2"

def test_cons_cumulative():
m = Model()

s1 = m.addVar("s1", vtype="I")
s2 = m.addVar("s2", vtype="I")
s3 = m.addVar("s3", vtype="I")

dur = [3, 2, 4]
dem = [2, 1, 3]
cap = 4

c = m.addConsCumulative([s1, s2, s3], dur, dem, cap)
assert c.isCumulative()
assert m.getCapacityCumulative(c) == cap
assert m.getDurationsCumulative(c) == { "s1": 3, "s2": 2, "s3": 4 }

def test_cons_indicator():
m = Model()
Expand Down