Beispiel #1
0
/**
 * APPheuristics callback
 *
 * Called by DIP, we interface with Python
 */
int DippyDecompApp::APPheuristics(const double* xhat, const double* origCost, vector<DecompSolution*>& xhatIPFeas)
{
   if (!m_pyHeuristics) {
      return 0;
   }

   PyObject* pSolution = pyTupleList_FromDoubleArray(xhat, m_colList);
   PyObject* pObjective = pyTupleList_FromDoubleArray(origCost, m_colList);
   PyObject* pSolList = PyObject_CallMethod(m_pProb, "solveHeuristics", "OO", pSolution, pObjective);

   if (pSolList == NULL) {
      throw UtilException("Error calling method prob.solveHeuristics()", "APPheuristics", "DippyDecompApp");
   }

   // This should never happen, pyHeuristics should be set to false in dippy.py
   if (pSolList == Py_None)
      // method exists, but is not implemented, return 0
   {
      return 0;
   }

   // APPheuristics returns dictionary of (variable, value) pairs
   const int len = PyObject_Length(pSolList);

   // loop through each solution
   for (int i = 0; i < len; i++) {
      pSolution = PyList_GetItem(pSolList, i);
      int*     varInds = NULL;
      double* varVals = NULL;
      int numPairs = pyColDict_AsPackedArrays(pSolution, m_colIndices, &varInds, &varVals);
      assert(numPairs == PyObject_Length(pSolution));
      double* sol = new double[m_numCols];
      UtilFillN(sol, m_numCols, 0.0);

      for (int j = 0; j < numPairs; j++) {
         sol[varInds[j]] = varVals[j];
      }

      xhatIPFeas.push_back(new DecompSolution(m_numCols, sol, origCost));
      delete [] sol;
      delete [] varInds;
      delete [] varVals;
   }

   return len;
}
Beispiel #2
0
/**
 * APPisUserFeasible callback
 *
 * Called by DIP, we interface with Python
 */
bool DippyDecompApp::APPisUserFeasible(const double* x, const int n_cols, const double tolZero)
{
   assert(n_cols == m_modelCore.getModel()->getColNames().size());
   PyObject* pSolutionList = pyTupleList_FromDoubleArray(x, m_colList);
   PyObject* pTolZero = PyFloat_FromDouble(tolZero);

   if (!m_pyIsUserFeasible) {
      return true;
   }

   PyObject* pResult = PyObject_CallMethod(m_pProb, "isUserFeasible", "Od", pSolutionList, pTolZero);

   if (pResult == NULL) {
      throw UtilException("Error calling method prob.isUserFeasible()", "APPisUserFeasible", "DippyDecompApp");
   }

   // This should not happen as having isUserFeasible present but not returning a boolean is not good
   if (pResult == Py_None) {
      // method exists, but not implemented, return true
      return true;
   }

   return (bool)PyObject_IsTrue(pResult);
}
Beispiel #3
0
/**
 * solveRelaxed callback
 *
 * This is called by DIP. This function interfaces with Python to
 * call the user defined function if it's present
 *
 * We're expected to populate varList (basically a vector) and
 * return the status of the subproblem solver.
 */
DecompSolverStatus DippyDecompApp::solveRelaxed(const int whichBlock,
						const double* redCostX, 
						const double convexDual, 
						DecompVarList& varList)
{
   if (!m_pySolveRelaxed) {
      return DecompSolStatNoSolution;
   }

   PyObject* pRelaxKey = PyList_GetItem(m_relaxedKeys, whichBlock);
   PyObject* pRedCostList = pyTupleList_FromDoubleArray(redCostX, m_colList);
   PyObject* pConvexDual = PyFloat_FromDouble(convexDual);
   // call solveRelaxed on DipProblem

   PyObject* pStatandVarList = PyObject_CallMethod(m_pProb, "solveRelaxed", "OOd", 
					             pRelaxKey,
					             pRedCostList,
					             pConvexDual);

   Py_DECREF(pRedCostList);
   Py_DECREF(pConvexDual);

   if ( (pStatandVarList == NULL) || (pStatandVarList == Py_None) ){
      throw UtilException("Error calling method prob.solveRelaxed()", "solveRelaxed", "DippyDecompApp");
   }

   // [status, varList] = relaxed_solver(...)
   PyObject * pStatus = PyTuple_GetItem(pStatandVarList, 0);

   int cStatus = PyInt_AsLong(pStatus);

   DecompSolverStatus status = (DecompSolverStatus)cStatus;

   PyObject * pVarList = PyTuple_GetItem(pStatandVarList, 1);

   int nVars = PyObject_Length(pVarList);

   // In the new design, we need to allow the possibility that the user will solve
   // the problem exactly, but not find any solutions with reduced costs zero
   // The below is is commented out and left in the source for posterity
   // tkr 11/11/15
   //if (nVars == 0) {
   //   throw UtilException("Empty variable list", "solveRelaxed", "DippyDecompApp");
   //}

   // solveRelaxed returns 3-tuples (cost, reduced cost, dictionary of (variable, value) pairs)
   // We can use these to construct a C++ DecompVar objects
   double cost, rc;
   PyObject *pDict, *pKeys, *pCol;
   string name;
   double value;

   for (int j = 0; j < nVars; j++) {
      PyObject* pTuple = PySequence_GetItem(pVarList, j);
      cost   = PyFloat_AsDouble(PyTuple_GetItem(pTuple, 0));
      rc     = PyFloat_AsDouble(PyTuple_GetItem(pTuple, 1));

      pDict = PyTuple_GetItem(pTuple, 2);
      pKeys = PyDict_Keys(pDict);
      vector<int>    varInds;
      vector<double> varVals;

      for (int n = 0; n < PyObject_Length(pDict); n++) {
         pCol  = PyList_GetItem(pKeys, n);
         value = PyFloat_AsDouble(PyDict_GetItem(pDict, pCol));
         varInds.push_back(m_colIndices[pCol]);
         varVals.push_back(value);
      }

      Py_DECREF(pKeys);
      Py_DECREF(pTuple);

      DecompVar* var =  new DecompVar(varInds, varVals, rc, cost);
      var->setBlockId(whichBlock);
      varList.push_back(var);
   }

   Py_DECREF(pStatandVarList);

   return status;
}
Beispiel #4
0
/**
 * Perform branching
 *
 * This function should populate the (down|up)Branch(LB|UB) vectors with (indices, bound-value) pairs
 * which define the branch.
 */
