diff --git a/CHANGELOG.md b/CHANGELOG.md index 0dadd7965..34c6ab543 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Added +- Added support for SOS1-constraints - Added getLinearConsIndicator - Added SCIP_LPPARAM, setIntParam, setRealParam, getIntParam, getRealParam, isOptimal, getObjVal, getRedcost for lpi - Added isFeasPositive diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index f53421164..5a04ded04 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1550,6 +1550,19 @@ cdef extern from "scip/cons_sos1.h": SCIP_CONS* cons, SCIP_VAR* var) + int SCIPgetNVarsSOS1(SCIP* scip, SCIP_CONS* cons) + + SCIP_VAR** SCIPgetVarsSOS1(SCIP* scip, SCIP_CONS* cons) + + SCIP_Real* SCIPgetWeightsSOS1(SCIP* scip, SCIP_CONS* cons) + + int SCIPgetNSOS1Vars(SCIP_CONSHDLR* conshdlr) + + SCIP_RETCODE SCIPmakeSOS1Feasible(SCIP* scip, + SCIP_CONSHDLR* conshdlr, + SCIP_SOL* solution, + SCIP_Bool* changed, + SCIP_Bool* success) cdef extern from "scip/cons_sos2.h": SCIP_RETCODE SCIPcreateConsSOS2(SCIP* scip, diff --git a/src/pyscipopt/scip.pxi b/src/pyscipopt/scip.pxi index 3b847ef60..5ea6f0cb0 100644 --- a/src/pyscipopt/scip.pxi +++ b/src/pyscipopt/scip.pxi @@ -1671,6 +1671,43 @@ cdef class Variable(Expr): else: mayround = SCIPvarMayRoundUp(self.scip_var) return mayround + + def varIsSOS1(self, Conshdlr conshdlr, Variable var): + """ + Returns whether variable is part of the SOS1 conflict graph + + Parameters + ---------- + conshdlr : Conshdlr + SOS1 constraint handler + var : Variable + variable to check + + Returns + ------- + bool + True if variable is part of the SOS1 conflict graph, False otherwise + + """ + return SCIPvarIsSOS1(conshdlr.scip_conshdlr, var.scip_var) + + def varGetNodeSOS1(self, Conshdlr conshdlr, Variable var): + """ + Returns SOS1 index of variable or -1 if variable is not part of the SOS1 conflict graph + + Parameters + ---------- + conshdlr : Conshdlr + SOS1 constraint handler + var : Variable + variable + + Returns + ------- + int + + """ + return SCIPvarGetNodeSOS1(conshdlr.scip_conshdlr, var.scip_var) class MatrixVariable(MatrixExpr): @@ -5853,6 +5890,156 @@ cdef class Model: return Constraint.create(scip_cons) + def addVarSOS1(self, Constraint cons, Variable var, weight): + """ + Add variable to SOS1 constraint. + + Parameters + ---------- + cons : Constraint + SOS1 constraint + var : Variable + new variable + weight : weight + weight of new variable + + """ + PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, cons.scip_cons, var.scip_var, weight)) + + def appendVarSOS1(self, Constraint cons, Variable var): + """ + Append variable to SOS1 constraint. + + Parameters + ---------- + cons : Constraint + SOS1 constraint + var : Variable + variable to append + + """ + PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, cons.scip_cons, var.scip_var)) + + def getNVarsSOS1(self, Constraint cons): + """ + Get number of variables in SOS1 constraint. + + Parameters + ---------- + cons : Constraint + SOS1 constraint + + Returns + ------- + int + number of variables in SOS1 constraint + + """ + return SCIPgetNVarsSOS1(self._scip, cons.scip_cons) + + def getVarsSOS1(self, Constraint cons): + """ + Get variables in SOS1 constraint. + + Parameters + ---------- + cons : Constraint + SOS1 constraint + + Returns + ------- + list of Variable + list of variables in SOS1 constraint + + """ + cdef SCIP_VAR** _vars + cdef int nvars + cdef int i + cdef Variable var + cdef list vars = [] + cdef size_t ptr + nvars = SCIPgetNVarsSOS1(self._scip, cons.scip_cons) + _vars = SCIPgetVarsSOS1(self._scip, cons.scip_cons) + for i in range(nvars): + ptr = (_vars[i]) + # check whether the corresponding variable exists already + if ptr in self._modelvars: + vars.append(self._modelvars[ptr]) + else: + # create a new variable + var = Variable.create(_vars[i]) + assert var.ptr() == ptr + self._modelvars[ptr] = var + vars.append(var) + return vars + + def getWeightsSOS1(self, Constraint cons): + """ + Get array of weights in SOS1 constraint (or NULL if not existent). + + Parameters + ---------- + cons : Constraint + SOS1 constraint + + Returns + ------- + list of float + list of weights in SOS1 constraint + + """ + cdef SCIP_VAR** vars + cdef SCIP_Real* weights + cdef int i + + constype = bytes(SCIPconshdlrGetName(SCIPconsGetHdlr(cons.scip_cons))).decode('UTF-8') + if not constype == 'SOS1': + raise Warning("Weights not available for constraints of type ", constype) + + vals = SCIPgetValsSOS1(self._scip, cons.scip_cons) + vars = SCIPgetVarsSOS1(self._scip, cons.scip_cons) + + valsdict = {} + for i in range(SCIPgetNVarsSOS1(self._scip, cons.scip_cons)): + valsdict[bytes(SCIPvarGetName(vars[i])).decode('utf-8')] = vals[i] + + return valsdict + + def getNSOS1Vars(self, Conshdlr conshdlr): + """ + Gets number of problem variables that are part of the SOS1 conflict graph + + Parameters + ---------- + conshdlr : Conshdlr + constraint handler + + Returns + ------- + int + number of SOS1 variables in constraint handler + + """ + return SCIPgetNSOS1Vars(conshdlr.scip_conshdlr) + + def makeSOS1Feasible(self, Conshdlr conshdlr, Solution sol): + """ + Makes the SOS1 conflict graph feasible + + Parameters + ---------- + conshdlr : Conshdlr + constraint handler + sol : Solution + solution to be made feasible + + """ + cdef SCIP_Bool changed + cdef SCIP_Bool success + PY_SCIP_CALL(SCIPmakeSOS1Feasible(self._scip, conshdlr.scip_conshdlr, sol.scip_sol, &changed, &success)) + if not success: + raise Warning("SOS1 conflict graph could not be made feasible") + def addConsSOS2(self, vars, weights=None, name="SOS2cons", initial=True, separate=True, enforce=True, check=True, propagate=True, local=False, dynamic=False, @@ -6317,36 +6504,6 @@ cdef class Model: PY_SCIP_CALL(SCIPaddCons(self._scip, cons.scip_cons)) Py_INCREF(cons) - def addVarSOS1(self, Constraint cons, Variable var, weight): - """ - Add variable to SOS1 constraint. - - Parameters - ---------- - cons : Constraint - SOS1 constraint - var : Variable - new variable - weight : weight - weight of new variable - - """ - PY_SCIP_CALL(SCIPaddVarSOS1(self._scip, cons.scip_cons, var.scip_var, weight)) - - def appendVarSOS1(self, Constraint cons, Variable var): - """ - Append variable to SOS1 constraint. - - Parameters - ---------- - cons : Constraint - SOS1 constraint - var : Variable - variable to append - - """ - PY_SCIP_CALL(SCIPappendVarSOS1(self._scip, cons.scip_cons, var.scip_var)) - def addVarSOS2(self, Constraint cons, Variable var, weight): """ Add variable to SOS2 constraint.