void CglClique::generateCuts(const OsiSolverInterface& si, OsiCuts & cs, const CglTreeInfo info) { int i; bool has_petol_set = petol != -1.0; if (! has_petol_set) si.getDblParam(OsiPrimalTolerance, petol); int numberOriginalRows = si.getNumRows(); if (info.inTree&&justOriginalRows_) numberOriginalRows = info.formulation_rows; int numberRowCutsBefore = cs.sizeRowCuts(); // First select which rows/columns we are interested in. if (!setPacking_) { selectFractionalBinaries(si); if (!sp_orig_row_ind) { selectRowCliques(si,numberOriginalRows); } } else { selectFractionals(si); delete[] sp_orig_row_ind; sp_numrows = numberOriginalRows; //sp_numcols = si.getNumCols(); sp_orig_row_ind = new int[sp_numrows]; for (i = 0; i < sp_numrows; ++i) sp_orig_row_ind[i] = i; } // Just original rows if (justOriginalRows_&&info.inTree) sp_numrows = CoinMin(info.formulation_rows,sp_numrows); createSetPackingSubMatrix(si); fgraph.edgenum = createNodeNode(); createFractionalGraph(); cl_indices = new int[sp_numcols]; cl_del_indices = new int[sp_numcols]; if (do_row_clique) find_rcl(cs); if (do_star_clique) find_scl(cs); if (!info.inTree&&((info.options&4)==4||((info.options&8)&&!info.pass))) { int numberRowCutsAfter = cs.sizeRowCuts(); for (int i=numberRowCutsBefore;i<numberRowCutsAfter;i++) cs.rowCutPtr(i)->setGloballyValid(); } delete[] cl_indices; cl_indices = 0; delete[] cl_del_indices; cl_del_indices = 0; deleteFractionalGraph(); delete[] node_node; node_node = 0; deleteSetPackingSubMatrix(); if (! has_petol_set) petol = -1; }
// Add cuts void CglStored::addCut(const OsiCuts & cs) { int numberRowCuts = cs.sizeRowCuts(); for (int i=0;i<numberRowCuts;i++) { cuts_.insert(*cs.rowCutPtr(i)); } }
//------------------------------------------------------------------- void OsiCuts::gutsOfCopy(const OsiCuts& source) { assert( sizeRowCuts()==0 ); assert( sizeColCuts()==0 ); assert( sizeCuts()==0 ); int i; int ne = source.sizeRowCuts(); for (i=0; i<ne; i++) insert( source.rowCut(i) ); ne = source.sizeColCuts(); for (i=0; i<ne; i++) insert( source.colCut(i) ); }
// LANNEZ SEBASTIEN added Thu May 25 01:22:51 EDT 2006 void OsiCuts::insert(const OsiCuts & cs) { for (OsiCuts::const_iterator it = cs.begin (); it != cs.end (); it++) { const OsiRowCut * rCut = dynamic_cast <const OsiRowCut * >(*it); const OsiColCut * cCut = dynamic_cast <const OsiColCut * >(*it); assert (rCut || cCut); if (rCut) insert (*rCut); else insert (*cCut); } }
int CPXPUBLIC CPX_CutCallback(CPXCENVptr xenv, void *cbdata, int wherefrom, void *cbhandle, int *useraction_p) { // cout << "Entering CPX Callback\n" << flush; CPXLPptr nodelp; CPXgetcallbacknodelp(xenv, cbdata, wherefrom, &nodelp); CoinCallbacks* ccc = (CoinCallbacks*)cbhandle; int length = CPXgetnumcols(xenv,nodelp) - 1; //hey, don't ask me! some VERY WIERD PHENOMENON... crap double objVal; double* solution = new double[length]; CPXgetcallbacknodeobjval(xenv, cbdata, wherefrom, &objVal); CPXgetcallbacknodex(xenv, cbdata, wherefrom, solution, 0, length-1); OsiCuts* cuts = new OsiCuts(); CoinCallbacks::CutReturn ret = ccc->cutCallback(objVal, solution, cuts); if(ret == CoinCallbacks::CR_AddCuts) { for(int i = cuts->sizeRowCuts(); i-->0;) { const OsiRowCut& c = cuts->rowCut(i); const CoinPackedVector& vec = c.row(); if(c.globallyValid()) /* Old Cplex-Versions did NOT have the last parameter (now set to "false"). * If you compile agains an older CPLEX version, simple *REMOVE* * ", false" * from the calls to CPXcutscallbackadd */ CPXcutcallbackadd(xenv, cbdata, wherefrom, vec.getNumElements(), c.rhs(), c.sense(), vec.getIndices(), vec.getElements(), false); //default to non-purgable cuts else CPXcutcallbackaddlocal(xenv, cbdata, wherefrom, vec.getNumElements(), c.rhs(), c.sense(), vec.getIndices(), vec.getElements()); cuts->eraseRowCut(i); } if(cuts->sizeColCuts() > 0) { cerr << "ColCuts currently not supported...\n"; OGDF_THROW_PARAM(LibraryNotSupportedException, lnscFunctionNotImplemented); } } *useraction_p = ( ret == CoinCallbacks::CR_Error) ? CPX_CALLBACK_FAIL : ( ret == CoinCallbacks::CR_AddCuts ) ? CPX_CALLBACK_SET : CPX_CALLBACK_DEFAULT; delete cuts; delete[] solution; // cout << "Leaving CPX Callback\n" << flush; return 0; // success }
//------------------------------------------------------------------- // Generate Stored cuts //------------------------------------------------------------------- void CglStoredUser::generateCuts(const OsiSolverInterface & si, OsiCuts & cs, const CglTreeInfo info) const { // Get basic problem information const double * solution = si.getColSolution(); if (info.inTree&&info.pass>numberPasses_) { // only continue if integer feasible int numberColumns=si.getNumCols(); int i; const double * colUpper = si.getColUpper(); const double * colLower = si.getColLower(); int numberAway=0; for (i=0;i<numberColumns;i++) { double value = solution[i]; // In case slightly away from bounds value = CoinMax(colLower[i],value); value = CoinMin(colUpper[i],value); if (si.isInteger(i)&&fabs(value-fabs(value+0.5))>1.0e-5) numberAway++; } if (numberAway) return; // let code branch } int numberRowCuts = cuts_.sizeRowCuts(); for (int i=0;i<numberRowCuts;i++) { const OsiRowCut * rowCutPointer = cuts_.rowCutPtr(i); double violation = rowCutPointer->violated(solution); if (violation>=requiredViolation_) cs.insert(*rowCutPointer); } }
/* Append cuts to the cuts_ array in a nodeInfo. The initial reference count is set to numberToBranchOn, which will normally be the number of arms defined for the CbcBranchingObject attached to the CbcNode that owns this CbcNodeInfo. */ void CbcNodeInfo::addCuts (OsiCuts & cuts, int numberToBranchOn, /*int * whichGenerator,*/int numberPointingToThis) { int numberCuts = cuts.sizeRowCuts(); if (numberCuts) { int i; if (!numberCuts_) { cuts_ = new CbcCountRowCut * [numberCuts]; } else { CbcCountRowCut ** temp = new CbcCountRowCut * [numberCuts+numberCuts_]; memcpy(temp, cuts_, numberCuts_*sizeof(CbcCountRowCut *)); delete [] cuts_; cuts_ = temp; } for (i = 0; i < numberCuts; i++) { CbcCountRowCut * thisCut = new CbcCountRowCut(*cuts.rowCutPtr(i), this, numberCuts_, -1, numberPointingToThis); thisCut->increment(numberToBranchOn); cuts_[numberCuts_++] = thisCut; #ifdef CBC_DEBUG #if CBC_DEBUG>1 int n = thisCut->row().getNumElements(); printf("Cut %d has %d entries, rhs %g %g =>", i, n, thisCut->lb(), thisCut->ub()); int j; const int * index = thisCut->row().getIndices(); const double * element = thisCut->row().getElements(); for (j = 0; j < n; j++) { printf(" (%d,%g)", index[j], element[j]); assert(fabs(element[j]) > 1.00e-12); } printf("\n"); #else int n = thisCut->row().getNumElements(); int j; const double * element = thisCut->row().getElements(); for (j = 0; j < n; j++) { assert(fabs(element[j]) > 1.00e-12); } #endif #endif } } }
bool isWiped (OsiCuts &cs) { if (cs.sizeColCuts () == 0) //(cs.sizeColCuts () != 1)) return false; CoinPackedVector lbs = cs.colCutPtr (cs.sizeColCuts () - 1) -> lbs (), ubs = cs.colCutPtr (cs.sizeColCuts () - 1) -> ubs (); return ((lbs.getNumElements () == 1) && (ubs.getNumElements () == 1) && (*(lbs.getIndices ()) == 0) && (*(lbs.getElements ()) == 1.) && (*(ubs.getIndices ()) == 0) && (*(ubs.getElements ()) == -1.)); }
int Cuts::insertAll(OsiCuts & cs, CoinRelFltEq& eq) { int r_val = 0; for (unsigned int i = 0 ; i < cuts_.size() ; i++) { if (cuts_[i] != NULL) { cs.insertIfNotDuplicate(*cuts_[i], eq); delete cuts_[i]; cuts_[i] = NULL; r_val++; } } return r_val; }
void WipeMakeInfeas (OsiCuts &cs) { //for (int i=cs.sizeRowCuts(); i--;) cs. eraseRowCut (i); //for (int i=cs.sizeColCuts(); i--;) cs. eraseColCut (i); OsiColCut *infeascut = new OsiColCut; if (infeascut) { int i=0; double upper = -1., lower = +1.; infeascut -> setLbs (1, &i, &lower); infeascut -> setUbs (1, &i, &upper); cs.insert (infeascut); delete infeascut; } }
void CglClique::recordClique(const int len, int* indices, OsiCuts& cs) { /* transform relative indices into user indices and order them */ for (int j = len - 1; j >= 0; j--) indices[j] = sp_orig_col_ind[indices[j]]; std::sort(indices, indices + len); OsiRowCut rowcut; double* coef = new double[len]; std::fill(coef, coef + len, 1.0); rowcut.setRow(len, indices, coef); rowcut.setUb(1.0); CoinAbsFltEq equal(1.0e-12); cs.insertIfNotDuplicate(rowcut,equal); delete[] coef; }
void CglLandP::scanExtraCuts(OsiCuts& cs, const double * colsol) const { int numAdded = 0; for (int i = extraCuts_.sizeRowCuts() - 1; i > -1 ; i--) { double violation = extraCuts_.rowCut(i).violated(colsol); if (violation > 0.) { cs.insert(extraCuts_.rowCut(i)); numAdded++; // std::cout<<"A cut computed in a previous iteration is violated by "<<violation<<"."<<std::endl; //extraCuts_.eraseRowCut(i); } } // std::cout<<"Added "<<numAdded<<" previously generated cuts."<<std::endl; }
void OaDecompositionBase::OaDebug::printEndOfProcedureDebugMessage(const OsiCuts &cs, bool foundSolution, double solValue, double milpBound, bool isInteger, bool feasible, std::ostream & os) const{ std::cout<<"------------------------------------------------------------------" <<std::endl; std::cout<<"OA procedure finished"<<std::endl; std::cout<<"Generated "<<cs.sizeRowCuts()<<std::endl; if (foundSolution) std::cout <<"Found NLP-integer feasible solution of value : "<<solValue<<std::endl; std::cout<<"Current MILP lower bound is : "<<milpBound<<std::endl; std::cout<<"-------------------------------------------------------------------"<<std::endl; std::cout<<"Stopped because : isInteger "<<isInteger<<", feasible "<<feasible<<std::endl<<std::endl; }
void LinearCutsGenerator::generateCuts(const OsiSolverInterface &solver, OsiCuts &cs, const CglTreeInfo info) const { //const OsiTMINLPInterface * tmp = dynamic_cast<const OsiTMINLPInterface *>(&solver); OsiTMINLPInterface * nlp = dynamic_cast<OsiTMINLPInterface *>(solver.clone());//const_cast<OsiTMINLPInterface *>(tmp); assert(nlp); OuterApprox oa; //si.writeMps("toto"); int numberRows = nlp->getNumRows(); for(int i = 0 ; i < 5 ; i++){ nlp->resolve(); OsiClpSolverInterface si; oa(*nlp, &si, solver.getColSolution(), true); si.resolve(); OsiCuts cuts; for(std::list<Coin::SmartPtr<CuttingMethod> >::const_iterator i = methods_.begin() ; i != methods_.end() ; i++){ (*i)->cgl->generateCuts(si, cuts, info); } std::vector<OsiRowCut *> mycuts(cuts.sizeRowCuts()); for(int i = 0 ; i < cuts.sizeRowCuts() ; i++){ mycuts[i] = cuts.rowCutPtr(i); cs.insert(*mycuts[i]); } nlp->applyRowCuts(mycuts.size(), const_cast<const OsiRowCut **> (&mycuts[0])); } // Take off slack cuts std::vector<int> kept; int numberRowsNow = nlp->getNumRows(); int * del = new int [numberRowsNow-numberRows]; nlp->resolve(); const double * activity = nlp->getRowActivity(); const double * lb = nlp->getRowLower(); const double * ub = nlp->getRowUpper(); CoinRelFltEq eq(1e-06); //int nDelete=0; for (int i=numberRowsNow -1;i>=numberRows;i--) { if ( !(eq(activity[i], lb[i]) || eq(activity[i], ub[i])) ) cs.eraseRowCut(i - numberRows); } delete [] del; delete nlp; }
bool BlisConGenerator::generateCons(OsiCuts & coinCuts , bool fullScan) { bool status = false; if (strategy_ == -2) { // This con generator has been disabled. return false; } OsiSolverInterface * solver = model_->solver(); #if defined(BLIS_DEBUG_MORE) std::cout << "model_->getNodeCount() = " << model_->getNodeCount() << std::endl; #endif if ( fullScan || ((strategy_ > 0) && (model_->getNumNodes() % strategy_) == 0) ) { //-------------------------------------------------- // Start to generate cons ... //-------------------------------------------------- int j; double start = CoinCpuTime(); int numConsBefore = coinCuts.sizeCuts(); int numRowsBefore = coinCuts.sizeRowCuts(); assert(generator_ != NULL); CglProbing* generator = dynamic_cast<CglProbing *>(generator_); if (!generator) { generator_->generateCuts(*solver, coinCuts); } else { // It is probing - return tight column bound CglTreeInfo info; generator->generateCutsAndModify(*solver, coinCuts, &info); const double * tightLower = generator->tightLower(); const double * lower = solver->getColLower(); const double * tightUpper = generator->tightUpper(); const double * upper = solver->getColUpper(); const double * solution = solver->getColSolution(); int numberColumns = solver->getNumCols(); double primalTolerance = 1.0e-8; for (j = 0; j < numberColumns; ++j) { if ( (tightUpper[j] == tightLower[j]) && (upper[j] > lower[j]) ) { // fix column j solver->setColLower(j, tightLower[j]); solver->setColUpper(j, tightUpper[j]); if ( (tightLower[j] > solution[j] + primalTolerance) || (tightUpper[j] < solution[j] - primalTolerance) ) { status = true; } } } } // EOF probing. //-------------------------------------------------- // Remove zero length row cuts. //-------------------------------------------------- int numRowCons = coinCuts.sizeRowCuts(); for (j = numRowsBefore; j < numRowCons; ++j) { OsiRowCut & rCut = coinCuts.rowCut(j); int len = rCut.row().getNumElements(); #ifdef BLIS_DEBUG_MORE std::cout << "Cut " << j<<": length = " << len << std::endl; #endif if (len == 0) { // Empty cuts coinCuts.eraseRowCut(j); --j; --numRowCons; #ifdef BLIS_DEBUG std::cout << "WARNING: Empty cut from " << name_ << std::endl; #endif } else if (len < 0) { #ifdef BLIS_DEBUG std::cout << "ERROR: Cut length = " << len << std::endl; #endif // Error assert(0); } } //-------------------------------------------------- // Update statistics. //-------------------------------------------------- ++calls_; numConsGenerated_ += (coinCuts.sizeCuts() - numConsBefore); time_ += (CoinCpuTime() - start); if (numConsGenerated_ == 0) { ++noConsCalls_; } } return status; }
/** Perform a branch-and-bound on given setup.*/ void CouenneBab::branchAndBound (Bonmin::BabSetupBase & s) { double remaining_time = s.getDoubleParameter(Bonmin::BabSetupBase::MaxTime) + CoinCpuTime(); /* Put a link to this into solver.*/ OsiBabSolver * babInfo = dynamic_cast<OsiBabSolver *>(s.continuousSolver()->getAuxiliaryInfo()); assert(babInfo); Bonmin::BabInfo * bonBabInfoPtr = dynamic_cast<Bonmin::BabInfo*>(babInfo); if (bonBabInfoPtr == NULL) { //Replace with a Bonmin::babInfo bonBabInfoPtr = new Bonmin::BabInfo(*babInfo); s.continuousSolver()->setAuxiliaryInfo(bonBabInfoPtr); delete bonBabInfoPtr; bonBabInfoPtr = dynamic_cast<Bonmin::BabInfo*>(s.continuousSolver()->getAuxiliaryInfo()); } bonBabInfoPtr->setBabPtr(this); s.nonlinearSolver()->solver()->setup_global_time_limit(s.getDoubleParameter(Bonmin::BabSetupBase::MaxTime)); OsiSolverInterface * solver = s.continuousSolver()->clone(); delete modelHandler_; modelHandler_ = s.continuousSolver()->messageHandler()->clone(); model_.passInMessageHandler(modelHandler_); model_.assignSolver(solver, true); // s.continuousSolver() = model_.solver(); // if(s.continuousSolver()->objects()!=NULL){ // model_.addObjects(s.continuousSolver()->numberObjects(),s.continuousSolver()->objects()); // } int specOpt = s.getIntParameter(Bonmin::BabSetupBase::SpecialOption); if (specOpt) { model_.setSpecialOptions(specOpt); if (specOpt==16) { Bonmin::CbcNlpStrategy strat(s.getIntParameter(Bonmin::BabSetupBase::MaxFailures), s.getIntParameter(Bonmin::BabSetupBase::MaxInfeasible), s.getIntParameter(Bonmin::BabSetupBase::FailureBehavior)); model_.setStrategy(strat); } } model_.setMaximumCutPasses(s.getIntParameter(Bonmin::BabSetupBase::NumCutPasses)); model_.setMaximumCutPassesAtRoot(s.getIntParameter(Bonmin::BabSetupBase::NumCutPassesAtRoot)); //Setup cutting plane methods for (Bonmin::BabSetupBase::CuttingMethods::iterator i = s.cutGenerators().begin() ; i != s.cutGenerators().end() ; i++) { Bonmin::OaDecompositionBase * oa = dynamic_cast<Bonmin::OaDecompositionBase *>(i->cgl); if (oa && oa->reassignLpsolver()) oa->assignLpInterface(model_.solver()); model_.addCutGenerator(i->cgl,i->frequency,i->id.c_str(), i->normal, i->atSolution); if(i->always){ model_.cutGenerators()[model_.numberCutGenerators()-1] ->setMustCallAgain(true); } } for (Bonmin::BabSetupBase::HeuristicMethods::iterator i = s.heuristics().begin() ; i != s.heuristics().end() ; i++) { CbcHeuristic * heu = i->heuristic; heu->setModel(&model_); model_.addHeuristic(heu, i->id.c_str()); } //need to record solver logLevel here int logLevel = s.continuousSolver()->messageHandler()->logLevel(); //Set true branch-and-bound parameters model_.setLogLevel(s.getIntParameter(Bonmin::BabSetupBase::BabLogLevel)); // Put back solver logLevel model_.solver()->messageHandler()->setLogLevel(logLevel); model_.setPrintFrequency(s.getIntParameter(Bonmin::BabSetupBase::BabLogInterval)); bool ChangedObject = false; //Pass over user set branching priorities to Cbc if (s.continuousSolver()->objects()==NULL) { //assert (s.branchingMethod() == NULL); const OsiTMINLPInterface * nlpSolver = s.nonlinearSolver(); //set priorities, prefered directions... const int * priorities = nlpSolver->getPriorities(); const double * upPsCosts = nlpSolver->getUpPsCosts(); const double * downPsCosts = nlpSolver->getDownPsCosts(); const int * directions = nlpSolver->getBranchingDirections(); bool hasPseudo = (upPsCosts!=NULL); model_.findIntegers(true,hasPseudo); OsiObject ** simpleIntegerObjects = model_.objects(); int numberObjects = model_.numberObjects(); if (priorities != NULL || directions != NULL || hasPseudo) { ChangedObject = true; for (int i = 0 ; i < numberObjects ; i++) { CbcObject * object = dynamic_cast<CbcObject *> (simpleIntegerObjects[i]); int iCol = object->columnNumber(); if (priorities) object->setPriority(priorities[iCol]); if (directions) object->setPreferredWay(directions[iCol]); if (upPsCosts) { CbcSimpleIntegerPseudoCost * pscObject = dynamic_cast<CbcSimpleIntegerPseudoCost*> (object); pscObject->setUpPseudoCost(upPsCosts[iCol]); pscObject->setDownPseudoCost(downPsCosts[iCol]); } } } #if 1 // Now pass user set Sos constraints (code inspired from CoinSolve.cpp) const TMINLP::SosInfo * sos = s.nonlinearSolver()->model()->sosConstraints(); if (!s.getIntParameter(Bonmin::BabSetupBase::DisableSos) && sos && sos->num > 0) { // we have some sos constraints const OsiTMINLPInterface * nlpSolver = s.nonlinearSolver(); const int & numSos = sos->num; (*nlpSolver->messageHandler())<<"Adding "<<sos->num<<" sos constraints." <<CoinMessageEol; CbcObject ** objects = new CbcObject*[numSos]; const int * starts = sos->starts; const int * indices = sos->indices; const char * types = sos->types; const double * weights = sos->weights; //verify if model has user set priorities bool hasPriorities = false; const int * varPriorities = nlpSolver->getPriorities(); int numberObjects = model_.numberObjects(); if (varPriorities) { for (int i = 0 ; i < numberObjects ; i++) { if (varPriorities[i]) { hasPriorities = true; break; } } } const int * sosPriorities = sos->priorities; if (sosPriorities) { for (int i = 0 ; i < numSos ; i++) { if (sosPriorities[i]) { hasPriorities = true; break; } } } for (int i = 0 ; i < numSos ; i++) { int start = starts[i]; int length = starts[i + 1] - start; #ifdef DO_IT_NWAY printf("setting nway object\n"), objects[i] = new CbcNWay(&model_, length, &indices[start], i); objects[i]->setPriority(1); #else objects[i] = new CbcSOS(&model_, length, &indices[start], &weights[start], i, types[i]); objects[i]->setPriority(10); #endif if (hasPriorities && sosPriorities && sosPriorities[i]) { objects[i]->setPriority(sosPriorities[i]); } } model_.addObjects (numSos, objects); for (int i = 0 ; i < numSos ; i++) delete objects[i]; delete [] objects; } #endif //If Setup contains more objects add them to Cbc if (s.objects().size()) { CbcObject ** objects = new CbcObject *[s.objects().size()]; for (unsigned int i = 0 ; i < s.objects().size() ; i++) { objects[i] = dynamic_cast<CbcObject *> (s.objects()[i]); assert(objects[i]); objects[i]->setModel(&model_); } model_.addObjects ((int) s.objects().size(), objects); delete [] objects; } replaceIntegers(model_.objects(), model_.numberObjects()); } else { // Pass in objects to Cbc // Redundant definition of default branching (as Default == User) assert (s.branchingMethod() != NULL); // Add nonlinear and integer objects (need to add OsiSOS) model_.addObjects (s.continuousSolver () -> numberObjects (), s.continuousSolver () -> objects ()); // Now model_ has only CouenneObjects and SOS objects // for (int i=0; i<nco; i++) // if (!(dynamic_cast <CbcSimpleInteger *> (s.continuousSolver () -> objects () [i]))) // model_ . objects () [nRealObj++] = s.continuousSolver () -> objects () [i] -> clone (); CbcBranchDefaultDecision branch; s.branchingMethod()->setSolver(model_.solver()); BonChooseVariable * strong2 = dynamic_cast<BonChooseVariable *>(s.branchingMethod()); if (strong2) strong2->setCbcModel(&model_); branch.setChooseMethod(*s.branchingMethod()); model_.setBranchingMethod(&branch); // prevent duplicating object when copying in CbcModel.cpp model_.solver()->deleteObjects(); } model_.setDblParam(CbcModel::CbcCutoffIncrement, s.getDoubleParameter(Bonmin::BabSetupBase::CutoffDecr)); model_.setCutoff(s.getDoubleParameter(Bonmin::BabSetupBase::Cutoff) + CUTOFF_TOL); model_.setDblParam(CbcModel::CbcAllowableGap, s.getDoubleParameter(Bonmin::BabSetupBase::AllowableGap)); model_.setDblParam(CbcModel::CbcAllowableFractionGap, s.getDoubleParameter(Bonmin::BabSetupBase::AllowableFractionGap)); // Definition of node selection strategy if (s.nodeComparisonMethod()==Bonmin::BabSetupBase::bestBound) { CbcCompareObjective compare; model_.setNodeComparison(compare); } else if (s.nodeComparisonMethod()==Bonmin::BabSetupBase::DFS) { CbcCompareDepth compare; model_.setNodeComparison(compare); } else if (s.nodeComparisonMethod()==Bonmin::BabSetupBase::BFS) { CbcCompareDefault compare; compare.setWeight(0.0); model_.setNodeComparison(compare); } else if (s.nodeComparisonMethod()==Bonmin::BabSetupBase::dynamic) { CbcCompareDefault compare; model_.setNodeComparison(compare); } else if (s.nodeComparisonMethod()==Bonmin::BabSetupBase::bestGuess) { // Right now, this is a mess. We need a separation of the // pseudo costs from the ChooseVariable method CbcCompareEstimate compare; model_.setNodeComparison(compare); GuessHeuristic * guessHeu = new GuessHeuristic(model_); model_.addHeuristic(guessHeu); delete guessHeu; } if (s.treeTraversalMethod() == Bonmin::BabSetupBase::HeapOnly) { //Do nothing this is the default of Cbc. } else if (s.treeTraversalMethod() == Bonmin::BabSetupBase::DiveFromBest) { CbcDiver treeTraversal; treeTraversal.initialize(s); model_.passInTreeHandler(treeTraversal); } else if (s.treeTraversalMethod() == Bonmin::BabSetupBase::ProbedDive) { CbcProbedDiver treeTraversal; treeTraversal.initialize(s); model_.passInTreeHandler(treeTraversal); } else if (s.treeTraversalMethod() == Bonmin::BabSetupBase::DfsDiveFromBest) { CbcDfsDiver treeTraversal; treeTraversal.initialize(s); model_.passInTreeHandler(treeTraversal); } else if (s.treeTraversalMethod() == Bonmin::BabSetupBase::DfsDiveDynamic) { CbcDfsDiver treeTraversal; treeTraversal.initialize(s); model_.passInTreeHandler(treeTraversal); DiverCompare compare; compare.setComparisonDive(*model_.nodeComparison()); compare.setComparisonBound(CbcCompareObjective()); CbcDfsDiver * dfs = dynamic_cast<CbcDfsDiver *> (model_.tree()); assert(dfs); compare.setDiver(dfs); model_.setNodeComparison(compare); } model_.setNumberStrong(s.getIntParameter(Bonmin::BabSetupBase::NumberStrong)); model_.setNumberBeforeTrust(s.getIntParameter(Bonmin::BabSetupBase::MinReliability)); model_.setNumberPenalties(8); model_.setDblParam(CbcModel::CbcMaximumSeconds, s.getDoubleParameter(Bonmin::BabSetupBase::MaxTime)); model_.setMaximumNodes(s.getIntParameter(Bonmin::BabSetupBase::MaxNodes)); model_.setMaximumNumberIterations(s.getIntParameter(Bonmin::BabSetupBase::MaxIterations)); model_.setMaximumSolutions(s.getIntParameter(Bonmin::BabSetupBase::MaxSolutions)); model_.setIntegerTolerance(s.getDoubleParameter(Bonmin::BabSetupBase::IntTol)); //Get objects from model_ if it is not null means there are some sos constraints or non-integer branching object // pass them to cut generators. OsiObject ** objects = model_.objects(); if (specOpt!=16 && objects) { int numberObjects = model_.numberObjects(); if (objects_ != NULL) { for (int i = 0 ; i < nObjects_; i++) delete objects_[i]; } delete [] objects_; objects_ = new OsiObject*[numberObjects]; nObjects_ = numberObjects; for (int i = 0 ; i < numberObjects; i++) { OsiObject * obj = objects[i]; CbcSimpleInteger * intObj = dynamic_cast<CbcSimpleInteger *> (obj); if (intObj) { objects_[i] = intObj->osiObject(); } else { CbcSOS * sosObj = dynamic_cast<CbcSOS *>(obj); if (sosObj) objects_[i] = sosObj->osiObject(model_.solver()); else {//Maybe an unsupported CbcObject CbcObject * cbcObj = dynamic_cast<CbcObject *>(obj); if (cbcObj) { std::cerr<<"Unsupported CbcObject appears in the code"<<std::endl; throw UNSUPPORTED_CBC_OBJECT; } else {//It has to be an OsiObject. objects_[i]=obj->clone(); } } } } CbcCutGenerator ** gen = model_.cutGenerators(); int numGen = model_.numberCutGenerators(); for (int i = 0 ; i < numGen ; i++) { Bonmin::OaDecompositionBase * oa = dynamic_cast<Bonmin::OaDecompositionBase * >(gen[i]->generator()); // if (oa) // printf ("\n\n\nat least one OADecompBase\n\n\n"); if (oa) // pass objects oa->setObjects(objects_,nObjects_); } } // if (objects_) { // for (int i = 0 ; i < nObjects_; i++) // delete objects_ [i]; // delete [] objects_; // } // OsiObject ** objects = model_.objects(); // int numObjects = model_.numberObjects(); // nObjects_ = 0; // objects_ = new OsiObject* [numObjects]; // for (int i=0; i < numObjects; ++i) // if (objects [i]) // objects_ [nObjects_++] = objects [i] -> clone (); try { //Get the time and start. { OsiTMINLPInterface * tmpOsi = NULL; if(s.nonlinearSolver() == s.continuousSolver()){ tmpOsi = dynamic_cast<OsiTMINLPInterface *> (model_.solver()); tmpOsi->forceSolverOutput(s.getIntParameter(Bonmin::BabSetupBase::RootLogLevel)); } model_.initialSolve(); if(tmpOsi != NULL){ tmpOsi->setSolverOutputToDefault(); } } int ival; s.options()->GetEnumValue("enable_dynamic_nlp", ival, "bonmin."); if(s.nonlinearSolver() == s.continuousSolver() && ival) { if(!model_.solver()->isProvenOptimal() ){//Something went wrong check if objective is linear and alternate model // can be solved OsiTMINLPInterface * tmpOsi = dynamic_cast<OsiTMINLPInterface *> (model_.solver()); TMINLPLinObj * tmp_tminlp = dynamic_cast<TMINLPLinObj *> (tmpOsi->model()); tmpOsi->setModel(tmp_tminlp->tminlp()); model_.initialSolve(); } else { LinearCutsGenerator cgl; cgl.initialize(s); OsiCuts cuts; cgl.generateCuts(*model_.solver(), cuts); std::vector<const OsiRowCut *> mycuts(cuts.sizeRowCuts()); for(int i = 0 ; i < cuts.sizeRowCuts() ; i++){ mycuts[i] = cuts.rowCutPtr(i); } model_. solver () -> applyRowCuts ((int) mycuts.size(), (const OsiRowCut **) &mycuts[0]); } //Added by Claudia OsiTMINLPInterface * nlpSolver = dynamic_cast<OsiTMINLPInterface *>(model_.solver()); if(nlpSolver && nlpSolver->getNewCutoffDecr()!=COIN_DBL_MAX) model_.setDblParam(CbcModel::CbcCutoffIncrement, nlpSolver->getNewCutoffDecr()); model_.solver()->resolve(); } // for Couenne model_.passInSolverCharacteristics (bonBabInfoPtr); continuousRelaxation_ =model_.solver()->getObjValue(); if (specOpt==16)//Set warm start point for Ipopt { #if 1 const double * colsol = model_.solver()->getColSolution(); const double * duals = model_.solver()->getRowPrice(); OsiTMINLPInterface * tnlpSolver = dynamic_cast<OsiTMINLPInterface *>(model_.solver()); // Primal dual point is not copied if one (supposedly a better one) has already been put into the solver. if(tnlpSolver->problem()->has_x_init() != 2){ model_.solver()->setColSolution(colsol); model_.solver()->setRowPrice(duals); } #else OsiTMINLPInterface * tnlpSolver = dynamic_cast<OsiTMINLPInterface *>(model_.solver()); CoinWarmStart * warm = tnlpSolver->solver()->getWarmStart(tnlpSolver->problem()); tnlpSolver->solver()->setWarmStart(warm, tnlpSolver->problem()); delete warm; #endif #if 0 // Sometimes primal dual point is problematic in the context of Cut-and-branch model_.solver()->resolve(); if(!model_.solver()->isProvenOptimal()) model_.solver()->setColSolution(NULL); #endif } #ifdef SIGNAL CoinSighandler_t saveSignal = SIG_DFL; // register signal handler saveSignal = signal (SIGINT,couenne_signal_handler); currentBranchModel = &model_; #endif // to get node parent info in Cbc, pass parameter 3. //model_.branchAndBound(3); remaining_time -= CoinCpuTime(); model_.setDblParam(CbcModel::CbcMaximumSeconds, remaining_time); if(remaining_time > 0.) model_.branchAndBound(); } catch(TNLPSolver::UnsolvedError *E){ s.nonlinearSolver()->model()->finalize_solution (TMINLP::MINLP_ERROR, 0, NULL, DBL_MAX); throw E; } numNodes_ = model_.getNodeCount(); bestObj_ = model_.getObjValue(); bestBound_ = model_.getBestPossibleObjValue(); mipIterationCount_ = model_.getIterationCount(); bool hasFailed = false; if (specOpt==16)//Did we continue branching on a failure { CbcNlpStrategy * nlpStrategy = dynamic_cast<CbcNlpStrategy *>(model_.strategy()); if (nlpStrategy) hasFailed = nlpStrategy->hasFailed(); else throw -1; } else hasFailed = s.nonlinearSolver()->hasContinuedOnAFailure(); // Output summarizing cut generators (taken from CbcSolver.cpp) // ToDo put into proper print level int numberGenerators = model_.numberCutGenerators(); for (int iGenerator=0;iGenerator<numberGenerators;iGenerator++) { CbcCutGenerator * generator = model_.cutGenerator(iGenerator); //CglStored * stored = dynamic_cast<CglStored*>(generator->generator()); if (true&&!(generator->numberCutsInTotal() || generator->numberColumnCuts())) continue; if(modelHandler_->logLevel() >= 1) { *modelHandler_ << generator->cutGeneratorName() << "was tried" << generator->numberTimesEntered() << "times and created" << generator->numberCutsInTotal()+generator->numberColumnCuts() << "cuts of which" << generator->numberCutsActive() << "were active after adding rounds of cuts"; // if (generator->timing()) { // char timebuf[20]; // sprintf(timebuf, "(%.3fs)", generator->timeInCutGenerator()); // *modelHandler_ << timebuf << CoinMessageEol; // } // else { // *modelHandler_ << CoinMessageEol; // } } } TMINLP::SolverReturn status = TMINLP::MINLP_ERROR; if (model_.numberObjects()==0) { if (bestSolution_) delete [] bestSolution_; OsiSolverInterface * solver = (s.nonlinearSolver() == s.continuousSolver())? model_.solver() : s.nonlinearSolver(); bestSolution_ = new double[solver->getNumCols()]; CoinCopyN(solver->getColSolution(), solver->getNumCols(), bestSolution_); bestObj_ = bestBound_ = solver->getObjValue(); } if (bonBabInfoPtr->bestSolution2().size() > 0) { assert((int) bonBabInfoPtr->bestSolution2().size() == s.nonlinearSolver()->getNumCols()); if (bestSolution_) delete [] bestSolution_; bestSolution_ = new double[s.nonlinearSolver()->getNumCols()]; std::copy(bonBabInfoPtr->bestSolution2().begin(), bonBabInfoPtr->bestSolution2().end(), bestSolution_); bestObj_ = (bonBabInfoPtr->bestObj2()); (*s.nonlinearSolver()->messageHandler())<<"\nReal objective function: " <<bestObj_<<CoinMessageEol; } else if (model_.bestSolution()) { if (bestSolution_) delete [] bestSolution_; bestSolution_ = new double[s.nonlinearSolver()->getNumCols()]; CoinCopyN(model_.bestSolution(), s.nonlinearSolver()->getNumCols(), bestSolution_); } if(remaining_time <= 0.){ status = TMINLP::LIMIT_EXCEEDED; if (bestSolution_) { mipStatus_ = Feasible; } else { mipStatus_ = NoSolutionKnown; } } else if (model_.status() == 0) { if(model_.isContinuousUnbounded()){ status = TMINLP::CONTINUOUS_UNBOUNDED; mipStatus_ = UnboundedOrInfeasible; } else if (bestSolution_) { status = TMINLP::SUCCESS; mipStatus_ = FeasibleOptimal; } else { status = TMINLP::INFEASIBLE; mipStatus_ = ProvenInfeasible; } } else if (model_.status() == 1 || model_.status() == 5) { #if (BONMIN_VERSION_MAJOR > 1) || (BONMIN_VERSION_MINOR > 6) status = model_.status() == 1 ? TMINLP::LIMIT_EXCEEDED : TMINLP::USER_INTERRUPT; #else status = TMINLP::LIMIT_EXCEEDED; #endif if (bestSolution_) { mipStatus_ = Feasible; } else { mipStatus_ = NoSolutionKnown; } } else if (model_.status()==2) { status = TMINLP::MINLP_ERROR; } // Which solution should we use? false if RBS's, true if Cbc's bool use_RBS_Cbc = !problem_ || !(problem_ -> getRecordBestSol ()) || !(problem_ -> getRecordBestSol () -> getHasSol()) || (((fabs (bestObj_) < COUENNE_INFINITY / 1e4) && (problem_ -> getRecordBestSol () -> getVal () > bestObj_))); /* if we do not pass the cbc solution and problem_ -> getRecordBestSol () -> getHasSol() is true, then there should be a solution vector in problem_ -> getRecordBestSol () */ assert(use_RBS_Cbc || problem_ -> getRecordBestSol () -> getSol() != NULL); s.nonlinearSolver () -> model () -> finalize_solution (status, s.nonlinearSolver () -> getNumCols (), use_RBS_Cbc ? bestSolution_ : problem_ -> getRecordBestSol () -> getSol (), use_RBS_Cbc ? bestObj_ : problem_ -> getRecordBestSol () -> getVal ()); }
// Generate cuts void CglFakeClique::generateCuts(const OsiSolverInterface& si, OsiCuts & cs, const CglTreeInfo info) { if (fakeSolver_) { assert (si.getNumCols()==fakeSolver_->getNumCols()); fakeSolver_->setColLower(si.getColLower()); const double * solution = si.getColSolution(); fakeSolver_->setColSolution(solution); fakeSolver_->setColUpper(si.getColUpper()); // get and set branch and bound cutoff double cutoff; si.getDblParam(OsiDualObjectiveLimit,cutoff); fakeSolver_->setDblParam(OsiDualObjectiveLimit,COIN_DBL_MAX); #ifdef COIN_HAS_CLP OsiClpSolverInterface * clpSolver = dynamic_cast<OsiClpSolverInterface *> (fakeSolver_); if (clpSolver) { // fix up fake solver const ClpSimplex * siSimplex = clpSolver->getModelPtr(); // need to set djs memcpy(siSimplex->primalColumnSolution(), si.getReducedCost(),si.getNumCols()*sizeof(double)); fakeSolver_->setDblParam(OsiDualObjectiveLimit,cutoff); } #endif const CoinPackedMatrix * matrixByRow = si.getMatrixByRow(); const double * elementByRow = matrixByRow->getElements(); const int * column = matrixByRow->getIndices(); const CoinBigIndex * rowStart = matrixByRow->getVectorStarts(); const int * rowLength = matrixByRow->getVectorLengths(); const double * rowUpper = si.getRowUpper(); const double * rowLower = si.getRowLower(); // Scan all rows looking for possibles int numberRows = si.getNumRows(); double tolerance = 1.0e-3; for (int iRow=0;iRow<numberRows;iRow++) { CoinBigIndex start = rowStart[iRow]; CoinBigIndex end = start + rowLength[iRow]; double upRhs = rowUpper[iRow]; double loRhs = rowLower[iRow]; double sum = 0.0; for (CoinBigIndex j=start;j<end;j++) { int iColumn=column[j]; double value = elementByRow[j]; sum += solution[iColumn]*value; } if (sum<loRhs-tolerance||sum>upRhs+tolerance) { // add as cut OsiRowCut rc; rc.setLb(loRhs); rc.setUb(upRhs); rc.setRow(end-start,column+start,elementByRow+start,false); CoinAbsFltEq equal(1.0e-12); cs.insertIfNotDuplicate(rc,equal); } } CglClique::generateCuts(*fakeSolver_,cs,info); if (probing_) { probing_->generateCuts(*fakeSolver_,cs,info); } } else { // just use real solver CglClique::generateCuts(si,cs,info); } }
//-------------------------------------------------------------------------- // test solution methods. void OsiCbcSolverInterfaceUnitTest(const std::string & mpsDir, const std::string & netlibDir) { { CoinRelFltEq eq; OsiCbcSolverInterface m; std::string fn = mpsDir+"exmip1"; m.readMps(fn.c_str(),"mps"); { OsiCbcSolverInterface im; OSIUNITTEST_ASSERT_ERROR(im.getNumCols() == 0, {}, "cbc", "default constructor"); OSIUNITTEST_ASSERT_ERROR(im.getModelPtr() != NULL, {}, "cbc", "default constructor"); } // Test copy constructor and assignment operator { OsiCbcSolverInterface lhs; { OsiCbcSolverInterface im(m); OsiCbcSolverInterface imC1(im); OSIUNITTEST_ASSERT_ERROR(imC1.getModelPtr() != im.getModelPtr(), {}, "cbc", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(imC1.getNumCols() == im.getNumCols(), {}, "cbc", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(imC1.getNumRows() == im.getNumRows(), {}, "cbc", "copy constructor"); OsiCbcSolverInterface imC2(im); OSIUNITTEST_ASSERT_ERROR(imC2.getModelPtr() != im.getModelPtr(), {}, "cbc", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(imC2.getNumCols() == im.getNumCols(), {}, "cbc", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(imC2.getNumRows() == im.getNumRows(), {}, "cbc", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(imC1.getModelPtr() != imC2.getModelPtr(), {}, "cbc", "copy constructor"); lhs = imC2; } // Test that lhs has correct values even though rhs has gone out of scope OSIUNITTEST_ASSERT_ERROR(lhs.getModelPtr() != m.getModelPtr(), {}, "cbc", "assignment operator"); OSIUNITTEST_ASSERT_ERROR(lhs.getNumCols() == m.getNumCols(), {}, "cbc", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(lhs.getNumRows() == m.getNumRows(), {}, "cbc", "copy constructor"); } // Test clone { OsiCbcSolverInterface cbcSi(m); OsiSolverInterface * siPtr = &cbcSi; OsiSolverInterface * siClone = siPtr->clone(); OsiCbcSolverInterface * cbcClone = dynamic_cast<OsiCbcSolverInterface*>(siClone); OSIUNITTEST_ASSERT_ERROR(cbcClone != NULL, {}, "cbc", "clone"); OSIUNITTEST_ASSERT_ERROR(cbcClone->getModelPtr() != cbcSi.getModelPtr(), {}, "cbc", "clone"); OSIUNITTEST_ASSERT_ERROR(cbcClone->getNumRows() == cbcSi.getNumRows(), {}, "cbc", "clone"); OSIUNITTEST_ASSERT_ERROR(cbcClone->getNumCols() == m.getNumCols(), {}, "cbc", "clone"); delete siClone; } // test infinity { OsiCbcSolverInterface si; OSIUNITTEST_ASSERT_ERROR(si.getInfinity() == OsiCbcInfinity, {}, "cbc", "infinity"); } // Test some catches if (!OsiCbcHasNDEBUG()) { OsiCbcSolverInterface solver; try { solver.setObjCoeff(0,0.0); OSIUNITTEST_ADD_OUTCOME("cbc", "setObjCoeff on empty model", "should throw exception", OsiUnitTest::TestOutcome::ERROR, false); } catch (CoinError e) { if (OsiUnitTest::verbosity >= 1) std::cout<<"Correct throw from setObjCoeff on empty model"<<std::endl; } std::string fn = mpsDir+"exmip1"; solver.readMps(fn.c_str(),"mps"); OSIUNITTEST_CATCH_ERROR(solver.setObjCoeff(0,0.0), {}, "cbc", "setObjCoeff on nonempty model"); try { int index[]={0,20}; double value[]={0.0,0.0,0.0,0.0}; solver.setColSetBounds(index,index+2,value); OSIUNITTEST_ADD_OUTCOME("cbc", "setColSetBounds on cols not in model", "should throw exception", OsiUnitTest::TestOutcome::ERROR, false); } catch (CoinError e) { if (OsiUnitTest::verbosity >= 1) std::cout<<"Correct throw from setObjCoeff on empty model"<<std::endl; } } { OsiCbcSolverInterface cbcSi(m); int nc = cbcSi.getNumCols(); int nr = cbcSi.getNumRows(); const double * cl = cbcSi.getColLower(); const double * cu = cbcSi.getColUpper(); const double * rl = cbcSi.getRowLower(); const double * ru = cbcSi.getRowUpper(); OSIUNITTEST_ASSERT_ERROR(nc == 8, return, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(nr == 5, return, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cl[0],2.5), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cl[1],0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cu[1],4.1), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cu[2],1.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(rl[0],2.5), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(rl[4],3.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(ru[1],2.1), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(ru[4],15.), {}, "cbc", "read and copy exmip1"); const double * cs = cbcSi.getColSolution(); OSIUNITTEST_ASSERT_ERROR(eq(cs[0],2.5), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cs[7],0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(!eq(cl[3],1.2345), {}, "cbc", "set col lower"); cbcSi.setColLower( 3, 1.2345 ); OSIUNITTEST_ASSERT_ERROR( eq(cbcSi.getColLower()[3],1.2345), {}, "cbc", "set col lower"); OSIUNITTEST_ASSERT_ERROR(!eq(cbcSi.getColUpper()[4],10.2345), {}, "cbc", "set col upper"); cbcSi.setColUpper( 4, 10.2345 ); OSIUNITTEST_ASSERT_ERROR( eq(cbcSi.getColUpper()[4],10.2345), {}, "cbc", "set col upper"); // LH: Objective will depend on how underlying solver constructs and maintains initial solution double objValue = cbcSi.getObjValue(); OSIUNITTEST_ASSERT_ERROR(eq(objValue,3.5) || eq(objValue,10.5), {}, "cbc", "getObjValue() before solve"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[0], 1.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[1], 0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[2], 0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[3], 0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[4], 2.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[5], 0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[6], 0.0), {}, "cbc", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cbcSi.getObjCoefficients()[7],-1.0), {}, "cbc", "read and copy exmip1"); } // Test matrixByRow method { const OsiCbcSolverInterface si(m); const CoinPackedMatrix * smP = si.getMatrixByRow(); OSIUNITTEST_ASSERT_ERROR(smP->getMajorDim() == 5, return, "cbc", "getMatrixByRow: major dim"); OSIUNITTEST_ASSERT_ERROR(smP->getMinorDim() == 8, return, "cbc", "getMatrixByRow: major dim"); OSIUNITTEST_ASSERT_ERROR(smP->getNumElements() == 14, return, "cbc", "getMatrixByRow: num elements"); OSIUNITTEST_ASSERT_ERROR(smP->getSizeVectorStarts() == 6, return, "cbc", "getMatrixByRow: num elements"); #ifdef OSICBC_TEST_MTX_STRUCTURE CoinRelFltEq eq; const double * ev = smP->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[0], 3.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[1], 1.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[2], -2.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[3], -1.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[4], -1.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[5], 2.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[6], 1.1), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[7], 1.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[8], 1.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[9], 2.8), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10], -1.2), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11], 5.6), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12], 1.0), {}, "cbc", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "cbc", "getMatrixByRow: elements"); const int * mi = smP->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "cbc", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 5, {}, "cbc", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 7, {}, "cbc", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 9, {}, "cbc", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 11, {}, "cbc", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 14, {}, "cbc", "getMatrixByRow: vector starts"); const int * ei = smP->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 1, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 3, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 4, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 7, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 1, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 2, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 2, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 5, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 3, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 6, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 0, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 4, {}, "cbc", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 7, {}, "cbc", "getMatrixByRow: indices"); #else // OSICBC_TEST_MTX_STRUCTURE CoinPackedMatrix exmip1Mtx ; exmip1Mtx.reverseOrderedCopyOf(BuildExmip1Mtx()) ; OSIUNITTEST_ASSERT_ERROR(exmip1Mtx.isEquivalent(*smP), {}, "cbc", "getMatrixByRow") ; #endif // OSICBC_TEST_MTX_STRUCTURE } // Test adding several cuts, and handling of a coefficient of infinity // in the constraint matrix. { OsiCbcSolverInterface fim; std::string fn = mpsDir+"exmip1"; fim.readMps(fn.c_str(),"mps"); // exmip1.mps has 2 integer variables with index 2 & 3 fim.initialSolve(); OsiRowCut cuts[3]; // Generate one ineffective cut plus two trivial cuts int c; int nc = fim.getNumCols(); int *inx = new int[nc]; for (c=0;c<nc;c++) inx[c]=c; double *el = new double[nc]; for (c=0;c<nc;c++) el[c]=1.0e-50+((double)c)*((double)c); cuts[0].setRow(nc,inx,el); cuts[0].setLb(-100.); cuts[0].setUb(500.); cuts[0].setEffectiveness(22); el[4]=0.0; // to get inf later for (c=2;c<4;c++) { el[0]=1.0; inx[0]=c; cuts[c-1].setRow(1,inx,el); cuts[c-1].setLb(1.); cuts[c-1].setUb(100.); cuts[c-1].setEffectiveness(c); } fim.writeMps("x1.mps"); fim.applyRowCuts(3,cuts); fim.writeMps("x2.mps"); // resolve - should get message about zero elements fim.resolve(); fim.writeMps("x3.mps"); // check integer solution const double * cs = fim.getColSolution(); CoinRelFltEq eq; OSIUNITTEST_ASSERT_ERROR(eq(cs[2], 1.0), {}, "cbc", "add cuts"); OSIUNITTEST_ASSERT_ERROR(eq(cs[3], 1.0), {}, "cbc", "add cuts"); // check will find invalid matrix el[0]=1.0/el[4]; inx[0]=0; cuts[0].setRow(nc,inx,el); cuts[0].setLb(-100.); cuts[0].setUb(500.); cuts[0].setEffectiveness(22); fim.applyRowCut(cuts[0]); // resolve - should get message about zero elements fim.resolve(); OSIUNITTEST_ASSERT_WARNING(fim.isAbandoned(), {}, "cbc", "add cuts"); delete[]el; delete[]inx; } // Test matrixByCol method { const OsiCbcSolverInterface si(m); const CoinPackedMatrix * smP = si.getMatrixByCol(); OSIUNITTEST_ASSERT_ERROR(smP->getMajorDim() == 8, return, "cbc", "getMatrixByCol: major dim"); OSIUNITTEST_ASSERT_ERROR(smP->getMinorDim() == 5, return, "cbc", "getMatrixByCol: minor dim"); OSIUNITTEST_ASSERT_ERROR(smP->getNumElements() == 14, return, "cbc", "getMatrixByCol: number of elements"); OSIUNITTEST_ASSERT_ERROR(smP->getSizeVectorStarts() == 9, return, "cbc", "getMatrixByCol: vector starts size"); #ifdef OSICBC_TEST_MTX_STRUCTURE CoinRelFltEq eq; const double * ev = smP->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 0], 3.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 1], 5.6), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 2], 1.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 3], 2.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 4], 1.1), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 5], 1.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 6],-2.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 7], 2.8), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 8],-1.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 9], 1.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10], 1.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11],-1.2), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12],-1.0), {}, "cbc", "getMatrixByCol: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "cbc", "getMatrixByCol: elements"); const CoinBigIndex * mi = smP->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 2, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 4, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 6, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 8, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 10, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[6] == 11, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[7] == 12, {}, "cbc", "getMatrixByCol: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[8] == 14, {}, "cbc", "getMatrixByCol: vector starts"); const int * ei = smP->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 4, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 0, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 1, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 1, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 2, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 0, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 3, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 0, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 4, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 2, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 3, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 0, {}, "cbc", "getMatrixByCol: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 4, {}, "cbc", "getMatrixByCol: indices"); #else // OSICBC_TEST_MTX_STRUCTURE CoinPackedMatrix &exmip1Mtx = BuildExmip1Mtx() ; OSIUNITTEST_ASSERT_ERROR(exmip1Mtx.isEquivalent(*smP), {}, "cbc", "getMatrixByCol"); #endif // OSICBC_TEST_MTX_STRUCTURE } //-------------- // Test rowsense, rhs, rowrange, matrixByRow, solver assignment { OsiCbcSolverInterface lhs; { OsiCbcSolverInterface siC1(m); const char * siC1rs = siC1.getRowSense(); OSIUNITTEST_ASSERT_ERROR(siC1rs[0] == 'G', {}, "cbc", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[1] == 'L', {}, "cbc", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[2] == 'E', {}, "cbc", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[3] == 'R', {}, "cbc", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[4] == 'R', {}, "cbc", "row sense"); const double * siC1rhs = siC1.getRightHandSide(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[0],2.5), {}, "cbc", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[1],2.1), {}, "cbc", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[2],4.0), {}, "cbc", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[3],5.0), {}, "cbc", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[4],15.), {}, "cbc", "right hand side"); const double * siC1rr = siC1.getRowRange(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[0],0.0), {}, "cbc", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[1],0.0), {}, "cbc", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[2],0.0), {}, "cbc", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[3],5.0-1.8), {}, "cbc", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[4],15.0-3.0), {}, "cbc", "row range"); const CoinPackedMatrix * siC1mbr = siC1.getMatrixByRow(); OSIUNITTEST_ASSERT_ERROR(siC1mbr != NULL, {}, "cbc", "matrix by row"); OSIUNITTEST_ASSERT_ERROR(siC1mbr->getMajorDim() == 5, return, "cbc", "matrix by row: major dim"); OSIUNITTEST_ASSERT_ERROR(siC1mbr->getMinorDim() == 8, return, "cbc", "matrix by row: major dim"); OSIUNITTEST_ASSERT_ERROR(siC1mbr->getNumElements() == 14, return, "cbc", "matrix by row: num elements"); OSIUNITTEST_ASSERT_ERROR(siC1mbr->getSizeVectorStarts() == 6, return, "cbc", "matrix by row: num elements"); #ifdef OSICBC_TEST_MTX_STRUCTURE const double * ev = siC1mbr->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 0], 3.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 1], 1.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 2],-2.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 3],-1.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 4],-1.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 5], 2.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 6], 1.1), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 7], 1.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 8], 1.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 9], 2.8), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10],-1.2), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11], 5.6), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12], 1.0), {}, "cbc", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "cbc", "matrix by row: elements"); const CoinBigIndex * mi = siC1mbr->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "cbc", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 5, {}, "cbc", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 7, {}, "cbc", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 9, {}, "cbc", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 11, {}, "cbc", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 14, {}, "cbc", "matrix by row: vector starts"); const int * ei = siC1mbr->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 1, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 3, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 4, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 7, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 1, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 2, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 2, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 5, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 3, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 6, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 0, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 4, {}, "cbc", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 7, {}, "cbc", "matrix by row: indices"); #else // OSICBC_TEST_MTX_STRUCTURE CoinPackedMatrix exmip1Mtx ; exmip1Mtx.reverseOrderedCopyOf(BuildExmip1Mtx()) ; OSIUNITTEST_ASSERT_ERROR(exmip1Mtx.isEquivalent(*siC1mbr), {}, "cbc", "matrix by row"); #endif // OSICBC_TEST_MTX_STRUCTURE OSIUNITTEST_ASSERT_WARNING(siC1rs == siC1.getRowSense(), {}, "cbc", "row sense"); OSIUNITTEST_ASSERT_WARNING(siC1rhs == siC1.getRightHandSide(), {}, "cbc", "right hand side"); OSIUNITTEST_ASSERT_WARNING(siC1rr == siC1.getRowRange(), {}, "cbc", "row range"); // Change CBC Model by adding free row OsiRowCut rc; rc.setLb(-COIN_DBL_MAX); rc.setUb( COIN_DBL_MAX); OsiCuts cuts; cuts.insert(rc); siC1.applyCuts(cuts); siC1rs = siC1.getRowSense(); OSIUNITTEST_ASSERT_ERROR(siC1rs[0] == 'G', {}, "cbc", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[1] == 'L', {}, "cbc", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[2] == 'E', {}, "cbc", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[3] == 'R', {}, "cbc", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[4] == 'R', {}, "cbc", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[5] == 'N', {}, "cbc", "row sense after adding row"); siC1rhs = siC1.getRightHandSide(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[0],2.5), {}, "cbc", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[1],2.1), {}, "cbc", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[2],4.0), {}, "cbc", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[3],5.0), {}, "cbc", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[4],15.), {}, "cbc", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[5],0.0), {}, "cbc", "right hand side after adding row"); siC1rr = siC1.getRowRange(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[0],0.0), {}, "cbc", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[1],0.0), {}, "cbc", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[2],0.0), {}, "cbc", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[3],5.0-1.8), {}, "cbc", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[4],15.0-3.0), {}, "cbc", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[5],0.0), {}, "cbc", "row range after adding row"); lhs = siC1; } // Test that lhs has correct values even though siC1 has gone out of scope const char * lhsrs = lhs.getRowSense(); OSIUNITTEST_ASSERT_ERROR(lhsrs[0] == 'G', {}, "cbc", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[1] == 'L', {}, "cbc", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[2] == 'E', {}, "cbc", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[3] == 'R', {}, "cbc", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[4] == 'R', {}, "cbc", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[5] == 'N', {}, "cbc", "row sense after assignment"); const double * lhsrhs = lhs.getRightHandSide(); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[0],2.5), {}, "cbc", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[1],2.1), {}, "cbc", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[2],4.0), {}, "cbc", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[3],5.0), {}, "cbc", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[4],15.), {}, "cbc", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[5],0.0), {}, "cbc", "right hand side after assignment"); const double *lhsrr = lhs.getRowRange(); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[0],0.0), {}, "cbc", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[1],0.0), {}, "cbc", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[2],0.0), {}, "cbc", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[3],5.0-1.8), {}, "cbc", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[4],15.0-3.0), {}, "cbc", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[5],0.0), {}, "cbc", "row range after assignment"); const CoinPackedMatrix * lhsmbr = lhs.getMatrixByRow(); OSIUNITTEST_ASSERT_ERROR(lhsmbr != NULL, {}, "cbc", "matrix by row after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsmbr->getMajorDim() == 6, return, "cbc", "matrix by row after assignment: major dim"); OSIUNITTEST_ASSERT_ERROR(lhsmbr->getNumElements() == 14, return, "cbc", "matrix by row after assignment: num elements"); #ifdef OSICBC_TEST_MTX_STRUCTURE const double * ev = lhsmbr->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 0], 3.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 1], 1.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 2],-2.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 3],-1.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 4],-1.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 5], 2.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 6], 1.1), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 7], 1.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 8], 1.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 9], 2.8), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10],-1.2), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11], 5.6), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12], 1.0), {}, "cbc", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "cbc", "matrix by row after assignment: elements"); const CoinBigIndex * mi = lhsmbr->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "cbc", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 5, {}, "cbc", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 7, {}, "cbc", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 9, {}, "cbc", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 11, {}, "cbc", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 14, {}, "cbc", "matrix by row after assignment: vector starts"); const int * ei = lhsmbr->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 1, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 3, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 4, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 7, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 1, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 2, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 2, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 5, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 3, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 6, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 0, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 4, {}, "cbc", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 7, {}, "cbc", "matrix by row after assignment: indices"); #else // OSICBC_TEST_MTX_STRUCTURE /* This admittedly looks bogus, but it's the equivalent operation on the matrix for inserting a cut of the form -Inf <= +Inf (i.e., a cut with no coefficients). */ CoinPackedMatrix exmip1Mtx ; exmip1Mtx.reverseOrderedCopyOf(BuildExmip1Mtx()) ; CoinPackedVector freeRow ; exmip1Mtx.appendRow(freeRow) ; OSIUNITTEST_ASSERT_ERROR(exmip1Mtx.isEquivalent(*lhsmbr), {}, "cbc", "matrix by row after assignment"); #endif // OSICBC_TEST_MTX_STRUCTURE } } // Test add/delete columns { OsiCbcSolverInterface m; std::string fn = mpsDir+"p0033"; m.readMps(fn.c_str(),"mps"); double inf = m.getInfinity(); CoinPackedVector c0; c0.insert(0, 4); c0.insert(1, 1); m.addCol(c0, 0, inf, 3); m.initialSolve(); double objValue = m.getObjValue(); CoinRelFltEq eq(1.0e-2); OSIUNITTEST_ASSERT_ERROR(eq(objValue,2520.57), {}, "cbc", "objvalue after adding col"); // Try deleting first column that's nonbasic at lower bound (0). int * d = new int[1]; CoinWarmStartBasis *cwsb = dynamic_cast<CoinWarmStartBasis *>(m.getWarmStart()) ; OSIUNITTEST_ASSERT_ERROR(cwsb != NULL, {}, "cbc", "get warmstart basis"); CoinWarmStartBasis::Status stati ; int iCol ; for (iCol = 0 ; iCol < cwsb->getNumStructural() ; iCol++) { stati = cwsb->getStructStatus(iCol) ; if (stati == CoinWarmStartBasis::atLowerBound) break ; } d[0]=iCol; m.deleteCols(1,d); delete [] d; delete cwsb; d=NULL; m.resolve(); objValue = m.getObjValue(); OSIUNITTEST_ASSERT_ERROR(eq(objValue,2520.57), {}, "clp", "objvalue after deleting first col"); // Try deleting column we added. If basic, go to initialSolve as deleting // basic variable trashes basis required for warm start. iCol = m.getNumCols()-1; cwsb = dynamic_cast<CoinWarmStartBasis *>(m.getWarmStart()) ; stati = cwsb->getStructStatus(iCol) ; delete cwsb; m.deleteCols(1,&iCol); if (stati == CoinWarmStartBasis::basic) { m.initialSolve() ; } else { m.resolve(); } objValue = m.getObjValue(); OSIUNITTEST_ASSERT_ERROR(eq(objValue,2520.57), {}, "clp", "objvalue after deleting added col"); } // Build a model { OsiCbcSolverInterface model; std::string fn = mpsDir+"p0033"; model.readMps(fn.c_str(),"mps"); // Point to data int numberRows = model.getNumRows(); const double * rowLower = model.getRowLower(); const double * rowUpper = model.getRowUpper(); int numberColumns = model.getNumCols(); const double * columnLower = model.getColLower(); const double * columnUpper = model.getColUpper(); const double * columnObjective = model.getObjCoefficients(); // get row copy CoinPackedMatrix rowCopy = *model.getMatrixByRow(); const int * column = rowCopy.getIndices(); const int * rowLength = rowCopy.getVectorLengths(); const CoinBigIndex * rowStart = rowCopy.getVectorStarts(); const double * element = rowCopy.getElements(); // solve model.initialSolve(); // Now build new model CoinModel build; // Row bounds int iRow; for (iRow=0;iRow<numberRows;iRow++) { build.setRowBounds(iRow,rowLower[iRow],rowUpper[iRow]); } // Column bounds and objective int iColumn; for (iColumn=0;iColumn<numberColumns;iColumn++) { build.setColumnLower(iColumn,columnLower[iColumn]); build.setColumnUpper(iColumn,columnUpper[iColumn]); build.setObjective(iColumn,columnObjective[iColumn]); } // Adds elements one by one by row (backwards by row) for (iRow=numberRows-1;iRow>=0;iRow--) { int start = rowStart[iRow]; for (int j=start;j<start+rowLength[iRow];j++) build(iRow,column[j],element[j]); } // Now create Model OsiCbcSolverInterface model2; model2.loadFromCoinModel(build); model2.initialSolve(); // Save - should be continuous model2.writeMps("continuous"); int * whichInteger = new int[numberColumns]; for (iColumn=0;iColumn<numberColumns;iColumn++) whichInteger[iColumn]=iColumn; // mark as integer model2.setInteger(whichInteger,numberColumns); delete [] whichInteger; // save - should be integer model2.writeMps("integer"); // Now do with strings attached // Save build to show how to go over rows CoinModel saveBuild = build; build = CoinModel(); // Column bounds for (iColumn=0;iColumn<numberColumns;iColumn++) { build.setColumnLower(iColumn,columnLower[iColumn]); build.setColumnUpper(iColumn,columnUpper[iColumn]); } // Objective - half the columns as is and half with multiplier of "1.0+multiplier" // Pick up from saveBuild (for no reason at all) for (iColumn=0;iColumn<numberColumns;iColumn++) { double value = saveBuild.objective(iColumn); if (iColumn*2<numberColumns) { build.setObjective(iColumn,columnObjective[iColumn]); } else { // create as string char temp[100]; sprintf(temp,"%g + abs(%g*multiplier)",value,value); build.setObjective(iColumn,temp); } } // It then adds rows one by one but for half the rows sets their values // with multiplier of "1.0+1.5*multiplier" for (iRow=0;iRow<numberRows;iRow++) { if (iRow*2<numberRows) { // add row in simple way int start = rowStart[iRow]; build.addRow(rowLength[iRow],column+start,element+start, rowLower[iRow],rowUpper[iRow]); } else { // As we have to add one by one let's get from saveBuild CoinModelLink triple=saveBuild.firstInRow(iRow); while (triple.column()>=0) { int iColumn = triple.column(); if (iColumn*2<numberColumns) { // just value as normal build(iRow,triple.column(),triple.value()); } else { // create as string char temp[100]; sprintf(temp,"%g + (1.5*%g*multiplier)",triple.value(), triple.value()); build(iRow,iColumn,temp); } triple=saveBuild.next(triple); } // but remember to do rhs build.setRowLower(iRow,rowLower[iRow]); build.setRowUpper(iRow,rowUpper[iRow]); } } // If small switch on error printing if (numberColumns<50) build.setLogLevel(1); // should fail as we never set multiplier OSIUNITTEST_ASSERT_ERROR(model2.loadFromCoinModel(build) != 0, {}, "cbc", "build model with missing multipliers"); build.associateElement("multiplier",0.0); OSIUNITTEST_ASSERT_ERROR(model2.loadFromCoinModel(build) == 0, {}, "cbc", "build model"); model2.initialSolve(); // It then loops with multiplier going from 0.0 to 2.0 in increments of 0.1 for (double multiplier=0.0;multiplier<2.0;multiplier+= 0.1) { build.associateElement("multiplier",multiplier); OSIUNITTEST_ASSERT_ERROR(model2.loadFromCoinModel(build,true) == 0, {}, "cbc", "build model with increasing multiplier"); model2.resolve(); } } // branch and bound { OsiCbcSolverInterface m; std::string fn = mpsDir+"p0033"; m.readMps(fn.c_str(),"mps"); m.initialSolve(); //m.messageHandler()->setLogLevel(0); m.getModelPtr()->messageHandler()->setLogLevel(0); m.branchAndBound(); } // branch and bound using CbcModel!!!!!!! { OsiCbcSolverInterface mm; OsiCbcSolverInterface m(&mm); std::string fn = mpsDir+"p0033"; m.readMps(fn.c_str(),"mps"); m.initialSolve(); m.branchAndBound(); } // Do common solverInterface testing { OsiCbcSolverInterface m; OsiSolverInterfaceCommonUnitTest(&m, mpsDir,netlibDir); } { OsiCbcSolverInterface mm; OsiCbcSolverInterface m(&mm); OsiSolverInterfaceCommonUnitTest(&m, mpsDir,netlibDir); } }
int main( int argc, char **argv ) { if ( argc < 2 ) { printf("Invalid number of parameters!\n"); exit( EXIT_FAILURE ); } char problemName[ 256 ]; getFileName( problemName, argv[1] ); clock_t start = clock(); OsiClpSolverInterface *realSolver = new OsiClpSolverInterface(); realSolver->getModelPtr()->setPerturbation(50); /* makes CLP faster for hard instances */ OsiSolverInterface *solver = (OsiSolverInterface*) realSolver; parseParameters( argc, argv ); readLP( solver, argv[1] ); FILE *log = NULL; if(!output.empty()) { log = fopen(output.c_str(), "a"); if(!log) { printf("Could not open the file!\n"); exit(EXIT_FAILURE); } } const int numCols = solver->getNumCols(), numRows = solver->getNumRows(); int pass = 0, newCuts = 0, totalCuts = 0; double pTime, opt, cgTime; CGraph *cgraph = NULL; if(sepMethod == Npsep) cgraph = build_cgraph_osi( solver ); if(!optFile.empty()) { getOptimals(); if(optimals.find(problemName) == optimals.end()) { fprintf(stderr, "ERROR: optimal value not found!\n"); exit(EXIT_FAILURE); } opt = optimals[problemName]; } solver->initialSolve(); if (!solver->isProvenOptimal()) { if (solver->isAbandoned()) { fprintf( stderr, "LP solver abandoned due to numerical dificulties.\n" ); exit( EXIT_FAILURE ); } if (solver->isProvenPrimalInfeasible()) { fprintf( stderr, "LP solver says PRIMAL INFEASIBLE.\n" ); exit( EXIT_FAILURE ); } if (solver->isProvenDualInfeasible()) { fprintf( stderr, "LP solver says DUAL INFEASIBLE.\n" ); exit( EXIT_FAILURE ); } if (solver->isPrimalObjectiveLimitReached()) { fprintf( stderr, "LP solver says isPrimalObjectiveLimitReached.\n" ); exit( EXIT_FAILURE ); } if (solver->isDualObjectiveLimitReached()) { fprintf( stderr, "LP solver says isDualObjectiveLimitReached.\n" ); exit( EXIT_FAILURE ); } if (solver->isIterationLimitReached()) { fprintf( stderr, "LP solver says isIterationLimitReached.\n" ); exit( EXIT_FAILURE ); } fprintf( stderr, "ERROR: Could not solve LP relaxation to optimality. Checking status...\n" ); exit( EXIT_FAILURE ); } double initialBound = solver->getObjValue(); printf("%.2lf %d %d %.7lf", ((double)(clock()-start)) / ((double)CLOCKS_PER_SEC), pass, 0, solver->getObjValue()); if(!optFile.empty()) { printf(" %.7lf %.7lf", opt, abs_mip_gap(solver->getObjValue(), opt)); } printf("\n"); do { clock_t startSep = clock(); newCuts = 0; switch (sepMethod) { case Npsep: { CglEClique cliqueGen; OsiCuts cuts; CglTreeInfo info; info.level = 0; info.pass = 1; vector<string> varNames = getVarNames(solver->getColNames(), numCols); cliqueGen.parseParameters( argc, (const char**)argv ); cliqueGen.setCGraph( cgraph ); cliqueGen.setGenOddHoles( true ); //allow (or not) inserting odd hole cuts cliqueGen.colNames = &varNames; cliqueGen.generateCuts( *solver, cuts, info ); newCuts = cuts.sizeCuts(); solver->applyCuts( cuts ); } break; case CglSepM: { CglClique cliqueGen; OsiCuts cuts; CglTreeInfo info; info.level = 0; info.pass = 1; cliqueGen.setMinViolation( MIN_VIOLATION ); cliqueGen.setStarCliqueReport(false); cliqueGen.setRowCliqueReport(false); cliqueGen.generateCuts( *solver, cuts, info ); newCuts = cuts.sizeCuts(); solver->applyCuts( cuts ); } break; Default: { fprintf( stderr, "Separation Method does not recognized!\n" ); exit( EXIT_FAILURE ); } } pTime = ((double)(clock()-start)) / ((double)CLOCKS_PER_SEC); if(pTime > MAX_TIME) break; totalCuts += newCuts; ++pass; if (newCuts) { solver->resolve(); if (!solver->isProvenOptimal()) { if (solver->isAbandoned()) { fprintf( stderr, "LP solver abandoned due to numerical dificulties.\n" ); exit( EXIT_FAILURE ); } if (solver->isProvenPrimalInfeasible()) { fprintf( stderr, "LP solver says PRIMAL INFEASIBLE.\n" ); exit( EXIT_FAILURE ); } if (solver->isProvenDualInfeasible()) { fprintf( stderr, "LP solver says DUAL INFEASIBLE.\n" ); exit( EXIT_FAILURE ); } if (solver->isPrimalObjectiveLimitReached()) { fprintf( stderr, "LP solver says isPrimalObjectiveLimitReached.\n" ); exit( EXIT_FAILURE ); } if (solver->isDualObjectiveLimitReached()) { fprintf( stderr, "LP solver says isDualObjectiveLimitReached.\n" ); exit( EXIT_FAILURE ); } if (solver->isIterationLimitReached()) { fprintf( stderr, "LP solver says isIterationLimitReached.\n" ); exit( EXIT_FAILURE ); } fprintf( stderr, "ERROR: Could not solve LP relaxation. Exiting.\n" ); exit( EXIT_FAILURE ); } pTime = ((double)(clock()-start)) / ((double)CLOCKS_PER_SEC); if(pTime > MAX_TIME) break; double sepTime = ((double)(clock()-startSep)) / ((double)CLOCKS_PER_SEC); printf("%.2lf %d %d %.7lf", sepTime, pass, newCuts, solver->getObjValue()); if(!optFile.empty()) printf(" %.7lf %.7lf", opt, abs_mip_gap(solver->getObjValue(), opt)); printf("\n"); } } while ( (newCuts>0) && (pass<MAX_PASSES) ) ; if(log) { double totalTime = ((double)(clock()-start)) / ((double)CLOCKS_PER_SEC); fprintf(log, "%s %.2lf %d %d %.7lf", problemName, totalTime, pass - 1, totalCuts, solver->getObjValue()); if(!optFile.empty()) fprintf(log, " %.7lf", abs_mip_gap(solver->getObjValue(), opt)); fprintf(log, "\n"); } if(cgraph) cgraph_free( &cgraph ); delete realSolver; return EXIT_SUCCESS; }
//------------------------------------------------------------------- // Generate Stored cuts //------------------------------------------------------------------- void CglStored::generateCuts(const OsiSolverInterface & si, OsiCuts & cs, const CglTreeInfo /*info*/) const { // Get basic problem information const double * solution = si.getColSolution(); int numberRowCuts = cuts_.sizeRowCuts(); for (int i=0;i<numberRowCuts;i++) { const OsiRowCut * rowCutPointer = cuts_.rowCutPtr(i); double violation = rowCutPointer->violated(solution); if (violation>=requiredViolation_) cs.insert(*rowCutPointer); } if (probingInfo_) { int number01 = probingInfo_->numberIntegers(); const cliqueEntry * entry = probingInfo_->fixEntries(); const int * toZero = probingInfo_->toZero(); const int * toOne = probingInfo_->toOne(); const int * integerVariable = probingInfo_->integerVariable(); const double * lower = si.getColLower(); const double * upper = si.getColUpper(); OsiRowCut cut; int column[2]; double element[2]; for (int i=0;i<number01;i++) { int iColumn=integerVariable[i]; if (upper[iColumn]==lower[iColumn]) continue; double value1 = solution[iColumn]; for (int j=toZero[i];j<toOne[i];j++) { int jColumn=sequenceInCliqueEntry(entry[j]); if (jColumn<number01) { jColumn=integerVariable[jColumn]; assert (jColumn>=0); double value2 = solution[jColumn]; if (oneFixesInCliqueEntry(entry[j])) { double violation = 1.0-value1-value2; if (violation>requiredViolation_) { //printf("XXX can do %d + %d >=1\n",iColumn,jColumn); cut.setLb(1.0); cut.setUb(COIN_DBL_MAX); column[0]=iColumn; element[0]=1.0; column[1]=jColumn; element[1]= 1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } else { double violation = value2-value1; if (violation>requiredViolation_) { //printf("XXX can do %d >= %d\n",iColumn,jColumn); cut.setLb(0.0); cut.setUb(COIN_DBL_MAX); column[0]=iColumn; element[0]=1.0; column[1]=jColumn; element[1]= -1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } } else { jColumn -= number01; // not 0-1 double value2 = solution[jColumn]; double lowerValue = lower[jColumn]; double upperValue = upper[jColumn]; if (oneFixesInCliqueEntry(entry[j])) { double violation = upperValue-value1*(upperValue-lowerValue)-value2; if (violation>requiredViolation_) { //printf("XXX can do %g*%d + %d >=%g\n",(upperValue-lowerValue),iColumn,jColumn,upperValue); cut.setLb(upperValue); cut.setUb(COIN_DBL_MAX); column[0]=iColumn; element[0]=upperValue-lowerValue; column[1]=jColumn; element[1]= 1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } else { double violation = value2-value1*(upperValue-lowerValue)-lowerValue; if (violation>requiredViolation_) { //printf("XXX can do %g*%d >= %d -%g\n",(upperValue-lowerValue),iColumn,jColumn,lowerValue); cut.setLb(-lowerValue); cut.setUb(COIN_DBL_MAX); column[0]=iColumn; element[0]=upperValue-lowerValue; column[1]=jColumn; element[1]= -1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } } } for (int j=toOne[i];j<toZero[i+1];j++) { int jColumn=sequenceInCliqueEntry(entry[j]); if (jColumn<number01) { jColumn=integerVariable[jColumn]; assert (jColumn>=0); double value2 = solution[jColumn]; if (oneFixesInCliqueEntry(entry[j])) { double violation = value1-value2; if (violation>requiredViolation_) { //printf("XXX can do %d <= %d\n",iColumn,jColumn); cut.setLb(-COIN_DBL_MAX); cut.setUb(0.0); column[0]=iColumn; element[0]=1.0; column[1]=jColumn; element[1]= -1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } else { double violation = value1+value2-1.0; if (violation>requiredViolation_) { //printf("XXX can do %d + %d <=1\n",iColumn,jColumn); cut.setLb(-COIN_DBL_MAX); cut.setUb(1.0); column[0]=iColumn; element[0]=1.0; column[1]=jColumn; element[1]= 1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } } else { jColumn -= number01; // not 0-1 double value2 = solution[jColumn]; double lowerValue = lower[jColumn]; double upperValue = upper[jColumn]; if (oneFixesInCliqueEntry(entry[j])) { double violation = lowerValue +(upperValue-lowerValue)*value1-value2; if (violation>requiredViolation_) { //printf("XXX can do %g*%d <= %d -%g\n",(upperValue-lowerValue),iColumn,jColumn,lowerValue); cut.setLb(-COIN_DBL_MAX); cut.setUb(-lowerValue); column[0]=iColumn; element[0]=upperValue-lowerValue; column[1]=jColumn; element[1]= -1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } else { double violation = (upperValue-lowerValue)*value1+value2-upperValue; if (violation>requiredViolation_) { //printf("XXX can do %g*%d + %d <=%g\n",(upperValue-lowerValue),iColumn,jColumn,upperValue); cut.setLb(-COIN_DBL_MAX); cut.setUb(upperValue); column[0]=iColumn; element[0]=upperValue-lowerValue; column[1]=jColumn; element[1]= 1.0; cut.setEffectiveness(violation); cut.setRow(2,column,element,false); cs.insert(cut); } } } } } } }
/** Get an inner-approximation constraint obtained by drawing a chord linking the two given points x and x2. * This only applies to nonlinear constraints featuring univariate functions (f(x) <= y).**/ bool HeuristicInnerApproximation::getMyInnerApproximation(Bonmin::OsiTMINLPInterface &si, OsiCuts &cs, int ind, const double * x, const double * x2) { int n, m, nnz_jac_g, nnz_h_lag; Ipopt::TNLP::IndexStyleEnum index_style; Bonmin::TMINLP2TNLP * problem = si.problem(); problem->get_nlp_info(n, m, nnz_jac_g, nnz_h_lag, index_style); double infty = si.getInfinity(); CoinPackedVector cut; double lb = -infty; double ub = 0; double g = 0; double g2 = 0; double diff = 0; double a = 0; problem->eval_gi(n, x, 1, ind, g); problem->eval_gi(n, x2, 1, ind, g2); Bonmin::vector<int> jCol(n); int nnz; problem->eval_grad_gi(n, x2, 0, ind, nnz, jCol(), NULL); Bonmin::vector<double> jValues(nnz); problem->eval_grad_gi(n, x2, 0, ind, nnz, NULL, jValues()); bool add = false; //printf("const %i nnz %i\n", ind, nnz); for (int i = 0; i < nnz; i++) { const int &colIdx = jCol[i]; if(index_style == Ipopt::TNLP::FORTRAN_STYLE) jCol[i]--; diff = x[colIdx] - x2[colIdx]; if (fabs(diff) >= 1e-8) { a = (g - g2) / diff; cut.insert(colIdx, a); ub = (a * x[colIdx] - g) - fabs(a * x[colIdx] - g)*1e-6; //printf("const %i col %i p[col] %g pp[col] %g g %g g2 %g diff %g\n",ind, colIdx, x[colIdx], x2[colIdx], g, g2, diff); add = true; } else { cut.insert(colIdx, jValues[i]); //printf("const %i col %i val %g\n",ind, colIdx, jValues[i]); } } if (add) { OsiRowCut newCut; newCut.setGloballyValidAsInteger(1); newCut.setLb(lb); //********* Perspective Extension ********// int binary_id = 0; // index corresponding to the binary variable activating the corresponding constraint const int* ids = problem->get_const_xtra_id(); // vector of indices corresponding to the binary variable activating the corresponding constraint // Get the index of the corresponding indicator binary variable binary_id = (ids == NULL) ? -1 : ids[ind]; if(binary_id>0) {// If this hyperplane is a linearization of a disjunctive constraint, we link its righthand side to the corresponding indicator binary variable cut.insert(binary_id, -ub); // ∂x ≤ ub => ∂x - ub*z ≤ 0 newCut.setUb(0); } else newCut.setUb(ub); //********* Perspective Extension ********// newCut.setRow(cut); cs.insert(newCut); //newCut.print(); return true; } return false; }
//----------------------------------------------------------------------------- // Generate Lift-and-Project cuts //------------------------------------------------------------------- void CglLiftAndProject::generateCuts(const OsiSolverInterface& si, OsiCuts& cs, const CglTreeInfo /*info*/) { // Assumes the mixed 0-1 problem // // min {cx: <Atilde,x> >= btilde} // // is in canonical form with all bounds, // including x_t>=0, -x_t>=-1 for x_t binary, // explicitly stated in the constraint matrix. // See ~/COIN/Examples/Cgl2/cgl2.cpp // for a general purpose "convert" function. // Reference [BCC]: Balas, Ceria, and Corneujols, // "A lift-and-project cutting plane algorithm // for mixed 0-1 program", Math Prog 58, (1993) // 295-324. // This implementation uses Normalization 1. // Given canonical problem and // the lp-relaxation solution, x, // the LAP cut generator attempts to construct // a cut for every x_j such that 0<x_j<1 // [BCC:307] // x_j is the strictly fractional binary variable // the cut is generated from int j = 0; // Get basic problem information // let Atilde be an m by n matrix const int m = si.getNumRows(); const int n = si.getNumCols(); const double * x = si.getColSolution(); // Remember - Atildes may have gaps.. const CoinPackedMatrix * Atilde = si.getMatrixByRow(); const double * AtildeElements = Atilde->getElements(); const int * AtildeIndices = Atilde->getIndices(); const CoinBigIndex * AtildeStarts = Atilde->getVectorStarts(); const int * AtildeLengths = Atilde->getVectorLengths(); const int AtildeFullSize = AtildeStarts[m]; const double * btilde = si.getRowLower(); // Set up memory for system (10) [BCC:307] // (the problem over the norm intersected // with the polar cone) // // min <<x^T,Atilde^T>,u> + x_ju_0 // s.t. // <B,w> = (0,...,0,beta_,beta)^T // w is nonneg for all but the // last two entries, which are free. // where // w = (u,v,v_0,u_0)in BCC notation // u and v are m-vectors; u,v >=0 // v_0 and u_0 are free-scalars, and // // B = Atilde^T -Atilde^T -e_j e_j // btilde^T e_0^T 0 0 // e_0^T btilde^T 1 0 // ^T indicates Transpose // e_0 is a (AtildeNCols x 1) vector of all zeros // e_j is e_0 with a 1 in the jth position // Storing B in column order. B is a (n+2 x 2m+2) matrix // But need to allow for possible gaps in Atilde. // At each iteration, only need to change 2 cols and objfunc // Sane design of OsiSolverInterface does not permit mucking // with matrix. // Because we must delete and add cols to alter matrix, // and we can only add columns on the end of the matrix // put the v_0 and u_0 columns on the end. // rather than as described in [BCC] // Initially allocating B with space for v_0 and u_O cols // but not populating, for efficiency. // B without u_0 and v_0 is a (n+2 x 2m) size matrix. int twoM = 2*m; int BNumRows = n+2; int BNumCols = twoM+2; int BFullSize = 2*AtildeFullSize+twoM+3; double * BElements = new double[BFullSize]; int * BIndices = new int[BFullSize]; CoinBigIndex * BStarts = new CoinBigIndex [BNumCols+1]; int * BLengths = new int[BNumCols]; int i, ij, k=0; int nPlus1=n+1; int offset = AtildeStarts[m]+m; for (i=0; i<m; i++){ for (ij=AtildeStarts[i];ij<AtildeStarts[i]+AtildeLengths[i];ij++){ BElements[k]=AtildeElements[ij]; BElements[k+offset]=-AtildeElements[ij]; BIndices[k]= AtildeIndices[ij]; BIndices[k+offset]= AtildeIndices[ij]; k++; } BElements[k]=btilde[i]; BElements[k+offset]=btilde[i]; BIndices[k]=n; BIndices[k+offset]=nPlus1; BStarts[i]= AtildeStarts[i]+i; BStarts[i+m]=offset+BStarts[i];// = AtildeStarts[m]+m+AtildeStarts[i]+i BLengths[i]= AtildeLengths[i]+1; BLengths[i+m]= AtildeLengths[i]+1; k++; } BStarts[twoM]=BStarts[twoM-1]+BLengths[twoM-1]; // Cols that will be deleted each iteration int BNumColsLessOne=BNumCols-1; int BNumColsLessTwo=BNumCols-2; const int delCols[2] = {BNumColsLessOne, BNumColsLessTwo}; // Set lower bound on u and v // u_0, v_0 will be reset as free const double solverINFINITY = si.getInfinity(); double * BColLowers = new double[BNumCols]; double * BColUppers = new double[BNumCols]; CoinFillN(BColLowers,BNumCols,0.0); CoinFillN(BColUppers,BNumCols,solverINFINITY); // Set row lowers and uppers. // The rhs is zero, for but the last two rows. // For these the rhs is beta_ double * BRowLowers = new double[BNumRows]; double * BRowUppers = new double[BNumRows]; CoinFillN(BRowLowers,BNumRows,0.0); CoinFillN(BRowUppers,BNumRows,0.0); BRowLowers[BNumRows-2]=beta_; BRowUppers[BNumRows-2]=beta_; BRowLowers[BNumRows-1]=beta_; BRowUppers[BNumRows-1]=beta_; // Calculate base objective <<x^T,Atilde^T>,u> // Note: at each iteration coefficient u_0 // changes to <x^T,e_j> // w=(u,v,beta,v_0,u_0) size 2m+3 // So, BOjective[2m+2]=x[j] double * BObjective= new double[BNumCols]; double * Atildex = new double[m]; CoinFillN(BObjective,BNumCols,0.0); Atilde->times(x,Atildex); // Atildex is size m, x is size n CoinDisjointCopyN(Atildex,m,BObjective); // Number of cols and size of Elements vector // in B without the v_0 and u_0 cols int BFullSizeLessThree = BFullSize-3; // Load B matrix into a column orders CoinPackedMatrix CoinPackedMatrix * BMatrix = new CoinPackedMatrix(true, BNumRows, BNumColsLessTwo, BFullSizeLessThree, BElements,BIndices, BStarts,BLengths); // Assign problem into a solver interface // Note: coneSi will cleanup the memory itself OsiSolverInterface * coneSi = si.clone(false); coneSi->assignProblem (BMatrix, BColLowers, BColUppers, BObjective, BRowLowers, BRowUppers); // Problem sense should default to "min" by default, // but just to be virtuous... coneSi->setObjSense(1.0); // The plot outline from here on down: // coneSi has been assigned B without the u_0 and v_0 columns // Calculate base objective <<x^T,Atilde^T>,u> // bool haveWarmStart = false; // For (j=0; j<n, j++) // if (!isBinary(x_j) || x_j<=0 || x_j>=1) continue; // // IMPROVEME: if(haveWarmStart) check if j attractive // add {-e_j,0,-1} matrix column for v_0 // add {e_j,0,0} matrix column for u_0 // objective coefficient for u_0 is x_j // if (haveWarmStart) // set warmstart info // solve min{objw:Bw=0; w>=0,except v_0, u_0 free} // if (bounded) // get warmstart info // haveWarmStart=true; // ustar = optimal u solution // ustar_0 = optimal u_0 solution // alpha^T= <ustar^T,Atilde> -ustar_0e_j^T // (double check <alpha^T,x> >= beta_ should be violated) // add <alpha^T,x> >= beta_ to cutset // endif // delete column for u_0 // this deletes all column info. // delete column for v_0 // endFor // clean up memory // return 0; int * nVectorIndices = new int[n]; CoinIotaN(nVectorIndices, n, 0); bool haveWarmStart = false; bool equalObj1, equalObj2; CoinRelFltEq eq; double v_0Elements[2] = {-1,1}; double u_0Elements[1] = {1}; CoinWarmStart * warmStart = 0; double * ustar = new double[m]; CoinFillN(ustar, m, 0.0); double* alpha = new double[n]; CoinFillN(alpha, n, 0.0); for (j=0;j<n;j++){ if (!si.isBinary(j)) continue; // Better to ask coneSi? No! // coneSi has no binInfo. equalObj1=eq(x[j],0); equalObj2=eq(x[j],1); if (equalObj1 || equalObj2) continue; // IMPROVEME: if (haveWarmStart) check if j attractive; // AskLL:wanted to declare u_0 and v_0 packedVec outside loop // and setIndices, but didn't see a method to do that(?) // (Could "insert". Seems inefficient) int v_0Indices[2]={j,nPlus1}; int u_0Indices[1]={j}; // CoinPackedVector v_0(2,v_0Indices,v_0Elements,false); CoinPackedVector u_0(1,u_0Indices,u_0Elements,false); #if CGL_DEBUG const CoinPackedMatrix *see1 = coneSi->getMatrixByRow(); #endif coneSi->addCol(v_0,-solverINFINITY,solverINFINITY,0); coneSi->addCol(u_0,-solverINFINITY,solverINFINITY,x[j]); if(haveWarmStart) { coneSi->setWarmStart(warmStart); coneSi->resolve(); } else { #if CGL_DEBUG const CoinPackedMatrix *see2 = coneSi->getMatrixByRow(); #endif coneSi->initialSolve(); } if(coneSi->isProvenOptimal()){ warmStart = coneSi->getWarmStart(); haveWarmStart=true; const double * wstar = coneSi->getColSolution(); CoinDisjointCopyN(wstar, m, ustar); Atilde->transposeTimes(ustar,alpha); alpha[j]+=wstar[BNumCols-1]; #if debug int p; double sum; for(p=0;p<n;p++)sum+=alpha[p]*x[p]; if (sum<=beta_){ throw CoinError("Cut not violated", "cutGeneration", "CglLiftAndProject"); } #endif // add <alpha^T,x> >= beta_ to cutset OsiRowCut rc; rc.setRow(n,nVectorIndices,alpha); rc.setLb(beta_); rc.setUb(solverINFINITY); cs.insert(rc); } // delete col for u_o and v_0 coneSi->deleteCols(2,delCols); // clean up memory } // clean up delete [] alpha; delete [] ustar; delete [] nVectorIndices; // BMatrix, BColLowers,BColUppers, BObjective, BRowLowers, BRowUppers // are all freed by OsiSolverInterface destructor (?) delete [] BLengths; delete [] BStarts; delete [] BIndices; delete [] BElements; }
int main(int argc, char **argv) { char *f_name_lp, *last_dot_pos, f_name[256], *f_name_pos; int i, ncol; if((argc < 2) || (argc > 2)) { printf("### ERROR: main(): Usage: One of the following\ncgl_data_test input_file_name.mps\ncgl_data_test input_file_name.lp\n"); exit(1); } f_name_lp = strdup(argv[1]); f_name_pos = strrchr(f_name_lp, '/'); if(f_name_pos != NULL) { strcpy(f_name, &(f_name_pos[1])); } else { strcpy(f_name, f_name_lp); } last_dot_pos = strrchr(f_name, '.'); if(last_dot_pos != NULL) { last_dot_pos = '\0'; } OsiClpSolverInterface *clp = new OsiClpSolverInterface; clp->messageHandler()->setLogLevel(0); if(strcmp(&(f_name_lp[strlen(f_name_lp)-3]), ".lp") == 0) { clp->readLp(f_name_lp); } else { if(strcmp(&(f_name_lp[strlen(f_name_lp)-4]), ".mps") == 0) { clp->readMps(f_name_lp); } else { printf("### ERROR: unrecognized file type\n"); exit(1); } } ncol = clp->getNumCols(); clp->initialSolve(); printf("LP value: %12.2f\n", clp->getObjValue()); OsiCuts cuts; // Define parameters for CglRedSplit generator CglParam cpar; cpar.setMAX_SUPPORT(ncol+1); CglRedSplitParam rspar(cpar); // Create a cut generator with the given parameters CglRedSplit cutGen(rspar); char *colType = new char[ncol]; for(i=0; i<ncol; i++) { if(clp->isContinuous(i)) { colType[i] = 'C'; } else { colType[i] = 'I'; } } int round, max_rounds = 10; for(round=0; round<max_rounds; round++) { cutGen.generateCuts(*clp, cuts); int ncuts = cuts.sizeRowCuts(); const OsiRowCut **newRowCuts = new const OsiRowCut * [ncuts]; for(i=0; i<ncuts; i++) { newRowCuts[i] = &cuts.rowCut(i); } clp->applyRowCuts(ncuts, newRowCuts); delete[] newRowCuts; printf("round %4d: %4d generated cuts new objective value: %12.2f\n", round, ncuts, clp->getObjValue()); clp->resolve(); if(clp->isAbandoned()) { printf("###ERROR: Numerical difficulties in Solver\n"); exit(1); } if(clp->isProvenPrimalInfeasible()) { printf("### WARNING: Problem is infeasible\n"); exit(1); } } delete clp; free(f_name_lp); delete[] colType; return(0); }
void OsiSpxSolverInterfaceUnitTest( const std::string & mpsDir, const std::string & netlibDir ) { // Test default constructor { OsiSpxSolverInterface m; OSIUNITTEST_ASSERT_ERROR(m.soplex_ != NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.getNumCols() == 0, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.rowsense_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.rhs_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.rowrange_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.colsol_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.rowsol_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.matrixByRow_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.matrixByCol_ == NULL, {}, "SoPlex", "default constructor"); OSIUNITTEST_ASSERT_ERROR(m.getApplicationData() == NULL, {}, "SoPlex", "default constructor"); int i=2346; m.setApplicationData(&i); OSIUNITTEST_ASSERT_ERROR(*((int *)(m.getApplicationData())) == i, {}, "SoPlex", "set application data"); } { CoinRelFltEq eq; OsiSpxSolverInterface m; std::string fn = mpsDir+"exmip1"; m.readMps(fn.c_str(),"mps"); // int ad = 13579; // m.setApplicationData(&ad); // OSIUNITTEST_ASSERT_ERROR(*((int *)(m.getApplicationData())) == ad, {}, "SoPlex", "set application data"); { const CoinPackedMatrix * colCopy = m.getMatrixByCol(); OSIUNITTEST_ASSERT_ERROR(colCopy->getNumCols() == 8, {}, "SoPlex", "exmip1 matrix"); OSIUNITTEST_ASSERT_ERROR(colCopy->getMajorDim() == 8, {}, "SoPlex", "exmip1 matrix"); OSIUNITTEST_ASSERT_ERROR(colCopy->getNumRows() == 5, {}, "SoPlex", "exmip1 matrix"); OSIUNITTEST_ASSERT_ERROR(colCopy->getMinorDim() == 5, {}, "SoPlex", "exmip1 matrix"); OSIUNITTEST_ASSERT_ERROR(colCopy->getVectorLengths()[7] == 2, {}, "SoPlex", "exmip1 matrix"); CoinPackedMatrix revColCopy; revColCopy.reverseOrderedCopyOf(*colCopy); CoinPackedMatrix rev2ColCopy; rev2ColCopy.reverseOrderedCopyOf(revColCopy); OSIUNITTEST_ASSERT_ERROR(rev2ColCopy.getNumCols() == 8, {}, "SoPlex", "twice reverse matrix copy"); OSIUNITTEST_ASSERT_ERROR(rev2ColCopy.getMajorDim() == 8, {}, "SoPlex", "twice reverse matrix copy"); OSIUNITTEST_ASSERT_ERROR(rev2ColCopy.getNumRows() == 5, {}, "SoPlex", "twice reverse matrix copy"); OSIUNITTEST_ASSERT_ERROR(rev2ColCopy.getMinorDim() == 5, {}, "SoPlex", "twice reverse matrix copy"); OSIUNITTEST_ASSERT_ERROR(rev2ColCopy.getVectorLengths()[7] == 2, {}, "SoPlex", "twice reverse matrix copy"); } // Test copy constructor and assignment operator { OsiSpxSolverInterface lhs; { OsiSpxSolverInterface im(m); OsiSpxSolverInterface imC1(im); OsiSpxSolverInterface imC2(im); lhs = imC2; } // Test that lhs has correct values even though rhs has gone out of scope OSIUNITTEST_ASSERT_ERROR(lhs.getNumCols() == m.getNumCols(), {}, "SoPlex", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(lhs.getNumRows() == m.getNumRows(), {}, "SoPlex", "copy constructor"); } // Test clone { OsiSpxSolverInterface soplexSi(m); OsiSolverInterface * siPtr = &soplexSi; OsiSolverInterface * siClone = siPtr->clone(); OsiSpxSolverInterface * soplexClone = dynamic_cast<OsiSpxSolverInterface*>(siClone); OSIUNITTEST_ASSERT_ERROR(soplexClone != NULL, {}, "SoPlex", "clone"); OSIUNITTEST_ASSERT_ERROR(soplexClone->getNumRows() == soplexSi.getNumRows(), {}, "SoPlex", "clone"); OSIUNITTEST_ASSERT_ERROR(soplexClone->getNumCols() == m.getNumCols(), {}, "SoPlex", "clone"); delete siClone; } // test infinity { OsiSpxSolverInterface si; OSIUNITTEST_ASSERT_ERROR(si.getInfinity() == soplex::infinity, {}, "SoPlex", "value for infinity"); } { OsiSpxSolverInterface soplexSi(m); int nc = soplexSi.getNumCols(); int nr = soplexSi.getNumRows(); const double * cl = soplexSi.getColLower(); const double * cu = soplexSi.getColUpper(); const double * rl = soplexSi.getRowLower(); const double * ru = soplexSi.getRowUpper(); OSIUNITTEST_ASSERT_ERROR(nc == 8, return, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(nr == 5, return, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cl[0],2.5), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cl[1],0.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cu[1],4.1), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(cu[2],1.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(rl[0],2.5), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(rl[4],3.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(ru[1],2.1), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(ru[4],15.), {}, "SoPlex", "read and copy exmip1"); double newCs[8] = {1., 2., 3., 4., 5., 6., 7., 8.}; soplexSi.setColSolution(newCs); const double * cs = soplexSi.getColSolution(); OSIUNITTEST_ASSERT_ERROR(eq(cs[0],1.0), {}, "SoPlex", "set col solution"); OSIUNITTEST_ASSERT_ERROR(eq(cs[7],8.0), {}, "SoPlex", "set col solution"); { OsiSpxSolverInterface solnSi(soplexSi); const double * cs = solnSi.getColSolution(); OSIUNITTEST_ASSERT_ERROR(eq(cs[0],1.0), {}, "SoPlex", "set col solution and copy"); OSIUNITTEST_ASSERT_ERROR(eq(cs[7],8.0), {}, "SoPlex", "set col solution and copy"); } OSIUNITTEST_ASSERT_ERROR(!eq(cl[3],1.2345), {}, "SoPlex", "set col lower"); soplexSi.setColLower( 3, 1.2345 ); OSIUNITTEST_ASSERT_ERROR( eq(cl[3],1.2345), {}, "SoPlex", "set col lower"); OSIUNITTEST_ASSERT_ERROR(!eq(cu[4],10.2345), {}, "SoPlex", "set col upper"); soplexSi.setColUpper( 4, 10.2345 ); OSIUNITTEST_ASSERT_ERROR( eq(cu[4],10.2345), {}, "SoPlex", "set col upper"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[0], 1.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[1], 0.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[2], 0.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[3], 0.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[4], 2.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[5], 0.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[6], 0.0), {}, "SoPlex", "read and copy exmip1"); OSIUNITTEST_ASSERT_ERROR(eq(soplexSi.getObjCoefficients()[7],-1.0), {}, "SoPlex", "read and copy exmip1"); } // Test getMatrixByRow method { const OsiSpxSolverInterface si(m); const CoinPackedMatrix * smP = si.getMatrixByRow(); OSIUNITTEST_ASSERT_ERROR(smP->getMajorDim() == 5, return, "SoPlex", "getMatrixByRow: major dim"); OSIUNITTEST_ASSERT_ERROR(smP->getNumElements() == 14, return, "SoPlex", "getMatrixByRow: num elements"); CoinRelFltEq eq; const double * ev = smP->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[0], 3.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[1], 1.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[2], -2.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[3], -1.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[4], -1.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[5], 2.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[6], 1.1), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[7], 1.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[8], 1.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[9], 2.8), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10], -1.2), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11], 5.6), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12], 1.0), {}, "SoPlex", "getMatrixByRow: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "SoPlex", "getMatrixByRow: elements"); const int * mi = smP->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "SoPlex", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 5, {}, "SoPlex", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 7, {}, "SoPlex", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 9, {}, "SoPlex", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 11, {}, "SoPlex", "getMatrixByRow: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 14, {}, "SoPlex", "getMatrixByRow: vector starts"); const int * ei = smP->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 1, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 3, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 4, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 7, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 1, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 2, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 2, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 5, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 3, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 6, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 0, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 4, {}, "SoPlex", "getMatrixByRow: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 7, {}, "SoPlex", "getMatrixByRow: indices"); } //-------------- // Test rowsense, rhs, rowrange, getMatrixByRow { OsiSpxSolverInterface lhs; { OsiSpxSolverInterface siC1(m); OSIUNITTEST_ASSERT_WARNING(siC1.rowrange_ == NULL, {}, "SoPlex", "row range"); OSIUNITTEST_ASSERT_WARNING(siC1.rowsense_ == NULL, {}, "SoPlex", "row sense"); OSIUNITTEST_ASSERT_WARNING(siC1.rhs_ == NULL, {}, "SoPlex", "right hand side"); OSIUNITTEST_ASSERT_WARNING(siC1.matrixByRow_ == NULL, {}, "SoPlex", "matrix by row"); OSIUNITTEST_ASSERT_WARNING(siC1.colsol_ == NULL, {}, "SoPlex", "col solution"); OSIUNITTEST_ASSERT_WARNING(siC1.rowsol_ == NULL, {}, "SoPlex", "row solution"); const char * siC1rs = siC1.getRowSense(); OSIUNITTEST_ASSERT_ERROR(siC1rs[0] == 'G', {}, "SoPlex", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[1] == 'L', {}, "SoPlex", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[2] == 'E', {}, "SoPlex", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[3] == 'R', {}, "SoPlex", "row sense"); OSIUNITTEST_ASSERT_ERROR(siC1rs[4] == 'R', {}, "SoPlex", "row sense"); const double * siC1rhs = siC1.getRightHandSide(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[0],2.5), {}, "SoPlex", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[1],2.1), {}, "SoPlex", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[2],4.0), {}, "SoPlex", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[3],5.0), {}, "SoPlex", "right hand side"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[4],15.), {}, "SoPlex", "right hand side"); const double * siC1rr = siC1.getRowRange(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[0],0.0), {}, "SoPlex", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[1],0.0), {}, "SoPlex", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[2],0.0), {}, "SoPlex", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[3],5.0-1.8), {}, "SoPlex", "row range"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[4],15.0-3.0), {}, "SoPlex", "row range"); const CoinPackedMatrix * siC1mbr = siC1.getMatrixByRow(); OSIUNITTEST_ASSERT_ERROR(siC1mbr != NULL, {}, "SoPlex", "matrix by row"); OSIUNITTEST_ASSERT_ERROR(siC1mbr->getMajorDim() == 5, return, "SoPlex", "matrix by row: major dim"); OSIUNITTEST_ASSERT_ERROR(siC1mbr->getNumElements() == 14, return, "SoPlex", "matrix by row: num elements"); const double * ev = siC1mbr->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 0], 3.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 1], 1.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 2],-2.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 3],-1.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 4],-1.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 5], 2.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 6], 1.1), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 7], 1.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 8], 1.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 9], 2.8), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10],-1.2), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11], 5.6), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12], 1.0), {}, "SoPlex", "matrix by row: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "SoPlex", "matrix by row: elements"); const CoinBigIndex * mi = siC1mbr->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "SoPlex", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 5, {}, "SoPlex", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 7, {}, "SoPlex", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 9, {}, "SoPlex", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 11, {}, "SoPlex", "matrix by row: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 14, {}, "SoPlex", "matrix by row: vector starts"); const int * ei = siC1mbr->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 1, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 3, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 4, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 7, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 1, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 2, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 2, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 5, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 3, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 6, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 0, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 4, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 7, {}, "SoPlex", "matrix by row: indices"); OSIUNITTEST_ASSERT_WARNING(siC1rs == siC1.getRowSense(), {}, "SoPlex", "row sense"); OSIUNITTEST_ASSERT_WARNING(siC1rhs == siC1.getRightHandSide(), {}, "SoPlex", "right hand side"); OSIUNITTEST_ASSERT_WARNING(siC1rr == siC1.getRowRange(), {}, "SoPlex", "row range"); // Change SOPLEX Model by adding free row OsiRowCut rc; rc.setLb(-COIN_DBL_MAX); rc.setUb( COIN_DBL_MAX); OsiCuts cuts; cuts.insert(rc); siC1.applyCuts(cuts); // Since model was changed, test that cached data is now freed. OSIUNITTEST_ASSERT_ERROR(siC1.rowrange_ == NULL, {}, "SoPlex", "free cached data after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1.rowsense_ == NULL, {}, "SoPlex", "free cached data after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1.rhs_ == NULL, {}, "SoPlex", "free cached data after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1.matrixByRow_ == NULL, {}, "SoPlex", "free cached data after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1.matrixByCol_ == NULL, {}, "SoPlex", "free cached data after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1.colsol_ == NULL, {}, "SoPlex", "free cached data after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1.rowsol_ == NULL, {}, "SoPlex", "free cached data after adding row"); siC1rs = siC1.getRowSense(); OSIUNITTEST_ASSERT_ERROR(siC1rs[0] == 'G', {}, "SoPlex", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[1] == 'L', {}, "SoPlex", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[2] == 'E', {}, "SoPlex", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[3] == 'R', {}, "SoPlex", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[4] == 'R', {}, "SoPlex", "row sense after adding row"); OSIUNITTEST_ASSERT_ERROR(siC1rs[5] == 'N', {}, "SoPlex", "row sense after adding row"); siC1rhs = siC1.getRightHandSide(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[0],2.5), {}, "SoPlex", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[1],2.1), {}, "SoPlex", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[2],4.0), {}, "SoPlex", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[3],5.0), {}, "SoPlex", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[4],15.), {}, "SoPlex", "right hand side after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rhs[5],0.0), {}, "SoPlex", "right hand side after adding row"); siC1rr = siC1.getRowRange(); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[0],0.0), {}, "SoPlex", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[1],0.0), {}, "SoPlex", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[2],0.0), {}, "SoPlex", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[3],5.0-1.8), {}, "SoPlex", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[4],15.0-3.0), {}, "SoPlex", "row range after adding row"); OSIUNITTEST_ASSERT_ERROR(eq(siC1rr[5],0.0), {}, "SoPlex", "row range after adding row"); lhs = siC1; } // Test that lhs has correct values even though siC1 has gone out of scope OSIUNITTEST_ASSERT_ERROR(lhs.rowrange_ == NULL, {}, "SoPlex", "freed origin after assignment"); OSIUNITTEST_ASSERT_ERROR(lhs.rowsense_ == NULL, {}, "SoPlex", "freed origin after assignment"); OSIUNITTEST_ASSERT_ERROR(lhs.rhs_ == NULL, {}, "SoPlex", "freed origin after assignment"); OSIUNITTEST_ASSERT_ERROR(lhs.matrixByRow_ == NULL, {}, "SoPlex", "freed origin after assignment"); OSIUNITTEST_ASSERT_ERROR(lhs.matrixByCol_ == NULL, {}, "SoPlex", "freed origin after assignment"); OSIUNITTEST_ASSERT_ERROR(lhs.colsol_ == NULL, {}, "SoPlex", "freed origin after assignment"); OSIUNITTEST_ASSERT_ERROR(lhs.rowsol_ == NULL, {}, "SoPlex", "freed origin after assignment"); const char * lhsrs = lhs.getRowSense(); OSIUNITTEST_ASSERT_ERROR(lhsrs[0] == 'G', {}, "SoPlex", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[1] == 'L', {}, "SoPlex", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[2] == 'E', {}, "SoPlex", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[3] == 'R', {}, "SoPlex", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[4] == 'R', {}, "SoPlex", "row sense after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsrs[5] == 'N', {}, "SoPlex", "row sense after assignment"); const double * lhsrhs = lhs.getRightHandSide(); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[0],2.5), {}, "SoPlex", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[1],2.1), {}, "SoPlex", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[2],4.0), {}, "SoPlex", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[3],5.0), {}, "SoPlex", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[4],15.), {}, "SoPlex", "right hand side after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrhs[5],0.0), {}, "SoPlex", "right hand side after assignment"); const double *lhsrr = lhs.getRowRange(); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[0],0.0), {}, "SoPlex", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[1],0.0), {}, "SoPlex", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[2],0.0), {}, "SoPlex", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[3],5.0-1.8), {}, "SoPlex", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[4],15.0-3.0), {}, "SoPlex", "row range after assignment"); OSIUNITTEST_ASSERT_ERROR(eq(lhsrr[5],0.0), {}, "SoPlex", "row range after assignment"); const CoinPackedMatrix * lhsmbr = lhs.getMatrixByRow(); OSIUNITTEST_ASSERT_ERROR(lhsmbr != NULL, {}, "SoPlex", "matrix by row after assignment"); OSIUNITTEST_ASSERT_ERROR(lhsmbr->getMajorDim() == 6, return, "SoPlex", "matrix by row after assignment: major dim"); OSIUNITTEST_ASSERT_ERROR(lhsmbr->getNumElements() == 14, return, "SoPlex", "matrix by row after assignment: num elements"); const double * ev = lhsmbr->getElements(); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 0], 3.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 1], 1.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 2],-2.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 3],-1.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 4],-1.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 5], 2.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 6], 1.1), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 7], 1.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 8], 1.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[ 9], 2.8), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[10],-1.2), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[11], 5.6), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[12], 1.0), {}, "SoPlex", "matrix by row after assignment: elements"); OSIUNITTEST_ASSERT_ERROR(eq(ev[13], 1.9), {}, "SoPlex", "matrix by row after assignment: elements"); const CoinBigIndex * mi = lhsmbr->getVectorStarts(); OSIUNITTEST_ASSERT_ERROR(mi[0] == 0, {}, "SoPlex", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[1] == 5, {}, "SoPlex", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[2] == 7, {}, "SoPlex", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[3] == 9, {}, "SoPlex", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[4] == 11, {}, "SoPlex", "matrix by row after assignment: vector starts"); OSIUNITTEST_ASSERT_ERROR(mi[5] == 14, {}, "SoPlex", "matrix by row after assignment: vector starts"); const int * ei = lhsmbr->getIndices(); OSIUNITTEST_ASSERT_ERROR(ei[ 0] == 0, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 1] == 1, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 2] == 3, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 3] == 4, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 4] == 7, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 5] == 1, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 6] == 2, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 7] == 2, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 8] == 5, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[ 9] == 3, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[10] == 6, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[11] == 0, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[12] == 4, {}, "SoPlex", "matrix by row after assignment: indices"); OSIUNITTEST_ASSERT_ERROR(ei[13] == 7, {}, "SoPlex", "matrix by row after assignment: indices"); } //-------------- } // Do common solverInterface testing by calling the // base class testing method. { OsiSpxSolverInterface m; OsiSolverInterfaceCommonUnitTest(&m, mpsDir,netlibDir); } }
//-------------------------------------------------------------------------- // test cut debugger methods. void OsiRowCutDebuggerUnitTest(const OsiSolverInterface * baseSiP, const std::string & mpsDir) { CoinRelFltEq eq; // Test default constructor { OsiRowCutDebugger r; OSIUNITTEST_ASSERT_ERROR(r.integerVariable_ == NULL, {}, "osirowcutdebugger", "default constructor"); OSIUNITTEST_ASSERT_ERROR(r.knownSolution_ == NULL, {}, "osirowcutdebugger", "default constructor"); OSIUNITTEST_ASSERT_ERROR(r.numberColumns_ == 0, {}, "osirowcutdebugger", "default constructor"); } { // Get non trivial instance OsiSolverInterface * imP = baseSiP->clone(); std::string fn = mpsDir+"exmip1"; imP->readMps(fn.c_str(),"mps"); OSIUNITTEST_ASSERT_ERROR(imP->getNumRows() == 5, {}, "osirowcutdebugger", "read exmip1"); /* Activate the debugger. The garbled name here is deliberate; the debugger should isolate the portion of the string between '/' and '.' (in normal use, this would be the base file name, stripped of the prefix and extension). */ imP->activateRowCutDebugger("ab cd /x/ /exmip1.asc"); int i; // return debugger const OsiRowCutDebugger * debugger = imP->getRowCutDebugger(); OSIUNITTEST_ASSERT_ERROR(debugger != NULL, {}, "osirowcutdebugger", "return debugger"); OSIUNITTEST_ASSERT_ERROR(debugger->numberColumns_ == 8, {}, "osirowcutdebugger", "return debugger"); const bool type[]={0,0,1,1,0,0,0,0}; const double values[]= {2.5, 0, 1, 1, 0.5, 3, 0, 0.26315789473684253}; CoinPackedVector objCoefs(8,imP->getObjCoefficients()); bool type_ok = true; #if 0 for (i=0;i<8;i++) type_ok &= type[i] == debugger->integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "???"); #endif double objValue = objCoefs.dotProduct(values); double debuggerObjValue = objCoefs.dotProduct(debugger->knownSolution_); OSIUNITTEST_ASSERT_ERROR(eq(objValue, debuggerObjValue), {}, "osirowcutdebugger", "objective value"); OsiRowCutDebugger rhs; { OsiRowCutDebugger rC1(*debugger); OSIUNITTEST_ASSERT_ERROR(rC1.numberColumns_ == 8, {}, "osirowcutdebugger", "copy constructor"); type_ok = true; for (i=0;i<8;i++) type_ok &= type[i] == rC1.integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "copy constructor"); OSIUNITTEST_ASSERT_ERROR(eq(objValue,objCoefs.dotProduct(rC1.knownSolution_)), {}, "osirowcutdebugger", "copy constructor"); rhs = rC1; OSIUNITTEST_ASSERT_ERROR(rhs.numberColumns_ == 8, {}, "osirowcutdebugger", "assignment operator"); type_ok = true; for (i=0;i<8;i++) type_ok &= type[i] == rhs.integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "assignment operator"); OSIUNITTEST_ASSERT_ERROR(eq(objValue,objCoefs.dotProduct(rhs.knownSolution_)), {}, "osirowcutdebugger", "assignment operator"); } // Test that rhs has correct values even though lhs has gone out of scope OSIUNITTEST_ASSERT_ERROR(rhs.numberColumns_ == 8, {}, "osirowcutdebugger", "assignment operator"); type_ok = true; for (i=0;i<8;i++) type_ok &= type[i] == rhs.integerVariable_[i]; OSIUNITTEST_ASSERT_ERROR(type_ok, {}, "osirowcutdebugger", "assignment operator"); OSIUNITTEST_ASSERT_ERROR(eq(objValue,objCoefs.dotProduct(rhs.knownSolution_)), {}, "osirowcutdebugger", "assignment operator"); OsiRowCut cut[2]; const int ne = 3; int inx[ne] = { 0, 2, 3 }; double el[ne] = { 1., 1., 1. }; cut[0].setRow(ne,inx,el); cut[0].setUb(5.); el[1]=5; cut[1].setRow(ne,inx,el); cut[1].setUb(5); OsiCuts cs; cs.insert(cut[0]); cs.insert(cut[1]); OSIUNITTEST_ASSERT_ERROR(!debugger->invalidCut(cut[0]), {}, "osirowcutdebugger", "recognize (in)valid cut"); OSIUNITTEST_ASSERT_ERROR( debugger->invalidCut(cut[1]), {}, "osirowcutdebugger", "recognize (in)valid cut"); OSIUNITTEST_ASSERT_ERROR(debugger->validateCuts(cs,0,2) == 1, {}, "osirowcutdebugger", "recognize (in)valid cut"); OSIUNITTEST_ASSERT_ERROR(debugger->validateCuts(cs,0,1) == 0, {}, "osirowcutdebugger", "recognize (in)valid cut"); delete imP; } }
//-------------------------------------------------------------------------- // test the simple rounding cut generators methods. void CglSimpleRoundingUnitTest( const OsiSolverInterface * baseSiP, const std::string mpsDir ) { // Test default constructor { CglSimpleRounding cg; } // Test copy & assignment { CglSimpleRounding rhs; { CglSimpleRounding cg; CglSimpleRounding cgC(cg); rhs=cg; } } // Test gcd and gcdn { CglSimpleRounding cg; int v = cg.gcd(122,356); assert(v==2); v=cg.gcd(356,122); assert(v==2); v=cg.gcd(54,67); assert(v==1); v=cg.gcd(67,54); assert(v==1); v=cg.gcd(485,485); assert(v==485); v=cg.gcd(17*13,17*23); assert( v==17); v=cg.gcd(17*13*5,17*23); assert( v==17); v=cg.gcd(17*13*23,17*23); assert(v==17*23); int a[4] = {12, 20, 32, 400}; v= cg.gcdv(4,a); assert(v== 4); int b[4] = {782, 4692, 51, 2754}; v= cg.gcdv(4,b); assert(v== 17); int c[4] = {50, 40, 30, 10}; v= cg.gcdv(4,c); assert(v== 10); } // Test generate cuts method on exmip1.5.mps { CglSimpleRounding cg; OsiSolverInterface * siP = baseSiP->clone(); std::string fn = mpsDir+"exmip1.5.mps"; siP->readMps(fn.c_str(),""); OsiCuts cuts; cg.generateCuts(*siP,cuts); // there should be 3 cuts int nRowCuts = cuts.sizeRowCuts(); assert(nRowCuts==3); // get the last "sr"=simple rounding cut that was derived OsiRowCut srRowCut2 = cuts.rowCut(2); CoinPackedVector srRowCutPV2 = srRowCut2.row(); // this is what the last cut should look like: i.e. the "solution" const int solSize = 2; int solCols[solSize]={2,3}; double solCoefs[solSize]={5.0, 4.0}; OsiRowCut solRowCut; solRowCut.setRow(solSize,solCols,solCoefs); solRowCut.setLb(-COIN_DBL_MAX); solRowCut.setUb(2.0); // Test for equality between the derived cut and the solution cut // Note: testing two OsiRowCuts are equal invokes testing two // CoinPackedVectors are equal which invokes testing two doubles // are equal. Usually not a good idea to test that two doubles are equal, // but in this cut the "doubles" represent integer values. Also allow that // different solvers have different orderings in packed vectors, which may // not match the ordering defined for solRowCut. assert(srRowCut2.OsiCut::operator==(solRowCut)) ; assert(srRowCut2.row().isEquivalent(solRowCut.row())) ; assert(srRowCut2.lb() == solRowCut.lb()) ; assert(srRowCut2.ub() == solRowCut.ub()) ; delete siP; } // Test generate cuts method on p0033 { CglSimpleRounding cg; OsiSolverInterface * siP = baseSiP->clone(); std::string fn = mpsDir+"p0033"; siP->readMps(fn.c_str(),"mps"); OsiCuts cuts; cg.generateCuts(*siP,cuts); // p0033 is the optimal solution to p0033 int objIndices[14] = { 0, 6, 7, 9, 13, 17, 18, 22, 24, 25, 26, 27, 28, 29 }; CoinPackedVector p0033(14,objIndices,1.0); // test that none of the generated cuts // chops off the optimal solution int nRowCuts = cuts.sizeRowCuts(); OsiRowCut rcut; CoinPackedVector rpv; int i; for (i=0; i<nRowCuts; i++){ rcut = cuts.rowCut(i); rpv = rcut.row(); double p0033Sum = (rpv*p0033).sum(); double rcutub = rcut.ub(); assert (p0033Sum <= rcutub); } // test that the cuts improve the // lp objective function value siP->initialSolve(); double lpRelaxBefore=siP->getObjValue(); OsiSolverInterface::ApplyCutsReturnCode rc = siP->applyCuts(cuts); siP->resolve(); double lpRelaxAfter=siP->getObjValue(); #ifdef CGL_DEBUG printf("\n\nOrig LP min=%f\n",lpRelaxBefore); printf("Final LP min=%f\n\n",lpRelaxAfter); #endif assert( lpRelaxBefore < lpRelaxAfter ); delete siP; } }
/** Standard cut generation methods. */ void OaDecompositionBase::generateCuts(const OsiSolverInterface &si, OsiCuts & cs, const CglTreeInfo info) { if (nlp_ == NULL) { throw CoinError("Error in cut generator for outer approximation no NLP ipopt assigned", "generateCuts", "OaDecompositionBase"); } // babInfo is used to communicate with the b-and-b solver (Cbc or Bcp). BabInfo * babInfo = dynamic_cast<BabInfo *> (si.getAuxiliaryInfo()); assert(babInfo); assert(babInfo->babPtr()); numSols_ = babInfo->babPtr()->model().getSolutionCount (); CglTreeInfo info_copy = info; const CbcNode * node = babInfo->babPtr()->model().currentNode(); info_copy.level = (node == NULL) ? 0 : babInfo->babPtr()->model().currentNode()->depth(); if(babInfo->hasSolution()) numSols_ ++; if (babInfo) if (!babInfo->mipFeasible()) return; //Get the continuous solution const double *colsol = si.getColSolution(); vector<double> savedColLower(nlp_->getNumCols()); CoinCopyN(nlp_->getColLower(), nlp_->getNumCols(), savedColLower()); vector<double> savedColUpper(nlp_->getNumCols()); CoinCopyN(nlp_->getColUpper(), nlp_->getNumCols(), savedColUpper()); OsiBranchingInformation brInfo(nlp_, false); brInfo.solution_ = colsol; //Check integer infeasibility bool isInteger = integerFeasible(*nlp_, brInfo, parameters_.cbcIntegerTolerance_, objects_, nObjects_); //Check nodeNumber if it did not change scan savedCuts_ if one is violated force it and exit int nodeNumber = babInfo->babPtr()->model().getNodeCount(); if(nodeNumber == currentNodeNumber_){ #ifdef OA_DEBUG printf("OA decomposition recalled from the same node!\n"); #endif int numCuts = savedCuts_.sizeRowCuts(); for(int i = 0 ; i < numCuts ; i++){ //Check if cuts off solution if(savedCuts_.rowCut(i).violated(colsol) > 0.){ #ifdef OA_DEBUG printf("A violated saved cut has been found\n"); #endif savedCuts_.rowCut(i).setEffectiveness(9.99e99); cs.insert(savedCuts_.rowCut(i)); savedCuts_.eraseRowCut(i); return; i--; numCuts--; } } } else { currentNodeNumber_ = nodeNumber; savedCuts_.dumpCuts(); } if (!isInteger) { if (!doLocalSearch(babInfo))//create sub mip solver. return; } //get the current cutoff double cutoff; si.getDblParam(OsiDualObjectiveLimit, cutoff); // Save solvers state if needed solverManip * lpManip = NULL; if (lp_ != NULL) { assert(lp_ == &si); lpManip = new solverManip(lp_, true, leaveSiUnchanged_, true, true); } else { lpManip = new solverManip(si); } lpManip->setObjects(objects_, nObjects_); double milpBound = performOa(cs, *lpManip, babInfo, cutoff, info_copy); if(babInfo->hasSolution()){ babInfo->babPtr()->model().setSolutionCount (numSols_ - 1); } //Transmit the bound found by the milp { if (milpBound>-1e100) { // Also store into solver if (babInfo) babInfo->setMipBound(milpBound); } } //Clean everything : // Reset the two solvers if (leaveSiUnchanged_) lpManip->restore(); delete lpManip; nlp_->setColLower(savedColLower()); nlp_->setColUpper(savedColUpper()); return; }
//------------------------------------------------------------- void CglZeroHalf::generateCuts(const OsiSolverInterface & si, OsiCuts & cs, const CglTreeInfo info) { if (mnz_) { int cnum=0,cnzcnt=0; int *cbeg=NULL, *ccnt=NULL,*cind=NULL,*cval=NULL,*crhs=NULL; char *csense=NULL; const double * solution = si.getColSolution(); if ((flags_&1)==0) { // redo bounds const double * columnLower = si.getColLower(); const double * columnUpper = si.getColUpper(); int numberColumns = si.getNumCols(); for (int iColumn=0;iColumn<numberColumns;iColumn++) { if (vlb_[iColumn]!=COIN_INT_MAX) { int ilo,iup; double lo = columnLower[iColumn]; if (lo<-COIN_INT_MAX) lo=-COIN_INT_MAX; ilo= static_cast<int> (ceil(lo)); double up = columnUpper[iColumn]; if (up>COIN_INT_MAX) up=COIN_INT_MAX; iup= static_cast<int> (floor(up)); vlb_[iColumn]=ilo; vub_[iColumn]=iup; } } } if (true) { cutInfo_.sep_012_cut(mr_,mc_,mnz_, mtbeg_,mtcnt_, mtind_, mtval_, vlb_, vub_, mrhs_, msense_, solution, info.inTree ? false : true, &cnum,&cnzcnt, &cbeg,&ccnt,&cind,&cval,&crhs,&csense); } else { int k = 4*mr_+2*mnz_; int * temp = new int[k]; int * mtbeg = temp; int * mtcnt = mtbeg + mr_; int * mtind = mtcnt+mr_; int * mtval = mtind+mnz_; int * mrhs = mtval+mnz_; char * msense = reinterpret_cast<char*> (mrhs+mr_); int i; k=0; int kel=0; for (i=0;i<mr_;i++) { int kel2=kel; int rhs = mrhs_[i]; for (int j=mtbeg_[i];j<mtbeg_[i]+mtcnt_[i];j++) { int iColumn=mtind_[j]; int value=mtval_[j]; if (vlb_[iColumn]<vub_[iColumn]) { mtind[kel]=mtind_[j]; mtval[kel++]=mtval_[j]; } else { rhs -= vlb_[iColumn]*value; } } if (kel>kel2) { mtcnt[k]=kel-kel2; mtbeg[k]=kel2; mrhs[k]=rhs; msense[k++]=msense_[i]; } } if (kel) { cutInfo_.sep_012_cut(k,mc_,kel, mtbeg,mtcnt, mtind, mtval, vlb_, vub_, mrhs, msense, solution, info.inTree ? false : true, &cnum,&cnzcnt, &cbeg,&ccnt,&cind,&cval,&crhs,&csense); } delete [] temp; } if (cnum) { // add cuts double * element = new double[mc_]; for (int i=0;i<cnum;i++) { int n = ccnt[i]; int start = cbeg[i]; for (int j=0;j<n;j++) element[j]=cval[start+j]; OsiRowCut rc; if (csense[i]=='L') { rc.setLb(-COIN_DBL_MAX); rc.setUb(crhs[i]); } else if (csense[i]=='G') { rc.setLb(crhs[i]); rc.setUb(COIN_DBL_MAX); } else { abort(); } rc.setRow(n,cind+start,element,false); if ((flags_&1)!=0) rc.setGloballyValid(); //double violation = rc.violated(solution); //if (violation>1.0e-6) cs.insert(rc); //else //printf("violation of %g\n",violation); } delete [] element; free(cbeg); free(ccnt); free(cind); free(cval); free(crhs); free(csense); } } }
// Solve int CouenneFeasPump::milpPhase (double *nSol, double *iSol, int niter, int *nsuciter) { const int depth = (model_ && (model_ -> currentNode ())) ? model_ -> currentNode () -> depth () : 0; double time0 = CoinCpuTime(); // INTEGER PART ///////////////////////////////////////////////////////// // Solve IP using nSol as the initial point to minimize weighted // l-1 distance from. If nSol==NULL, the MILP is created using the // original milp's LP solution. double z = solveMILP (nSol, iSol, niter, nsuciter); // if no MILP solution was found, bail out bool try_again = false; if (!iSol || z >= COIN_DBL_MAX/2) { problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: could not find IP solution\n"); // find non-tabu solution in the solution pool while (!pool_ -> Set (). empty ()) { // EXTRACT the closest (to nSol) IP solution from the pool pool_ -> findClosestAndReplace (iSol, iSol, problem_ -> nVars ()); CouenneFPsolution newSol (problem_, iSol); // we found a solution that is not in the tabu list if (tabuPool_ . find (newSol) == tabuPool_ . end ()) { try_again = true; break; } } if (!try_again) { // try moving around current solution // SCIP could not find a MILP solution and we're somewhat // locked. Round current solution and feed it to the NLP. This // is better than just bailing out. int n = problem_ -> nVars (); if (!iSol) iSol = new double [n]; for (int i=0; i<n; i++) iSol [i] = (problem_ -> Var (i) -> isInteger ()) ? COUENNE_round (nSol [i]) : nSol [i]; } // if (!try_again) { // nothing to do, bail out // problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: could not find from pool either, bailing out\n"); // break; // } } bool isChecked = false; // If a solution was found, but is in the tabu list, two choices: // // 1) the pool is empty: do a round of cuts and repeat; // // 2) the pool is nonempty: extract the best solution from the // pool and use it instead CouenneFPsolution checkedSol (problem_, iSol, false); // false is for not allocating space for this if (tabuPool_. find (checkedSol) == tabuPool_ . end ()) tabuPool_. insert (CouenneFPsolution (problem_, iSol)); // only insertion to tabu pool: we check its feasibility now else { problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: found solution is tabu\n"); // Current solution was visited before. Replace it with another // MILP solution from the pool, if any. if (tabuMgt_ == FP_TABU_NONE) break; else if ((tabuMgt_ == FP_TABU_POOL) && !(pool_ -> Set (). empty ())) { // try to find non-tabu solution in the solution pool do { // retrieve the top solution from the pool pool_ -> findClosestAndReplace (iSol, nSol, problem_ -> nVars ()); CouenneFPsolution newSol (problem_, iSol); // we found a solution that is not in the tabu list if (tabuPool_. find (newSol) == tabuPool_ . end ()) break; // the pool is empty -> bail out if (pool_ -> Set ().empty ()) { delete[] iSol; iSol = NULL; } } while( !pool_ -> Set ().empty() ); } else if (((tabuMgt_ == FP_TABU_CUT) || ((pool_ -> Set (). empty ()) && iSol))) { OsiCuts cs; problem_ -> domain () -> push (problem_ -> nVars (), iSol, milp_ -> getColLower (), milp_ -> getColUpper (), false); // don't copy vectors couenneCG_ -> genRowCuts (*milp_, cs, 0, NULL); // remaining three arguments NULL by default problem_ -> domain () -> pop (); if (cs.sizeRowCuts ()) { milp_ -> applyCuts (cs); if (nSep++ < nSepRounds_) continue; } else break; // nothing left to do, just bail out } else if ((tabuMgt_ == FP_TABU_PERTURB) && iSol) { // perturb solution const CouNumber *lb = milp_ -> getColLower (), *ub = milp_ -> getColUpper (); double downMoves = 0., upMoves = 0.; int startIndex = (int) floor (CoinDrand48 () * problem_ -> nOrigVars ()); for (int ii = problem_ -> nOrigVars (); ii--; lb++, ub++) { if (problem_ -> Var (ii) -> Multiplicity () <= 0) continue; // make perturbation start from pseudo-random points int i = (startIndex + ii) % problem_ -> nOrigVars (); if (problem_ -> Var (i) -> isInteger ()) { double rnd = CoinDrand48 (), down = 0., up = 1.; // if there is room on the left (resp. right) of the // solution, consider moving down (resp. up). Make moves // less likely as we don't want to perturb too many // variables #define RND_DECR_EXPONENT .5 if (iSol [i] >= lb [i] + 1.) down = 1. / pow (1. + (downMoves += 1.), RND_DECR_EXPONENT); if (iSol [i] <= ub [i] - 1.) up = 1. - 1. / pow (1. + (upMoves += 1.), RND_DECR_EXPONENT); if (rnd < down) iSol [i] -= 1.; else if (rnd > up) iSol [i] += 1.; } } } } problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: checking IP solution for feasibility\n"); isChecked = problem_ -> checkNLP0 (iSol, z, true, false, // don't care about obj true, // stop at first violation true); // checkAll // Possible improvement: if IP-infeasible or if z does not improve // the best solution found so far, then try the following: // // 1) Fix integer variables to IP solution's corresponding components // 2) Solve restriction with original obj // 3) While not found (MI)NLP solution or // solution MINLP infeasible or // z not better // 4) Get solution x* from pool // 5) Solve with original objective // 6) Done // 7) If found solution, set it, otherwise keep previously found (IP-feasible) one if ((!isChecked || // not MINLP feasible z > problem_ -> getCutOff ())) { // not improving problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: infeasible/non-improving (feas==%d, z=%g, cutoff=%g), looping on pool\n", isChecked, z, problem_->getCutOff ()); bool try_again; do { try_again = false; if (CoinCpuTime () > problem_ -> getMaxCpuTime ()) break; // Check if fixing integers to those in iSol yields an // infeasible problem. If so, don't optimize if (fixIntVariables (iSol)) { nlp_ -> setInitSol (iSol); if (problem_ -> Jnlst () -> ProduceOutput (J_ALL, J_NLPHEURISTIC)) { printf ("----------------------- Solving NLP:\n"); problem_ -> print (); printf ("-----------------------\n"); } status = app_ -> OptimizeTNLP (nlp_); if (nlp_ -> getSolution ()) { // check if non-NULL if (nSol) CoinCopyN (nlp_ -> getSolution (), problem_ -> nVars (), nSol); else nSol = CoinCopyOfArray (nlp_ -> getSolution (), problem_ -> nVars ()); } if (nlp_ -> getSolution () && (problem_ -> Jnlst () -> ProduceOutput (J_ALL, J_NLPHEURISTIC))) { // check if non-NULL printf ("######################## NLP solution (loop through pool):\n"); for (int i=0; i< problem_ -> nVars ();) { printf ("%+e ", nlp_ -> getSolution () [i]); if (!(++i % 15)) printf ("\n"); } } z = nlp_ -> getSolValue (); if (z < problem_ -> getCutOff ()) // don't waste time if not improving isChecked = problem_ -> checkNLP0 (nSol, z, true, false, // don't care about obj true, // stop at first violation true); // checkAll if (isChecked && (z < problem_ -> getCutOff ())) break; } // find non-tabu solution in the solution pool while (!pool_ -> Set (). empty ()) { // EXTRACT the closest (to nSol) IP solution from the pool pool_ -> findClosestAndReplace (iSol, nSol, problem_ -> nVars ()); CouenneFPsolution newSol (problem_, iSol); // we found a solution that is not in the tabu list if (tabuPool_ . find (newSol) == tabuPool_ . end ()) { try_again = true; break; } } } while (try_again); } // Whatever the previous block yielded, we are now going to check // if we have a new solution, and if so we save it. if (isChecked) { problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: IP solution is MINLP feasible\n"); // solution is MINLP feasible! Save it. retval = 1; objVal = z; // Found a MINLP-feasible solution, but to keep diversity do NOT // use the best available. Just use this. // // #ifdef FM_CHECKNLP2 // # ifdef FM_TRACE_OPTSOL // problem_->getRecordBestSol()->update(); // best = problem_->getRecordBestSol()->getSol(); // objVal = problem_->getRecordBestSol()->getVal(); // # else /* not FM_TRACE_OPTSOL */ // best = problem_->getRecordBestSol()->getModSol(problem_->nVars()); // objVal = z; // # endif /* not FM_TRACE_OPTSOL */ // #else /* not FM_CHECKNLP2 */ // # ifdef FM_TRACE_OPTSOL // problem_->getRecordBestSol()->update(iSol, problem_->nVars(), // z, problem_->getFeasTol()); // best = problem_->getRecordBestSol()->getSol(); // objVal = problem_->getRecordBestSol()->getVal(); // # else /* not FM_TRACE_OPTSOL */ best = iSol; // objVal = z; // # ifdef FM_TRACE_OPTSOL // problem_->getRecordBestSol()->update(); // best = problem_->getRecordBestSol()->getSol(); // objVal = problem_->getRecordBestSol()->getVal(); // # endif /* not FM_TRACE_OPTSOL */ // #endif /* not FM_CHECKNLP2 */ if (z < problem_ -> getCutOff ()) { problem_ -> setCutOff (objVal); t_chg_bounds *chg_bds = NULL; if (objInd >= 0) { chg_bds = new t_chg_bounds [problem_ -> nVars ()]; chg_bds [objInd].setUpper (t_chg_bounds::CHANGED); } // If, with new cutoff, bound tightening clears the whole // feasible set, stop bool is_still_feas = problem_ -> btCore (chg_bds); if (chg_bds) delete [] chg_bds; // don't tighten MILP if BT says it's infeasible if (!is_still_feas) break; // Update lb/ub on milp and nlp here const CouNumber *plb = problem_ -> Lb (), *pub = problem_ -> Ub (), *mlb = milp_ -> getColLower (), *mub = milp_ -> getColUpper (); for (int i=problem_ -> nVars (), j=0; i--; ++j, ++plb, ++pub) { bool neglect = problem_ -> Var (j) -> Multiplicity () <= 0; if (*plb > *mlb++) milp_ -> setColLower (j, neglect ? 0. : *plb); if (*pub < *mub++) milp_ -> setColUpper (j, neglect ? 0. : *pub); } } break; } else { problem_ -> Jnlst () -> Printf (J_WARNING, J_NLPHEURISTIC, "FP: IP solution NOT MINLP feasible\n"); if (milpCuttingPlane_ == FP_CUT_EXTERNAL || milpCuttingPlane_ == FP_CUT_POST) { // Solution is IP- but not MINLP feasible: it might get cut by // linearization cuts. If so, add a round of cuts and repeat. OsiCuts cs; problem_ -> domain () -> push (milp_); couenneCG_ -> genRowCuts (*milp_, cs, 0, NULL); // remaining three arguments NULL by default problem_ -> domain () -> pop (); if (cs.sizeRowCuts ()) { // the (integer, NLP infeasible) solution could be separated milp_ -> applyCuts (cs); // found linearization cut, now re-solve MILP (not quite a FP) if (milpCuttingPlane_ == FP_CUT_EXTERNAL && nSep++ < nSepRounds_) continue; } } } }
void CglOddHole::generateCuts(const OsiRowCutDebugger * /*debugger*/, const CoinPackedMatrix & rowCopy, const double * solution, const double * dj, OsiCuts & cs, const int * suitableRow, const int * fixedColumn, const CglTreeInfo info, bool packed) { CoinPackedMatrix columnCopy = rowCopy; columnCopy.reverseOrdering(); // Get basic problem information int nRows=columnCopy.getNumRows(); int nCols=columnCopy.getNumCols(); const int * column = rowCopy.getIndices(); const CoinBigIndex * rowStart = rowCopy.getVectorStarts(); const int * rowLength = rowCopy.getVectorLengths(); const int * row = columnCopy.getIndices(); const CoinBigIndex * columnStart = columnCopy.getVectorStarts(); const int * columnLength = columnCopy.getVectorLengths(); // we need only look at suitable rows and variables with unsatisfied 0-1 // lookup from true row to compressed matrix int * mrow = new int[nRows]; // lookup from true column to compressed int * lookup = new int[nCols]; // number of columns in compressed matrix int nSmall=0; int i; //do lookup from true sequence to compressed int n=0; for (i=0;i<nRows;i++) { if (suitableRow[i]>0) { mrow[i]=n++; } else { mrow[i]=-1; } } for (i=0;i<nCols;i++) { if (!fixedColumn[i]) { lookup[i]=nSmall++; } else { lookup[i]=-1; } } int nSmall2=2*nSmall; // we don't know how big matrix will be #define MAXELS 50000 int maxels=MAXELS; //How do I do reallocs in C++? // 1.0 - value x(i) - value x(j) for each node pair (or reverse if cover) double * cost = reinterpret_cast<double *> (malloc(maxels*sizeof(double))); // arc i.e. j which can be reached from i int * to= reinterpret_cast<int *> (malloc(maxels*sizeof(int))); //original row for each arc int * rowfound=reinterpret_cast<int *> (malloc(maxels*sizeof(int))); // start of each column int * starts=new int[2*nSmall+1]; starts[0]=0; // useful array for marking if already connected int * mark =new int[nSmall2]; memset(mark,0,nSmall2*sizeof(int)); n=0; //number of elements in matrix for (i=0;i<nCols;i++) { int icol=lookup[i]; if (icol>=0) { // column in compressed matrix int k; double dd=1.0000001-solution[i]; mark[icol]=1; // reallocate if matrix reached size limit if (n+nCols>maxels) { maxels*=2; cost=reinterpret_cast<double *> (realloc(cost,maxels*sizeof(double))); to=reinterpret_cast<int *> (realloc(to,maxels*sizeof(int))); rowfound=reinterpret_cast<int *> (realloc(rowfound,maxels*sizeof(int))); } // get all other connected variables for (k=columnStart[i];k<columnStart[i]+columnLength[i];k++) { int irow=row[k]; int jrow=mrow[irow]; // but only if row in compressed matrix if (jrow>=0) { int j; for (j=rowStart[irow];j<rowStart[irow]+rowLength[irow];j++) { int jcol=column[j]; int kcol=lookup[jcol]; if (kcol>=0&&!mark[kcol]) { cost[n]=dd-solution[jcol]; to[n]=kcol; rowfound[n++]=irow;//original row mark[kcol]=1; } } } } starts[icol+1]=n; // zero out markers for next column mark[icol]=0; for (k=starts[icol];k<starts[icol+1];k++) { int ito=to[k]; if (ito<0||ito>=nSmall) abort(); mark[to[k]]=0; } } } //if cover then change sign - otherwise make sure positive if (packed) { for (i=0;i<n;i++) { if (cost[i]<1.0e-10) { cost[i]=1.0e-10; } } } else { for (i=0;i<n;i++) { cost[i]=-cost[i]; if (cost[i]<1.0e-10) { cost[i]=1.0e-10; } } } // we are going to double size if (2*n>maxels) { maxels=2*n; cost=reinterpret_cast<double *> (realloc(cost,maxels*sizeof(double))); to=reinterpret_cast<int *> (realloc(to,maxels*sizeof(int))); rowfound=reinterpret_cast<int *> (realloc(rowfound,maxels*sizeof(int))); } /* copy and make bipartite*/ for (i=0;i<nSmall;i++) { int k,j=i+nSmall; for (k=starts[i];k<starts[i+1];k++) { int ito=to[k]; to[n]=ito; to[k]=ito+nSmall; cost[n]=cost[k]; rowfound[n++]=rowfound[k];; } starts[j+1]=n; } //random numbers to winnow out duplicate cuts double * check = new double[nCols]; if (info.randomNumberGenerator) { const CoinThreadRandom * randomGenerator = info.randomNumberGenerator; for (i=0;i<nCols;i++) { check[i]=randomGenerator->randomDouble(); } } else { CoinSeedRandom(13579); for (i=0;i<nCols;i++) { check[i]=CoinDrand48(); // NOT on a thread by thread basis } } // Shortest path algorithm from Dijkstra - is there a better one? typedef struct { double cost; //cost to starting node int back; //previous node } Path; typedef struct { double cost; //cost to starting node int node; //node } Item; Item * stack = new Item [nSmall2]; Path * path = new Path [nSmall2]; // arrays below are used only if looks promising // allocate here // we don't know how many cuts will be generated int ncuts=0; int maxcuts=1000; double * hash = reinterpret_cast<double *> (malloc(maxcuts*sizeof(double))); // to clean (should not be needed) int * clean = new int[nSmall2]; int * candidate = new int[CoinMax(nSmall2,nCols)]; double * element = new double[nCols]; // in case we want to sort double_double_int_triple * sortit = new double_double_int_triple [nCols]; memset(mark,0,nSmall2*sizeof(int)); int * countcol = new int[nCols]; memset(countcol,0,nCols*sizeof(int)); int bias = packed ? 0 : 1; //amount to add before halving // If nSmall large then should do a randomized subset // Improvement 1 int icol; for (icol=0;icol<nSmall;icol++) { int j; int jcol=icol+nSmall; int istack=1; for (j=0;j<nSmall2;j++) { path[j].cost=1.0e70; path[j].back=nSmall2+1; } path[icol].cost=0.0; path[icol].back=-1; stack[0].cost=0.0; stack[0].node=icol; mark[icol]=1; while(istack) { Item thisItem=stack[--istack]; double thisCost=thisItem.cost; int inode=thisItem.node; int k; mark[inode]=0; //say available for further work // See if sorting every so many would help (and which way)? // Improvement 2 for (k=starts[inode];k<starts[inode+1];k++) { int jnode=to[k]; if (!mark[jnode]&&thisCost+cost[k]<path[jnode].cost-1.0e-12) { path[jnode].cost=thisCost+cost[k]; path[jnode].back=inode; // add to stack stack[istack].cost=path[jnode].cost; stack[istack++].node=jnode; mark[jnode]=1; #ifdef CGL_DEBUG assert (istack<=nSmall2); #endif } } } bool good=(path[jcol].cost<0.9999); if (good) { /* try */ int ii; int nrow2=0; int nclean=0; double sum=0; #ifdef CGL_DEBUG printf("** %d ",jcol-nSmall); #endif ii=1; candidate[0]=jcol; while(jcol!=icol) { int jjcol; jcol=path[jcol].back; if (jcol>=nSmall) { jjcol=jcol-nSmall; } else { jjcol=jcol; } #ifdef CGL_DEBUG printf(" %d",jjcol); #endif if (mark[jjcol]) { // good=false; // probably means this is from another cycle (will have been found) // one of cycles must be zero cost // printf("variable already on chain!\n"); } else { mark[jjcol]=1; clean[nclean++]=jjcol; candidate[ii++]=jcol; #ifdef CGL_DEBUG assert (ii<=nSmall2); #endif } } #ifdef CGL_DEBUG printf("\n"); #endif for (j=0;j<nclean;j++) { int k=clean[j]; mark[k]=0; } if (good) { int k; for (k=ii-1;k>0;k--) { int jk,kk=candidate[k]; int ix=0; for (jk=starts[kk];jk<starts[kk+1];jk++) { int ito=to[jk]; if (ito==candidate[k-1]) { ix=1; // back to original row mrow[nrow2++]=rowfound[jk]; break; } } if (!ix) { good=false; } } if ((nrow2&1)!=1) { good=false; } if (good) { int nincut=0; for (k=0;k<nrow2;k++) { int j,irow=mrow[k]; for (j=rowStart[irow];j<rowStart[irow]+rowLength[irow];j++) { int icol=column[j]; if (!countcol[icol]) candidate[nincut++]=icol; countcol[icol]++; } } #ifdef CGL_DEBUG printf("true constraint %d",nrow2); #endif nrow2=nrow2>>1; double rhs=nrow2; if (!packed) rhs++; // +1 for cover ii=0; for (k=0;k<nincut;k++) { int jcol=candidate[k]; if (countcol[jcol]) { #ifdef CGL_DEBUG printf(" %d %d",jcol,countcol[jcol]); #endif int ihalf=(countcol[jcol]+bias)>>1; if (ihalf) { element[ii]=ihalf; sum+=solution[jcol]*element[ii]; /*printf("%d %g %g\n",jcol,element[ii],sumall[jcol]);*/ candidate[ii++]=jcol; } countcol[jcol]=0; } } #ifdef CGL_DEBUG printf("\n"); #endif OsiRowCut rc; double violation=0.0; if (packed) { violation = sum-rhs; rc.setLb(-COIN_DBL_MAX); rc.setUb(rhs); } else { // other way for cover violation = rhs-sum; rc.setUb(COIN_DBL_MAX); rc.setLb(rhs); } if (violation<minimumViolation_) { #ifdef CGL_DEBUG printf("why no cut\n"); #endif good=false; } else { if (static_cast<double> (ii) * minimumViolationPer_>violation|| ii>maximumEntries_) { #ifdef CGL_DEBUG printf("why no cut\n"); #endif if (packed) { // sort and see if we can get down to length // relax by taking out ones with solution 0.0 nincut=ii; for (k=0;k<nincut;k++) { int jcol=candidate[k]; double value = fabs(dj[jcol]); if (solution[jcol]) value = -solution[jcol]; sortit[k].dj=value; sortit[k].element=element[k]; sortit[k].sequence=jcol; } // sort std::sort(sortit,sortit+nincut,double_double_int_triple_compare()); nincut = CoinMin(nincut,maximumEntries_); sum=0.0; for (k=0;k<nincut;k++) { int jcol=sortit[k].sequence; candidate[k]=jcol; element[k]=sortit[k].element; sum+=solution[jcol]*element[k]; } violation = sum-rhs; ii=nincut; if (violation<minimumViolation_) { good=false; } } else { good=false; } } } if (good) { //this assumes not many cuts int j; #if 0 double value=0.0; for (j=0;j<ii;j++) { int icol=candidate[j]; value += check[icol]*element[j]; } #else CoinPackedVector candidatePv(ii,candidate,element); candidatePv.sortIncrIndex(); double value = candidatePv.dotProduct(check); #endif for (j=0;j<ncuts;j++) { if (value==hash[j]) { //could check equality - quicker just to assume break; } } if (j==ncuts) { //new if (ncuts==maxcuts) { maxcuts *= 2; hash = reinterpret_cast<double *> (realloc(hash,maxcuts*sizeof(double))); } hash[ncuts++]=value; rc.setRow(ii,candidate,element); #ifdef CGL_DEBUG printf("sum %g rhs %g %d\n",sum,rhs,ii); if (debugger) assert(!debugger->invalidCut(rc)); #endif cs.insert(rc); } } } /* end of adding cut */ } } } delete [] countcol; delete [] element; delete [] candidate; delete [] sortit; delete [] clean; delete [] path; delete [] stack; free(hash); delete [] check; delete [] mark; delete [] starts; delete [] lookup; delete [] mrow; free(rowfound); free(to); free(cost); }