diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index 744e673e7..ab7d0e5e8 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -333,6 +333,9 @@ cdef extern from "scip/scip.h": ctypedef struct SCIP_CONS: pass + ctypedef struct SCIP_DECOMP: + pass + ctypedef struct SCIP_ROW: pass @@ -1586,6 +1589,97 @@ cdef extern from "scip/scip_cons.h": SCIP_CONS* cons, FILE* file) +cdef extern from "scip/pub_dcmp.h": + SCIP_RETCODE SCIPdecompCreate(SCIP_DECOMP** decomp, + BMS_BLKMEM* blkmem, + int nblocks, + SCIP_Bool original, + SCIP_Bool benderslabels) + + SCIP_Bool SCIPdecompIsOriginal(SCIP_DECOMP *decomp) + + void SCIPdecompSetUseBendersLabels(SCIP_DECOMP *decomp, SCIP_Bool benderslabels) + + SCIP_Bool SCIPdecompUseBendersLabels(SCIP_DECOMP *decomp) + + int SCIPdecompGetNBlocks(SCIP_DECOMP *decomp) + + SCIP_Real SCIPdecompGetAreaScore(SCIP_DECOMP *decomp) + + SCIP_Real SCIPdecompGetModularity(SCIP_DECOMP *decomp) + + SCIP_RETCODE SCIPdecompGetVarsSize(SCIP_DECOMP *decomp, int *varssize, int nblocks) + + SCIP_RETCODE SCIPdecompGetConssSize(SCIP_DECOMP *decomp, int *consssize, int nblocks) + + int SCIPdecompGetNBorderVars(SCIP_DECOMP *decomp) + + int SCIPdecompGetNBorderConss(SCIP_DECOMP *decomp) + + int SCIPdecompGetNBlockGraphEdges(SCIP_DECOMP *decomp) + + int SCIPdecompGetNBlockGraphComponents(SCIP_DECOMP *decomp) + + int SCIPdecompGetNBlockGraphArticulations(SCIP_DECOMP *decomp) + + int SCIPdecompGetBlockGraphMaxDegree(SCIP_DECOMP *decomp) + + int SCIPdecompGetBlockGraphMinDegree(SCIP_DECOMP *decomp) + + SCIP_RETCODE SCIPdecompSetVarsLabels(SCIP_DECOMP *decomp, SCIP_VAR **vrs, int *labels, int nvars) + + void SCIPdecompGetVarsLabels(SCIP_DECOMP *decomp, SCIP_VAR **vrs, int *labels, int nvars) + + SCIP_RETCODE SCIPdecompSetConsLabels(SCIP_DECOMP *decomp, SCIP_CONS **conss, int *labels, int nconss) + + void SCIPdecompGetConsLabels(SCIP_DECOMP *decomp, SCIP_CONS **conss, int *labels, int nconss) + + SCIP_RETCODE SCIPdecompClear(SCIP_DECOMP *decomp, SCIP_Bool clearvarlabels, SCIP_Bool clearconslabels) + + char* SCIPdecompPrintStats(SCIP_DECOMP *decomp, char *strbuf) + + +cdef extern from "scip/scip_dcmp.h": + SCIP_RETCODE SCIPcreateDecomp(SCIP* scip, + SCIP_DECOMP** decomp, + int nblocks, + SCIP_Bool original, + SCIP_Bool benderslabels) + + SCIP_RETCODE SCIPaddDecomp(SCIP* scip, SCIP_DECOMP* decomp) + + void SCIPfreeDecomp(SCIP* scip, SCIP_DECOMP** decomp) + + void SCIPgetDecomps(SCIP* scip, + SCIP_DECOMP*** decomp, + int* ndecomps, + SCIP_Bool original) + + SCIP_RETCODE SCIPhasConsOnlyLinkVars(SCIP* scip, + SCIP_DECOMP* decomp, + SCIP_CONS *cons, + SCIP_Bool* hasonlylinkvars) + + SCIP_RETCODE SCIPcomputeDecompConsLabels(SCIP* scip, + SCIP_DECOMP* decomp, + SCIP_CONS** conss, + int nconss) + + SCIP_RETCODE SCIPcomputeDecompVarsLabels(SCIP* scip, + SCIP_DECOMP* decomp, + SCIP_CONS** conss, + int nconss) + SCIP_RETCODE SCIPassignDecompLinkConss(SCIP* scip, + SCIP_DECOMP* decomp, + SCIP_CONS** conss, + int* nskipconss) + + SCIP_RETCODE SCIPcomputeDecompStats(SCIP* scip, + SCIP_DECOMP *decomp, + SCIP_CONS** conss, + int nconss, + int* nskipconss) + cdef extern from "blockmemshell/memory.h": void BMScheckEmptyMemory() long long BMSgetMemoryUsed() @@ -1950,6 +2044,14 @@ cdef class Constraint: @staticmethod cdef create(SCIP_CONS* scipcons) +cdef class Decomposition: + cdef SCIP_DECOMP* scip_decomp + + cdef public object data + + @staticmethod + cdef create(SCIP_DECOMP* scip_decomp) + cdef class Model: cdef SCIP* _scip cdef SCIP_Bool* _valid diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 8274c64b4..ba32df944 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -323,7 +323,7 @@ cdef class Event: attr = getattr(PY_SCIP_EVENTTYPE, name) if isinstance(attr, int): EventNames[attr] = name - + def __repr__(self): return str(self.getType()) @@ -975,6 +975,128 @@ cdef class Constraint: return (self.__class__ == other.__class__ and self.scip_cons == (other).scip_cons) +cdef class Decomposition: + """Base class holding a pointer to SCIP decomposition""" + + @staticmethod + cdef create(SCIP_DECOMP* scip_decomp): + if scip_decomp == NULL: + raise Warning("cannot create Constraint with SCIP_CONS* == NULL") + decomp = Decomposition() + decomp.scip_decomp = scip_decomp + return decomp + + def isOriginal(self): + return SCIPdecompIsOriginal(self.scip_decomp) + + def getAreaScore(self): + return SCIPdecompGetAreaScore(self.scip_decomp) + + def getModularity(self): + return SCIPdecompGetModularity(self.scip_decomp) + + def getNBorderVars(self): + return SCIPdecompGetNBorderVars(self.scip_decomp) + + def getNBorderConss(self): + return SCIPdecompGetNBorderConss(self.scip_decomp) + + def getNBlockGraphEdges(self): + return SCIPdecompGetNBlockGraphEdges(self.scip_decomp) + + def getNBlockGraphComponents(self): + return SCIPdecompGetNBlockGraphComponents(self.scip_decomp) + + def getNblockGraphArticulations(self): + return SCIPdecompGetNBlockGraphArticulations(self.scip_decomp) + + def getBlockGraphMaxDegree(self): + return SCIPdecompGetBlockGraphMaxDegree(self.scip_decomp) + + def getBlockGraphMinDegree(self): + return SCIPdecompGetBlockGraphMinDegree(self.scip_decomp) + + def getConsLabels(self, conss): + """get {cons: label} pair for python constraints conss""" + cdef int nconss = len(conss) + cdef int* labels = malloc(nconss * sizeof(int)) + cdef SCIP_CONS** scip_conss = malloc(nconss * sizeof(SCIP_CONS*)) + + for i in range(nconss): + scip_conss[i] = (conss[i]).scip_cons + + PY_SCIP_CALL(SCIPdecompGetConsLabels(self.scip_decomp, scip_conss, labels, nconss)) + + cons_labels = {} + for i in range(nconss): + cons_labels[conss[i]] = labels[i] + free(labels) + free(scip_conss) + return cons_labels + + def setConsLabels(self, cons_labels): + """ applies labels to constraints in decomposition. + :param cons_labels dict of {constraint: label} pairs to be applied + """ + cons_labels = cons_labels.items() + + cdef int nconss = len(cons_labels) + cdef SCIP_CONS** scip_conss = malloc(nconss* sizeof(SCIP_CONS*)) + cdef int* labels = malloc(nconss * sizeof(int)) + + for i in range(nconss): + scip_conss[i] = (cons_labels[i][0]).scip_cons + labels[i] = cons_labels[i][1] + + PY_SCIP_CALL(SCIPdecompSetConsLabels(self.scip_decomp, scip_conss, labels, nconss)) + + free(scip_conss) + free(labels) + + def getVarsLabels(self, vrs): + """get {var: label} pairs for python variables vrs""" + + cdef int nvars = len(vrs) + cdef int* labels = malloc(nvars * sizeof(int)) + cdef SCIP_VAR** scip_vars = malloc(nvars * sizeof(SCIP_VAR*)) + + for i in range(nvars): + scip_vars[i] = (vrs[i]).scip_var + + PY_SCIP_CALL(SCIPdecompGetVarsLabels(self.scip_decomp, scip_vars, labels, nvars)) + + var_labels = {} + for i in range(nvars): + var_labels[vrs[i]] = labels[i] + + free(labels) + free(scip_vars) + return var_labels + + def setVarLabels(self, var_labels): + """set {var: label} pairs in decomposition""" + var_labels = var_labels.items() + + cdef int nvars= len(var_labels) + cdef SCIP_VAR** scip_vars = malloc(nvars * sizeof(SCIP_VAR*)) + cdef int* labels = malloc(nvars * sizeof(int)) + + for i in range(nvars): + scip_vars[i] = (var_labels[i][0]).scip_var + labels[i] = var_labels[i][1] + + PY_SCIP_CALL(SCIPdecompSetVarsLabels(self.scip_decomp, scip_vars, labels, nvars)) + + free(scip_vars) + free(labels) + + def clear(self, varlabels =True, conslabels = True): + """clears variable and/or constraint labels from decomposition""" + + if not (varlabels or conslabels): + ... # TODO does decomp clear do anything if both options are false? + else: + PY_SCIP_CALL(SCIPdecompClear(self.scip_decomp, varlabels, conslabels)) cdef void relayMessage(SCIP_MESSAGEHDLR *messagehdlr, FILE *file, const char *msg) noexcept: sys.stdout.write(msg.decode('UTF-8')) @@ -2600,7 +2722,7 @@ cdef class Model: PyCons = Constraint.create(conj_cons) return PyCons - + def addConsDisjunction(self, conss, name = '', initial = True, relaxcons = None, enforce=True, check =True, local=False, modifiable = False, dynamic = False): @@ -2651,7 +2773,7 @@ cdef class Model: PyCons = Constraint.create(disj_cons) PY_SCIP_CALL(SCIPreleaseCons(self._scip, &disj_cons)) return PyCons - + def createConsDisjunction(self, conss, name = '', initial = True, relaxcons = None, enforce=True, check =True, local=False, modifiable = False, dynamic = False): @@ -2700,6 +2822,7 @@ cdef class Model: PY_SCIP_CALL(SCIPreleaseCons(self._scip, &(pycons).scip_cons)) PyCons = Constraint.create(disj_cons) return PyCons + def addConsElemDisjunction(self, Constraint disj_cons, Constraint cons): """Appends a constraint to a disjunction. @@ -2713,7 +2836,6 @@ cdef class Model: return disj_cons - def getConsNVars(self, Constraint constraint): """ Gets number of variables in a constraint. @@ -3682,6 +3804,10 @@ cdef class Model: """Presolve the problem.""" PY_SCIP_CALL(SCIPpresolve(self._scip)) + # custom decomposition methods + def addDecomposition(self, Decomposition decomp): + PY_SCIP_CALL(SCIPaddDecomp(self._scip, decomp.scip_decomp)) + # Benders' decomposition methods def initBendersDefault(self, subproblems): """initialises the default Benders' decomposition with a dictionary of subproblems @@ -5057,13 +5183,13 @@ cdef class Model: def getStage(self): """Retrieve current SCIP stage""" return SCIPgetStage(self._scip) - + def getStageName(self): """Returns name of current stage as string""" if not StageNames: self._getStageNames() return StageNames[self.getStage()] - + def _getStageNames(self): """Gets names of stages""" for name in dir(PY_SCIP_STAGE):