diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b624e14e..890f87101 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ ## Unreleased ### Added - Added getLinearConsIndicator -- Added SCIP_LPPARAM, setIntParam, setRealParam, getIntParam, getRealParam for lpi +- Added SCIP_LPPARAM, setIntParam, setRealParam, getIntParam, getRealParam, isOptimal, getObjVal, getRedcost for lpi - Added isFeasPositive ### Fixed ### Changed diff --git a/src/pyscipopt/lp.pxi b/src/pyscipopt/lp.pxi index 85339475b..80dbdf2a5 100644 --- a/src/pyscipopt/lp.pxi +++ b/src/pyscipopt/lp.pxi @@ -361,8 +361,31 @@ cdef class LP: return objval + def isOptimal(self): + """ + returns true iff LP was solved to optimality. + + Returns + ------- + bool + + """ + return SCIPlpiIsOptimal(self.lpi) + + def getObjVal(self): + """Returns the objective value of the last LP solve.""" + assert self.isOptimal(), "LP is not optimal" + + cdef SCIP_Real objval + + PY_SCIP_CALL(SCIPlpiGetSol(self.lpi, &objval, NULL, NULL, NULL, NULL)) + + return objval + def getPrimal(self): """Returns the primal solution of the last LP solve.""" + assert self.isOptimal(), "LP is not optimal" + cdef int ncols = self.ncols() cdef SCIP_Real* c_primalsol = malloc(ncols * sizeof(SCIP_Real)) cdef int i @@ -381,6 +404,8 @@ cdef class LP: def getDual(self): """Returns the dual solution of the last LP solve.""" + assert self.isOptimal(), "LP is not optimal" + cdef int nrows = self.nrows() cdef SCIP_Real* c_dualsol = malloc(nrows * sizeof(SCIP_Real)) cdef int i @@ -445,17 +470,37 @@ cdef class LP: return niters + def getActivity(self): + """Returns the row activity vector of the last LP solve.""" + assert self.isOptimal(), "LP is not optimal" + + cdef int nrows = self.nrows() + cdef SCIP_Real* c_activity = malloc(nrows * sizeof(SCIP_Real)) + cdef int i + + PY_SCIP_CALL(SCIPlpiGetSol(self.lpi, NULL, NULL, NULL, c_activity, NULL)) + + activity = [0.0] * nrows + for i in range(nrows): + activity[i] = c_activity[i] + + free(c_activity) + + return activity + def getRedcost(self): """Returns the reduced cost vector of the last LP solve.""" + assert self.isOptimal(), "LP is not optimal" + 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 = [] + redcost = [0.0] * ncols for i in range(ncols): - redcost[i].append(c_redcost[i]) + redcost[i] = c_redcost[i] free(c_redcost) diff --git a/src/pyscipopt/scip.pxd b/src/pyscipopt/scip.pxd index afd372728..f53421164 100644 --- a/src/pyscipopt/scip.pxd +++ b/src/pyscipopt/scip.pxd @@ -1393,6 +1393,7 @@ cdef extern from "scip/scip.h": SCIP_RETCODE SCIPlpiGetNCols(SCIP_LPI* lpi, int* ncols) SCIP_RETCODE SCIPlpiSolveDual(SCIP_LPI* lpi) SCIP_RETCODE SCIPlpiSolvePrimal(SCIP_LPI* lpi) + SCIP_RETCODE SCIPlpiIsOptimal(SCIP_LPI* lpi) SCIP_RETCODE SCIPlpiGetObjval(SCIP_LPI* lpi, SCIP_Real* objval) SCIP_RETCODE SCIPlpiGetSol(SCIP_LPI* lpi, SCIP_Real* objval, SCIP_Real* primsol, SCIP_Real* dualsol, SCIP_Real* activity, SCIP_Real* redcost) SCIP_RETCODE SCIPlpiGetIterations(SCIP_LPI* lpi, int* iterations) diff --git a/tests/test_lp.py b/tests/test_lp.py index 1f383c18b..7cc90585d 100644 --- a/tests/test_lp.py +++ b/tests/test_lp.py @@ -73,4 +73,19 @@ def test_lp(): solval = myLP.solve() + assert(myLP.isOptimal()) + assert(myLP.getPrimal() is not None) + assert(myLP.getDual() is not None) + + redcost = myLP.getRedcost() + assert(redcost is not None) + for i in redcost: + assert(i is not None) + + activity = myLP.getActivity() + assert(activity is not None) + for i in activity: + assert(i is not None) + + assert round(myLP.getObjVal() == solval) assert round(5.0 == solval)