this repo has no description
at develop 38 kB view raw
1// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Gleb Belov <gleb.belov@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#ifdef _MSC_VER 13#define _CRT_SECURE_NO_WARNINGS 14#endif 15 16#include <cmath> 17#include <cstdio> 18#include <cstring> 19#include <fstream> 20#include <iomanip> 21#include <iostream> 22#include <sstream> 23#include <stdexcept> 24#include <string> 25 26using namespace std; 27 28#include <minizinc/solvers/MIP/MIP_osicbc_wrap.hh> 29#include <minizinc/utils.hh> 30 31#include <coin/CbcConfig.h> 32#include <coin/CbcEventHandler.hpp> 33#include <coin/CbcSolver.hpp> 34#include <coin/CglCutGenerator.hpp> 35#include <coin/CglPreProcess.hpp> 36#include <coin/ClpConfig.h> 37#include <coin/CoinSignal.hpp> 38 39#define WANT_SOLUTION 40 41string MIP_osicbc_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options*) { 42 string v = "MIP wrapper for COIN-BC "; 43 v += CBC_VERSION; // E.g., 2.9 stable or 2.9.7 latest release 44 v += ", using CLP "; 45 v += CLP_VERSION; 46 v += " Compiled " __DATE__ " " __TIME__; 47 return v; 48} 49 50string MIP_osicbc_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options*) { 51 return string(CBC_VERSION) + "/" + string(CLP_VERSION); 52} 53 54string MIP_osicbc_wrapper::getId() { return "coin-bc"; } 55 56string MIP_osicbc_wrapper::getName() { return "COIN-BC"; } 57 58vector<string> MIP_osicbc_wrapper::getTags() { 59 return {"mip", "float", "api", "osicbc", "coinbc", "cbc"}; 60} 61 62vector<string> MIP_osicbc_wrapper::getStdFlags() { return {"-a", "-p", "-s"}; } 63 64void MIP_osicbc_wrapper::Options::printHelp(ostream& os) { 65 os << "COIN-BC MIP wrapper options:" 66 << std::endl 67 // -s print statistics 68 // << " --readParam <file> read OSICBC parameters from file 69 // << "--writeParam <file> write OSICBC parameters to file 70 // << "--tuneParam instruct OSICBC to tune parameters instead of solving 71 << " --cbcArgs, --cbcFlags, --cbc-flags \"args\"\n" 72 " command-line args passed to callCbc, e.g., \"-cuts off -preprocess off -passc 1\"." 73 << std::endl 74 // \"-preprocess off\" recommended in 2.9.6 75 << " --writeModel <file>" << endl 76 << " write model to <file> (.mps)" << std::endl 77 << " -a, --all\n print intermediate solutions for optimization problems\n" 78 " (not from FeasPump. Can be slow.)" 79 << std::endl 80 << " -p <N>\n use N threads, default: 1. CBC should be configured with " 81 "--enable-cbc-parallel" 82 << std::endl 83 // << "--nomippresolve disable MIP presolving NOT IMPL" << std::endl 84 << " --solver-time-limit <N>\n stop search after N milliseconds" 85 << std::endl 86 // << "--workmem <N> maximal amount of RAM used, MB" << std::endl 87 // << "--readParam <file> read OSICBC parameters from file" << std::endl 88 // << "--writeParam <file> write OSICBC parameters to file" << std::endl 89 // << "--tuneParam instruct OSICBC to tune parameters instead of solving NOT IMPL" 90 91 << " --absGap <n>\n absolute gap |primal-dual| to stop" << std::endl 92 << " --relGap <n>\n relative gap |primal-dual|/<solver-dep> to stop. Default 1e-8, set <0 " 93 "to use backend's default" 94 << std::endl 95 << " --intTol <n>\n integrality tolerance for a variable. Default 1e-8" 96 << std::endl 97 // << "--objDiff <n> objective function discretization. Default 1.0" << std::endl 98 99 << std::endl; 100} 101 102bool MIP_osicbc_wrapper::Options::processOption(int& i, std::vector<std::string>& argv) { 103 MiniZinc::CLOParser cop(i, argv); 104 if (string(argv[i]) == "-a" || string(argv[i]) == "--all" || 105 string(argv[i]) == "--all-solutions") { 106 flag_all_solutions = true; 107 } else if (string(argv[i]) == "-f") { 108 // std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl; 109 } else if (cop.get("--writeModel", &sExportModel)) { 110 } else if (cop.get("-p", &nThreads)) { 111 } else if (cop.get("--solver-time-limit", &nTimeout)) { 112 } else if (cop.get("--workmem", &nWorkMemLimit)) { 113 } else if (cop.get("--readParam", &sReadParams)) { 114 } else if (cop.get("--writeParam", &sWriteParams)) { 115 } else if (cop.get("--cbcArgs --cbcFlags --cbc-flags --solver-flags", &cbc_cmdOptions)) { 116 } else if (cop.get("--absGap", &absGap)) { 117 } else if (cop.get("--relGap", &relGap)) { 118 } else if (cop.get("--intTol", &intTol)) { 119 // } else if ( cop.get( "--objDiff", &objDiff ) ) { 120 } else 121 return false; 122 return true; 123} 124 125void MIP_osicbc_wrapper::wrap_assert(bool cond, string msg, bool fTerm) { 126 if (!cond) { 127 // strcpy(osicbc_buffer, "[NO ERROR STRING GIVEN]"); 128 // CBCgeterrorstring (env, status, osicbc_buffer); 129 string msgAll = (" MIP_osicbc_wrapper runtime error: " + msg + " " + osicbc_buffer); 130 cerr << msgAll << endl; 131 if (fTerm) { 132 cerr << "TERMINATING." << endl; 133 throw runtime_error(msgAll); 134 } 135 } 136} 137 138void MIP_osicbc_wrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, 139 MIP_wrapper::VarType* vt, string* names) { 140 /// Convert var types: 141 // vector<char> ctype(n); 142 // vector<char*> pcNames(n); 143 CoinPackedVector cpv; 144 vector<CoinPackedVectorBase*> pCpv(n, &cpv); 145 osi.addCols(n, pCpv.data(), lb, ub, obj); // setting integer & names later 146 // status = CBCnewcols (env, lp, n, obj, lb, ub, &ctype[0], &pcNames[0]); 147 // wrap_assert( !status, "Failed to declare variables." ); 148} 149 150void MIP_osicbc_wrapper::addRow(int nnz, int* rmatind, double* rmatval, 151 MIP_wrapper::LinConType sense, double rhs, int mask, 152 string rowName) { 153 /// Convert var types: 154 double rlb = rhs, rub = rhs; 155 switch (sense) { 156 case LQ: 157 rlb = -osi.getInfinity(); 158 break; 159 case EQ: 160 break; 161 case GQ: 162 rub = osi.getInfinity(); 163 break; 164 default: 165 throw runtime_error(" MIP_wrapper: unknown constraint type"); 166 } 167 // ignoring mask for now. TODO 168 // 1-by-1 too slow: 169 // try { 170 // CoinPackedVector cpv(nnz, rmatind, rmatval); 171 // osi.addRow(cpv, rlb, rub); 172 // } catch (const CoinError& err) { 173 // cerr << " COIN-OR Error: " << err.message() << endl; 174 // throw runtime_error(err.message()); 175 // } 176 /// Segfault: 177 // rowStarts.push_back(columns.size()); 178 // columns.insert(columns.end(), rmatind, rmatind + nnz); 179 // element.insert(element.end(), rmatval, rmatval + nnz); 180 rows.push_back(CoinPackedVector(nnz, rmatind, rmatval)); 181 rowlb.push_back(rlb); 182 rowub.push_back(rub); 183} 184 185bool MIP_osicbc_wrapper::addWarmStart(const std::vector<VarId>& vars, 186 const std::vector<double> vals) { 187 assert(vars.size() == vals.size()); 188 static_assert(sizeof(VarId) == sizeof(int), "VarId should be (u)int currently"); 189 for (int i = 0; i < vars.size(); ++i) warmstart[vars[i]] = vals[i]; 190 return true; 191} 192 193/// SolutionCallback ------------------------------------------------------------------------ 194/// OSICBC ensures thread-safety?? TODO 195/// Event handling copied from examples/interrupt.cpp, Cbc 2.9.8 rev 2272 196 197/************************************************************************ 198 199This main program shows how to take advantage of the standalone cbc in your program, 200while still making major modifications. 201This is very like driver4 but allows interrupts in clp for faster stopping 202It would be up to user to clean up output as stopping in Clp seems to 203give correct results but can think it is stopping in an odd way. 204To make cleaner would need more events defined (in Cbc AND Clp) 205First it reads in an integer model from an mps file 206Then it initializes the integer model with cbc defaults 207Then it calls CbcMain1 passing all parameters apart from first but with callBack to modify stuff 208Finally it prints solution 209 210************************************************************************/ 211/* Meaning of whereFrom: 212 1 after initial solve by dualsimplex etc 213 2 after preprocessing 214 3 just before branchAndBound (so user can override) 215 4 just after branchAndBound (before postprocessing) 216 5 after postprocessing 217*/ 218/* Meaning of model status is as normal 219 status 220 -1 before branchAndBound 221 0 finished - check isProvenOptimal or isProvenInfeasible to see if solution found 222 (or check value of best solution) 223 1 stopped - on maxnodes, maxsols, maxtime 224 2 difficulties so run was abandoned 225 (5 event user programmed event occurred) 226 227 cbc secondary status of problem 228 -1 unset (status_ will also be -1) 229 0 search completed with solution 230 1 linear relaxation not feasible (or worse than cutoff) 231 2 stopped on gap 232 3 stopped on nodes 233 4 stopped on time 234 5 stopped on user event 235 6 stopped on solutions 236 7 linear relaxation unbounded 237 238 but initially check if status is 0 and secondary status is 1 -> infeasible 239 or you can check solver status. 240*/ 241/* Return non-zero to return quickly */ 242// static int callBack(CbcModel * model, int whereFrom) 243//{ 244// int returnCode=0; 245// switch (whereFrom) { 246// case 1: 247// case 2: 248// if (!model->status()&&model->secondaryStatus()) 249// returnCode=1; 250// break; 251// case 3: 252// { 253// //CbcCompareUser compare; 254// //model->setNodeComparison(compare); 255// } 256// break; 257// case 4: 258// // If not good enough could skip postprocessing 259// break; 260// case 5: 261// break; 262// default: 263// abort(); 264// } 265// return returnCode; 266//} 267static int cancelAsap = 0; 268/* 269 0 - not yet in Cbc 270 1 - in Cbc with new signal handler 271 2 - ending Cbc 272*/ 273static int statusOfCbc = 0; 274static CoinSighandler_t saveSignal = static_cast<CoinSighandler_t>(0); 275 276extern "C" { 277static void 278#if defined(_MSC_VER) 279 __cdecl 280#endif // _MSC_VER 281 signal_handler(int /*whichSignal*/) { 282 cancelAsap = 3; 283 return; 284} 285} 286/** This is so user can trap events and do useful stuff. 287 288 CbcModel model_ is available as well as anything else you care 289 to pass in 290*/ 291 292struct EventUserInfo { 293 MIP_wrapper::CBUserInfo* pCbui = 0; 294 CglPreProcess* pPP = 0; 295}; 296 297extern CglPreProcess* cbcPreProcessPointer; 298class MyEventHandler3 : public CbcEventHandler { 299public: 300 /**@name Overrides */ 301 //@{ 302 virtual CbcAction event(CbcEvent whichEvent); 303 //@} 304 305 /**@name Constructors, destructor etc*/ 306 //@{ 307 /** Default constructor. */ 308 MyEventHandler3(EventUserInfo& u_); 309 /// Constructor with pointer to model (redundant as setEventHandler does) 310 MyEventHandler3(CbcModel* model, EventUserInfo& u_); 311 /** Destructor */ 312 virtual ~MyEventHandler3(); 313 /** The copy constructor. */ 314 MyEventHandler3(const MyEventHandler3& rhs); 315 /// Assignment 316 MyEventHandler3& operator=(const MyEventHandler3& rhs); 317 /// Clone 318 virtual CbcEventHandler* clone() const; 319 //@} 320 321protected: 322 // data goes here 323 EventUserInfo ui; 324 double bestSolutionValue_ = DBL_MAX; // always min 325}; 326//------------------------------------------------------------------- 327// Default Constructor 328//------------------------------------------------------------------- 329MyEventHandler3::MyEventHandler3(EventUserInfo& u_) : CbcEventHandler(), ui(u_) { assert(0); } 330 331//------------------------------------------------------------------- 332// Copy constructor 333//------------------------------------------------------------------- 334MyEventHandler3::MyEventHandler3(const MyEventHandler3& rhs) : CbcEventHandler(rhs), ui(rhs.ui) {} 335 336// Constructor with pointer to model 337MyEventHandler3::MyEventHandler3(CbcModel* model, EventUserInfo& u_) 338 : CbcEventHandler(model), ui(u_) {} 339 340//------------------------------------------------------------------- 341// Destructor 342//------------------------------------------------------------------- 343MyEventHandler3::~MyEventHandler3() {} 344 345//---------------------------------------------------------------- 346// Assignment operator 347//------------------------------------------------------------------- 348MyEventHandler3& MyEventHandler3::operator=(const MyEventHandler3& rhs) { 349 if (this != &rhs) { 350 CbcEventHandler::operator=(rhs); 351 } 352 ui = rhs.ui; 353 return *this; 354} 355//------------------------------------------------------------------- 356// Clone 357//------------------------------------------------------------------- 358CbcEventHandler* MyEventHandler3::clone() const { return new MyEventHandler3(*this); } 359 360CbcEventHandler::CbcAction MyEventHandler3::event(CbcEvent whichEvent) { 361 if (!statusOfCbc) { 362 // override signal handler 363 // register signal handler 364 saveSignal = signal(SIGINT, signal_handler); 365 statusOfCbc = 1; 366 } 367 if ((cancelAsap & 2) != 0) { 368 // printf("Cbc got cancel\n"); 369 // switch off Clp cancel 370 cancelAsap &= 2; 371 return stop; 372 } 373 // If in sub tree carry on 374 if (!model_->parentModel()) { 375 if (whichEvent == endSearch && statusOfCbc == 1) { 376 // switch off cancel 377 cancelAsap = 0; 378 // restore signal handler 379 signal(SIGINT, saveSignal); 380 statusOfCbc = 2; 381 } 382 if (whichEvent == solution || whichEvent == heuristicSolution) { 383 // John Forrest 27.2.16: 384 // check not duplicate 385 if (model_->getObjValue() < bestSolutionValue_) { 386 bestSolutionValue_ = model_->getObjValue(); 387 // If preprocessing was done solution will be to processed model 388 // int numberColumns = model_->getNumCols(); 389 const double* bestSolution = model_->bestSolution(); 390 assert(bestSolution); 391 // printf("value of solution is %g\n",model_->getObjValue()); 392 393 // Trying to obtain solution for the original model: 394 assert(model_ && model_->solver()); 395 // double objOffset=0; 396 // model_->solver()->getDblParam(OsiObjOffset, objOffset); 397 double objVal = 398 (model_->getObjValue()); //- objOffset); John Forrest suggested to remove, 17.11.17 399 double bestBnd = (model_->getBestPossibleObjValue()); //- objOffset); 400 if (0 != cbcPreProcessPointer) { 401 if (OsiSolverInterface* cbcPreOrig = cbcPreProcessPointer->originalModel()) { 402 objVal *= cbcPreOrig->getObjSense(); 403 bestBnd *= cbcPreOrig->getObjSense(); 404 } 405 } else { 406 objVal *= model_->getObjSense(); 407 bestBnd *= model_->getObjSense(); 408 } 409 OsiSolverInterface* origModel = 0; 410 if (0 != cbcPreProcessPointer && 0 != model_->continuousSolver()) { 411 OsiSolverInterface* solver = (model_->continuousSolver()->clone()); 412 // ? model_->continuousSolver()->clone() 413 // : model_->continuousSolver()->clone(); 414 int numberColumns = solver->getNumCols(); 415 for (int i = 0; i < numberColumns; i++) { 416 if (solver->isInteger(i)) { 417 solver->setColLower(i, bestSolution[i]); 418 solver->setColUpper(i, bestSolution[i]); 419 } 420 } 421 solver->resolve(); 422 cbcPreProcessPointer->postProcess(*solver, false); 423 delete solver; 424 origModel = cbcPreProcessPointer->originalModel(); 425 ui.pCbui->pOutput->x = origModel->getColSolution(); 426 } else { 427 origModel = model_->solver(); 428 ui.pCbui->pOutput->x = bestSolution; 429 } 430 if (ui.pCbui->fVerb) 431 cerr << " % OBJ VAL RAW: " << model_->getObjValue() << " OBJ VAL ORIG(?): " << objVal 432 << " % BND RAW: " << model_->getBestPossibleObjValue() << " BND ORIG(?): " 433 << bestBnd 434 // << " &prepro: " << cbcPreProcessPointer 435 // << " &model_._solver(): " << model_->solver() 436 << " orig NCols: " << ui.pCbui->pOutput->nCols 437 << " prepro NCols: " << model_->getNumCols(); 438 assert(origModel->getNumCols() == ui.pCbui->pOutput->nCols); 439 if (ui.pCbui->fVerb) { 440 if (ui.pCbui->pOutput->nObjVarIndex >= 0) 441 cerr << " objVAR: " << ui.pCbui->pOutput->x[ui.pCbui->pOutput->nObjVarIndex]; 442 cerr << endl; 443 } 444 ui.pCbui->pOutput->objVal = objVal; 445 // origModel->getObjValue(); 446 ui.pCbui->pOutput->status = MIP_wrapper::SAT; 447 ui.pCbui->pOutput->statusName = "feasible from a callback"; 448 ui.pCbui->pOutput->bestBound = bestBnd; 449 ui.pCbui->pOutput->dWallTime = 450 std::chrono::duration<double>(std::chrono::steady_clock::now() - 451 ui.pCbui->pOutput->dWallTime0) 452 .count(); 453 ui.pCbui->pOutput->dCPUTime = model_->getCurrentSeconds(); 454 ui.pCbui->pOutput->nNodes = model_->getNodeCount(); 455 ui.pCbui->pOutput->nOpenNodes = -1; // model_->getNodeCount2(); 456 457 /// Call the user function: 458 if (ui.pCbui->solcbfn) { 459 (*(ui.pCbui->solcbfn))(*(ui.pCbui->pOutput), ui.pCbui->psi); 460 ui.pCbui->printed = true; 461 } 462 return noAction; // carry on 463 } else { 464 return noAction; // carry on 465 } 466 } else { 467 return noAction; 468 } 469 } else { 470 return noAction; // carry on 471 } 472} 473 474/** This is so user can trap events and do useful stuff. 475 476 ClpSimplex model_ is available as well as anything else you care 477 to pass in 478*/ 479class MyEventHandler4 : public ClpEventHandler { 480public: 481 /**@name Overrides */ 482 //@{ 483 virtual int event(Event whichEvent); 484 //@} 485 486 /**@name Constructors, destructor etc*/ 487 //@{ 488 /** Default constructor. */ 489 MyEventHandler4(); 490 /// Constructor with pointer to model (redundant as setEventHandler does) 491 MyEventHandler4(ClpSimplex* model); 492 /** Destructor */ 493 virtual ~MyEventHandler4(); 494 /** The copy constructor. */ 495 MyEventHandler4(const MyEventHandler4& rhs); 496 /// Assignment 497 MyEventHandler4& operator=(const MyEventHandler4& rhs); 498 /// Clone 499 virtual ClpEventHandler* clone() const; 500 //@} 501 502protected: 503 // data goes here 504}; 505//------------------------------------------------------------------- 506// Default Constructor 507//------------------------------------------------------------------- 508MyEventHandler4::MyEventHandler4() : ClpEventHandler() {} 509 510//------------------------------------------------------------------- 511// Copy constructor 512//------------------------------------------------------------------- 513MyEventHandler4::MyEventHandler4(const MyEventHandler4& rhs) : ClpEventHandler(rhs) {} 514 515// Constructor with pointer to model 516MyEventHandler4::MyEventHandler4(ClpSimplex* model) : ClpEventHandler(model) {} 517 518//------------------------------------------------------------------- 519// Destructor 520//------------------------------------------------------------------- 521MyEventHandler4::~MyEventHandler4() {} 522 523//---------------------------------------------------------------- 524// Assignment operator 525//------------------------------------------------------------------- 526MyEventHandler4& MyEventHandler4::operator=(const MyEventHandler4& rhs) { 527 if (this != &rhs) { 528 ClpEventHandler::operator=(rhs); 529 } 530 return *this; 531} 532//------------------------------------------------------------------- 533// Clone 534//------------------------------------------------------------------- 535ClpEventHandler* MyEventHandler4::clone() const { return new MyEventHandler4(*this); } 536 537int MyEventHandler4::event(Event whichEvent) { 538 if ((cancelAsap & 1) != 0) { 539 // printf("Clp got cancel\n"); 540 return 5; 541 } else { 542 return -1; 543 } 544} 545// end SolutionCallback --------------------------------------------------------------------- 546 547MIP_osicbc_wrapper::Status MIP_osicbc_wrapper::convertStatus(CbcModel* pModel) { 548 Status s = Status::UNKNOWN; 549 /* Converting the status. */ 550 if (pModel->isProvenOptimal()) { 551 s = Status::OPT; 552 output.statusName = "Optimal"; 553 // wrap_assert(osi., "Optimality reported but pool empty?", false); 554 } else if (pModel->isProvenInfeasible()) { 555 s = Status::UNSAT; 556 output.statusName = "Infeasible"; 557 } else if (pModel->isProvenDualInfeasible()) { 558 s = Status::UNBND; 559 output.statusName = "Dual infeasible"; 560 // s = Status::UNSATorUNBND; 561 } else if // wrong: (pModel->getColSolution()) 562 (fabs(pModel->getObjValue()) < 1e50) { 563 s = Status::SAT; 564 output.statusName = "Feasible"; 565 } else if (pModel->isAbandoned()) { // AFTER feas-ty 566 s = Status::__ERROR; 567 output.statusName = "Abandoned"; 568 } else { 569 s = Status::UNKNOWN; 570 output.statusName = "Unknown"; 571 } 572 return s; 573} 574 575MIP_osicbc_wrapper::Status MIP_osicbc_wrapper::convertStatus() { 576 Status s = Status::UNKNOWN; 577 /* Converting the status. */ 578 if (osi.isProvenOptimal()) { 579 s = Status::OPT; 580 output.statusName = "Optimal"; 581 // wrap_assert(osi., "Optimality reported but pool empty?", false); 582 } else if (osi.isProvenPrimalInfeasible()) { 583 s = Status::UNSAT; 584 output.statusName = "Infeasible"; 585 } else if (osi.isProvenDualInfeasible()) { 586 s = Status::UNBND; 587 output.statusName = "Dual infeasible"; 588 // s = Status::UNSATorUNBND; 589 } else if (osi.isAbandoned()) { 590 s = Status::__ERROR; 591 output.statusName = "Abandoned"; 592 } else if // wrong: (pModel->getColSolution()) 593 (fabs(osi.getObjValue()) < osi.getInfinity()) { 594 s = Status::SAT; 595 output.statusName = "Feasible"; 596 cout << " getSolverObjValue(as minim) == " << osi.getObjValue() << endl; 597 } else { 598 s = Status::UNKNOWN; 599 output.statusName = "Unknown"; 600 } 601 return s; 602} 603 604void MIP_osicbc_wrapper::solve() { // Move into ancestor? 605 if (options->flag_all_solutions && 0 == nProbType) 606 cerr << "WARNING. --all-solutions for SAT problems not implemented." << endl; 607 try { 608 /// Not using CoinPackedMatrix any more, so need to add all constraints at once: 609 /// But this gives segf: 610 // osi.addRows(rowStarts.size(), rowStarts.data(), 611 // columns.data(), element.data(), rowlb.data(), rowub.data()); 612 /// So: 613 MIP_wrapper::addPhase1Vars(); // only now 614 if (fVerbose) cerr << " MIP_osicbc_wrapper: adding constraints physically..." << flush; 615 vector<CoinPackedVectorBase*> pRows(rowlb.size()); 616 for (int i = 0; i < rowlb.size(); ++i) pRows[i] = &rows[i]; 617 osi.addRows(rowlb.size(), pRows.data(), rowlb.data(), rowub.data()); 618 // rowStarts.clear(); 619 // columns.clear(); 620 // element.clear(); 621 pRows.clear(); 622 rows.clear(); 623 rowlb.clear(); 624 rowub.clear(); 625 if (fVerbose) cerr << " done." << endl; 626 /////////////// Last-minute solver options ////////////////// 627 // osi->loadProblem(*matrix, 628 { 629 std::vector<VarId> integer_vars; 630 for (unsigned int i = 0; i < colObj.size(); i++) { 631 if (REAL != colTypes[i] 632 // && is_used[i] 633 ) { 634 integer_vars.push_back(i); 635 } 636 } 637 osi.setInteger(integer_vars.data(), integer_vars.size()); 638 } 639 if (options->sExportModel.size()) { 640 // Not implemented for OsiClp: 641 // osi.setColNames(colNames, 0, colObj.size(), 0); 642 vector<const char*> colN(colObj.size()); 643 for (int j = 0; j < colNames.size(); ++j) colN[j] = colNames[j].c_str(); 644 osi.writeMpsNative(options->sExportModel.c_str(), 0, colN.data()); 645 } 646 647 // Tell solver to return fast if presolve or initial solve infeasible 648 osi.getModelPtr()->setMoreSpecialOptions(3); 649 // allow Clp to handle interrupts 650 MyEventHandler4 clpEventHandler; 651 osi.getModelPtr()->passInEventHandler(&clpEventHandler); 652 653 /* switch on/off output to the screen */ 654 class NullCoinMessageHandler : public CoinMessageHandler { 655 int print() { return 0; } 656 void checkSeverity() {} 657 } nullHandler; 658 659 // CbcSolver control(osi); 660 // // initialize ??????? 661 // control.fillValuesInSolver(); 662 // CbcModel * pModel = control.model(); 663 if (fVerbose) cerr << " Model creation..." << endl; 664 665 // #define __USE_CbcSolver__ -- not linked rev2274 666 /// FOR WARMSTART 667 for (const auto& vv : warmstart) { 668 osi.setColName(vv.first, colNames[vv.first]); 669 } 670#ifdef __USE_CbcSolver__ 671 CbcSolver control(osi); 672 // initialize 673 control.fillValuesInSolver(); 674 CbcModel& model = *control.model(); 675#else 676 CbcModel model(osi); 677#endif 678 // CbcSolver control(osi); 679 // control.solve(); 680 if (options->absGap >= 0.0) model.setAllowableGap(options->absGap); 681 if (options->relGap >= 0.0) model.setAllowableFractionGap(options->relGap); 682 if (options->intTol >= 0.0) model.setIntegerTolerance(options->intTol); 683 // model.setCutoffIncrement( objDiff ); 684 685 /// WARMSTART 686 { 687 std::vector<std::pair<std::string, double> > mipstart; 688 for (const auto& vv : warmstart) { 689 mipstart.push_back(std::make_pair(colNames[vv.first], vv.second)); 690 } 691 warmstart.clear(); 692 model.setMIPStart(mipstart); 693 } 694 695 CoinMessageHandler msgStderr(stderr); 696 697 class StderrCoinMessageHandler : public CoinMessageHandler { 698 int print() { 699 cerr << messageBuffer_ << endl; 700 return 0; 701 } 702 void checkSeverity() {} 703 } stderrHandler; 704 705 if (fVerbose) { 706 // osi.messageHandler()->setLogLevel(1); 707 // osi.getModelPtr()->setLogLevel(1); 708 // osi.getRealSolverPtr()->messageHandler()->setLogLevel(0); 709 // DOES NOT WORK: TODO 710 // model.passInMessageHandler( &stderrHandler ); 711 msgStderr.setLogLevel(0, 1); 712 model.passInMessageHandler(&msgStderr); 713 // model.setLogLevel(1); 714 // model.solver()->messageHandler()->setLogLevel(0); 715 } else { 716 model.passInMessageHandler(&nullHandler); 717 model.messageHandler()->setLogLevel(0); 718 model.setLogLevel(0); 719 model.solver()->setHintParam(OsiDoReducePrint, true, OsiHintTry); 720 // osi.passInMessageHandler(&nullHandler); 721 // osi.messageHandler()->setLogLevel(0); 722 // osi.setHintParam(OsiDoReducePrint, true, OsiHintTry); 723 } 724 725 if (options->nTimeout != 0) { 726 // osi.setMaximumSeconds(nTimeout); 727 model.setMaximumSeconds(static_cast<double>(options->nTimeout) / 1000.0); 728 } 729 730 /// TODO 731 // if(all_solutions && obj.getImpl()) { 732 // IloNum lastObjVal = (obj.getSense() == IloObjective::Minimize ) ? 733 // _iloosicbc->use(SolutionCallback(_iloenv, lastObjVal, *this)); 734 // Turn off OSICBC logging 735 736 /// Solution callback 737 output.nCols = colObj.size(); 738 // x.resize(output.nCols); 739 // output.x = &x[0]; 740 741 if (options->flag_all_solutions && cbui.solcbfn) { 742 // Event handler. Should be after CbcMain0()? 743 EventUserInfo ui; 744 ui.pCbui = &cbui; 745 // ui.pPP = 0; 746 MyEventHandler3 eventHandler(&model, ui); 747 model.passInEventHandler(&eventHandler); 748 } 749 750 /// Cuts needed 751 if (cbui.cutcbfn) { 752 /// This class is passed to CBC to organize cut callbacks 753 /// We need original solutions here (combinatorial cuts) 754 class CutCallback : public CglCutGenerator { 755 MIP_wrapper::CBUserInfo& cbui; 756 757 public: 758 CutCallback(MIP_wrapper::CBUserInfo& ui) : cbui(ui) {} 759 CglCutGenerator* clone() const override { return new CutCallback(cbui); } 760 /// Make sure this overrides but we might need to compile this with old CBC as well 761 bool needsOriginalModel() const /*override*/ { return true; } 762 void generateCuts(const OsiSolverInterface& si, OsiCuts& cs, 763 const CglTreeInfo info = CglTreeInfo()) override { 764 cbui.pOutput->nCols = si.getNumCols(); 765 MZN_ASSERT_HARD_MSG( 766 cbui.pOutput->nCols == ((MIP_wrapper*)(cbui.wrapper))->colNames.size(), 767 "CBC cut callback: current model is different? Ncols=" 768 << cbui.pOutput->nCols << ", originally " 769 << ((MIP_wrapper*)(cbui.wrapper))->colNames.size() 770 << ". If you have an old version of CBC, to use combinatorial cuts" 771 " run with --cbcArgs '-preprocess off'"); 772 cbui.pOutput->x = si.getColSolution(); // change the pointer? 773 MIP_wrapper::CutInput cuts; 774 cbui.cutcbfn(*cbui.pOutput, cuts, cbui.psi, 775 info.options & 128); // options&128: integer candidate 776 for (const auto& cut : cuts) { // Convert cut sense 777 OsiRowCut rc; 778 switch (cut.sense) { 779 case LQ: 780 rc.setUb(cut.rhs); 781 break; 782 case GQ: 783 rc.setLb(cut.rhs); 784 break; 785 default: 786 assert(EQ == cut.sense); 787 rc.setLb(cut.rhs); 788 rc.setUb(cut.rhs); 789 } 790 rc.setRow(cut.rmatind.size(), cut.rmatind.data(), cut.rmatval.data()); 791 cs.insertIfNotDuplicate(rc); 792 } 793 } 794 }; 795 CutCallback ccb(cbui); 796 model.addCutGenerator(&ccb, 10, "MZN_cuts", true, true); // also at solution 797 } 798 799 if (1 < options->nThreads) { 800 options->cbc_cmdOptions += " -threads "; 801 ostringstream oss; 802 oss << options->nThreads; 803 options->cbc_cmdOptions += oss.str(); 804 } 805 options->cbc_cmdOptions += " -solve"; 806 options->cbc_cmdOptions += " -quit"; 807 808 cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); 809 output.dCPUTime = clock(); 810 811 /* OLD: Optimize the problem and obtain solution. */ 812 // model.branchAndBound(); 813 // osi.branchAndBound(); 814 815 /// TAKEN FORM DRIVER3.CPP, seems to use most features: 816// CbcMain0(model); 817// CbcCbcParamUtils::setCbcModelDefaults(model) ; 818// const char * argv2[]={"mzn-cbc","-solve","-quit"}; 819// CbcMain1(3,argv2,model); 820#ifdef __USE_CbcSolver__ 821 if (fVerbose) 822 cerr << " Calling control.solve() with options '" << options->cbc_cmdOptions << "'..." 823 << endl; 824 control.solve(options->cbc_cmdOptions.c_str(), 1); 825#else 826#define __USE_callCbc1__ 827#ifdef __USE_callCbc1__ 828 if (fVerbose) 829 cerr << " Calling callCbc with options '" << options->cbc_cmdOptions << "'..." << endl; 830 callCbc(options->cbc_cmdOptions, model); 831// callCbc1(cbc_cmdOptions, model, callBack); 832// What is callBack() for? TODO 833#else 834 CbcMain0(model); 835 // should be here? 836 // // Event handler 837 // EventUserInfo ui; 838 // MyEventHandler3 eventHandler( &model, ui ); 839 // model.passInEventHandler(&eventHandler); 840 /* Now go into code for standalone solver 841 Could copy arguments and add -quit at end to be safe 842 but this will do 843 */ 844 vector<string> argvS; 845 MiniZinc::split(cbc_cmdOptions, argvS); 846 vector<const char*> argv; 847 MiniZinc::vecString2vecPChar(argvS, argv); 848 if (fVerbose) cerr << " Calling CbcMain1 with options '" << cbc_cmdOptions << "'..." << endl; 849 CbcMain1(argv.size(), argv.data(), model, callBack); 850#endif 851#endif 852 853 output.dWallTime = 854 std::chrono::duration<double>(std::chrono::steady_clock::now() - output.dWallTime0).count(); 855 output.dCPUTime = (clock() - output.dCPUTime) / CLOCKS_PER_SEC; 856 857 output.status = convertStatus(&model); 858 // output.status = convertStatus(); 859 860 /// Continuing to fill the output object: 861 if (Status::OPT == output.status || Status::SAT == output.status) { 862 output.objVal = model.getObjValue(); 863 // output.objVal = osi.getObjValue(); 864 865 /* The size of the problem should be obtained by asking OSICBC what 866 the actual size is, rather than using what was passed to CBCcopylp. 867 cur_numrows and cur_numcols store the current number of rows and 868 columns, respectively. */ // ?????????????? TODO 869 870 int cur_numcols = model.getNumCols(); 871 // int cur_numcols = osi.getNumCols (); 872 assert(cur_numcols == colObj.size()); 873 874 wrap_assert(model.getColSolution(), "Failed to get variable values."); 875 x.assign(model.getColSolution(), model.getColSolution() + cur_numcols); // ColSolution(); 876 output.x = x.data(); 877 // output.x = osi.getColSolution(); 878 if (cbui.solcbfn && (!options->flag_all_solutions || !cbui.printed)) { 879 cbui.solcbfn(output, cbui.psi); 880 } 881 } 882 output.bestBound = model.getBestPossibleObjValue(); 883 // output.bestBound = -1; 884 output.nNodes = model.getNodeCount(); 885 // output.nNodes = osi.getNodeCount(); 886 output.nOpenNodes = -1; 887 888 } catch (CoinError& err) { 889 err.print(true); 890 } 891} 892 893void MIP_osicbc_wrapper::setObjSense(int s) { osi.setObjSense(-s); } 894 895/* 896 897try the following for example: 898 899CbcMain0(model); 900const char * argv2[]={"driver4","-cuts","off" ,"-preprocess","off","-passc","1","-solve","-quit"}; 901CbcMain1(9,argv2,model); 902 903you can add any feature you want to argv2 ... 904 905if you want to add cuts yourself, or heuristics, do the following: 906 907 OsiSolverInterface *solver2 = osi; 908 CglPreProcess *process = new CglPreProcess; 909 solver2 = process->preProcess(*solver,false,2); 910 911 CbcModel model1(*solver2); 912 913 model1.initialSolve(); 914 915 //============================================== 916 917 CglProbing generator1; 918 generator1.setUsingObjective(true); 919 generator1.setMaxPass(1); 920 generator1.setMaxPassRoot(5); 921 generator1.setMaxProbe(10); 922 generator1.setMaxProbeRoot(1000); 923 generator1.setMaxLook(50); 924 generator1.setMaxLookRoot(500); 925 generator1.setMaxElements(200); 926 generator1.setRowCuts(3); 927 928 CglGomory generator2; 929 generator2.setLimit(300); 930 931 CglKnapsackCover generator3; 932 933 CglRedSplit generator4; 934 generator4.setLimit(200); 935 936 CglClique generator5; 937 generator5.setStarCliqueReport(false); 938 generator5.setRowCliqueReport(false); 939 940 CglMixedIntegerRounding2 mixedGen; 941 CglFlowCover flowGen; 942 943 CglGMI cut1; 944 CglMixedIntegerRounding2 cut2; 945 CglOddHole cut3; 946 CglSimpleRounding cut4; 947 CglResidualCapacity cut5; 948 CglTwomir cut6; 949 CglZeroHalf cut7; 950 951 model1.addCutGenerator(&generator1,-1,"Probing"); 952 model1.addCutGenerator(&generator2,-1,"Gomory"); 953 model1.addCutGenerator(&generator3,-1,"Knapsack"); 954 model1.addCutGenerator(&generator4,-1,"RedSplit"); 955 model1.addCutGenerator(&generator5,-1,"Clique"); 956 model1.addCutGenerator(&flowGen,-1,"FlowCover"); 957 model1.addCutGenerator(&mixedGen,-1,"MixedIntegerRounding"); 958 model1.addCutGenerator(&cut1,-1,"GMI"); 959 model1.addCutGenerator(&cut2,-1,"MixedIntegerRounding2"); 960 model1.addCutGenerator(&cut3,-1,"OddHole"); 961 model1.addCutGenerator(&cut4,-1,"SimpleRounding"); 962 model1.addCutGenerator(&cut5,-1,"ResidualCapacity"); 963 model1.addCutGenerator(&cut6,-1,"Twomir"); 964 model1.addCutGenerator(&cut7,-1,"ZeroHalf"); 965 966 967 968 CbcRounding heuristic1(model1); 969 CbcHeuristicLocal heuristic2(model1); 970 971 972 model1.addHeuristic(&heuristic1); 973 model1.addHeuristic(&heuristic2); 974 975 976 977 978 model1.setMaximumCutPassesAtRoot(50); 979 model1.setMaximumCutPasses(1000); 980 981 982 983 model1.branchAndBound(); 984 985 986 OsiSolverInterface * solver3; 987 988 process->postProcess(*model1.solver()); 989 990 solver3 = solver; 991 992 or, use the default strategy: 993 994CbcStrategyDefault strategy(5); 995model1.setStrategy(strategy); 996 997 998 999 1000 1001 1002On Sun, Oct 11, 2015 at 8:38 PM, Gleb Belov <gleb.belov@monash.edu> wrote: 1003 1004 Hi, 1005 1006 I am trying to call Cbc 2.9.6 from my program. When using the tutorial-style approach 1007 1008 OsiClpSolverInterface osi; 1009 osi.add ....... 1010 CbcModel model(osi); 1011 model.branchAndBound(); 1012 1013 there seem to be no cuts and other stuff applied. When using the method from the examples, 1014 1015 CbcMain0(model); 1016 const char * argv2[]={"driver4","-solve","-quit"}; 1017 CbcMain1(3,argv2,model); 1018 1019 there are cuts applied, but obviously different (less aggressive) to the standalone Cbc 1020executable. I also tried CbcSolver class but its method solve() is not found by the linker. So what 1021is the 'standard' way of using the 'default' add-ons? 1022 1023 Moreover. The attached example crashes both in the standalone Cbc and in the CbcCmain0/1 variant 1024after a few minutes. 1025 1026 Thanks 1027 1028 _______________________________________________ 1029 Cbc mailing list 1030 Cbc@list.coin-or.org 1031 http://list.coin-or.org/mailman/listinfo/cbc 1032 1033 1034 1035 1036Hi, what is currently good way to have a solution callback in Cbc? the 1037interrupt example shows 2 ways, don't know which is right. 1038 1039Moreover, it says that the solution would be given for the preprocessed 1040model. Is it possible to produce one for the original? Is it possible to 1041call other functions from inside, such as number of nodes, dual bound? 1042 1043Thanks 1044 1045From john.forrest at fastercoin.com Thu Oct 8 10:34:15 2015 1046From: john.forrest at fastercoin.com (John Forrest) 1047Date: Thu, 8 Oct 2015 15:34:15 +0100 1048Subject: [Cbc] Solution callbacks 1049In-Reply-To: <5615F778.9020601@monash.edu> 1050References: <5615F778.9020601@monash.edu> 1051Message-ID: <56167EE7.6000607@fastercoin.com> 1052 1053Gleb, 1054 1055On 08/10/15 05:56, Gleb Belov wrote: 1056> Hi, what is currently good way to have a solution callback in Cbc? the 1057> interrupt example shows 2 ways, don't know which is right. 1058> 1059 1060It is the event handling code you would be using. 1061> Moreover, it says that the solution would be given for the 1062> preprocessed model. Is it possible to produce one for the original? 1063 1064At present no. In principle not difficult. First the callback function 1065would have to be modified to get passed the CglPreProcess object - 1066easy. Then in event handler you could make a copy of object and 1067postsolve (you need a copy as postsolve deletes data). 1068> Is it possible to call other functions from inside, such as number of 1069> nodes, dual bound? 1070 1071Yes - you have CbcModel * model_ so things like that are available (or 1072could easily be made available) 1073 1074> 1075> Thanks 1076> 1077 1078John Forrest 1079 1080 1081 */