this repo has no description
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 */