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 <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 */