this repo has no description
at develop 45 kB view raw
1// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Gleb Belov <gleb.belov@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#ifdef _MSC_VER 13#define _CRT_SECURE_NO_WARNINGS 14#endif 15 16#include <minizinc/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, &current_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, &current_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}