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/config.hh>
17#include <minizinc/exception.hh>
18#include <minizinc/file_utils.hh>
19#include <minizinc/utils_savestream.hh>
20
21#include <cmath>
22#include <cstring>
23#include <ctime>
24#include <fstream>
25#include <iomanip>
26#include <iostream>
27#include <sstream>
28#include <stdexcept>
29#include <string>
30
31#ifdef GUROBI_PLUGIN
32#ifdef HAS_DLFCN_H
33#include <dlfcn.h>
34#elif defined HAS_WINDOWS_H
35#define NOMINMAX // Ensure the words min/max remain available
36#include <Windows.h>
37#undef ERROR
38#endif
39#endif
40
41#include <minizinc/solvers/MIP/MIP_gurobi_wrap.hh>
42#include <minizinc/utils.hh>
43
44using namespace std;
45
46string MIPGurobiWrapper::getDescription(FactoryOptions& factoryOpt,
47 MiniZinc::SolverInstanceBase::Options* opt) {
48 ostringstream oss;
49 oss << "MIP wrapper for Gurobi library " << getVersion(factoryOpt, nullptr);
50 oss << ". Compiled " __DATE__ " " __TIME__;
51 return oss.str();
52}
53
54string MIPGurobiWrapper::getVersion(FactoryOptions& factoryOpt,
55 MiniZinc::SolverInstanceBase::Options* opt) {
56 ostringstream oss;
57 MIPGurobiWrapper mgw(factoryOpt, nullptr); // to avoid opening the env
58 try {
59 mgw.checkDLL();
60 int major;
61 int minor;
62 int technical;
63 mgw.dll_GRBversion(&major, &minor, &technical);
64 oss << major << '.' << minor << '.' << technical;
65 return oss.str();
66 } catch (MiniZinc::InternalError&) {
67 return "<unknown version>";
68 }
69}
70
71vector<string> MIPGurobiWrapper::getRequiredFlags(FactoryOptions& f) {
72 FactoryOptions factoryOpt;
73 MIPGurobiWrapper mgw(factoryOpt, nullptr);
74 try {
75 mgw.checkDLL();
76 return {};
77 } catch (MiniZinc::InternalError&) {
78 return {"--gurobi-dll"};
79 }
80}
81
82vector<string> MIPGurobiWrapper::getFactoryFlags() { return {"--gurobi-dll"}; }
83
84string MIPGurobiWrapper::getId() { return "gurobi"; }
85
86string MIPGurobiWrapper::getName() { return "Gurobi"; }
87
88vector<string> MIPGurobiWrapper::getTags() { return {"mip", "float", "api"}; }
89
90vector<string> MIPGurobiWrapper::getStdFlags() { return {"-i", "-p", "-s", "-v"}; }
91
92vector<string> gurobi_dlls() {
93 const vector<string> versions = {
94 "913", "912", "911", "910", "904", // Potential future versions which should load correctly
95 "903", "902", "901", "900", "811", "810", "801", "800", "752",
96 "751", "750", "702", "701", "700", "652", "651", "650"};
97 vector<string> dlls;
98 string lastMajorVersion;
99 for (const auto& version : versions) {
100 string majorVersion = version.substr(0, 2);
101 if (majorVersion != lastMajorVersion) {
102 dlls.push_back("gurobi" + majorVersion);
103 lastMajorVersion = majorVersion;
104 }
105#ifdef _WIN32
106 dlls.push_back("C:\\gurobi" + version + "\\win64\\bin\\gurobi" + majorVersion + ".dll");
107#elif __APPLE__
108 dlls.push_back("/Library/gurobi" + version + "/mac64/lib/libgurobi" + majorVersion + ".dylib");
109#else
110 dlls.push_back("/opt/gurobi" + version + "/linux64/lib/libgurobi" + majorVersion + ".so");
111#endif
112 }
113
114 return dlls;
115}
116
117void MIPGurobiWrapper::Options::printHelp(ostream& os) {
118 os << "GUROBI MIP wrapper options:"
119 << std::endl
120 // -s print statistics
121 // << " --readParam <file> read GUROBI parameters from file
122 // << "--writeParam <file> write GUROBI parameters to file
123 // << "--tuneParam instruct GUROBI to tune parameters instead of solving
124 << " -f\n free search (default)" << std::endl
125 << " --fixed-search\n fixed search (approximation of the model's one by branching "
126 "priorities)"
127 << std::endl
128 << " --uniform-search\n 'more fixed' search (all variables in the search anns get "
129 "priority 1)"
130 << std::endl
131 << " --mipfocus <n>\n 1: feasibility, 2: optimality, 3: move bound (default is 0, "
132 "balanced)"
133 << std::endl
134 << " -i\n print intermediate solutions for optimization problems" << std::endl
135 << " -p <N>, --parallel <N>\n use N threads, default: 1."
136 << std::endl
137 // << " --nomippresolve disable MIP presolving NOT IMPL" << std::endl
138 << " --solver-time-limit <N>, --solver-time\n"
139 " stop search after N milliseconds wall time"
140 << std::endl
141 << " --solver-time-limit-feas <N>, --solver-tlf\n"
142 " stop search after N milliseconds wall time after the first feasible solution"
143 << std::endl
144 << " -n <N>, --num-solutions <N>\n"
145 " stop search after N solutions"
146 << std::endl
147 << " -r <N>, --random-seed <N>\n"
148 " random seed, integer"
149 << std::endl
150 << " --workmem <N>, --nodefilestart <N>\n"
151 " maximal RAM for node tree used before writing to node file, GB, default: 0.5"
152 << std::endl
153 << " --nodefiledir <path>\n"
154 " nodefile directory"
155 << std::endl
156 << " --writeModel <file>\n write model to <file> (.lp, .mps, .sav, ...)" << std::endl
157 << " --readParam <file>\n read GUROBI parameters from file" << std::endl
158 << " --writeParam <file>\n write GUROBI parameters to file" << std::endl
159 << " --readConcurrentParam <fileN>\n"
160 " read GUROBI parameters from file. Several such commands provide the"
161 " parameter files for concurrent solves (applied after all other settings)"
162 << std::endl
163 // << " --tuneParam instruct GUROBI to tune parameters instead of solving NOT
164 // IMPL"
165
166 << "\n --absGap <n>\n absolute gap |primal-dual| to stop" << std::endl
167 << " --relGap <n>\n relative gap |primal-dual|/<solver-dep> to stop. Default 1e-8, set <0 "
168 "to use backend's default"
169 << std::endl
170 << " --feasTol <n>\n primal feasibility tolerance. Default 1e-8" << std::endl
171 << " --intTol <n>\n integrality tolerance for a variable. Gurobi recommends at least "
172 "feasTol. Default 1e-8"
173 << std::endl
174 // << " --objDiff <n> objective function discretization. Default 1.0" << std::endl
175
176 << " --nonConvex <n>\n non-convexity. -1: solver default, 0: none, 1: if presolved, 2: "
177 "global. Default value 2."
178 << std::endl
179
180 << "\n --gurobi-dll <file> or <basename>\n Gurobi DLL, or base name, such as gurobi75, "
181 "when using plugin. Default range tried: "
182 << gurobi_dlls().front() << " .. " << gurobi_dlls().back() << std::endl
183 << std::endl;
184}
185
186bool MIPGurobiWrapper::FactoryOptions::processOption(int& i, std::vector<std::string>& argv,
187 const std::string& workingDir) {
188 MiniZinc::CLOParser cop(i, argv);
189 return cop.get("--gurobi-dll", &gurobiDll);
190}
191
192bool MIPGurobiWrapper::Options::processOption(int& i, std::vector<std::string>& argv,
193 const std::string& workingDir) {
194 MiniZinc::CLOParser cop(i, argv);
195 std::string buf;
196 if (cop.get("-i")) {
197 flagIntermediate = true;
198 } else if (string(argv[i]) == "-f") { // NOLINT: Allow repeated empty if
199 } else if (string(argv[i]) == "--fixed-search") { // NOLINT: Allow repeated empty if
200 nFreeSearch = MIPGurobiWrapper::SearchType::FIXED_SEARCH;
201 } else if (string(argv[i]) == "--uniform-search") { // NOLINT: Allow repeated empty if
202 nFreeSearch = MIPGurobiWrapper::SearchType::UNIFORM_SEARCH;
203 } else if (cop.get("--mipfocus --mipFocus --MIPFocus --MIPfocus",
204 &nMIPFocus)) { // NOLINT: Allow repeated empty if
205 } else if (cop.get("--writeModel --exportModel --writemodel --exportmodel", &buf)) {
206 sExportModel = MiniZinc::FileUtils::file_path(buf, workingDir);
207 } else if (cop.get("-p --parallel", &nThreads)) { // NOLINT: Allow repeated empty if
208 } else if (cop.get("--solver-time-limit --solver-time",
209 &nTimeout1000)) { // NOLINT: Allow repeated empty if
210 } else if (cop.get("--solver-time-limit-feas --solver-tlf",
211 &nTimeoutFeas1000)) { // NOLINT: Allow repeated empty if
212 } else if (cop.get("-n --num-solutions", &nSolLimit)) { // NOLINT: Allow repeated empty if
213 } else if (cop.get("-r --random-seed", &nSeed)) { // NOLINT: Allow repeated empty if
214 } else if (cop.get("--workmem --nodefilestart",
215 &nWorkMemLimit)) { // NOLINT: Allow repeated empty if
216 } else if (cop.get("--nodefiledir --NodefileDir",
217 &sNodefileDir)) { // NOLINT: Allow repeated empty if
218 } else if (cop.get("--readParam --readParams", &buf)) {
219 sReadParams = MiniZinc::FileUtils::file_path(buf, workingDir);
220 } else if (cop.get("--writeParam --writeParams", &buf)) {
221 sWriteParams = MiniZinc::FileUtils::file_path(buf, workingDir);
222 } else if (cop.get("--readConcurrentParam --readConcurrentParams", &buf)) {
223 sConcurrentParamFiles.push_back(MiniZinc::FileUtils::file_path(buf, workingDir));
224 } else if (cop.get("--absGap", &absGap)) { // NOLINT: Allow repeated empty if
225 } else if (cop.get("--relGap", &relGap)) { // NOLINT: Allow repeated empty if
226 } else if (cop.get("--feasTol", &feasTol)) { // NOLINT: Allow repeated empty if
227 } else if (cop.get("--intTol", &intTol)) { // NOLINT: Allow repeated empty if
228 } else if (cop.get("--nonConvex --nonconvex --NonConvex",
229 &nonConvex)) { // NOLINT: Allow repeated empty if
230 // } else if ( cop.get( "--objDiff", &objDiff ) ) {
231 } else {
232 return false;
233 }
234 return true;
235}
236
237void MIPGurobiWrapper::wrapAssert(bool cond, const string& msg, bool fTerm) {
238 if (!cond) {
239 _gurobiBuffer = "[NO ERROR STRING GIVEN]";
240 if (_error != 0) {
241 _gurobiBuffer = dll_GRBgeterrormsg(_env);
242 }
243 string msgAll =
244 (" MIPGurobiWrapper runtime error: " + _gurobiBuffer + "\nMessage from caller: " + msg);
245 cerr << msgAll << "\nGurobi error code: " << _error << endl;
246 if (fTerm) {
247 cerr << "TERMINATING." << endl;
248 throw runtime_error(msgAll);
249 }
250 }
251}
252
253#ifdef GUROBI_PLUGIN
254
255namespace {
256void* dll_open(const char* file) {
257#ifdef HAS_DLFCN_H
258 if (MiniZinc::FileUtils::is_absolute(file)) {
259 return dlopen(file, RTLD_NOW);
260 }
261 return dlopen((std::string("lib") + file + ".so").c_str(), RTLD_NOW);
262
263#else
264 if (MiniZinc::FileUtils::is_absolute(file)) {
265 return LoadLibrary(file);
266 }
267 return LoadLibrary((std::string(file) + ".dll").c_str());
268#endif
269}
270void* dll_sym(void* dll, const char* sym) {
271#ifdef HAS_DLFCN_H
272 void* ret = dlsym(dll, sym);
273#else
274 void* ret = GetProcAddress((HMODULE)dll, sym);
275#endif
276 if (ret == nullptr) {
277 throw MiniZinc::InternalError("cannot load symbol " + string(sym) + " from gurobi dll");
278 }
279 return ret;
280}
281void dll_close(void* dll) {
282#ifdef HAS_DLFCN_H
283 dlclose(dll);
284#else
285 FreeLibrary((HMODULE)dll);
286#endif
287}
288} // namespace
289
290#endif
291
292void MIPGurobiWrapper::checkDLL() {
293#ifdef GUROBI_PLUGIN
294 _gurobiDll = nullptr;
295 if (!_factoryOptions.gurobiDll.empty()) {
296 _gurobiDll = dll_open(_factoryOptions.gurobiDll.c_str());
297 } else {
298 for (const auto& s : gurobi_dlls()) {
299 _gurobiDll = dll_open(s.c_str());
300 if (nullptr != _gurobiDll) {
301 break;
302 }
303 }
304 }
305
306 if (_gurobiDll == nullptr) {
307 if (_factoryOptions.gurobiDll.empty()) {
308 throw MiniZinc::InternalError("cannot load gurobi dll, specify --gurobi-dll");
309 }
310 throw MiniZinc::InternalError("cannot load gurobi dll `" + _factoryOptions.gurobiDll + "'");
311 }
312
313 *(void**)(&dll_GRBversion) = dll_sym(_gurobiDll, "GRBversion");
314 *(void**)(&dll_GRBaddconstr) = dll_sym(_gurobiDll, "GRBaddconstr");
315 *(void**)(&dll_GRBaddgenconstrMin) = dll_sym(_gurobiDll, "GRBaddgenconstrMin");
316 *(void**)(&dll_GRBaddqconstr) = dll_sym(_gurobiDll, "GRBaddqconstr");
317 *(void**)(&dll_GRBaddgenconstrIndicator) = dll_sym(_gurobiDll, "GRBaddgenconstrIndicator");
318 *(void**)(&dll_GRBaddvars) = dll_sym(_gurobiDll, "GRBaddvars");
319 *(void**)(&dll_GRBcbcut) = dll_sym(_gurobiDll, "GRBcbcut");
320 *(void**)(&dll_GRBcbget) = dll_sym(_gurobiDll, "GRBcbget");
321 *(void**)(&dll_GRBcblazy) = dll_sym(_gurobiDll, "GRBcblazy");
322 *(void**)(&dll_GRBfreeenv) = dll_sym(_gurobiDll, "GRBfreeenv");
323 *(void**)(&dll_GRBfreemodel) = dll_sym(_gurobiDll, "GRBfreemodel");
324 *(void**)(&dll_GRBgetdblattr) = dll_sym(_gurobiDll, "GRBgetdblattr");
325 *(void**)(&dll_GRBgetdblattrarray) = dll_sym(_gurobiDll, "GRBgetdblattrarray");
326 *(void**)(&dll_GRBgetenv) = dll_sym(_gurobiDll, "GRBgetenv");
327 *(void**)(&dll_GRBgeterrormsg) = dll_sym(_gurobiDll, "GRBgeterrormsg");
328 *(void**)(&dll_GRBgetintattr) = dll_sym(_gurobiDll, "GRBgetintattr");
329 *(void**)(&dll_GRBloadenv) = dll_sym(_gurobiDll, "GRBloadenv");
330 *(void**)(&dll_GRBgetconcurrentenv) = dll_sym(_gurobiDll, "GRBgetconcurrentenv");
331 *(void**)(&dll_GRBnewmodel) = dll_sym(_gurobiDll, "GRBnewmodel");
332 *(void**)(&dll_GRBoptimize) = dll_sym(_gurobiDll, "GRBoptimize");
333 *(void**)(&dll_GRBreadparams) = dll_sym(_gurobiDll, "GRBreadparams");
334 *(void**)(&dll_GRBsetcallbackfunc) = dll_sym(_gurobiDll, "GRBsetcallbackfunc");
335 *(void**)(&dll_GRBsetdblparam) = dll_sym(_gurobiDll, "GRBsetdblparam");
336 *(void**)(&dll_GRBsetintattr) = dll_sym(_gurobiDll, "GRBsetintattr");
337 *(void**)(&dll_GRBsetintattrlist) = dll_sym(_gurobiDll, "GRBsetintattrlist");
338 *(void**)(&dll_GRBsetdblattrelement) = dll_sym(_gurobiDll, "GRBsetdblattrelement");
339 *(void**)(&dll_GRBsetdblattrlist) = dll_sym(_gurobiDll, "GRBsetdblattrlist");
340 *(void**)(&dll_GRBsetobjectiven) = dll_sym(_gurobiDll, "GRBsetobjectiven");
341 *(void**)(&dll_GRBsetintparam) = dll_sym(_gurobiDll, "GRBsetintparam");
342 *(void**)(&dll_GRBsetstrparam) = dll_sym(_gurobiDll, "GRBsetstrparam");
343 *(void**)(&dll_GRBterminate) = dll_sym(_gurobiDll, "GRBterminate");
344 *(void**)(&dll_GRBupdatemodel) = dll_sym(_gurobiDll, "GRBupdatemodel");
345 *(void**)(&dll_GRBwrite) = dll_sym(_gurobiDll, "GRBwrite");
346 *(void**)(&dll_GRBwriteparams) = dll_sym(_gurobiDll, "GRBwriteparams");
347 *(void**)(&dll_GRBemptyenv) = dll_sym(_gurobiDll, "GRBemptyenv");
348 *(void**)(&dll_GRBgetnumparams) = dll_sym(_gurobiDll, "GRBgetnumparams");
349 *(void**)(&dll_GRBgetparamname) = dll_sym(_gurobiDll, "GRBgetparamname");
350 *(void**)(&dll_GRBgetparamtype) = dll_sym(_gurobiDll, "GRBgetparamtype");
351 *(void**)(&dll_GRBgetintparaminfo) = dll_sym(_gurobiDll, "GRBgetintparaminfo");
352 *(void**)(&dll_GRBgetdblparaminfo) = dll_sym(_gurobiDll, "GRBgetdblparaminfo");
353 *(void**)(&dll_GRBgetstrparaminfo) = dll_sym(_gurobiDll, "GRBgetstrparaminfo");
354
355#else
356
357 dll_GRBversion = GRBversion;
358 dll_GRBaddconstr = GRBaddconstr;
359 dll_GRBaddgenconstrIndicator = GRBaddgenconstrIndicator;
360 dll_GRBaddvars = GRBaddvars;
361 dll_GRBcbcut = GRBcbcut;
362 dll_GRBcbget = GRBcbget;
363 dll_GRBcblazy = GRBcblazy;
364 dll_GRBfreeenv = GRBfreeenv;
365 dll_GRBfreemodel = GRBfreemodel;
366 dll_GRBgetdblattr = GRBgetdblattr;
367 dll_GRBgetdblattrarray = GRBgetdblattrarray;
368 dll_GRBgetenv = GRBgetenv;
369 dll_GRBgeterrormsg = GRBgeterrormsg;
370 dll_GRBgetintattr = GRBgetintattr;
371 dll_GRBloadenv = GRBloadenv;
372 dll_GRBnewmodel = GRBnewmodel;
373 dll_GRBoptimize = GRBoptimize;
374 dll_GRBreadparams = GRBreadparams;
375 dll_GRBsetcallbackfunc = GRBsetcallbackfunc;
376 dll_GRBsetdblparam = GRBsetdblparam;
377 dll_GRBsetintattr = GRBsetintattr;
378 dll_GRBsetintattrlist = GRBsetintattrlist;
379 dll_GRBsetdblattrelement = GRBsetdblattrelement;
380 dll_GRBsetdblattrlist = GRBsetdblattrlist;
381 dll_GRBsetintparam = GRBsetintparam;
382 dll_GRBsetstrparam = GRBsetstrparam;
383 dll_GRBterminate = GRBterminate;
384 dll_GRBupdatemodel = GRBupdatemodel;
385 dll_GRBwrite = GRBwrite;
386 dll_GRBwriteparams = GRBwriteparams;
387 dll_GRBemptyenv = GRBemptyenv;
388 dll_GRBgetnumparams = GRBgetnumparams;
389 dll_GRBgetparamname = GRBgetparamname;
390 dll_GRBgetparamtype = GRBgetparamtype;
391 dll_GRBgetintparaminfo = dll_GRBgetintparaminfo;
392 dll_GRBgetdblparaminfo = dll_GRBgetdblparaminfo;
393 dll_GRBgetstrparaminfo = dll_GRBgetstrparaminfo;
394
395#endif
396}
397
398void MIPGurobiWrapper::openGUROBI() {
399 checkDLL();
400
401 /* Initialize the GUROBI environment */
402 {
403 // cout << "% " << flush; // Gurobi 7.5.2 prints "Academic License..."
404 MiniZinc::StreamRedir redirStdout(stdout, stderr);
405 _error = dll_GRBloadenv(&_env, nullptr);
406 }
407 wrapAssert(_error == 0, "Could not open GUROBI environment.");
408 _error = dll_GRBsetintparam(_env, "OutputFlag", 0); // Switch off output
409 // _error = dll_GRBsetintparam(_env, "LogToConsole",
410 // fVerbose ? 1 : 0); // also when flagIntermediate? TODO
411 /* Create the problem. */
412 _error =
413 dll_GRBnewmodel(_env, &_model, "mzn_gurobi", 0, nullptr, nullptr, nullptr, nullptr, nullptr);
414 wrapAssert(_model != nullptr, "Failed to create LP.");
415}
416
417void MIPGurobiWrapper::closeGUROBI() {
418 /* Free model */
419
420 // If not allocated, skip
421 if (nullptr != _model) {
422 /* Free up the problem as allocated by GRB_createprob, if necessary */
423 dll_GRBfreemodel(_model);
424 _model = nullptr;
425 }
426
427 /* Free environment */
428
429 if (nullptr != _env) {
430 dll_GRBfreeenv(_env);
431 }
432 /// and at last:
433// MIPWrapper::cleanup();
434#ifdef GUROBI_PLUGIN
435 // dll_close(_gurobiDll); // Is called too many times, disabling. 2019-05-06
436#endif
437}
438
439std::vector<MiniZinc::SolverConfig::ExtraFlag> MIPGurobiWrapper::getExtraFlags(
440 FactoryOptions& factoryOpt) {
441 enum GurobiParamType { T_INT = 1, T_DOUBLE = 2, T_STRING = 3 };
442
443 MIPGurobiWrapper mgw(factoryOpt, nullptr);
444 GRBenv* env;
445 try {
446 mgw.checkDLL();
447 mgw.dll_GRBemptyenv(&env);
448 int num_params = mgw.dll_GRBgetnumparams(env);
449 std::vector<MiniZinc::SolverConfig::ExtraFlag> flags;
450 flags.reserve(num_params);
451 for (int i = 0; i < num_params; i++) {
452 char* name;
453 mgw.dll_GRBgetparamname(env, i, &name);
454 std::string param_name(name);
455 MiniZinc::SolverConfig::ExtraFlag::FlagType param_type;
456 std::vector<std::string> param_range;
457 std::string param_default;
458 int type = mgw.dll_GRBgetparamtype(env, name);
459 if (param_name == GRB_INT_PAR_THREADS || param_name == GRB_DBL_PAR_TIMELIMIT ||
460 param_name == GRB_INT_PAR_SOLUTIONLIMIT || param_name == GRB_INT_PAR_SEED ||
461 param_name == GRB_DBL_PAR_NODEFILESTART || param_name == GRB_STR_PAR_NODEFILEDIR ||
462 param_name == GRB_DBL_PAR_MIPGAPABS || param_name == GRB_INT_PAR_MIPFOCUS ||
463 param_name == GRB_DBL_PAR_MIPGAP || param_name == GRB_DBL_PAR_INTFEASTOL ||
464 param_name == GRB_DBL_PAR_FEASIBILITYTOL || param_name == GRB_INT_PAR_NONCONVEX ||
465 param_name == GRB_INT_PAR_PRECRUSH || param_name == GRB_INT_PAR_LAZYCONSTRAINTS ||
466 param_name == GRB_STR_PAR_DUMMY) {
467 // These parameters are handled by us or are not useful
468 continue;
469 }
470 switch (type) {
471 case T_INT: {
472 int current_value;
473 int min_value;
474 int max_value;
475 int default_value;
476 mgw.dll_GRBgetintparaminfo(env, name, ¤t_value, &min_value, &max_value,
477 &default_value);
478 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT;
479 param_range = {std::to_string(min_value), std::to_string(max_value)};
480 param_default = std::to_string(default_value);
481 break;
482 }
483 case T_DOUBLE: {
484 double current_value;
485 double min_value;
486 double max_value;
487 double default_value;
488 mgw.dll_GRBgetdblparaminfo(env, name, ¤t_value, &min_value, &max_value,
489 &default_value);
490 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_FLOAT;
491 param_range = {std::to_string(min_value), std::to_string(max_value)};
492 param_default = std::to_string(default_value);
493 break;
494 }
495 case T_STRING: {
496 char current_value[GRB_MAX_STRLEN];
497 char default_value[GRB_MAX_STRLEN];
498 mgw.dll_GRBgetstrparaminfo(env, name, current_value, default_value);
499 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_STRING;
500 param_default = default_value;
501 break;
502 }
503 default:
504 break;
505 }
506 flags.emplace_back("--gurobi-" + param_name, param_name, param_type, param_range,
507 param_default);
508 }
509 return flags;
510 } catch (MiniZinc::InternalError&) {
511 return {};
512 }
513 return {};
514}
515
516void MIPGurobiWrapper::doAddVars(size_t n, double* obj, double* lb, double* ub,
517 MIPWrapper::VarType* vt, string* names) {
518 /// Convert var types:
519 vector<char> ctype(n);
520 vector<char*> pcNames(n);
521 for (size_t i = 0; i < n; ++i) {
522 pcNames[i] = (char*)names[i].c_str();
523 switch (vt[i]) {
524 case REAL:
525 ctype[i] = GRB_CONTINUOUS;
526 break;
527 case INT:
528 ctype[i] = GRB_INTEGER;
529 break;
530 case BINARY:
531 ctype[i] = GRB_BINARY;
532 break;
533 default:
534 throw runtime_error(" MIPWrapper: unknown variable type");
535 }
536 }
537 _error = dll_GRBaddvars(_model, static_cast<int>(n), 0, nullptr, nullptr, nullptr, obj, lb, ub,
538 &ctype[0], &pcNames[0]);
539 wrapAssert(_error == 0, "Failed to declare variables.");
540 _error = dll_GRBupdatemodel(_model);
541 wrapAssert(_error == 0, "Failed to update model.");
542}
543
544static char get_grb_sense(MIPWrapper::LinConType s) {
545 switch (s) {
546 case MIPWrapper::LQ:
547 return GRB_LESS_EQUAL;
548 case MIPWrapper::EQ:
549 return GRB_EQUAL;
550 case MIPWrapper::GQ:
551 return GRB_GREATER_EQUAL;
552 default:
553 throw runtime_error(" MIPGurobiWrapper: unknown constraint sense");
554 }
555}
556
557void MIPGurobiWrapper::addRow(int nnz, int* rmatind, double* rmatval, MIPWrapper::LinConType sense,
558 double rhs, int mask, const string& rowName) {
559 //// Make sure in order to notice the indices of lazy constr:
560 ++nRows;
561 /// Convert var types:
562 char ssense = get_grb_sense(sense);
563 const char* pRName = rowName.c_str();
564 _error = dll_GRBaddconstr(_model, nnz, rmatind, rmatval, ssense, rhs, pRName);
565 wrapAssert(_error == 0, "Failed to add constraint.");
566 int nLazyAttr = 0;
567 const bool fUser = (MaskConsType_Usercut & mask) != 0;
568 const bool fLazy = (MaskConsType_Lazy & mask) != 0;
569 /// Gurobi 6.5.2 has lazyness 1-3.
570 if (fUser) {
571 if (fLazy) {
572 nLazyAttr = 2; // just active lazy
573 } else {
574 nLazyAttr = 3; // even LP-active
575 }
576 } else if (fLazy) {
577 nLazyAttr = 1; // very lazy
578 }
579 if (nLazyAttr != 0) {
580 nLazyIdx.push_back(nRows - 1);
581 nLazyValue.push_back(nLazyAttr);
582 }
583}
584
585void MIPGurobiWrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind,
586 double* rmatval, MIPWrapper::LinConType sense,
587 double rhs, const string& rowName) {
588 wrapAssert(0 <= bVal && 1 >= bVal, "Gurobi: addIndicatorConstraint: bVal not 0/1");
589 //// Make sure in order to notice the indices of lazy constr: also here? TODO
590 ++nRows;
591 char ssense = get_grb_sense(sense);
592 _error = dll_GRBaddgenconstrIndicator(_model, rowName.c_str(), iBVar, bVal, nnz, rmatind, rmatval,
593 ssense, rhs);
594 wrapAssert(_error == 0, "Failed to add indicator constraint.");
595}
596
597void MIPGurobiWrapper::addMinimum(int iResultVar, int nnz, int* ind, const std::string& rowName) {
598 _error = dll_GRBaddgenconstrMin(_model, rowName.c_str(), iResultVar, nnz, (const int*)ind,
599 GRB_INFINITY);
600 wrapAssert(_error == 0, "Failed: GRBaddgenconstrMin.");
601}
602
603void MIPGurobiWrapper::addTimes(int x, int y, int z, const string& rowName) {
604 /// As x*y - z == 0
605 double zCoef = -1.0;
606 double xyCoef = 1.0;
607 _error =
608 dll_GRBaddqconstr(_model, 1, &z, &zCoef, 1, &x, &y, &xyCoef, GRB_EQUAL, 0.0, rowName.c_str());
609 /// Gurobi 9.0.1 says we cannot have GRB_EQUAL but seems to work.
610 wrapAssert(_error == 0, "Failed: GRBaddqconstr.");
611}
612
613bool MIPGurobiWrapper::addSearch(const std::vector<VarId>& vars, const std::vector<int>& pri) {
614 assert(vars.size() == pri.size());
615 static_assert(sizeof(VarId) == sizeof(int), "VarId should be (u)int currently");
616 _error = dll_GRBsetintattrlist(_model, "BranchPriority", static_cast<int>(vars.size()),
617 (int*)vars.data(), (int*)pri.data());
618 wrapAssert(_error == 0, "Failed to add branching priorities");
619 return true;
620}
621
622int MIPGurobiWrapper::getFreeSearch() { return _options->nFreeSearch; }
623
624bool MIPGurobiWrapper::addWarmStart(const std::vector<VarId>& vars,
625 const std::vector<double>& vals) {
626 assert(vars.size() == vals.size());
627 static_assert(sizeof(VarId) == sizeof(int), "VarId should be (u)int currently");
628 // _error = GRBsetdblattrelement(_model, "Start", 0, 1.0);
629 _error = dll_GRBsetdblattrlist(_model, "Start", static_cast<int>(vars.size()), (int*)vars.data(),
630 (double*)vals.data());
631 wrapAssert(_error == 0, "Failed to add warm start");
632 return true;
633}
634
635bool MIPGurobiWrapper::defineMultipleObjectives(const MultipleObjectives& mo) {
636 setObjSense(1); // Maximize
637 for (int iobj = 0; iobj < mo.size(); ++iobj) {
638 const auto& obj = mo.getObjectives()[iobj];
639 int objvar = obj.getVariable();
640 double coef = 1.0;
641 _error = dll_GRBsetobjectiven(_model, iobj, static_cast<int>(mo.size()) - iobj, obj.getWeight(),
642 0.0, 0.0, nullptr, 0.0, 1, &objvar, &coef);
643 wrapAssert(_error == 0, "Failed to set objective " + std::to_string(iobj));
644 }
645 return true;
646}
647
648void MIPGurobiWrapper::setVarBounds(int iVar, double lb, double ub) {
649 wrapAssert(lb <= ub, "mzn-gurobi: setVarBounds: lb>ub");
650 _error = dll_GRBsetdblattrelement(_model, GRB_DBL_ATTR_LB, iVar, lb);
651 wrapAssert(_error == 0, "mzn-gurobi: failed to set var lb.");
652 _error = dll_GRBsetdblattrelement(_model, GRB_DBL_ATTR_UB, iVar, ub);
653 wrapAssert(_error == 0, "mzn-gurobi: failed to set var ub.");
654}
655
656void MIPGurobiWrapper::setVarLB(int iVar, double lb) {
657 _error = dll_GRBsetdblattrelement(_model, GRB_DBL_ATTR_LB, iVar, lb);
658 wrapAssert(_error == 0, "mzn-gurobi: failed to set var lb.");
659}
660
661void MIPGurobiWrapper::setVarUB(int iVar, double ub) {
662 _error = dll_GRBsetdblattrelement(_model, GRB_DBL_ATTR_UB, iVar, ub);
663 wrapAssert(_error == 0, "mzn-gurobi: failed to set var ub.");
664}
665
666/// SolutionCallback ------------------------------------------------------------------------
667/// Gurobi ensures thread-safety
668static int __stdcall solcallback(GRBmodel* model, void* cbdata, int where, void* usrdata) {
669 auto* info = (MIPWrapper::CBUserInfo*)usrdata;
670 auto* gw = static_cast<MIPGurobiWrapper*>(info->wrapper);
671
672 double nodecnt = 0.0;
673 double actnodes = 0.0;
674 double objVal = 0.0;
675 int solcnt = 0;
676 int newincumbent = 0;
677
678 if (GRB_CB_MIP == where) {
679 /* General MIP callback */
680 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIP_OBJBND, &info->pOutput->bestBound);
681 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIP_NODLFT, &actnodes);
682 info->pOutput->nOpenNodes = static_cast<int>(actnodes);
683 /// Check time after the 1st feas
684 if (-1e100 != info->nTime1Feas) {
685 double tNow;
686 gw->dll_GRBcbget(cbdata, where, GRB_CB_RUNTIME, (void*)&tNow);
687 if (tNow - info->nTime1Feas >= info->nTimeoutFeas) {
688 gw->dll_GRBterminate(model);
689 }
690 }
691 } else if (GRB_CB_MESSAGE == where) {
692 /* Message callback */
693 if (info->fVerb) {
694 char* msg;
695 gw->dll_GRBcbget(cbdata, where, GRB_CB_MSG_STRING, &msg);
696 cerr << msg << flush;
697 }
698 } else if (GRB_CB_MIPSOL == where) {
699 /* MIP solution callback */
700 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_NODCNT, &nodecnt);
701 info->pOutput->nNodes = static_cast<int>(nodecnt);
702 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_OBJ, &objVal);
703 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_SOLCNT, &solcnt);
704
705 if (fabs(info->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) {
706 newincumbent = 1;
707 // Not confirmed yet, see lazy cuts
708 // info->pOutput->objVal = objVal;
709 // info->pOutput->status = MIPWrapper::SAT;
710 // info->pOutput->statusName = "feasible from a callback";
711 }
712 if (newincumbent != 0) {
713 assert(info->pOutput->x);
714 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPSOL_SOL, (void*)info->pOutput->x);
715
716 info->pOutput->dWallTime = std::chrono::duration<double>(std::chrono::steady_clock::now() -
717 info->pOutput->dWallTime0)
718 .count();
719 info->pOutput->dCPUTime = double(std::clock() - info->pOutput->cCPUTime0) / CLOCKS_PER_SEC;
720 }
721
722 /// Callback for lazy cuts
723 /// Before printing
724 if ((info->cutcbfn != nullptr) && ((info->cutMask & MIPWrapper::MaskConsType_Lazy) != 0)) {
725 MIPWrapper::CutInput cutInput;
726 cerr << " GRB: GRB_CB_MIPSOL (" << objVal << ") -> cut callback " << endl;
727 info->cutcbfn(*info->pOutput, cutInput, info->psi, true);
728 for (auto& cd : cutInput) {
729 // assert( cd.mask & MIPWrapper::MaskConsType_Lazy );
730 if ((cd.mask & MIPWrapper::MaskConsType_Lazy) != 0) { // take only lazy constr generators
731 int _error =
732 gw->dll_GRBcblazy(cbdata, static_cast<int>(cd.rmatind.size()), cd.rmatind.data(),
733 cd.rmatval.data(), get_grb_sense(cd.sense), cd.rhs);
734 if (_error != 0) {
735 cerr << " GRB_wrapper: failed to add lazy cut. " << endl;
736 } else {
737 newincumbent = -1;
738 }
739 // info->pOutput->objVal = 1e100; // to mark that we can get a new incumbent
740 // which should be printed
741 }
742 }
743 }
744 if (solcnt >= 0 /*This is solution number for Gurobi*/ && newincumbent >= 0) {
745 if (fabs(info->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) {
746 newincumbent = 1;
747 info->pOutput->objVal = objVal;
748 info->pOutput->status = MIPWrapper::SAT;
749 info->pOutput->statusName = "feasible from a callback";
750 }
751 }
752 if (newincumbent > 0) {
753 info->pOutput->dCPUTime = double(std::clock() - info->pOutput->cCPUTime0) / CLOCKS_PER_SEC;
754
755 /// Set time for the 1st feas
756 if (0 <= info->nTimeoutFeas && -1e100 == info->nTime1Feas) {
757 gw->dll_GRBcbget(cbdata, where, GRB_CB_RUNTIME, (void*)&info->nTime1Feas);
758 }
759
760 /// Call the user function:
761 if (info->solcbfn != nullptr) {
762 (*info->solcbfn)(*info->pOutput, info->psi);
763 }
764
765 if (0 == info->nTimeoutFeas) {
766 gw->dll_GRBterminate(model); // Straight after feas
767 }
768 }
769 } else if (GRB_CB_MIPNODE == where) {
770 int status;
771 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPNODE_STATUS, &status);
772 if (status == GRB_OPTIMAL && (info->cutcbfn != nullptr)) { // if cut handler given
773 MIPWrapper::Output outpRlx;
774 outpRlx.x = info->pOutput->x; // using the sol output storage TODO?
775 outpRlx.nCols = info->pOutput->nCols;
776 assert(outpRlx.x && outpRlx.nCols);
777 // dll_GRBcbget(cbdata, where, GRB_CB_MIPNODE_RELOBJ, outpRlx.objVal);
778 gw->dll_GRBcbget(cbdata, where, GRB_CB_MIPNODE_REL, (void*)outpRlx.x);
779 // cerr << " GRB: GRB_CB_MIPNODE -> cut callback " << endl;
780 MIPWrapper::CutInput cutInput;
781 info->cutcbfn(outpRlx, cutInput, info->psi, false);
782 // static int nCuts=0;
783 // nCuts += cutInput.size();
784 // if ( cutInput.size() )
785 // cerr << "\n N CUTS: " << nCuts << endl;
786 for (auto& cd : cutInput) {
787 if ((cd.mask & (MIPWrapper::MaskConsType_Usercut | MIPWrapper::MaskConsType_Lazy)) == 0) {
788 throw runtime_error("Cut callback: should be user/lazy");
789 }
790 if ((cd.mask & MIPWrapper::MaskConsType_Usercut) != 0) {
791 int _error =
792 gw->dll_GRBcbcut(cbdata, static_cast<int>(cd.rmatind.size()), cd.rmatind.data(),
793 cd.rmatval.data(), get_grb_sense(cd.sense), cd.rhs);
794 if (_error != 0) {
795 cerr << " GRB_wrapper: failed to add user cut. " << endl;
796 }
797 }
798 if ((cd.mask & MIPWrapper::MaskConsType_Lazy) != 0) {
799 int _error =
800 gw->dll_GRBcblazy(cbdata, static_cast<int>(cd.rmatind.size()), cd.rmatind.data(),
801 cd.rmatval.data(), get_grb_sense(cd.sense), cd.rhs);
802 if (_error != 0) {
803 cerr << " GRB_wrapper: failed to add lazy cut. " << endl;
804 }
805 }
806 }
807 }
808 }
809 return 0;
810} /* END logcallback */
811// end SolutionCallback ---------------------------------------------------------------------
812
813MIPGurobiWrapper::Status MIPGurobiWrapper::convertStatus(int gurobiStatus) {
814 Status s = Status::UNKNOWN;
815 ostringstream oss;
816 /* Converting the status. */
817 if (gurobiStatus == GRB_OPTIMAL) {
818 s = Status::OPT;
819 oss << "Optimal";
820 } else if (gurobiStatus == GRB_INF_OR_UNBD) {
821 s = Status::UNSATorUNBND;
822 oss << "Infeasible or unbounded";
823 } else if (gurobiStatus == GRB_INFEASIBLE) {
824 s = Status::UNSAT;
825 oss << "Infeasible";
826 } else if (gurobiStatus == GRB_UNBOUNDED) {
827 oss << "Unbounded";
828 s = Status::UNBND;
829 } else {
830 int solcount = 0;
831 _error = dll_GRBgetintattr(_model, "SolCount", &solcount);
832 wrapAssert(_error == 0, " Failure to access solution count.", false);
833 if (solcount != 0) {
834 s = Status::SAT;
835 }
836 oss << "Gurobi stopped with status " << gurobiStatus;
837 }
838 output.statusName = _gurobiStatusBuffer = oss.str();
839 return s;
840}
841
842void MIPGurobiWrapper::solve() { // Move into ancestor?
843 _error = dll_GRBupdatemodel(_model); // for model export
844 wrapAssert(_error == 0, "Failed to update model.");
845
846 /// ADDING LAZY CONSTRAINTS IF ANY
847 if (!nLazyIdx.empty()) {
848 assert(nLazyIdx.size() == nLazyValue.size());
849 if (fVerbose) {
850 cerr << " MIPGurobiWrapper: marking " << nLazyIdx.size() << " lazy cuts." << endl;
851 }
852 _error = dll_GRBsetintattrlist(_model, "Lazy", static_cast<int>(nLazyIdx.size()),
853 nLazyIdx.data(), nLazyValue.data());
854 wrapAssert(_error == 0, "Failed to set constraint attribute.");
855 nLazyIdx.clear();
856 nLazyValue.clear();
857 _error = dll_GRBupdatemodel(_model); // for model export
858 wrapAssert(_error == 0, "Failed to update model after modifying some constraint attr.");
859 }
860
861 /////////////// Last-minute solver options //////////////////
862 /* Turn on output to file */
863 _error = dll_GRBsetstrparam(dll_GRBgetenv(_model), "LogFile",
864 ""); // FAILS to switch off in Ubuntu 15.04
865 /* Turn on output to the screen */
866 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), "OutputFlag",
867 /*fVerbose ? 1 :*/ 0); // switch off, redirect in callback
868 // _error = dll_GRBsetintparam(dll_GRBgetenv(_model), "LogToConsole",
869 // fVerbose ? 1 : 0); // also when flagIntermediate? TODO
870 wrapAssert(_error == 0, " GUROBI Warning: Failure to switch screen indicator.", false);
871 // _error = dll_GRB_setintparam (_env, GRB_PARAM_ClockType, 1); // CPU time
872 // _error = dll_GRB_setintparam (_env, GRB_PARAM_MIP_Strategy_CallbackReducedLP, GRB__OFF); //
873 // Access original model
874 if (!_options->sExportModel.empty()) {
875 _error = dll_GRBwrite(_model, _options->sExportModel.c_str());
876 wrapAssert(_error == 0, "Failed to write LP to disk.", false);
877 }
878
879 /// TODO
880 // if(all_solutions && obj.getImpl()) {
881 // IloNum lastObjVal = (obj.getSense() == IloObjective::Minimize ) ?
882 // _ilogurobi->use(SolutionCallback(_iloenv, lastObjVal, *this));
883 // Turn off GUROBI logging
884
885 if (_options->nThreads > 0) {
886 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_THREADS, _options->nThreads);
887 // int nn; // THE SETTING FAILS TO WORK IN 6.0.5.
888 // _error = dll_getintparam(_env, GRB_INT_PAR_THREADS, &nn);
889 // cerr << "Set " << nThreads << " threads, reported " << nn << endl;
890 wrapAssert(_error == 0, "Failed to set GRB_INT_PAR_THREADS.", false);
891 }
892
893 if (_options->nTimeout1000 > 0) {
894 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), GRB_DBL_PAR_TIMELIMIT,
895 static_cast<double>(_options->nTimeout1000) / 1000.0);
896 wrapAssert(_error == 0, "Failed to set GRB_PARAM_TimeLimit.", false);
897 }
898
899 if (_options->nSolLimit > 0) {
900 _error =
901 dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_SOLUTIONLIMIT, _options->nSolLimit);
902 wrapAssert(_error == 0, "Failed to set GRB_INT_PAR_SOLLIMIT.", false);
903 }
904
905 if (_options->nSeed >= 0) {
906 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_SEED, _options->nSeed);
907 wrapAssert(_error == 0, "Failed to set GRB_INT_PAR_SEED.", false);
908 }
909
910 if (_options->nWorkMemLimit > 0 && _options->nWorkMemLimit < 1e200) {
911 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), "NodefileStart", _options->nWorkMemLimit);
912 wrapAssert(_error == 0, "Failed to set NodefileStart.", false);
913 }
914
915 if (!_options->sNodefileDir.empty()) {
916 _error =
917 dll_GRBsetstrparam(dll_GRBgetenv(_model), "NodefileDir", _options->sNodefileDir.c_str());
918 wrapAssert(_error == 0, "Failed to set NodefileDir.", false);
919 }
920
921 if (_options->absGap >= 0.0) {
922 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), "MIPGapAbs", _options->absGap);
923 wrapAssert(_error == 0, "Failed to set MIPGapAbs.", false);
924 }
925 if (_options->nMIPFocus > 0) {
926 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_MIPFOCUS, _options->nMIPFocus);
927 wrapAssert(_error == 0, "Failed to set GRB_INT_PAR_MIPFOCUS.", false);
928 }
929
930 if (_options->relGap >= 0.0) {
931 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), "MIPGap", _options->relGap);
932 wrapAssert(_error == 0, "Failed to set MIPGap.", false);
933 }
934 if (_options->intTol >= 0.0) {
935 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), "IntFeasTol", _options->intTol);
936 wrapAssert(_error == 0, "Failed to set IntFeasTol.", false);
937 }
938 if (_options->feasTol >= 0.0) {
939 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), "FeasibilityTol", _options->feasTol);
940 wrapAssert(_error == 0, "Failed to set FeasTol.", false);
941 }
942 if (_options->nonConvex >= 0) {
943#ifdef GRB_INT_PAR_NONCONVEX
944 int major;
945 int minor;
946 int technical;
947 dll_GRBversion(&major, &minor, &technical);
948 if (major >= 9) {
949 _error =
950 dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_NONCONVEX, _options->nonConvex);
951 wrapAssert(_error == 0, "Failed to set " GRB_INT_PAR_NONCONVEX, false);
952 } else {
953 std::cerr << "WARNING: Non-convex solving is unavailable in this version of Gurobi"
954 << std::endl;
955 }
956#else
957 std::cerr << "WARNING: Non-convex solving is unavailable in this version of Gurobi"
958 << std::endl;
959#endif
960 }
961
962 /// Solution callback
963 output.nCols = static_cast<int>(colObj.size());
964 _x.resize(output.nCols);
965 output.x = &_x[0];
966 SolCallbackFn solcbfn = cbui.solcbfn;
967 if (true) { // NOLINT: Need for logging
968 cbui.fVerb = fVerbose;
969 cbui.nTimeoutFeas = _options->nTimeoutFeas1000 / 1000.0;
970 if (!_options->flagIntermediate) {
971 cbui.solcbfn = nullptr;
972 }
973 if (cbui.cutcbfn != nullptr) {
974 assert(cbui.cutMask & (MaskConsType_Usercut | MaskConsType_Lazy));
975 if ((cbui.cutMask & MaskConsType_Usercut) != 0) {
976 // For user cuts, needs to keep some info after presolve
977 if (fVerbose) {
978 cerr << " MIPGurobiWrapper: user cut callback enabled, setting PreCrush=1" << endl;
979 }
980 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_PRECRUSH, 1);
981 wrapAssert(_error == 0, "Failed to set GRB_INT_PAR_PRECRUSH.", false);
982 }
983 if ((cbui.cutMask & MaskConsType_Lazy) != 0) {
984 // For lazy cuts, Gurobi disables some presolves
985 if (fVerbose) {
986 cerr << " MIPGurobiWrapper: lazy cut callback enabled, setting LazyConstraints=1"
987 << endl;
988 }
989 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), GRB_INT_PAR_LAZYCONSTRAINTS, 1);
990 wrapAssert(_error == 0, "Failed to set GRB_INT_PAR_LAZYCONSTRAINTS.", false);
991 }
992 }
993 _error = dll_GRBsetcallbackfunc(_model, solcallback, (void*)&cbui);
994 wrapAssert(_error == 0, "Failed to set callback", false);
995 }
996
997 // Process extra flags options
998 for (auto& it : _options->extraParams) {
999 auto name = it.first.substr(9);
1000 int type = dll_GRBgetparamtype(dll_GRBgetenv(_model), name.c_str());
1001 enum GurobiParamType { T_INT = 1, T_DOUBLE = 2, T_STRING = 3 };
1002 switch (type) {
1003 case T_INT:
1004 _error = dll_GRBsetintparam(dll_GRBgetenv(_model), name.c_str(), stoi(it.second));
1005 break;
1006 case T_DOUBLE:
1007 _error = dll_GRBsetdblparam(dll_GRBgetenv(_model), name.c_str(), stod(it.second));
1008 break;
1009 case T_STRING:
1010 _error = dll_GRBsetstrparam(dll_GRBgetenv(_model), name.c_str(), it.second.c_str());
1011 break;
1012 default:
1013 wrapAssert(false, "Could not determine type of parameter " + name, false);
1014 break;
1015 }
1016 wrapAssert(_error == 0, "Failed to set parameter " + name + " = " + it.second, false);
1017 }
1018
1019 /// after all modifs
1020 if (!_options->sReadParams.empty()) {
1021 _error = dll_GRBreadparams(dll_GRBgetenv(_model), _options->sReadParams.c_str());
1022 wrapAssert(_error == 0, "Failed to read GUROBI parameters.", false);
1023 }
1024
1025 if (!_options->sWriteParams.empty()) {
1026 _error = dll_GRBwriteparams(dll_GRBgetenv(_model), _options->sWriteParams.c_str());
1027 wrapAssert(_error == 0, "Failed to write GUROBI parameters.", false);
1028 }
1029
1030 /* See if we should set up concurrent solving */
1031 if (!_options->sConcurrentParamFiles.empty()) {
1032 int iSetting = -1;
1033 for (const auto& paramFile : _options->sConcurrentParamFiles) {
1034 ++iSetting;
1035 auto* env_i = dll_GRBgetconcurrentenv(_model, iSetting);
1036 _error = dll_GRBreadparams(env_i, paramFile.c_str());
1037 wrapAssert(_error == 0, "Failed to read GUROBI parameters from file " + paramFile, false);
1038 }
1039 }
1040
1041 cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now();
1042 output.dCPUTime = cbui.pOutput->cCPUTime0 = std::clock();
1043
1044 /* Optimize the problem and obtain solution. */
1045 _error = dll_GRBoptimize(_model);
1046 wrapAssert(_error == 0, "Failed to optimize MIP.");
1047
1048 output.dWallTime =
1049 std::chrono::duration<double>(std::chrono::steady_clock::now() - output.dWallTime0).count();
1050 output.dCPUTime = (std::clock() - output.dCPUTime) / CLOCKS_PER_SEC;
1051
1052 int solstat;
1053 _error = dll_GRBgetintattr(_model, GRB_INT_ATTR_STATUS, &solstat);
1054 wrapAssert(_error == 0, "Failed to get MIP status.", false);
1055 output.status = convertStatus(solstat);
1056
1057 /// Continuing to fill the output object:
1058 if (Status::OPT == output.status || Status::SAT == output.status) {
1059 _error = dll_GRBgetdblattr(_model, GRB_DBL_ATTR_OBJVAL, &output.objVal);
1060 wrapAssert(_error == 0, "No MIP objective value available.");
1061
1062 // int cur_numrows = dll_GRB_getnumrows (env, lp);
1063 int cur_numcols = getNCols();
1064 assert(cur_numcols == colObj.size());
1065
1066 _x.resize(cur_numcols);
1067 output.x = &_x[0];
1068 _error = dll_GRBgetdblattrarray(_model, GRB_DBL_ATTR_X, 0, cur_numcols, (double*)output.x);
1069 wrapAssert(_error == 0, "Failed to get variable values.");
1070 if (!_options->flagIntermediate && (solcbfn != nullptr)) {
1071 solcbfn(output, cbui.psi);
1072 }
1073 }
1074 output.bestBound = std::numeric_limits<double>::has_quiet_NaN
1075 ? std::numeric_limits<double>::quiet_NaN()
1076 : std::numeric_limits<double>::max();
1077 int nObj = 0;
1078 dll_GRBgetintattr(_model, GRB_INT_ATTR_NUMOBJ, &nObj);
1079 if (1 >= nObj) {
1080 _error = dll_GRBgetdblattr(_model, GRB_DBL_ATTR_OBJBOUNDC, &output.bestBound);
1081 wrapAssert(_error == 0, "Failed to get the best bound.", false);
1082 }
1083 double nNodes = -1;
1084 _error = dll_GRBgetdblattr(_model, GRB_DBL_ATTR_NODECOUNT, &nNodes);
1085 output.nNodes = static_cast<int>(nNodes);
1086 output.nOpenNodes = 0;
1087}
1088
1089void MIPGurobiWrapper::setObjSense(int s) {
1090 _error = dll_GRBsetintattr(_model, GRB_INT_ATTR_MODELSENSE, s > 0 ? GRB_MAXIMIZE : GRB_MINIMIZE);
1091 wrapAssert(_error == 0, "Failed to set obj sense.");
1092}