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