bool DippyAlgoMixin::chooseBranchSet(DecompAlgo* algo,
				     std::vector< std::pair<int, double> >& downBranchLB,
                                     std::vector< std::pair<int, double> >& downBranchUB,
                                     std::vector< std::pair<int, double> >& upBranchLB,
                                     std::vector< std::pair<int, double> >& upBranchUB)
{
   bool ret_val;

   if (!m_utilParam->GetSetting("pyBranchMethod", true)) {
      return algo->DecompAlgo::chooseBranchSet(downBranchLB, downBranchUB, 
					       upBranchLB, upBranchUB);
   }

   DippyDecompApp* app = (DippyDecompApp*)algo->getDecompApp();
   // copy the current solution into a Python list
   const double* xhat = algo->getXhat();
   PyObject* pSolutionList = pyTupleList_FromDoubleArray(xhat, app->m_colList);
   // try to call chooseBranchSet on the DipProblem python object
   PyObject* pResult = PyObject_CallMethod(m_pProb, "chooseBranchSet", "O", 
					   pSolutionList);

   if (pResult == NULL) {
      //something's gone wrong with the function call, a Python exception has 
      //been set 
      throw UtilException("Error calling method prob.chooseBranchSet()", 
			  "chooseBranchSet", "DippyDecompAlgo");
   }

   // need more error checking/assertion setting here
   if (pResult == Py_None) {
      // if chooseBranchSet returns None, do default branching for this algo
      ret_val = algo->DecompAlgo::chooseBranchSet(downBranchLB, downBranchUB, upBranchLB, upBranchUB);
      
      // Original comment: No branching set was returned. This shouldn't happen
      // tkr 11/11/15: Actually, it can happen if the solution is integral, but not feasible.
      // This happens sometimes when column generation is halted because of tailoff and
      // the solution to the relaxation is feasible. I'm leaving the commented code here for
      // posterity
      //assert(ret_val == true);

      //if (!ret_val){
      // throw UtilException("No branch set found in prob.chooseBranchSet()", 
      //		     "chooseBranchSet", "DippyDecompAlgo");
      //}

      if (downBranchUB.size() > 0) {
         PyObject* downBranchVar, * upBranchVar;
         pDownLB = PyDict_New(); // Down branch LBs is an empty dictionary
         pDownUB = PyDict_New();
         downBranchVar = PyList_GetItem(app->m_colList, downBranchUB[0].first);
         PyDict_SetItem(pDownUB, downBranchVar, 
			PyInt_FromLong(static_cast<int>(round(downBranchUB[0].second))));
         pUpLB = PyDict_New();
         upBranchVar = PyList_GetItem(app->m_colList, upBranchLB[0].first);
         PyDict_SetItem(pUpLB, upBranchVar, 
			PyInt_FromLong(static_cast<int>(round(upBranchLB[0].second))));
         pUpUB = PyDict_New(); // Up branch UBs is an empty dictionary
         assert(downBranchVar == upBranchVar);
      }else{
	 //No branching set was returned. Zero out pointers to old branching 
	 //sets
	 assert(ret_val == false);
	 pDownLB = NULL;
	 pDownUB = NULL;
	 pUpLB = NULL;
	 pUpUB = NULL;
      }
      return ret_val;
   } else {
      // or else, the function should have returned 4 lists of (name, value) tuples
      pDownLB = PyTuple_GetItem(pResult, 0);
      pDownUB = PyTuple_GetItem(pResult, 1);
      pUpLB = PyTuple_GetItem(pResult, 2);
      pUpUB = PyTuple_GetItem(pResult, 3);
      // copy the python dictionaries into the result vectors
      pyColDict_AsPairedVector(pDownLB, downBranchLB, app->m_colIndices);
      pyColDict_AsPairedVector(pDownUB, downBranchUB, app->m_colIndices);
      pyColDict_AsPairedVector(pUpLB, upBranchLB, app->m_colIndices);
      pyColDict_AsPairedVector(pUpUB, upBranchUB, app->m_colIndices);
      return true;
   }
}