/* Generate cuts for the model data contained in si. The generated cuts are inserted into and returned in the collection of cuts cs. */ bool AbcCutGenerator::generateCuts( OsiCuts & cs , bool fullScan) { int howOften = whenCutGenerator_; if (howOften == -100) return false; if (howOften > 0) howOften = howOften % 1000000; else howOften = 1; if (!howOften) howOften = 1; bool returnCode = false; OsiSolverInterface * solver = model_->solver(); #if defined(ABC_DEBUG_MORE) std::cout << "model_->getNodeCount() = " << model_->getNodeCount() << std::endl; #endif if (fullScan || (model_->getNodeCount() % howOften) == 0 ) { CglProbing* generator = dynamic_cast<CglProbing*>(generator_); if (!generator) { generator_->generateCuts(*solver,cs); } else { // Probing - return tight column bounds CglTreeInfo info; generator->generateCutsAndModify(*solver,cs,&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 j; 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 solver->setColLower(j, tightLower[j]); solver->setColUpper(j, tightUpper[j]); if (tightLower[j] > solution[j] + primalTolerance || tightUpper[j] < solution[j] - primalTolerance) returnCode = true; } } } } return returnCode; }
bool MILPSolverCLP::solve(const bool & skipPresolve) { if (!solvedYet) { if (skipPresolve) { solvectl->setPresolveType(ClpSolve::presolveOff); } else { solvectl->setPresolveType(ClpSolve::presolveOn); } lp->setSolveOptions(*solvectl); lp->initialSolve(); solvedYet = true; } else { lp->resolve(); } if (!lp->isProvenOptimal()) { return false; } if (!hasIntegerVariables) { return true; } delete milp; milp = 0; //lp->setHintParam(OsiDoReducePrint,true,OsiHintTry); if (!milp) { milp = new CbcModel(*lp); //milp->solver()->setHintParam(OsiDoReducePrint,true,OsiHintTry); } CglProbing pg; pg.setUsingObjective(true); pg.setMaxPass(3); pg.setMaxProbe(100); pg.setMaxLook(50); pg.setRowCuts(3); milp->addCutGenerator(&pg,-1); milp->setLogLevel(0); milp->branchAndBound(); return (milp->isProvenOptimal()); }
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; }
/** Add milp cut generators according to options.*/ void CouenneSetup::addMilpCutGenerators () { enum extraInfo_ {CUTINFO_NONE, CUTINFO_MIG, CUTINFO_PROBING, CUTINFO_CLIQUE}; // extra data structure to avoid repeated code below struct cutInfo { const char *optname; CglCutGenerator *cglptr; const char *cglId; enum extraInfo_ extraInfo; } cutList [] = { {(const char*)"Gomory_cuts",new CglGomory, (const char*)"Mixed Integer Gomory",CUTINFO_MIG}, {(const char*)"probing_cuts",new CglProbing, (const char*) "Probing", CUTINFO_PROBING}, {(const char*)"mir_cuts",new CglMixedIntegerRounding2, (const char*) "Mixed Integer Rounding", CUTINFO_NONE}, {(const char*)"2mir_cuts", new CglTwomir, (const char*) "2-MIR", CUTINFO_NONE}, {(const char*)"cover_cuts", new CglKnapsackCover, (const char*) "Cover", CUTINFO_NONE}, {(const char*)"clique_cuts", new CglClique, (const char*) "Clique", CUTINFO_CLIQUE}, {(const char*)"lift_and_project_cuts",new CglLandP,(const char*)"Lift and Project",CUTINFO_NONE}, {(const char*)"reduce_split_cuts",new CglRedSplit,(const char*) "Reduce and Split",CUTINFO_NONE}, {(const char*)"flow_covers_cuts",new CglFlowCover,(const char*) "Flow cover cuts", CUTINFO_NONE}, {NULL, NULL, NULL, CUTINFO_NONE}}; int freq; for (int i=0; cutList [i]. optname; i++) { options_ -> GetIntegerValue (std::string (cutList [i]. optname), freq, "couenne."); if (!freq) { delete cutList [i].cglptr; continue; } CuttingMethod cg; cg.frequency = freq; cg.cgl = cutList [i].cglptr; cg.id = std::string (cutList [i]. cglId); cutGenerators_.push_back (cg); // options for particular cases switch (cutList [i].extraInfo) { case CUTINFO_MIG: { CglGomory *gc = dynamic_cast <CglGomory *> (cutList [i].cglptr); if (!gc) break; gc -> setLimitAtRoot(512); gc -> setLimit(50); } break; case CUTINFO_PROBING: { CglProbing *pc = dynamic_cast <CglProbing *> (cutList [i].cglptr); if (!pc) break; pc->setUsingObjective(1); pc->setMaxPass(3); pc->setMaxPassRoot(3); // Number of unsatisfied variables to look at pc->setMaxProbe(10); pc->setMaxProbeRoot(50); // How far to follow the consequences pc->setMaxLook(10); pc->setMaxLookRoot(50); pc->setMaxLookRoot(10); // Only look at rows with fewer than this number of elements pc->setMaxElements(200); pc->setRowCuts(3); } break; case CUTINFO_CLIQUE: { CglClique *clique = dynamic_cast <CglClique *> (cutList [i].cglptr); if (!clique) break; clique -> setStarCliqueReport(false); clique -> setRowCliqueReport(false); clique -> setMinViolation(0.1); } break; //case CUTINFO_NONE: default: break; } } double givenAllowFGap2 = 0.0; options_->GetNumericValue(std::string("allowable_fraction_gap"), givenAllowFGap2, "bonmin."); double upval = 1e50; #ifdef FM_UP_BND printf("CutOff value:\n"); scanf("%lf", &upval); #else /* not FM_UP_BND */ options_->GetNumericValue(std::string("art_cutoff"), upval, "bonmin."); #endif /* FM_UP_BND */ if(upval < 1e50) { double newCO = (1-givenAllowFGap2) * upval; couenneProb_->setCutOff(newCO); printf("CutOff set to %f\n", newCO); #ifdef FM_TRACE_OPTSOL if(couenneProb_->getRecordBestSol()->getHasSol()) { if(newCO < couenneProb_->getRecordBestSol()->getVal()) { couenneProb_->getRecordBestSol()->setVal(newCO); } } #endif } }
// Generate constraints for the model data contained in si. // The generated constraints are inserted into and returned in the // constraint pool. // Default implementation use Cgl cut generators. bool BlisConGenerator::generateConstraints(BcpsConstraintPool &conPool) { bool status = false; OsiSolverInterface * solver = model_->solver(); #if defined(BLIS_DEBUG_MORE) std::cout << "model_->getNodeCount() = " << model_->getNodeCount() << std::endl; #endif //-------------------------------------------------- // Start to generate constraints... //-------------------------------------------------- assert(generator_ != NULL); int j; OsiCuts newOsiCuts; CglProbing* generator = dynamic_cast<CglProbing *>(generator_); if (generator) { // It is CglProbing - return tight column bounds CglTreeInfo info; generator->generateCutsAndModify(*solver, newOsiCuts, &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; } } } } else { // Other Cgl cut generators generator_->generateCuts(*solver, newOsiCuts); } //-------------------------------------------------- // Create blis constraints and remove zero length row cuts. //-------------------------------------------------- int numNewConstraints = newOsiCuts.sizeRowCuts(); for (j = 0; j < numNewConstraints; ++j) { OsiRowCut & rCut = newOsiCuts.rowCut(j); int len = rCut.row().getNumElements(); #ifdef BLIS_DEBUG_MORE std::cout << "Cut " << j<<": length = " << len << std::endl; #endif if (len > 0) { // Create BlisConstraints from OsiCuts. BlisConstraint *blisCon = BlisOsiCutToConstraint(&rCut); conPool.addConstraint(blisCon); } else if (len == 0) { // Empty cuts #ifdef BLIS_DEBUG std::cout << "WARNING: Empty cut from " << name_ << std::endl; #endif } else { #ifdef BLIS_DEBUG std::cout << "ERROR: Cut length = " << len << std::endl; #endif // Error assert(0); } } // Adjust cut strategy. if ( (strategy_ == BlisCutStrategyAuto) && (noConsCalls_ > BLIS_CUT_DISABLE) ) { strategy_ = BlisCutStrategyNone; } return status; }
int doBaCParam (CoinParam *param) { assert (param != 0) ; CbcGenParam *genParam = dynamic_cast<CbcGenParam *>(param) ; assert (genParam != 0) ; CbcGenCtlBlk *ctlBlk = genParam->obj() ; assert (ctlBlk != 0) ; CbcModel *model = ctlBlk->model_ ; assert (model != 0) ; /* Setup to return nonfatal/fatal error (1/-1) by default. */ int retval ; if (CoinParamUtils::isInteractive()) { retval = 1 ; } else { retval = -1 ; } ctlBlk->setBaBStatus(CbcGenCtlBlk::BACAbandon, CbcGenCtlBlk::BACmInvalid, CbcGenCtlBlk::BACwNotStarted, false, 0) ; /* We ain't gonna do squat without a good model. */ if (!ctlBlk->goodModel_) { std::cout << "** Current model not valid!" << std::endl ; return (retval) ; } /* Start the clock ticking. */ double time1 = CoinCpuTime() ; double time2 ; /* Create a clone of the model which we can modify with impunity. Extract the underlying solver for convenient access. */ CbcModel babModel(*model) ; OsiSolverInterface *babSolver = babModel.solver() ; assert (babSolver != 0) ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: initial babSolver is " << std::hex << babSolver << std::dec << ", log level " << babSolver->messageHandler()->logLevel() << "." << std::endl ; # endif /* Solve the root relaxation. Bail unless it solves to optimality. */ if (!solveRelaxation(&babModel)) { ctlBlk->setBaBStatus(&babModel, CbcGenCtlBlk::BACwBareRoot) ; return (0) ; } # if COIN_CBC_VERBOSITY > 0 std::cout << "doBaCParam: initial relaxation z = " << babSolver->getObjValue() << "." << std::endl ; # endif /* Are we up for fixing variables based on reduced cost alone? */ if (ctlBlk->djFix_.action_ == true) { reducedCostHack(babSolver, ctlBlk->djFix_.threshold_) ; } /* Time to consider preprocessing. We'll do a bit of setup before getting to the meat of the issue. preIppSolver will hold a clone of the unpreprocessed constraint system. We'll need it when we postprocess. ippSolver holds the preprocessed constraint system. Again, we clone it and give the clone to babModel for B&C. Presumably we need an unmodified copy of the preprocessed system to do postprocessing, but the copy itself is hidden inside the preprocess object. */ OsiSolverInterface *preIppSolver = 0 ; CglPreProcess ippObj ; bool didIPP = false ; int numberChanged = 0 ; int numberOriginalColumns = babSolver->getNumCols() ; CbcGenCtlBlk::IPPControl ippAction = ctlBlk->getIPPAction() ; if (!(ippAction == CbcGenCtlBlk::IPPOff || ippAction == CbcGenCtlBlk::IPPStrategy)) { double timeLeft = babModel.getMaximumSeconds() ; preIppSolver = babSolver->clone() ; OsiSolverInterface *ippSolver ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: clone made prior to IPP is " << std::hex << preIppSolver << std::dec << ", log level " << preIppSolver->messageHandler()->logLevel() << "." << std::endl ; # endif preIppSolver->setHintParam(OsiDoInBranchAndCut, true, OsiHintDo) ; ippObj.messageHandler()->setLogLevel(babModel.logLevel()) ; CglProbing probingGen ; probingGen.setUsingObjective(true) ; probingGen.setMaxPass(3) ; probingGen.setMaxProbeRoot(preIppSolver->getNumCols()) ; probingGen.setMaxElements(100) ; probingGen.setMaxLookRoot(50) ; probingGen.setRowCuts(3) ; ippObj.addCutGenerator(&probingGen) ; /* For preProcessNonDefault, the 2nd parameter controls the conversion of clique and SOS constraints. 0 does nothing, -1 converts <= to ==, and 2 and 3 form SOS sets under strict and not-so-strict conditions, respectively. */ int convert = 0 ; if (ippAction == CbcGenCtlBlk::IPPEqual) { convert = -1 ; } else if (ippAction == CbcGenCtlBlk::IPPEqualAll) { convert = -2 ; } else if (ippAction == CbcGenCtlBlk::IPPSOS) { convert = 2 ; } else if (ippAction == CbcGenCtlBlk::IPPTrySOS) { convert = 3 ; } ippSolver = ippObj.preProcessNonDefault(*preIppSolver, convert, 10) ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: solver returned from IPP is " << std::hex << ippSolver << std::dec ; if (ippSolver) { std::cout << ", log level " << ippSolver->messageHandler()->logLevel() ; } std::cout << "." << std::endl ; # endif /* ippSolver == 0 is success of a sort --- integer preprocess has found the problem to be infeasible or unbounded. Need to think about how to indicate status. */ if (!ippSolver) { std::cout << "Integer preprocess says infeasible or unbounded" << std::endl ; delete preIppSolver ; ctlBlk->setBaBStatus(&babModel, CbcGenCtlBlk::BACwIPP) ; return (0) ; } # if COIN_CBC_VERBOSITY > 0 else { std::cout << "After integer preprocessing, model has " << ippSolver->getNumRows() << " rows, " << ippSolver->getNumCols() << " columns, and " << ippSolver->getNumElements() << " elements." << std::endl ; } # endif preIppSolver->setHintParam(OsiDoInBranchAndCut, false, OsiHintDo) ; ippSolver->setHintParam(OsiDoInBranchAndCut, false, OsiHintDo) ; if (ippAction == CbcGenCtlBlk::IPPSave) { ippSolver->writeMps("presolved", "mps", 1.0) ; std::cout << "Integer preprocessed model written to `presolved.mps' " << "as minimisation problem." << std::endl ; } OsiSolverInterface *osiTmp = ippSolver->clone() ; babModel.assignSolver(osiTmp) ; babSolver = babModel.solver() ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: clone of IPP solver passed to babModel is " << std::hex << babSolver << std::dec << ", log level " << babSolver->messageHandler()->logLevel() << "." << std::endl ; # endif if (!solveRelaxation(&babModel)) { delete preIppSolver ; ctlBlk->setBaBStatus(&babModel, CbcGenCtlBlk::BACwIPPRelax) ; return (0) ; } # if COIN_CBC_VERBOSITY > 0 std::cout << "doBaCParam: presolved relaxation z = " << babSolver->getObjValue() << "." << std::endl ; # endif babModel.setMaximumSeconds(timeLeft - (CoinCpuTime() - time1)) ; didIPP = true ; } /* At this point, babModel and babSolver hold the constraint system we'll use for B&C (either the original system or the preprocessed system) and we have a solution to the lp relaxation. If we're using the COSTSTRATEGY option, set up priorities here and pass them to the babModel. */ if (ctlBlk->priorityAction_ != CbcGenCtlBlk::BPOff) { setupPriorities(&babModel, ctlBlk->priorityAction_) ; } /* Install heuristics and cutting planes. */ installHeuristics(ctlBlk, &babModel) ; installCutGenerators(ctlBlk, &babModel) ; /* Set up status print frequency for babModel. */ if (babModel.getNumCols() > 2000 || babModel.getNumRows() > 1500 || babModel.messageHandler()->logLevel() > 1) babModel.setPrintFrequency(100) ; /* If we've read in a known good solution for debugging, activate the row cut debugger. */ if (ctlBlk->debugSol_.values_) { if (ctlBlk->debugSol_.numCols_ == babModel.getNumCols()) { babSolver->activateRowCutDebugger(ctlBlk->debugSol_.values_) ; } else { std::cout << "doBaCParam: debug file has incorrect number of columns." << std::endl ; } } /* Set ratio-based integrality gap, if specified by user. */ if (ctlBlk->setByUser_[CbcCbcParam::GAPRATIO] == true) { double obj = babSolver->getObjValue() ; double gapRatio = babModel.getDblParam(CbcModel::CbcAllowableFractionGap) ; double gap = gapRatio * (1.0e-5 + fabs(obj)) ; babModel.setAllowableGap(gap) ; std::cout << "doBaCParam: Continuous objective = " << obj << ", so allowable gap set to " << gap << std::endl ; } /* A bit of mystery code. As best I can figure, setSpecialOptions(2) suppresses the removal of warm start information when checkSolution runs an lp to check a solution. John's comment, ``probably faster to use a basis to get integer solutions'' makes some sense in this context. Didn't try to track down moreMipOptions just yet. */ babModel.setSpecialOptions(babModel.specialOptions() | 2) ; /* { int ndx = whichParam(MOREMIPOPTIONS,numberParameters,parameters) ; int moreMipOptions = parameters[ndx].intValue() ; if (moreMipOptions >= 0) { printf("more mip options %d\n",moreMipOptions); babModel.setSearchStrategy(moreMipOptions); } } */ /* Begin the final run-up to branch-and-cut. Make sure that objects are set up in the solver. It's possible that whoever loaded the model into the solver also set up objects. But it's also entirely likely that none exist to this point (and interesting to note that IPP doesn't need to know anything about objects). */ setupObjects(babSolver, didIPP, &ippObj) ; /* Set the branching method. We can't do this until we establish objects, because the constructor will set up arrays based on the number of objects, and there's no provision to set this information after creation. Arguably not good --- it'd be nice to set this in the prototype model that's cloned for this routine. In CoinSolve, shadowPriceMode is handled with the TESTOSI option. */ OsiChooseStrong strong(babSolver) ; strong.setNumberBeforeTrusted(babModel.numberBeforeTrust()) ; strong.setNumberStrong(babModel.numberStrong()) ; strong.setShadowPriceMode(ctlBlk->chooseStrong_.shadowPriceMode_) ; CbcBranchDefaultDecision decision ; decision.setChooseMethod(strong) ; babModel.setBranchingMethod(decision) ; /* Here I've deleted a huge block of code that deals with external priorities, branch direction, pseudocosts, and solution. (PRIORITYIN) Also a block of code that generates C++ code. */ /* Set up strategy for branch-and-cut. Note that the integer code supplied to setupPreProcessing is *not* compatible with the IPPAction enum. But at least it's documented. See desiredPreProcess_ in CbcStrategyDefault. `1' is accidentally equivalent to IPPOn. */ if (ippAction == CbcGenCtlBlk::IPPStrategy) { CbcStrategyDefault strategy(true, 5, 5) ; strategy.setupPreProcessing(1) ; babModel.setStrategy(strategy) ; } /* Yes! At long last, we're ready for the big call. Do branch and cut. In general, the solver used to return the solution will not be the solver we passed in, so reset babSolver here. */ int statistics = (ctlBlk->printOpt_ > 0) ? ctlBlk->printOpt_ : 0 ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: solver at call to branchAndBound is " << std::hex << babModel.solver() << std::dec << ", log level " << babModel.solver()->messageHandler()->logLevel() << "." << std::endl ; # endif babModel.branchAndBound(statistics) ; babSolver = babModel.solver() ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: solver at return from branchAndBound is " << std::hex << babModel.solver() << std::dec << ", log level " << babModel.solver()->messageHandler()->logLevel() << "." << std::endl ; # endif /* Write out solution to preprocessed model. */ if (ctlBlk->debugCreate_ == "createAfterPre" && babModel.bestSolution()) { CbcGenParamUtils::saveSolution(babSolver, "debug.file") ; } /* Print some information about branch-and-cut. */ # if COIN_CBC_VERBOSITY > 0 std::cout << "Cuts at root node changed objective from " << babModel.getContinuousObjective() << " to " << babModel.rootObjectiveAfterCuts() << std::endl ; for (int iGen = 0 ; iGen < babModel.numberCutGenerators() ; iGen++) { CbcCutGenerator *generator = babModel.cutGenerator(iGen) ; std::cout << generator->cutGeneratorName() << " was tried " << generator->numberTimesEntered() << " times and created " << generator->numberCutsInTotal() << " cuts of which " << generator->numberCutsActive() << " were active after adding rounds of cuts" ; if (generator->timing()) { std::cout << " ( " << generator->timeInCutGenerator() << " seconds)" ; } std::cout << std::endl ; } # endif time2 = CoinCpuTime(); ctlBlk->totalTime_ += time2 - time1; /* If we performed integer preprocessing, time to back it out. */ if (ippAction != CbcGenCtlBlk::IPPOff) { # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: solver passed to IPP postprocess is " << std::hex << babSolver << std::dec << "." << std::endl ; # endif ippObj.postProcess(*babSolver); babModel.assignSolver(preIppSolver) ; babSolver = babModel.solver() ; # if CBC_TRACK_SOLVERS > 0 std::cout << "doBaCParam: solver in babModel after IPP postprocess is " << std::hex << babSolver << std::dec << "." << std::endl ; # endif } /* Write out postprocessed solution to debug file, if requested. */ if (ctlBlk->debugCreate_ == "create" && babModel.bestSolution()) { CbcGenParamUtils::saveSolution(babSolver, "debug.file") ; } /* If we have a good solution, detach the solver with the answer. Fill in the rest of the status information for the benefit of the wider world. */ bool keepAnswerSolver = false ; OsiSolverInterface *answerSolver = 0 ; if (babModel.bestSolution()) { babModel.setModelOwnsSolver(false) ; keepAnswerSolver = true ; answerSolver = babSolver ; } ctlBlk->setBaBStatus(&babModel, CbcGenCtlBlk::BACwBAC, keepAnswerSolver, answerSolver) ; /* And one last bit of information & statistics. */ ctlBlk->printBaBStatus() ; std::cout << " " ; if (keepAnswerSolver) { std::cout << "objective " << babModel.getObjValue() << "; " ; } std::cout << babModel.getNodeCount() << " nodes and " << babModel.getIterationCount() << " iterations - took " << time2 - time1 << " seconds" << std::endl ; return (0) ; }