diff --git a/CHANGELOG.md b/CHANGELOG.md index 72dd6bf6..dbed40ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- Added cdef type declaration of loop variables for slight speedup - Added wrappers for setting and getting heuristic timing - Added transformed option to getVarDict, updated test - Added categorical data example diff --git a/src/pyscipopt/benders.pxi b/src/pyscipopt/benders.pxi index 66a394d8..4f171709 100644 --- a/src/pyscipopt/benders.pxi +++ b/src/pyscipopt/benders.pxi @@ -175,8 +175,8 @@ cdef SCIP_RETCODE PyBendersSolvesub (SCIP* scip, SCIP_BENDERS* benders, SCIP_SOL cdef SCIP_RETCODE PyBendersPostsolve (SCIP* scip, SCIP_BENDERS* benders, SCIP_SOL* sol, SCIP_BENDERSENFOTYPE type, int* mergecands, int npriomergecands, int nmergecands, SCIP_Bool checkint, SCIP_Bool infeasible, SCIP_Bool* merged) noexcept with gil: - cdef SCIP_BENDERSDATA* bendersdata - bendersdata = SCIPbendersGetData(benders) + cdef SCIP_BENDERSDATA* bendersdata = SCIPbendersGetData(benders) + cdef int i PyBenders = bendersdata if sol == NULL: solution = None diff --git a/src/pyscipopt/conshdlr.pxi b/src/pyscipopt/conshdlr.pxi index 20717780..2410fec4 100644 --- a/src/pyscipopt/conshdlr.pxi +++ b/src/pyscipopt/conshdlr.pxi @@ -168,48 +168,54 @@ cdef SCIP_RETCODE PyConsFree (SCIP* scip, SCIP_CONSHDLR* conshdlr) noexcept with return SCIP_OKAY cdef SCIP_RETCODE PyConsInit (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consinit(constraints) return SCIP_OKAY cdef SCIP_RETCODE PyConsExit (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consexit(constraints) return SCIP_OKAY cdef SCIP_RETCODE PyConsInitpre (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consinitpre(constraints) return SCIP_OKAY cdef SCIP_RETCODE PyConsExitpre (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consexitpre(constraints) return SCIP_OKAY cdef SCIP_RETCODE PyConsInitsol (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consinitsol(constraints) return SCIP_OKAY cdef SCIP_RETCODE PyConsExitsol (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, SCIP_Bool restart) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consexitsol(constraints, restart) @@ -244,8 +250,9 @@ cdef SCIP_RETCODE PyConsTrans (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS* s return SCIP_OKAY cdef SCIP_RETCODE PyConsInitlp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, SCIP_Bool* infeasible) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) result_dict = PyConshdlr.consinitlp(constraints) @@ -253,8 +260,9 @@ cdef SCIP_RETCODE PyConsInitlp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** return SCIP_OKAY cdef SCIP_RETCODE PyConsSepalp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, int nusefulconss, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) result_dict = PyConshdlr.conssepalp(constraints, nusefulconss) @@ -263,8 +271,9 @@ cdef SCIP_RETCODE PyConsSepalp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** cdef SCIP_RETCODE PyConsSepasol (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, int nusefulconss, SCIP_SOL* sol, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) solution = Solution.create(scip, sol) @@ -274,8 +283,9 @@ cdef SCIP_RETCODE PyConsSepasol (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS* cdef SCIP_RETCODE PyConsEnfolp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, int nusefulconss, SCIP_Bool solinfeasible, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) result_dict = PyConshdlr.consenfolp(constraints, nusefulconss, solinfeasible) @@ -283,8 +293,9 @@ cdef SCIP_RETCODE PyConsEnfolp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** return SCIP_OKAY cdef SCIP_RETCODE PyConsEnforelax (SCIP* scip, SCIP_SOL* sol, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, int nusefulconss, SCIP_Bool solinfeasible, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) solution = Solution.create(scip, sol) @@ -294,8 +305,9 @@ cdef SCIP_RETCODE PyConsEnforelax (SCIP* scip, SCIP_SOL* sol, SCIP_CONSHDLR* con cdef SCIP_RETCODE PyConsEnfops (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, int nusefulconss, SCIP_Bool solinfeasible, SCIP_Bool objinfeasible, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) result_dict = PyConshdlr.consenfops(constraints, nusefulconss, solinfeasible, objinfeasible) @@ -304,8 +316,9 @@ cdef SCIP_RETCODE PyConsEnfops (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** cdef SCIP_RETCODE PyConsCheck (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, SCIP_SOL* sol, SCIP_Bool checkintegrality, SCIP_Bool checklprows, SCIP_Bool printreason, SCIP_Bool completely, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) solution = Solution.create(scip, sol) @@ -315,8 +328,9 @@ cdef SCIP_RETCODE PyConsCheck (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** cdef SCIP_RETCODE PyConsProp (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss, int nusefulconss, int nmarkedconss, SCIP_PROPTIMING proptiming, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) result_dict = PyConshdlr.consprop(constraints, nusefulconss, nmarkedconss, proptiming) @@ -328,8 +342,9 @@ cdef SCIP_RETCODE PyConsPresol (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** int nnewdelconss, int nnewaddconss, int nnewupgdconss, int nnewchgcoefs, int nnewchgsides, int* nfixedvars, int* naggrvars, int* nchgvartypes, int* nchgbds, int* naddholes, int* ndelconss, int* naddconss, int* nupgdconss, int* nchgcoefs, int* nchgsides, SCIP_RESULT* result) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) # dictionary for input/output parameters @@ -401,8 +416,9 @@ cdef SCIP_RETCODE PyConsDisable (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS* return SCIP_OKAY cdef SCIP_RETCODE PyConsDelvars (SCIP* scip, SCIP_CONSHDLR* conshdlr, SCIP_CONS** conss, int nconss) noexcept with gil: + cdef int i PyConshdlr = getPyConshdlr(conshdlr) - cdef constraints = [] + constraints = [] for i in range(nconss): constraints.append(getPyCons(conss[i])) PyConshdlr.consdelvars(constraints) diff --git a/src/pyscipopt/cutsel.pxi b/src/pyscipopt/cutsel.pxi index d259fb28..93b44531 100644 --- a/src/pyscipopt/cutsel.pxi +++ b/src/pyscipopt/cutsel.pxi @@ -72,9 +72,10 @@ cdef SCIP_RETCODE PyCutselExitsol (SCIP* scip, SCIP_CUTSEL* cutsel) noexcept wit cdef SCIP_RETCODE PyCutselSelect (SCIP* scip, SCIP_CUTSEL* cutsel, SCIP_ROW** cuts, int ncuts, SCIP_ROW** forcedcuts, int nforcedcuts, SCIP_Bool root, int maxnselectedcuts, int* nselectedcuts, SCIP_RESULT* result) noexcept with gil: - cdef SCIP_CUTSELDATA* cutseldata + cdef SCIP_CUTSELDATA* cutseldata = SCIPcutselGetData(cutsel) cdef SCIP_ROW* scip_row - cutseldata = SCIPcutselGetData(cutsel) + cdef int i + PyCutsel = cutseldata # translate cuts to python diff --git a/src/pyscipopt/lp.pxi b/src/pyscipopt/lp.pxi index 04f6fde3..c17c900a 100644 --- a/src/pyscipopt/lp.pxi +++ b/src/pyscipopt/lp.pxi @@ -63,19 +63,14 @@ cdef class LP: lb -- lower bound (default 0.0) ub -- upper bound (default infinity) """ - nnonz = len(entries) - + cdef int nnonz = len(entries) cdef SCIP_Real* c_coefs = malloc(nnonz * sizeof(SCIP_Real)) cdef int* c_inds = malloc(nnonz * sizeof(int)) - cdef SCIP_Real c_obj - cdef SCIP_Real c_lb - cdef SCIP_Real c_ub - cdef int c_beg - - c_obj = obj - c_lb = lb - c_ub = ub if ub != None else self.infinity() - c_beg = 0 + cdef SCIP_Real c_obj = obj + cdef SCIP_Real c_lb = lb + cdef SCIP_Real c_ub = ub if ub != None else self.infinity() + cdef int c_beg = 0 + cdef int i for i,entry in enumerate(entries): c_inds[i] = entry[0] @@ -95,17 +90,15 @@ cdef class LP: lbs -- lower bounds (default 0.0) ubs -- upper bounds (default infinity) """ - - ncols = len(entrieslist) - nnonz = sum(len(entries) for entries in entrieslist) - + cdef int ncols = len(entrieslist) cdef SCIP_Real* c_objs = malloc(ncols * sizeof(SCIP_Real)) cdef SCIP_Real* c_lbs = malloc(ncols * sizeof(SCIP_Real)) cdef SCIP_Real* c_ubs = malloc(ncols * sizeof(SCIP_Real)) cdef SCIP_Real* c_coefs cdef int* c_inds cdef int* c_beg - + cdef int nnonz = sum(len(entries) for entries in entrieslist) + cdef int i if nnonz > 0: c_coefs = malloc(nnonz * sizeof(SCIP_Real)) @@ -158,18 +151,13 @@ cdef class LP: lhs -- left-hand side of the row (default 0.0) rhs -- right-hand side of the row (default infinity) """ - beg = 0 - nnonz = len(entries) - + cdef int nnonz = len(entries) cdef SCIP_Real* c_coefs = malloc(nnonz * sizeof(SCIP_Real)) cdef int* c_inds = malloc(nnonz * sizeof(int)) - cdef SCIP_Real c_lhs - cdef SCIP_Real c_rhs - cdef int c_beg - - c_lhs = lhs - c_rhs = rhs if rhs != None else self.infinity() - c_beg = 0 + cdef SCIP_Real c_lhs = lhs + cdef SCIP_Real c_rhs = rhs if rhs != None else self.infinity() + cdef int c_beg = 0 + cdef int i for i,entry in enumerate(entries): c_inds[i] = entry[0] @@ -188,16 +176,16 @@ cdef class LP: lhss -- left-hand side of the row (default 0.0) rhss -- right-hand side of the row (default infinity) """ - nrows = len(entrieslist) - nnonz = sum(len(entries) for entries in entrieslist) - + cdef int nrows = len(entrieslist) cdef SCIP_Real* c_lhss = malloc(nrows * sizeof(SCIP_Real)) cdef SCIP_Real* c_rhss = malloc(nrows * sizeof(SCIP_Real)) + cdef int* c_beg = malloc(nrows * sizeof(int)) + cdef int nnonz = sum(len(entries) for entries in entrieslist) cdef SCIP_Real* c_coefs = malloc(nnonz * sizeof(SCIP_Real)) cdef int* c_inds = malloc(nnonz * sizeof(int)) - cdef int* c_beg = malloc(nrows * sizeof(int)) + cdef int tmp = 0 + cdef int i - tmp = 0 for i,entries in enumerate(entrieslist): c_lhss[i] = lhss[i] if lhss != None else 0.0 c_rhss[i] = rhss[i] if rhss != None else self.infinity() @@ -232,6 +220,8 @@ cdef class LP: firstcol -- first column (default 0) lastcol -- last column (default ncols - 1) """ + cdef int i + lastcol = lastcol if lastcol != None else self.ncols() - 1 if firstcol > lastcol: @@ -261,6 +251,8 @@ cdef class LP: firstrow -- first row (default 0) lastrow -- last row (default nrows - 1) """ + cdef int i + lastrow = lastrow if lastrow != None else self.nrows() - 1 if firstrow > lastrow: @@ -290,8 +282,9 @@ cdef class LP: col -- column to change obj -- new objective coefficient """ - cdef int c_col = col cdef SCIP_Real c_obj = obj + cdef int c_col = col + PY_SCIP_CALL(SCIPlpiChgObj(self.lpi, 1, &c_col, &c_obj)) def chgCoef(self, row, col, newval): @@ -312,9 +305,10 @@ cdef class LP: lb -- new lower bound ub -- new upper bound """ - cdef int c_col = col cdef SCIP_Real c_lb = lb cdef SCIP_Real c_ub = ub + cdef int c_col = col + PY_SCIP_CALL(SCIPlpiChgBounds(self.lpi, 1, &c_col, &c_lb, &c_ub)) def chgSide(self, row, lhs, rhs): @@ -325,9 +319,10 @@ cdef class LP: lhs -- new left-hand side rhs -- new right-hand side """ - cdef int c_row = row cdef SCIP_Real c_lhs = lhs cdef SCIP_Real c_rhs = rhs + cdef int c_row = row + PY_SCIP_CALL(SCIPlpiChgSides(self.lpi, 1, &c_row, &c_lhs, &c_rhs)) def clear(self): @@ -337,13 +332,17 @@ cdef class LP: def nrows(self): """Returns the number of rows.""" cdef int nrows + PY_SCIP_CALL(SCIPlpiGetNRows(self.lpi, &nrows)) + return nrows def ncols(self): """Returns the number of columns.""" cdef int ncols + PY_SCIP_CALL(SCIPlpiGetNCols(self.lpi, &ncols)) + return ncols def solve(self, dual=True): @@ -359,12 +358,15 @@ cdef class LP: cdef SCIP_Real objval PY_SCIP_CALL(SCIPlpiGetObjval(self.lpi, &objval)) + return objval def getPrimal(self): """Returns the primal solution of the last LP solve.""" - ncols = self.ncols() + cdef int ncols = self.ncols() cdef SCIP_Real* c_primalsol = malloc(ncols * sizeof(SCIP_Real)) + cdef int i + PY_SCIP_CALL(SCIPlpiGetSol(self.lpi, NULL, c_primalsol, NULL, NULL, NULL)) primalsol = [0.0] * ncols for i in range(ncols): @@ -379,8 +381,10 @@ cdef class LP: def getDual(self): """Returns the dual solution of the last LP solve.""" - nrows = self.nrows() + cdef int nrows = self.nrows() cdef SCIP_Real* c_dualsol = malloc(nrows * sizeof(SCIP_Real)) + cdef int i + PY_SCIP_CALL(SCIPlpiGetSol(self.lpi, NULL, NULL, c_dualsol, NULL, NULL)) dualsol = [0.0] * nrows for i in range(nrows): @@ -395,10 +399,16 @@ cdef class LP: def getPrimalRay(self): """Returns a primal ray if possible, None otherwise.""" + cdef int ncols + cdef SCIP_Real* c_ray + cdef int i + if not SCIPlpiHasPrimalRay(self.lpi): return None + ncols = self.ncols() - cdef SCIP_Real* c_ray = malloc(ncols * sizeof(SCIP_Real)) + c_ray = malloc(ncols * sizeof(SCIP_Real)) + PY_SCIP_CALL(SCIPlpiGetPrimalRay(self.lpi, c_ray)) ray = [0.0] * ncols for i in range(ncols): @@ -409,10 +419,16 @@ cdef class LP: def getDualRay(self): """Returns a dual ray if possible, None otherwise.""" + cdef int nrows + cdef SCIP_Real* c_ray + cdef int i + if not SCIPlpiHasDualRay(self.lpi): return None + nrows = self.nrows() - cdef SCIP_Real* c_ray = malloc(nrows * sizeof(SCIP_Real)) + c_ray = malloc(nrows * sizeof(SCIP_Real)) + PY_SCIP_CALL(SCIPlpiGetDualfarkas(self.lpi, c_ray)) ray = [0.0] * nrows for i in range(nrows): @@ -424,14 +440,17 @@ cdef class LP: def getNIterations(self): """Returns the number of LP iterations of the last LP solve.""" cdef int niters + PY_SCIP_CALL(SCIPlpiGetIterations(self.lpi, &niters)) + return niters def getRedcost(self): """Returns the reduced cost vector of the last LP solve.""" - ncols = self.ncols() - + cdef int ncols = self.ncols() cdef SCIP_Real* c_redcost = malloc(ncols * sizeof(SCIP_Real)) + cdef int i + PY_SCIP_CALL(SCIPlpiGetSol(self.lpi, NULL, NULL, NULL, NULL, c_redcost)) redcost = [] @@ -439,12 +458,14 @@ cdef class LP: redcost[i].append(c_redcost[i]) free(c_redcost) + return redcost def getBasisInds(self): """Returns the indices of the basic columns and rows; index i >= 0 corresponds to column i, index i < 0 to row -i-1""" - nrows = self.nrows() - cdef int* c_binds = malloc(nrows * sizeof(int)) + cdef int nrows = self.nrows() + cdef int* c_binds = malloc(nrows * sizeof(int)) + cdef int i PY_SCIP_CALL(SCIPlpiGetBasisInd(self.lpi, c_binds)) @@ -453,4 +474,5 @@ cdef class LP: binds.append(c_binds[i]) free(c_binds) + return binds diff --git a/src/pyscipopt/reader.pxi b/src/pyscipopt/reader.pxi index 2c45585d..13fc13d1 100644 --- a/src/pyscipopt/reader.pxi +++ b/src/pyscipopt/reader.pxi @@ -45,9 +45,10 @@ cdef SCIP_RETCODE PyReaderWrite (SCIP* scip, SCIP_READER* reader, FILE* file, SCIP_VAR** fixedvars, int nfixedvars, int startnvars, SCIP_CONS** conss, int nconss, int maxnconss, int startnconss, SCIP_Bool genericnames, SCIP_RESULT* result) noexcept with gil: - cdef SCIP_READERDATA* readerdata - readerdata = SCIPreaderGetData(reader) + cdef SCIP_READERDATA* readerdata = SCIPreaderGetData(reader) cdef int fd = fileno(file) + cdef int i + PyFile = os.fdopen(fd, "w", closefd=False) PyName = name.decode('utf-8') PyBinVars = [Variable.create(vars[i]) for i in range(nbinvars)] @@ -61,4 +62,5 @@ cdef SCIP_RETCODE PyReaderWrite (SCIP* scip, SCIP_READER* reader, FILE* file, PyBinVars, PyIntVars, PyImplVars, PyContVars, PyFixedVars, startnvars, PyConss, maxnconss, startnconss, genericnames) result[0] = result_dict.get("result", result[0]) + return SCIP_OKAY diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index f1eea037..5636d578 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -807,6 +807,7 @@ cdef class Row: """ cdef SCIP_COL** cols = SCIProwGetCols(self.scip_row) + cdef int i return [Column.create(cols[i]) for i in range(self.getNNonz())] def getVals(self): @@ -819,6 +820,7 @@ cdef class Row: """ cdef SCIP_Real* vals = SCIProwGetVals(self.scip_row) + cdef int i return [vals[i] for i in range(self.getNNonz())] def getAge(self): @@ -903,6 +905,7 @@ cdef class NLRow: cdef SCIP_VAR** linvars = SCIPnlrowGetLinearVars(self.scip_nlrow) cdef SCIP_Real* lincoefs = SCIPnlrowGetLinearCoefs(self.scip_nlrow) cdef int nlinvars = SCIPnlrowGetNLinearVars(self.scip_nlrow) + cdef int i return [(Variable.create(linvars[i]), lincoefs[i]) for i in range(nlinvars)] def getLhs(self): @@ -981,6 +984,7 @@ cdef class Solution: def __getitem__(self, Expr expr): # fast track for Variable + cdef SCIP_Real coeff if isinstance(expr, Variable): self._checkStage("SCIPgetSolVal") var = expr @@ -999,23 +1003,20 @@ cdef class Solution: def __repr__(self): cdef SCIP_VAR* scip_var - + cdef int i vals = {} self._checkStage("SCIPgetSolVal") for i in range(SCIPgetNOrigVars(self.scip)): scip_var = SCIPgetOrigVars(self.scip)[i] - # extract name cname = bytes(SCIPvarGetName(scip_var)) name = cname.decode('utf-8') - vals[name] = SCIPgetSolVal(self.scip, self.sol, scip_var) return str(vals) def _checkStage(self, method): if method in ["SCIPgetSolVal", "getSolObjVal"]: stage_check = SCIPgetStage(self.scip) not in [SCIP_STAGE_INIT, SCIP_STAGE_FREE] - if not stage_check or self.sol == NULL and SCIPgetStage(self.scip) != SCIP_STAGE_SOLVING: raise Warning(f"{method} can only be called with a valid solution or in stage SOLVING (current stage: {SCIPgetStage(self.scip)})") @@ -1049,7 +1050,6 @@ cdef class Solution: PY_SCIP_CALL(SCIPretransformSol(self.scip, self.sol)) cdef Solution targetSol = Solution.create(target._scip, NULL) cdef SCIP_VAR** source_vars = SCIPgetOrigVars(self.scip) - PY_SCIP_CALL(SCIPtranslateSubSol(target._scip, self.scip, self.sol, NULL, source_vars, &(targetSol.sol))) return targetSol @@ -1175,7 +1175,8 @@ cdef class DomainChanges: list of BoundChange """ - nboundchgs = SCIPdomchgGetNBoundchgs(self.scip_domchg) + cdef int nboundchgs = SCIPdomchgGetNBoundchgs(self.scip_domchg) + cdef int i return [BoundChange.create(SCIPdomchgGetBoundchg(self.scip_domchg, i)) for i in range(nboundchgs)] @@ -1284,6 +1285,7 @@ cdef class Node: return [] cdef SCIP_CONS** addedconss = malloc(addedconsssize * sizeof(SCIP_CONS*)) cdef int nconss + cdef int i SCIPnodeGetAddedConss(self.scip_node, addedconss, &nconss, addedconsssize) assert nconss == addedconsssize constraints = [Constraint.create(addedconss[i]) for i in range(nconss)] @@ -1358,18 +1360,16 @@ cdef class Node: cdef int nbranchvars = self.getNParentBranchings() if nbranchvars == 0: return None - cdef SCIP_VAR** branchvars = malloc(nbranchvars * sizeof(SCIP_VAR*)) cdef SCIP_Real* branchbounds = malloc(nbranchvars * sizeof(SCIP_Real)) cdef SCIP_BOUNDTYPE* boundtypes = malloc(nbranchvars * sizeof(SCIP_BOUNDTYPE)) - + cdef int i SCIPnodeGetParentBranchings(self.scip_node, branchvars, branchbounds, boundtypes, &nbranchvars, nbranchvars) py_variables = [Variable.create(branchvars[i]) for i in range(nbranchvars)] py_branchbounds = [branchbounds[i] for i in range(nbranchvars)] py_boundtypes = [boundtypes[i] for i in range(nbranchvars)] - free(boundtypes) free(branchbounds) free(branchvars) @@ -1638,7 +1638,6 @@ cdef class Variable(Expr): mayround = SCIPvarMayRoundDown(self.scip_var) else: mayround = SCIPvarMayRoundUp(self.scip_var) - return mayround cdef class Constraint: @@ -2661,9 +2660,11 @@ cdef class Model: """ cdef SCIP_LPI* lpi - PY_SCIP_CALL(SCIPgetLPI(self._scip, &lpi)) cdef int iters = 0 + + PY_SCIP_CALL(SCIPgetLPI(self._scip, &lpi)) PY_SCIP_CALL(SCIPlpiGetIterations(lpi, &iters)) + return iters # Objective function @@ -2714,9 +2715,10 @@ cdef class Model: set all other variables objective coefficient to zero (Default value = 'true') """ - - cdef SCIP_VAR** _vars - cdef int _nvars + cdef SCIP_VAR** vars + cdef int nvars + cdef SCIP_Real coef + cdef int i # turn the constant value into an Expr instance for further processing if not isinstance(expr, Expr): @@ -2729,10 +2731,10 @@ cdef class Model: if clear: # clear existing objective function self.addObjoffset(-self.getObjoffset()) - _vars = SCIPgetOrigVars(self._scip) - _nvars = SCIPgetNOrigVars(self._scip) - for i in range(_nvars): - PY_SCIP_CALL(SCIPchgVarObj(self._scip, _vars[i], 0.0)) + vars = SCIPgetOrigVars(self._scip) + nvars = SCIPgetNOrigVars(self._scip) + for i in range(nvars): + PY_SCIP_CALL(SCIPchgVarObj(self._scip, vars[i], 0.0)) if expr[CONST] != 0.0: self.addObjoffset(expr[CONST]) @@ -3406,18 +3408,18 @@ cdef class Model: """ cdef SCIP_VAR** _vars - cdef SCIP_VAR* _var - cdef int _nvars - vars = [] + cdef int nvars + cdef int i + vars = [] if transformed: _vars = SCIPgetVars(self._scip) - _nvars = SCIPgetNVars(self._scip) + nvars = SCIPgetNVars(self._scip) else: _vars = SCIPgetOrigVars(self._scip) - _nvars = SCIPgetNOrigVars(self._scip) + nvars = SCIPgetNOrigVars(self._scip) - for i in range(_nvars): + for i in range(nvars): ptr = (_vars[i]) # check whether the corresponding variable exists already @@ -3633,6 +3635,7 @@ cdef class Model: cdef int _nleaves cdef int _nchildren cdef int _nsiblings + cdef int i PY_SCIP_CALL(SCIPgetOpenNodesData(self._scip, &_leaves, &_children, &_siblings, &_nleaves, &_nchildren, &_nsiblings)) @@ -3699,8 +3702,10 @@ cdef class Model: """ cdef SCIP_COL** cols cdef int ncols + cdef int i PY_SCIP_CALL(SCIPgetLPColsData(self._scip, &cols, &ncols)) + return [Column.create(cols[i]) for i in range(ncols)] def getLPRowsData(self): @@ -3714,8 +3719,10 @@ cdef class Model: """ cdef SCIP_ROW** rows cdef int nrows + cdef int i PY_SCIP_CALL(SCIPgetLPRowsData(self._scip, &rows, &nrows)) + return [Row.create(rows[i]) for i in range(nrows)] def getNLPRows(self): @@ -3752,10 +3759,12 @@ cdef class Model: """ cdef int nrows = SCIPgetNLPRows(self._scip) cdef int* inds = malloc(nrows * sizeof(int)) + cdef int i PY_SCIP_CALL(SCIPgetLPBasisInd(self._scip, inds)) result = [inds[i] for i in range(nrows)] free(inds) + return result def getLPBInvRow(self, row): @@ -3775,10 +3784,12 @@ cdef class Model: # TODO: sparsity information cdef int nrows = SCIPgetNLPRows(self._scip) cdef SCIP_Real* coefs = malloc(nrows * sizeof(SCIP_Real)) + cdef int i PY_SCIP_CALL(SCIPgetLPBInvRow(self._scip, row, coefs, NULL, NULL)) result = [coefs[i] for i in range(nrows)] free(coefs) + return result def getLPBInvARow(self, row): @@ -3798,10 +3809,12 @@ cdef class Model: # TODO: sparsity information cdef int ncols = SCIPgetNLPCols(self._scip) cdef SCIP_Real* coefs = malloc(ncols * sizeof(SCIP_Real)) + cdef int i PY_SCIP_CALL(SCIPgetLPBInvARow(self._scip, row, NULL, coefs, NULL, NULL)) result = [coefs[i] for i in range(ncols)] free(coefs) + return result def isLPSolBasic(self): @@ -4245,12 +4258,12 @@ cdef class Model: assert lincons.expr.degree() <= 1, "given constraint is not linear, degree == %d" % lincons.expr.degree() terms = lincons.expr.terms - cdef SCIP_CONS* scip_cons - cdef int nvars = len(terms.items()) - - vars_array = malloc(nvars * sizeof(SCIP_VAR*)) - coeffs_array = malloc(nvars * sizeof(SCIP_Real)) + cdef SCIP_VAR** vars_array = malloc(nvars * sizeof(SCIP_VAR*)) + cdef SCIP_Real* coeffs_array = malloc(nvars * sizeof(SCIP_Real)) + cdef SCIP_CONS* scip_cons + cdef SCIP_Real coeff + cdef int i for i, (key, coeff) in enumerate(terms.items()): vars_array[i] = (key[0]).scip_var @@ -4267,6 +4280,7 @@ cdef class Model: free(vars_array) free(coeffs_array) + return PyCons def _createConsQuadratic(self, ExprCons quadcons, **kwargs): @@ -4340,15 +4354,17 @@ cdef class Model: cdef SCIP_EXPR* expr cdef SCIP_EXPR** varexprs cdef SCIP_EXPR** monomials - cdef int* idxs cdef SCIP_CONS* scip_cons + cdef int* idxs + cdef int i + cdef int j terms = cons.expr.terms # collect variables - variables = {var.ptr():var for term in terms for var in term} + variables = {var.ptr(): var for term in terms for var in term} variables = list(variables.values()) - varindex = {var.ptr():idx for (idx,var) in enumerate(variables)} + varindex = {var.ptr(): i for (i, var) in enumerate(variables)} # create monomials for terms monomials = malloc(len(terms) * sizeof(SCIP_EXPR*)) @@ -4389,6 +4405,7 @@ cdef class Model: PY_SCIP_CALL(SCIPreleaseExpr(self._scip, &monomials[i])) free(monomials) free(termcoefs) + return PyCons def _createConsGenNonlinear(self, cons, **kwargs): @@ -4410,6 +4427,8 @@ cdef class Model: cdef SCIP_EXPR** scipexprs cdef SCIP_CONS* scip_cons cdef int nchildren + cdef int c + cdef int i # get arrays from python's expression tree expr = cons.expr @@ -4705,6 +4724,9 @@ cdef class Model: The created and added Constraint objects. """ + cdef int n_conss + cdef int i + def ensure_iterable(elem, length): if isinstance(elem, Iterable): return elem @@ -4718,9 +4740,10 @@ cdef class Model: if isinstance(name, str): if name == "": - name = ["" for idx in range(n_conss)] + name = ["" for i in range(n_conss)] else: - name = ["%s_%s" % (name, idx) for idx in range(n_conss)] + name = ["%s_%s" % (name, i) for i in range(n_conss)] + initial = ensure_iterable(initial, n_conss) separate = ensure_iterable(separate, n_conss) enforce = ensure_iterable(enforce, n_conss) @@ -4777,6 +4800,12 @@ cdef class Model: The created disjunction constraint """ + cdef SCIP_EXPR* scip_expr + cdef SCIP_CONS* scip_cons + cdef SCIP_CONS* disj_cons + cdef int n_conss + cdef int i + def ensure_iterable(elem, length): if isinstance(elem, Iterable): return elem @@ -4787,12 +4816,6 @@ cdef class Model: conss = list(conss) n_conss = len(conss) - cdef SCIP_CONS* disj_cons - - cdef SCIP_CONS* scip_cons - - cdef SCIP_EXPR* scip_expr - PY_SCIP_CALL(SCIPcreateConsDisjunction( self._scip, &disj_cons, str_conversion(name), 0, &scip_cons, NULL, initial, enforce, check, local, modifiable, dynamic @@ -4810,6 +4833,7 @@ cdef class Model: PY_SCIP_CALL(SCIPaddCons(self._scip, disj_cons)) PyCons = Constraint.create(disj_cons) PY_SCIP_CALL(SCIPreleaseCons(self._scip, &disj_cons)) + return PyCons def addConsElemDisjunction(self, Constraint disj_cons, Constraint cons): @@ -4878,16 +4902,17 @@ cdef class Model: list of Variable """ + cdef SCIP_VAR** _vars + cdef int nvars cdef SCIP_Bool success - cdef int _nvars + cdef int i - SCIPgetConsNVars(self._scip, constraint.scip_cons, &_nvars, &success) - - cdef SCIP_VAR** _vars = malloc(_nvars * sizeof(SCIP_VAR*)) - SCIPgetConsVars(self._scip, constraint.scip_cons, _vars, _nvars*sizeof(SCIP_VAR*), &success) + SCIPgetConsNVars(self._scip, constraint.scip_cons, &nvars, &success) + _vars = malloc(nvars * sizeof(SCIP_VAR*)) + SCIPgetConsVars(self._scip, constraint.scip_cons, _vars, nvars*sizeof(SCIP_VAR*), &success) vars = [] - for i in range(_nvars): + for i in range(nvars): ptr = (_vars[i]) # check whether the corresponding variable exists already if ptr in self._modelvars: @@ -4898,6 +4923,7 @@ cdef class Model: assert var.ptr() == ptr self._modelvars[ptr] = var vars.append(var) + return vars def printCons(self, Constraint constraint): @@ -5044,7 +5070,8 @@ cdef class Model: """ cdef SCIP_CONS* scip_cons - cdef int _nvars + cdef int nvars + cdef int i PY_SCIP_CALL(SCIPcreateConsSOS1(self._scip, &scip_cons, str_conversion(name), 0, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode)) @@ -5060,6 +5087,7 @@ cdef class Model: PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, scip_cons, var.scip_var, weights[i])) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) + return Constraint.create(scip_cons) def addConsSOS2(self, vars, weights=None, name="SOS2cons", @@ -5104,7 +5132,8 @@ cdef class Model: """ cdef SCIP_CONS* scip_cons - cdef int _nvars + cdef int nvars + cdef int i PY_SCIP_CALL(SCIPcreateConsSOS2(self._scip, &scip_cons, str_conversion(name), 0, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode)) @@ -5120,7 +5149,8 @@ cdef class Model: PY_SCIP_CALL(SCIPaddVarSOS2(self._scip, scip_cons, var.scip_var, weights[i])) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) - return Constraint.create(scip_cons) + + return Constraint.create(scip_cons) def addConsAnd(self, vars, resvar, name="ANDcons", initial=True, separate=True, enforce=True, check=True, @@ -5163,16 +5193,17 @@ cdef class Model: The newly created AND constraint """ + cdef int nvars = len(vars) + cdef SCIP_VAR** _vars = malloc(nvars * sizeof(SCIP_VAR*)) + cdef SCIP_VAR* _resvar cdef SCIP_CONS* scip_cons + cdef int i - nvars = len(vars) - - _vars = malloc(len(vars) * sizeof(SCIP_VAR*)) - for idx, var in enumerate(vars): - _vars[idx] = (var).scip_var - _resVar = (resvar).scip_var + _resvar = (resvar).scip_var + for i, var in enumerate(vars): + _vars[i] = (var).scip_var - PY_SCIP_CALL(SCIPcreateConsAnd(self._scip, &scip_cons, str_conversion(name), _resVar, nvars, _vars, + PY_SCIP_CALL(SCIPcreateConsAnd(self._scip, &scip_cons, str_conversion(name), _resvar, nvars, _vars, initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode)) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) @@ -5224,16 +5255,17 @@ cdef class Model: The newly created OR constraint """ + cdef int nvars = len(vars) + cdef SCIP_VAR** _vars = malloc(nvars * sizeof(SCIP_VAR*)) + cdef SCIP_VAR* _resvar cdef SCIP_CONS* scip_cons + cdef int i - nvars = len(vars) - - _vars = malloc(len(vars) * sizeof(SCIP_VAR*)) - for idx, var in enumerate(vars): - _vars[idx] = (var).scip_var - _resVar = (resvar).scip_var + _resvar = (resvar).scip_var + for i, var in enumerate(vars): + _vars[i] = (var).scip_var - PY_SCIP_CALL(SCIPcreateConsOr(self._scip, &scip_cons, str_conversion(name), _resVar, nvars, _vars, + PY_SCIP_CALL(SCIPcreateConsOr(self._scip, &scip_cons, str_conversion(name), _resvar, nvars, _vars, initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode)) PY_SCIP_CALL(SCIPaddCons(self._scip, scip_cons)) @@ -5285,14 +5317,14 @@ cdef class Model: The newly created XOR constraint """ + cdef int nvars = len(vars) + cdef SCIP_VAR** _vars = malloc(nvars * sizeof(SCIP_VAR*)) cdef SCIP_CONS* scip_cons - - nvars = len(vars) + cdef int i assert type(rhsvar) is type(bool()), "Provide BOOLEAN value as rhsvar, you gave %s." % type(rhsvar) - _vars = malloc(len(vars) * sizeof(SCIP_VAR*)) - for idx, var in enumerate(vars): - _vars[idx] = (var).scip_var + for i, var in enumerate(vars): + _vars[i] = (var).scip_var PY_SCIP_CALL(SCIPcreateConsXor(self._scip, &scip_cons, str_conversion(name), rhsvar, nvars, _vars, initial, separate, enforce, check, propagate, local, modifiable, dynamic, removable, stickingatnode)) @@ -5353,8 +5385,9 @@ cdef class Model: The newly created Cardinality constraint """ - cdef SCIP_CONS* scip_cons cdef SCIP_VAR* indvar + cdef SCIP_CONS* scip_cons + cdef int i PY_SCIP_CALL(SCIPcreateConsCardinality(self._scip, &scip_cons, str_conversion(name), 0, NULL, cardval, NULL, NULL, initial, separate, enforce, check, propagate, local, dynamic, removable, stickingatnode)) @@ -5426,9 +5459,11 @@ cdef class Model: The newly created Indicator constraint """ - assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__ - cdef SCIP_CONS* scip_cons cdef SCIP_VAR* _binVar + cdef SCIP_CONS* scip_cons + cdef SCIP_Real coeff + assert isinstance(cons, ExprCons), "given constraint is not ExprCons but %s" % cons.__class__.__name__ + if cons._lhs is not None and cons._rhs is not None: raise ValueError("expected inequality that has either only a left or right hand side") @@ -5887,9 +5922,9 @@ cdef class Model: list of NLRow """ - cdef SCIP_NLROW** nlrows + cdef SCIP_NLROW** nlrows = SCIPgetNLPNlRows(self._scip) + cdef int i - nlrows = SCIPgetNLPNlRows(self._scip) return [NLRow.create(nlrows[i]) for i in range(self.getNNlRows())] def getNlRowSolActivity(self, NLRow nlrow, Solution sol = None): @@ -5999,23 +6034,24 @@ cdef class Model: """ cdef SCIP_EXPR* expr + cdef int termidx # linear terms - cdef SCIP_EXPR** _linexprs - cdef SCIP_Real* _lincoefs - cdef int _nlinvars + cdef SCIP_EXPR** linexprs + cdef SCIP_Real* lincoefs + cdef SCIP_Real lincoef + cdef int nlinvars # bilinear terms - cdef int _nbilinterms cdef SCIP_EXPR* bilinterm1 cdef SCIP_EXPR* bilinterm2 cdef SCIP_Real bilincoef + cdef int nbilinterms # quadratic terms - cdef int _nquadterms - cdef SCIP_Real sqrcoef - cdef SCIP_Real lincoef cdef SCIP_EXPR* sqrexpr + cdef SCIP_Real sqrcoef + cdef int nquadterms # variables cdef SCIP_VAR* scipvar1 @@ -6025,17 +6061,17 @@ cdef class Model: assert self.checkQuadraticNonlinear(cons), "constraint is not quadratic" expr = SCIPgetExprNonlinear(cons.scip_cons) - SCIPexprGetQuadraticData(expr, NULL, &_nlinvars, &_linexprs, &_lincoefs, &_nquadterms, &_nbilinterms, NULL, NULL) + SCIPexprGetQuadraticData(expr, NULL, &nlinvars, &linexprs, &lincoefs, &nquadterms, &nbilinterms, NULL, NULL) linterms = [] bilinterms = [] quadterms = [] - for termidx in range(_nlinvars): - var = Variable.create(SCIPgetVarExprVar(_linexprs[termidx])) - linterms.append((var,_lincoefs[termidx])) + for termidx in range(nlinvars): + var = Variable.create(SCIPgetVarExprVar(linexprs[termidx])) + linterms.append((var, lincoefs[termidx])) - for termidx in range(_nbilinterms): + for termidx in range(nbilinterms): SCIPexprGetQuadraticBilinTerm(expr, termidx, &bilinterm1, &bilinterm2, &bilincoef, NULL, NULL) scipvar1 = SCIPgetVarExprVar(bilinterm1) scipvar2 = SCIPgetVarExprVar(bilinterm2) @@ -6046,7 +6082,7 @@ cdef class Model: else: quadterms.append((var1,bilincoef,0.0)) - for termidx in range(_nquadterms): + for termidx in range(nquadterms): SCIPexprGetQuadraticQuadTerm(expr, termidx, NULL, &lincoef, &sqrcoef, NULL, NULL, &sqrexpr) if sqrexpr == NULL: continue @@ -6081,17 +6117,18 @@ cdef class Model: list of Constraint """ - cdef SCIP_CONS** _conss - cdef int _nconss - conss = [] + cdef SCIP_CONS** conss + cdef int nconss + cdef int i if transformed: - _conss = SCIPgetConss(self._scip) - _nconss = SCIPgetNConss(self._scip) + conss = SCIPgetConss(self._scip) + nconss = SCIPgetNConss(self._scip) else: - _conss = SCIPgetOrigConss(self._scip) - _nconss = SCIPgetNOrigConss(self._scip) - return [Constraint.create(_conss[i]) for i in range(_nconss)] + conss = SCIPgetOrigConss(self._scip) + nconss = SCIPgetNOrigConss(self._scip) + + return [Constraint.create(conss[i]) for i in range(nconss)] def getNConss(self, transformed=True): """ @@ -6150,19 +6187,21 @@ cdef class Model: dict of str to float """ - cdef SCIP_Real* _vals - cdef SCIP_VAR** _vars + cdef SCIP_VAR** vars + cdef SCIP_Real* vals + cdef int i constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8') if not constype == 'linear': raise Warning("coefficients not available for constraints of type ", constype) - _vals = SCIPgetValsLinear(self._scip, cons.scip_cons) - _vars = SCIPgetVarsLinear(self._scip, cons.scip_cons) + vals = SCIPgetValsLinear(self._scip, cons.scip_cons) + vars = SCIPgetVarsLinear(self._scip, cons.scip_cons) valsdict = {} for i in range(SCIPgetNVarsLinear(self._scip, cons.scip_cons)): - valsdict[bytes(SCIPvarGetName(_vars[i])).decode('utf-8')] = _vals[i] + valsdict[bytes(SCIPvarGetName(vars[i])).decode('utf-8')] = vals[i] + return valsdict def getRowLinear(self, Constraint cons): @@ -6345,6 +6384,8 @@ cdef class Model: """ cdef SCIP** subprobs cdef SCIP_BENDERS* benders + cdef int nsubproblems + cdef int i # checking whether subproblems is a dictionary if isinstance(subproblems, dict): @@ -6359,8 +6400,8 @@ cdef class Model: # if subproblems is a dictionary, then the dictionary is turned into a c array if isdict: - for idx, subprob in enumerate(subproblems.values()): - subprobs[idx] = (subprob)._scip + for i, subprob in enumerate(subproblems.values()): + subprobs[i] = (subprob)._scip else: subprobs[0] = (subproblems)._scip @@ -6380,40 +6421,37 @@ cdef class Model: If the user wants to resolve the subproblems, they must free them by calling freeBendersSubproblems() """ - cdef SCIP_BENDERS** _benders - cdef SCIP_Bool _infeasible - cdef int nbenders + cdef SCIP_BENDERS** benders = SCIPgetBenders(self._scip) + cdef SCIP_Bool infeasible + cdef SCIP_Bool solvecip = True + cdef int nbenders = SCIPgetNActiveBenders(self._scip) cdef int nsubproblems - - solvecip = True - - nbenders = SCIPgetNActiveBenders(self._scip) - _benders = SCIPgetBenders(self._scip) + cdef int i + cdef int j # solving all subproblems from all Benders' decompositions for i in range(nbenders): - nsubproblems = SCIPbendersGetNSubproblems(_benders[i]) + nsubproblems = SCIPbendersGetNSubproblems(benders[i]) for j in range(nsubproblems): PY_SCIP_CALL(SCIPsetupBendersSubproblem(self._scip, - _benders[i], self._bestSol.sol, j, SCIP_BENDERSENFOTYPE_CHECK)) + benders[i], self._bestSol.sol, j, SCIP_BENDERSENFOTYPE_CHECK)) PY_SCIP_CALL(SCIPsolveBendersSubproblem(self._scip, - _benders[i], self._bestSol.sol, j, &_infeasible, solvecip, NULL)) + benders[i], self._bestSol.sol, j, &infeasible, solvecip, NULL)) def freeBendersSubproblems(self): """Calls the free subproblem function for the Benders' decomposition. This will free all subproblems for all decompositions. """ - cdef SCIP_BENDERS** _benders - cdef int nbenders + cdef SCIP_BENDERS** benders = SCIPgetBenders(self._scip) + cdef int nbenders = SCIPgetNActiveBenders(self._scip) cdef int nsubproblems - - nbenders = SCIPgetNActiveBenders(self._scip) - _benders = SCIPgetBenders(self._scip) + cdef int i + cdef int j # solving all subproblems from all Benders' decompositions for i in range(nbenders): - nsubproblems = SCIPbendersGetNSubproblems(_benders[i]) + nsubproblems = SCIPbendersGetNSubproblems(benders[i]) for j in range(nsubproblems): - PY_SCIP_CALL(SCIPfreeBendersSubproblem(self._scip, _benders[i], + PY_SCIP_CALL(SCIPfreeBendersSubproblem(self._scip, benders[i], j)) def updateBendersLowerbounds(self, lowerbounds, Benders benders=None): @@ -6850,11 +6888,17 @@ cdef class Model: model : Model A model containing the created copy """ - - orig_vars = SCIPgetVars(self._scip) - vars = malloc(len(to_fix) * sizeof(SCIP_VAR*)) - vals = malloc(len(fix_vals) * sizeof(SCIP_Real)) - j = 0 + cdef SCIP* subscip + cdef SCIP_HASHMAP* varmap + cdef SCIP_VAR** orig_vars = SCIPgetVars(self._scip) + cdef SCIP_VAR** vars = malloc(len(to_fix) * sizeof(SCIP_VAR*)) + cdef SCIP_Real* vals = malloc(len(fix_vals) * sizeof(SCIP_Real)) + cdef SCIP_Real val + cdef SCIP_Bool valid + cdef SCIP_Bool success + cdef int i + cdef int j = 0 + name_to_val = {var.name: val for var, val in zip(to_fix, fix_vals)} for i, var in enumerate(self.getVars()): if var.name in name_to_val: @@ -6862,11 +6906,6 @@ cdef class Model: vals[j] = name_to_val[var.name] j+= 1 - cdef SCIP_Bool success - cdef SCIP_Bool valid - cdef SCIP* subscip - cdef SCIP_HASHMAP* varmap - PY_SCIP_CALL(SCIPcreate(&subscip)) PY_SCIP_CALL( SCIPhashmapCreate(&varmap, SCIPblkmem(subscip), self.getNVars()) ) PY_SCIP_CALL( SCIPcopyLargeNeighborhoodSearch(self._scip, subscip, varmap, "LNhS_subscip", vars, vals, @@ -6876,6 +6915,7 @@ cdef class Model: free(vars) free(vals) SCIPhashmapFree(&varmap) + return sub_model def translateSubSol(self, Model sub_model, Solution sol, heur) -> Solution: @@ -6896,21 +6936,22 @@ cdef class Model: solution : Solution The corresponding solution in the main model """ - + cdef SCIP_VAR** vars = malloc(self.getNVars() * sizeof(SCIP_VAR*)) cdef SCIP_SOL* real_sol - cdef SCIP_SOL* subscip_sol + cdef SCIP_SOL* subscip_sol = sol.sol + cdef SCIP_HEUR* _heur cdef SCIP_Bool success - subscip_sol = sol.sol - vars = malloc(self.getNVars() * sizeof(SCIP_VAR*)) + cdef int i + for i, var in enumerate(sub_model.getVars()): vars[i] = (var).scip_var - cdef SCIP_HEUR* _heur name = str_conversion(heur.name) _heur = SCIPfindHeur(self._scip, name) PY_SCIP_CALL( SCIPtranslateSubSol(self._scip, sub_model._scip, subscip_sol, _heur, vars, &real_sol) ) solution = Solution.create(self._scip, real_sol) free(vars) + return solution def createCons(self, Conshdlr conshdlr, name, initial=True, separate=True, enforce=True, check=True, propagate=True, @@ -7359,14 +7400,14 @@ cdef class Model: number of fractional implicit integer variables """ + cdef SCIP_VAR** lpcands + cdef SCIP_Real* lpcandssol + cdef SCIP_Real* lpcandsfrac cdef int ncands cdef int nlpcands cdef int npriolpcands cdef int nfracimplvars - - cdef SCIP_VAR** lpcands - cdef SCIP_Real* lpcandssol - cdef SCIP_Real* lpcandsfrac + cdef int i PY_SCIP_CALL(SCIPgetLPBranchCands(self._scip, &lpcands, &lpcandssol, &lpcandsfrac, &nlpcands, &npriolpcands, &nfracimplvars)) @@ -7389,10 +7430,10 @@ cdef class Model: number of candidates with maximal priority """ + cdef SCIP_VAR** pseudocands cdef int npseudocands cdef int npriopseudocands - - cdef SCIP_VAR** pseudocands + cdef int i PY_SCIP_CALL(SCIPgetPseudoBranchCands(self._scip, &pseudocands, &npseudocands, &npriopseudocands)) @@ -8327,10 +8368,10 @@ cdef class Model: list of Solution """ - cdef SCIP_SOL** _sols - cdef SCIP_SOL* _sol - _sols = SCIPgetSols(self._scip) - nsols = SCIPgetNSols(self._scip) + cdef SCIP_SOL** _sols = SCIPgetSols(self._scip) + cdef int nsols = SCIPgetNSols(self._scip) + cdef int i + sols = [] for i in range(nsols): @@ -8515,13 +8556,13 @@ cdef class Model: """ assert SCIPhasPrimalRay(self._scip), "The problem does not have a primal ray." - - cdef int _nvars = SCIPgetNVars(self._scip) - cdef SCIP_VAR ** _vars = SCIPgetVars(self._scip) + cdef SCIP_VAR** vars = SCIPgetVars(self._scip) + cdef int nvars = SCIPgetNVars(self._scip) + cdef int i ray = [] - for i in range(_nvars): - ray.append(float(SCIPgetPrimalRayVal(self._scip, _vars[i]))) + for i in range(nvars): + ray.append(float(SCIPgetPrimalRayVal(self._scip, vars[i]))) return ray @@ -9046,13 +9087,14 @@ cdef class Model: dict mapping parameter names to their values. """ - cdef SCIP_PARAM** params + cdef SCIP_PARAM** params = SCIPgetParams(self._scip) + cdef int i - params = SCIPgetParams(self._scip) result = {} for i in range(SCIPgetNParams(self._scip)): name = SCIPparamGetName(params[i]).decode('utf-8') result[name] = self.getParam(name) + return result def setParams(self, params): @@ -9233,8 +9275,12 @@ cdef class Model: the objective sense (Default value = 'minimize') """ - + cdef SCIP_VAR** vars + cdef int nvars + cdef SCIP_Real* _coeffs cdef SCIP_OBJSENSE objsense + cdef SCIP_Real coef + cdef int i if sense == "minimize": objsense = SCIP_OBJSENSE_MINIMIZE @@ -9250,13 +9296,11 @@ cdef class Model: if coeffs[CONST] != 0.0: raise ValueError("Constant offsets in objective are not supported!") - cdef SCIP_VAR** _vars - cdef int _nvars - _vars = SCIPgetOrigVars(self._scip) - _nvars = SCIPgetNOrigVars(self._scip) - _coeffs = malloc(_nvars * sizeof(SCIP_Real)) + vars = SCIPgetOrigVars(self._scip) + nvars = SCIPgetNOrigVars(self._scip) + _coeffs = malloc(nvars * sizeof(SCIP_Real)) - for i in range(_nvars): + for i in range(nvars): _coeffs[i] = 0.0 for term, coef in coeffs.terms.items(): @@ -9264,11 +9308,11 @@ cdef class Model: if term != CONST: assert len(term) == 1 var = term[0] - for i in range(_nvars): - if _vars[i] == var.scip_var: + for i in range(nvars): + if vars[i] == var.scip_var: _coeffs[i] = coef - PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, _vars, &_coeffs[0], _nvars)) + PY_SCIP_CALL(SCIPchgReoptObjective(self._scip, objsense, vars, &_coeffs[0], nvars)) free(_coeffs) @@ -9457,16 +9501,15 @@ cdef class Model: float """ - assert isinstance(gains, list) - nchildren = len(gains) + cdef int nchildren = len(gains) + cdef SCIP_Real* _gains = malloc(nchildren * sizeof(SCIP_Real)) + cdef int i - cdef int _nchildren = nchildren - _gains = malloc(_nchildren * sizeof(SCIP_Real)) - for i in range(_nchildren): + for i in range(nchildren): _gains[i] = gains[i] - score = SCIPgetBranchScoreMultiple(self._scip, var.scip_var, _nchildren, _gains) + score = SCIPgetBranchScoreMultiple(self._scip, var.scip_var, nchildren, _gains) free(_gains) @@ -9524,11 +9567,13 @@ cdef class Model: The feature mappings for the columns, edges, and rows """ - cdef SCIP* scip = self._scip - cdef int i, j, k, col_i cdef SCIP_VARTYPE vtype cdef SCIP_Real sim, prod + cdef int col_i + cdef int i + cdef int j + cdef int k # Check if SCIP is in the correct stage if SCIPgetStage(scip) != SCIP_STAGE_SOLVING: @@ -9550,7 +9595,7 @@ cdef class Model: "basis_zero": 15, "best_incumbent_val": 16, "avg_incumbent_val": 17, "age": 18} if prev_col_features is None: - col_features = [[0 for _ in range(n_col_features)] for _ in range(ncols)] + col_features = [[0 for i in range(n_col_features)] for j in range(ncols)] else: assert len(prev_col_features) > 0, "Previous column features is empty" col_features = prev_col_features @@ -9558,7 +9603,7 @@ cdef class Model: if not suppress_warnings: raise Warning(f"The number of columns has changed. Previous column data being ignored") else: - col_features = [[0 for _ in range(n_col_features)] for _ in range(ncols)] + col_features = [[0 for i in range(n_col_features)] for j in range(ncols)] prev_col_features = None if len(prev_col_features[0]) != n_col_features: raise Warning(f"Dimension mismatch in provided previous features and new features:" @@ -9566,8 +9611,11 @@ cdef class Model: cdef SCIP_SOL* sol = SCIPgetBestSol(scip) cdef SCIP_VAR* var - cdef SCIP_Real lb, ub, solval + cdef SCIP_Real lb + cdef SCIP_Real ub + cdef SCIP_Real solval cdef SCIP_BASESTAT basis_status + for i in range(ncols): col_i = SCIPcolGetLPPos(cols[i]) var = SCIPcolGetVar(cols[i]) @@ -9633,8 +9681,8 @@ cdef class Model: col_features[col_i][col_feature_map["avg_incumbent_val"]] = SCIPvarGetAvgSol(var) # Generate row features - cdef int nrows = SCIPgetNLPRows(scip) cdef SCIP_ROW** rows = SCIPgetLPRows(scip) + cdef int nrows = SCIPgetNLPRows(scip) if static_only: n_row_features = 6 @@ -9646,7 +9694,7 @@ cdef class Model: "basis_lower": 10, "basis_basic": 11, "basis_upper": 12, "basis_zero": 13} if prev_row_features is None: - row_features = [[0 for _ in range(n_row_features)] for _ in range(nrows)] + row_features = [[0 for i in range(n_row_features)] for j in range(nrows)] else: assert len(prev_row_features) > 0, "Previous row features is empty" row_features = prev_row_features @@ -9654,14 +9702,17 @@ cdef class Model: if not suppress_warnings: raise Warning(f"The number of rows has changed. Previous row data being ignored") else: - row_features = [[0 for _ in range(n_row_features)] for _ in range(nrows)] + row_features = [[0 for i in range(n_row_features)] for j in range(nrows)] prev_row_features = None if len(prev_row_features[0]) != n_row_features: raise Warning(f"Dimension mismatch in provided previous features and new features:" f"{len(prev_row_features[0])} != {n_row_features}") + cdef SCIP_Real lhs + cdef SCIP_Real rhs + cdef SCIP_Real cst cdef int nnzrs = 0 - cdef SCIP_Real lhs, rhs, cst + for i in range(nrows): # lhs <= activity + cst <= rhs @@ -9718,10 +9769,11 @@ cdef class Model: # Generate edge (coefficient) features cdef SCIP_COL** row_cols cdef SCIP_Real * row_vals + n_edge_features = 3 edge_feature_map = {"col_idx": 0, "row_idx": 1, "coef": 2} if prev_edge_features is None: - edge_features = [[0 for _ in range(n_edge_features)] for _ in range(nnzrs)] + edge_features = [[0 for i in range(n_edge_features)] for j in range(nnzrs)] j = 0 for i in range(nrows): # coefficient indexes and values @@ -9739,13 +9791,12 @@ cdef class Model: if not suppress_warnings: raise Warning(f"The number of coefficients in the LP has changed. Previous edge data being ignored") else: - edge_features = [[0 for _ in range(3)] for _ in range(nnzrs)] + edge_features = [[0 for i in range(3)] for j in range(nnzrs)] prev_edge_features = None if len(prev_edge_features[0]) != 3: raise Warning(f"Dimension mismatch in provided previous features and new features:" f"{len(prev_edge_features[0])} != 3") - return (col_features, edge_features, row_features, {"col": col_feature_map, "edge": edge_feature_map, "row": row_feature_map}) @@ -9908,6 +9959,8 @@ def readStatistics(filename): Statistics """ + cdef int i + result = {} file = open(filename) data = file.readlines() @@ -10009,8 +10062,9 @@ def readStatistics(filename): treated_result = dict((treated_keys[key], value) for (key, value) in result.items()) stats = Statistics(**treated_result) + return stats - + # debugging memory management def is_memory_freed(): return BMSgetMemoryUsed() == 0 diff --git a/tests/test_event.py b/tests/test_event.py index 1cd4c59d..b4b292ff 100644 --- a/tests/test_event.py +++ b/tests/test_event.py @@ -60,7 +60,6 @@ def eventexec(self, event): else: assert event.getType() == self.event_type - def test_event(): all_events = [SCIP_EVENTTYPE.DISABLED,SCIP_EVENTTYPE.VARADDED,SCIP_EVENTTYPE.VARDELETED,SCIP_EVENTTYPE.VARFIXED,SCIP_EVENTTYPE.VARUNLOCKED,SCIP_EVENTTYPE.OBJCHANGED,SCIP_EVENTTYPE.GLBCHANGED,SCIP_EVENTTYPE.GUBCHANGED,SCIP_EVENTTYPE.LBTIGHTENED,SCIP_EVENTTYPE.LBRELAXED,SCIP_EVENTTYPE.UBTIGHTENED,SCIP_EVENTTYPE.UBRELAXED,SCIP_EVENTTYPE.GHOLEADDED,SCIP_EVENTTYPE.GHOLEREMOVED,SCIP_EVENTTYPE.LHOLEADDED,SCIP_EVENTTYPE.LHOLEREMOVED,SCIP_EVENTTYPE.IMPLADDED,SCIP_EVENTTYPE.PRESOLVEROUND,SCIP_EVENTTYPE.NODEFOCUSED,SCIP_EVENTTYPE.NODEFEASIBLE,SCIP_EVENTTYPE.NODEINFEASIBLE,SCIP_EVENTTYPE.NODEBRANCHED,SCIP_EVENTTYPE.NODEDELETE,SCIP_EVENTTYPE.FIRSTLPSOLVED,SCIP_EVENTTYPE.LPSOLVED,SCIP_EVENTTYPE.POORSOLFOUND,SCIP_EVENTTYPE.BESTSOLFOUND,SCIP_EVENTTYPE.ROWADDEDSEPA,SCIP_EVENTTYPE.ROWDELETEDSEPA,SCIP_EVENTTYPE.ROWADDEDLP,SCIP_EVENTTYPE.ROWDELETEDLP,SCIP_EVENTTYPE.ROWCOEFCHANGED,SCIP_EVENTTYPE.ROWCONSTCHANGED,SCIP_EVENTTYPE.ROWSIDECHANGED,SCIP_EVENTTYPE.SYNC,SCIP_EVENTTYPE.GBDCHANGED,SCIP_EVENTTYPE.LBCHANGED,SCIP_EVENTTYPE.UBCHANGED,SCIP_EVENTTYPE.BOUNDTIGHTENED,SCIP_EVENTTYPE.BOUNDRELAXED,SCIP_EVENTTYPE.BOUNDCHANGED,SCIP_EVENTTYPE.LHOLECHANGED,SCIP_EVENTTYPE.HOLECHANGED,SCIP_EVENTTYPE.DOMCHANGED,SCIP_EVENTTYPE.VARCHANGED,SCIP_EVENTTYPE.VAREVENT,SCIP_EVENTTYPE.NODESOLVED,SCIP_EVENTTYPE.NODEEVENT,SCIP_EVENTTYPE.LPEVENT,SCIP_EVENTTYPE.SOLFOUND,SCIP_EVENTTYPE.SOLEVENT,SCIP_EVENTTYPE.ROWCHANGED,SCIP_EVENTTYPE.ROWEVENT] @@ -82,8 +81,6 @@ def test_event(): s.addCons(quicksum(x[i] for i in range(100) if i%j==0) >= random.randint(10,100)) s.optimize() - - def test_event_handler_callback(): m = Model() @@ -100,4 +97,4 @@ def callback(model, event): m.optimize() - assert number_of_calls == 2 \ No newline at end of file + assert number_of_calls == 2