this repo has no description
at develop 35 kB view raw
1// * -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3/* 4 * Main authors: 5 * Gleb Belov <gleb.belov@monash.edu> 6 */ 7 8/* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12#ifdef _MSC_VER 13#define _CRT_SECURE_NO_WARNINGS 14#endif 15 16#include <minizinc/solvers/MIP/MIP_scip_wrap.hh> 17#include <minizinc/utils.hh> 18 19#include <array> 20#include <cmath> 21#include <cstring> 22#include <fstream> 23#include <iomanip> 24#include <iostream> 25#include <sstream> 26#include <stdexcept> 27#include <string> 28 29using namespace std; 30 31/// Load SCIP DLL with the given path 32ScipPlugin::ScipPlugin(const std::string& dll) : Plugin(dll) { load(); } 33 34/// Load SCIP DLL with default search paths on Windows 35ScipPlugin::ScipPlugin() 36 : Plugin( 37#ifdef _WIN32 38 { 39 "libscip", "scip", "C:\\Program Files\\SCIPOptSuite 7.0.1\\bin\\libscip.dll", 40 "C:\\Program Files\\SCIPOptSuite 7.0.0\\bin\\libscip.dll", 41 "C:\\Program Files\\SCIPOptSuite 6.0.2\\bin\\scip.dll", 42 "C:\\Program Files\\SCIPOptSuite 6.0.1\\bin\\scip.dll", 43 "C:\\Program Files\\SCIPOptSuite 6.0.0\\bin\\scip.dll", 44 "C:\\Program Files\\SCIPOptSuite 5.0.1\\bin\\scip.dll", 45 "C:\\Program Files\\SCIPOptSuite 5.0.0\\bin\\scip.dll", 46 "C:\\Program Files\\SCIPOptSuite 4.0.1\\bin\\scip.dll", 47 "C:\\Program Files\\SCIPOptSuite 4.0.0\\bin\\scip.dll", 48 "C:\\Program Files (x86)\\SCIPOptSuite 7.0.1\\bin\\scip.dll", 49 "C:\\Program Files (x86)\\SCIPOptSuite 7.0.0\\bin\\scip.dll", 50 "C:\\Program Files (x86)\\SCIPOptSuite 6.0.2\\bin\\scip.dll", 51 "C:\\Program Files (x86)\\SCIPOptSuite 6.0.1\\bin\\scip.dll", 52 "C:\\Program Files (x86)\\SCIPOptSuite 6.0.0\\bin\\scip.dll", 53 "C:\\Program Files (x86)\\SCIPOptSuite 5.0.1\\bin\\scip.dll", 54 "C:\\Program Files (x86)\\SCIPOptSuite 5.0.0\\bin\\scip.dll", 55 "C:\\Program Files (x86)\\SCIPOptSuite 4.0.1\\bin\\scip.dll", 56 "C:\\Program Files (x86)\\SCIPOptSuite 4.0.0\\bin\\scip.dll", 57 } 58#else 59 "libscip" 60#endif 61 ) { 62 load(); 63} 64 65void ScipPlugin::load() { 66 load_symbol(SCIPmajorVersion); 67 load_symbol(SCIPminorVersion); 68 load_symbol(SCIPtechVersion); 69 load_symbol(SCIPsubversion); 70 load_symbol(SCIPprintError); 71 load_symbol(SCIPcreate); 72 load_symbol(SCIPincludeDefaultPlugins); 73 load_symbol(SCIPcreateProbBasic); 74 load_symbol(SCIPfree); 75 load_symbol(SCIPcreateVarBasic); 76 load_symbol(SCIPaddVar); 77 load_symbol(SCIPreleaseVar); 78#ifndef NDEBUG 79 load_symbol(SCIPinfinity); 80#endif 81 load_symbol(SCIPcreateConsBasicLinear); 82 load_symbol(SCIPcreateConsBasicQuadratic); 83 load_symbol(SCIPaddCons); 84 load_symbol(SCIPreleaseCons); 85 load_symbol(SCIPchgVarLbGlobal); 86 load_symbol(SCIPchgVarUbGlobal); 87 load_symbol(SCIPgetNegatedVar); 88 load_symbol(SCIPcreateConsBasicIndicator); 89 load_symbol(SCIPcreateConsBasicBounddisjunction); 90 load_symbol(SCIPcreateConsBasicCumulative); 91 load_symbol(SCIPgetNSolsFound); 92 load_symbol(SCIPgetNSols); 93 load_symbol(SCIPsetIntParam); 94 load_symbol(SCIPsetRealParam); 95 load_symbol(SCIPwriteOrigProblem); 96 load_symbol(SCIPsetMessagehdlrQuiet); 97 load_symbol(SCIPmessagehdlrCreate); 98 load_symbol(SCIPsetMessagehdlr); 99 load_symbol(SCIPreadParams); 100 load_symbol(SCIPwriteParams); 101 load_symbol(SCIPsolve); 102 load_symbol(SCIPgetStatus); 103 load_symbol(SCIPgetPrimalbound); 104 load_symbol(SCIPgetDualbound); 105 load_symbol(SCIPgetSolVals); 106 load_symbol(SCIPgetBestSol); 107 load_symbol(SCIPgetNTotalNodes); 108 load_symbol(SCIPgetNNodes); 109 load_symbol(SCIPgetNNodesLeft); 110 load_symbol(SCIPfreeTransform); 111 load_symbol(SCIPsetObjsense); 112 load_symbol(SCIPeventhdlrGetName); 113 load_symbol(SCIPcatchEvent); 114 load_symbol(SCIPdropEvent); 115 load_symbol(SCIPeventGetType); 116 load_symbol(SCIPgetSolOrigObj); 117 load_symbol(SCIPincludeEventhdlrBasic); 118 load_symbol(SCIPsetEventhdlrInit); 119 load_symbol(SCIPsetEventhdlrExit); 120 load_symbol(SCIPmessagePrintErrorHeader); 121 load_symbol(SCIPmessagePrintError); 122 load_symbol(SCIPgetNVars); 123 load_symbol(SCIPgetNConss); 124 load_symbol(SCIPgetParams); 125 load_symbol(SCIPgetNParams); 126 load_symbol(SCIPparamGetName); 127 load_symbol(SCIPparamGetType); 128 load_symbol(SCIPparamGetDesc); 129 load_symbol(SCIPparamGetBoolDefault); 130 load_symbol(SCIPparamGetCharAllowedValues); 131 load_symbol(SCIPparamGetCharDefault); 132 load_symbol(SCIPparamGetIntDefault); 133 load_symbol(SCIPparamGetIntMin); 134 load_symbol(SCIPparamGetIntMax); 135 load_symbol(SCIPparamGetLongintDefault); 136 load_symbol(SCIPparamGetLongintMin); 137 load_symbol(SCIPparamGetLongintMax); 138 load_symbol(SCIPparamGetRealDefault); 139 load_symbol(SCIPparamGetRealMin); 140 load_symbol(SCIPparamGetRealMax); 141 load_symbol(SCIPparamGetStringDefault); 142 load_symbol(SCIPgetParam); 143 load_symbol(SCIPchgBoolParam); 144 load_symbol(SCIPchgIntParam); 145 load_symbol(SCIPchgLongintParam); 146 load_symbol(SCIPchgRealParam); 147 load_symbol(SCIPchgCharParam); 148 load_symbol(SCIPchgStringParam); 149} 150 151#define SCIP_PLUGIN_CALL_R(plugin, x) \ 152 { \ 153 SCIP_RETCODE _ret = (x); \ 154 if (_ret != SCIP_OKAY) { \ 155 (plugin)->SCIPmessagePrintErrorHeader(__FILE__, __LINE__); \ 156 (plugin)->SCIPmessagePrintError("Error <%d> in function call\n", _ret); \ 157 return _ret; \ 158 } \ 159 } 160 161string MIPScipWrapper::getDescription(FactoryOptions& factoryOpt, 162 MiniZinc::SolverInstanceBase::Options* opt) { 163 ostringstream oss; 164 oss << "MIP wrapper for SCIP " << getVersion(factoryOpt, opt) 165 << ". Compiled " __DATE__ " " __TIME__; 166 return oss.str(); 167} 168string MIPScipWrapper::getVersion(FactoryOptions& factoryOpt, 169 MiniZinc::SolverInstanceBase::Options* opt) { 170 try { 171 auto* p = factoryOpt.scipDll.empty() ? new ScipPlugin() : new ScipPlugin(factoryOpt.scipDll); 172 ostringstream oss; 173 oss << p->SCIPmajorVersion() << '.' << p->SCIPminorVersion() << '.' << p->SCIPtechVersion() 174 << '.' << p->SCIPsubversion(); 175 delete p; 176 return oss.str(); 177 } catch (MiniZinc::Plugin::PluginError&) { 178 return "<unknown version>"; 179 } 180} 181vector<string> MIPScipWrapper::getRequiredFlags(FactoryOptions& factoryOpt) { 182 try { 183 ScipPlugin p; 184 return {}; 185 } catch (MiniZinc::Plugin::PluginError&) { 186 return {"--scip-dll"}; 187 } 188} 189 190vector<string> MIPScipWrapper::getFactoryFlags() { return {"--scip-dll"}; }; 191 192string MIPScipWrapper::getId() { return "scip"; } 193 194string MIPScipWrapper::getName() { return "SCIP"; } 195 196vector<string> MIPScipWrapper::getTags() { return {"mip", "float", "api"}; } 197 198vector<string> MIPScipWrapper::getStdFlags() { return {"-i", "-p", "-s"}; } 199 200void MIPScipWrapper::Options::printHelp(ostream& os) { 201 os << "SCIP MIP wrapper options:" 202 << std::endl 203 // -s print statistics 204 // << " --readParam <file> read SCIP parameters from file 205 // << "--writeParam <file> write SCIP parameters to file 206 // << "--tuneParam instruct SCIP to tune parameters instead of solving 207 << "--writeModel <file> write model to <file> (.lp, .mps, ...?)" << std::endl 208 << "-i print intermediate solutions for optimization problems" << std::endl 209 << "-p <N>, --parallel <N>\n use N threads, default: 1" 210 << std::endl 211 // << "--nomippresolve disable MIP presolving NOT IMPL" << std::endl 212 << "--solver-time-limit <N> stop search after N milliseconds" << std::endl 213 << "--workmem <N> maximal amount of RAM used, MB" << std::endl 214 << "--readParam <file> read SCIP parameters from file" << std::endl 215 << "--writeParam <file> write SCIP parameters to file" 216 << std::endl 217 // << "--tuneParam instruct SCIP to tune parameters instead of solving NOT IMPL" 218 219 << "--absGap <n> absolute gap |primal-dual| to stop" << std::endl 220 << "--relGap <n> relative gap |primal-dual|/<solver-dep> to stop. Default 1e-8, set " 221 "<0 " 222 "to use backend's default" 223 << std::endl 224 << "--intTol <n> integrality tolerance for a variable. Default 1e-8" 225 << std::endl 226 // << "--objDiff <n> objective function discretization. Default 1.0" << std::endl 227 << "--scip-dll <file> load the SCIP library from the given file (absolute path or file " 228 "basename), default 'scip'" 229 << std::endl 230 << std::endl; 231} 232 233static inline bool beginswith(const string& s, const string& t) { 234 return s.compare(0, t.length(), t) == 0; 235} 236 237bool MIPScipWrapper::FactoryOptions::processOption(int& i, std::vector<std::string>& argv, 238 const std::string& workingDir) { 239 MiniZinc::CLOParser cop(i, argv); 240 return cop.get("--scip-dll", &scipDll); 241} 242 243bool MIPScipWrapper::Options::processOption(int& i, vector<string>& argv, 244 const std::string& workingDir) { 245 MiniZinc::CLOParser cop(i, argv); 246 std::string buffer; 247 if (cop.get("-i")) { 248 flagIntermediate = true; 249 } else if (string(argv[i]) == "-f") { // NOLINT: Allow repeated empty if 250 // std::cerr << " Flag -f: ignoring fixed strategy anyway." << std::endl; 251 } else if (cop.get("--writeModel", &buffer)) { 252 sExportModel = MiniZinc::FileUtils::file_path(buffer); 253 } else if (cop.get("-p --parallel", &nThreads)) { // NOLINT: Allow repeated empty if 254 } else if (cop.get("--solver-time-limit", &nTimeout)) { // NOLINT: Allow repeated empty if 255 } else if (cop.get("--workmem", &nWorkMemLimit)) { // NOLINT: Allow repeated empty if 256 } else if (cop.get("--readParam", &buffer)) { 257 sReadParams = MiniZinc::FileUtils::file_path(buffer); 258 } else if (cop.get("--writeParam", &buffer)) { 259 sWriteParams = MiniZinc::FileUtils::file_path(buffer); 260 } else if (cop.get("--absGap", &absGap)) { // NOLINT: Allow repeated empty if 261 } else if (cop.get("--relGap", &relGap)) { // NOLINT: Allow repeated empty if 262 } else if (cop.get("--intTol", &intTol)) { // NOLINT: Allow repeated empty if 263 // } else if ( cop.get( "--objDiff", &objDiff ) ) { 264 } else { 265 return false; 266 } 267 return true; 268error: 269 return false; 270} 271 272// NOLINTNEXTLINE(readability-identifier-naming) 273void MIPScipWrapper::SCIP_PLUGIN_CALL(SCIP_RETCODE retcode, const string& msg, bool fTerm) { 274 /* evaluate return code of the SCIP process */ 275 if (retcode != SCIP_OKAY) { 276 /* write error back trace */ 277 _plugin->SCIPprintError(retcode); 278 string msgAll = (" MIPScipWrapper runtime error, see output: " + msg); 279 cerr << msgAll << endl; 280 if (fTerm) { 281 cerr << "TERMINATING." << endl; 282 throw runtime_error(msgAll); 283 } 284 } 285} 286 287SCIP_RETCODE MIPScipWrapper::openSCIP() { 288 if (_factoryOptions.scipDll.empty()) { 289 _plugin = new ScipPlugin(); 290 } else { 291 _plugin = new ScipPlugin(_factoryOptions.scipDll); 292 } 293 294 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPcreate(&_scip)); 295 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPincludeDefaultPlugins(_scip)); 296 297 /* create empty problem */ 298 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPcreateProbBasic(_scip, "mzn_scip")); 299 return SCIP_OKAY; 300} 301 302SCIP_RETCODE MIPScipWrapper::closeSCIP() { 303 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPfree(&_scip)); 304 305 delete _plugin; 306 /// and at last: 307 // MIPWrapper::cleanup(); 308 return SCIP_OKAY; 309} 310 311std::vector<MiniZinc::SolverConfig::ExtraFlag> MIPScipWrapper::getExtraFlags( 312 FactoryOptions& factoryOpt) { 313 try { 314 MIPScipWrapper msw(factoryOpt, nullptr); 315 auto* params = msw._plugin->SCIPgetParams(msw._scip); 316 int num_params = msw._plugin->SCIPgetNParams(msw._scip); 317 std::vector<MiniZinc::SolverConfig::ExtraFlag> res; 318 res.reserve(num_params); 319 for (int i = 0; i < num_params; i++) { 320 auto* param = params[i]; 321 std::string name = std::string(msw._plugin->SCIPparamGetName(param)); 322 if (name == "lp/threads" || name == "limits/time" || name == "limits/memory" || 323 name == "limits/absgap" || name == "limits/gap" || name == "numerics/feastol") { 324 // Handled by stdFlags 325 continue; 326 } 327 // Replace / in param name with _ (can't use - as some names have - in them already) 328 auto type = msw._plugin->SCIPparamGetType(param); 329 std::string desc(msw._plugin->SCIPparamGetDesc(param)); 330 MiniZinc::SolverConfig::ExtraFlag::FlagType param_type; 331 std::vector<std::string> param_range; 332 std::string param_default; 333 switch (type) { 334 case SCIP_ParamType::SCIP_PARAMTYPE_BOOL: 335 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_BOOL; 336 param_range = {"true", "false"}; 337 param_default = msw._plugin->SCIPparamGetBoolDefault(param) != 0 ? "true" : "false"; 338 break; 339 case SCIP_ParamType::SCIP_PARAMTYPE_CHAR: { 340 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_STRING; 341 param_default = msw._plugin->SCIPparamGetCharDefault(param); 342 auto* allowed_values = msw._plugin->SCIPparamGetCharAllowedValues(param); 343 if (allowed_values != nullptr) { 344 for (int i = 0; i < strlen(allowed_values); i++) { 345 param_range.emplace_back(1, allowed_values[i]); 346 } 347 } 348 break; 349 } 350 case SCIP_ParamType::SCIP_PARAMTYPE_INT: 351 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; 352 param_range.push_back(std::to_string(msw._plugin->SCIPparamGetIntMin(param))); 353 param_range.push_back(std::to_string(msw._plugin->SCIPparamGetIntMax(param))); 354 param_default = std::to_string(msw._plugin->SCIPparamGetIntDefault(param)); 355 break; 356 case SCIP_ParamType::SCIP_PARAMTYPE_LONGINT: 357 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; 358 param_range.push_back(std::to_string(msw._plugin->SCIPparamGetLongintMin(param))); 359 param_range.push_back(std::to_string(msw._plugin->SCIPparamGetLongintMax(param))); 360 param_default = std::to_string(msw._plugin->SCIPparamGetLongintDefault(param)); 361 break; 362 case SCIP_ParamType::SCIP_PARAMTYPE_REAL: 363 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_FLOAT; 364 param_range.push_back(std::to_string(msw._plugin->SCIPparamGetRealMin(param))); 365 param_range.push_back(std::to_string(msw._plugin->SCIPparamGetRealMax(param))); 366 param_default = std::to_string(msw._plugin->SCIPparamGetRealDefault(param)); 367 break; 368 case SCIP_ParamType::SCIP_PARAMTYPE_STRING: 369 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_STRING; 370 param_default = msw._plugin->SCIPparamGetStringDefault(param); 371 break; 372 default: 373 break; 374 } 375 res.emplace_back("--scip-" + name, desc, param_type, param_range, param_default); 376 } 377 return res; 378 } catch (MiniZinc::Plugin::PluginError&) { 379 return {}; 380 } 381 return {}; 382} 383 384SCIP_RETCODE MIPScipWrapper::doAddVarsSCIP(size_t n, double* obj, double* lb, double* ub, 385 MIPWrapper::VarType* vt, string* names) { 386 /// Convert var types: 387 // vector<char> ctype(n); 388 // vector<char*> pcNames(n); 389 for (size_t j = 0; j < n; ++j) { 390 // pcNames[i] = (char*)names[i].c_str(); 391 SCIP_VARTYPE ctype; 392 switch (vt[j]) { 393 case REAL: 394 ctype = SCIP_VARTYPE_CONTINUOUS; 395 break; 396 case INT: 397 ctype = SCIP_VARTYPE_INTEGER; 398 break; 399 case BINARY: 400 ctype = SCIP_VARTYPE_BINARY; 401 break; 402 default: 403 throw runtime_error(" MIPWrapper: unknown variable type"); 404 } 405 _scipVars.resize(_scipVars.size() + 1); 406 if (fPhase1Over) { 407 assert(_scipVars.size() == colObj.size()); 408 } 409 SCIP_PLUGIN_CALL_R( 410 _plugin, _plugin->SCIPcreateVarBasic(_scip, &_scipVars.back(), names[j].c_str(), lb[j], 411 ub[j], obj[j], ctype)); 412 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPaddVar(_scip, _scipVars.back())); 413 } 414 // retcode = SCIP_newcols (env, lp, n, obj, lb, ub, &ctype[0], &pcNames[0]); 415 // wrap_assert( !retcode, "Failed to declare variables." ); 416 return SCIP_OKAY; 417} 418 419SCIP_RETCODE MIPScipWrapper::delSCIPVars() { 420 for (auto& v : _scipVars) { 421 _plugin->SCIPreleaseVar(_scip, &v); 422 } 423 return SCIP_OKAY; 424} 425 426SCIP_RETCODE MIPScipWrapper::addRowSCIP(int nnz, int* rmatind, double* rmatval, 427 MIPWrapper::LinConType sense, double rhs, int mask, 428 const string& rowName) { 429 /// Convert var types: 430 double lh = -SCIPinfinityPlugin(_plugin, _scip); 431 double rh = SCIPinfinityPlugin(_plugin, _scip); 432 switch (sense) { 433 case LQ: 434 rh = rhs; 435 break; 436 case EQ: 437 lh = rh = rhs; 438 break; 439 case GQ: 440 lh = rhs; 441 break; 442 default: 443 throw runtime_error(" MIPWrapper: unknown constraint type"); 444 } 445 const int ccnt = 0; 446 const int rcnt = 1; 447 const int rmatbeg[] = {0}; 448 char* pRName = (char*)rowName.c_str(); 449 // ignoring mask for now. TODO 450 SCIP_CONS* cons; 451 vector<SCIP_VAR*> ab(nnz); 452 453 for (int j = 0; j < nnz; ++j) { 454 ab[j] = _scipVars[rmatind[j]]; 455 } 456 457 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPcreateConsBasicLinear(_scip, &cons, rowName.c_str(), nnz, 458 &ab[0], rmatval, lh, rh)); 459 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPaddCons(_scip, cons)); 460 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPreleaseCons(_scip, &cons)); 461 return SCIP_OKAY; 462 // retcode = SCIP_addrows (env, lp, ccnt, rcnt, nnz, &rhs, 463 // &ssense, rmatbeg, rmatind, rmatval, 464 // nullptr, &pRName); 465 // wrap_assert( !retcode, "Failed to add constraint." ); 466} 467 468void MIPScipWrapper::setVarBounds(int iVar, double lb, double ub) { 469 SCIP_PLUGIN_CALL(lb <= ub ? SCIP_OKAY : SCIP_ERROR, "scip interface: setVarBounds: lb>ub"); 470 setVarLB(iVar, lb); 471 setVarUB(iVar, ub); 472} 473 474void MIPScipWrapper::setVarLB(int iVar, double lb) { 475 auto res = _plugin->SCIPchgVarLbGlobal(_scip, _scipVars[iVar], lb); 476 SCIP_PLUGIN_CALL(res, "scip interface: failed to set var lb."); 477} 478 479void MIPScipWrapper::setVarUB(int iVar, double ub) { 480 auto res = _plugin->SCIPchgVarUbGlobal(_scip, _scipVars[iVar], ub); 481 SCIP_PLUGIN_CALL(res, "scip interface: failed to set var ub."); 482} 483 484void MIPScipWrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, 485 double* rmatval, MIPWrapper::LinConType sense, 486 double rhs, const string& rowName) { 487 MZN_ASSERT_HARD_MSG(0 <= bVal && 1 >= bVal, "SCIP: addIndicatorConstraint: bVal not 0/1"); 488 //// Make sure in order to notice the indices of lazy constr: also here? TODO 489 // ++ nRows; 490 491 SCIP_CONS* cons; 492 vector<SCIP_VAR*> ab(nnz); 493 SCIP_VAR* 494 indicator_var; // SCIP 6.0.1 requires that the implication is active for indicator_x == 1 495 496 for (int j = 0; j < nnz; ++j) { 497 ab[j] = _scipVars[rmatind[j]]; 498 } 499 500 indicator_var = _scipVars[iBVar]; 501 if (0 == bVal) { 502 SCIP_PLUGIN_CALL(_plugin->SCIPgetNegatedVar(_scip, indicator_var, &indicator_var)); 503 } 504 505 if (LQ == sense || EQ == sense) { 506 SCIP_PLUGIN_CALL(_plugin->SCIPcreateConsBasicIndicator( 507 _scip, &cons, rowName.c_str(), indicator_var, nnz, ab.data(), rmatval, rhs)); 508 SCIP_PLUGIN_CALL(_plugin->SCIPaddCons(_scip, cons)); 509 SCIP_PLUGIN_CALL(_plugin->SCIPreleaseCons(_scip, &cons)); 510 } 511 if (GQ == sense || EQ == sense) { 512 std::vector<double> rmatvalNEG(nnz); 513 for (int i = nnz; (i--) != 0;) { 514 rmatvalNEG[i] = -rmatval[i]; 515 } 516 SCIP_PLUGIN_CALL(_plugin->SCIPcreateConsBasicIndicator( 517 _scip, &cons, rowName.c_str(), indicator_var, nnz, ab.data(), rmatvalNEG.data(), -rhs)); 518 SCIP_PLUGIN_CALL(_plugin->SCIPaddCons(_scip, cons)); 519 SCIP_PLUGIN_CALL(_plugin->SCIPreleaseCons(_scip, &cons)); 520 } 521} 522 523void MIPScipWrapper::addBoundsDisj(int n, double* fUB, double* bnd, int* vars, int nF, double* fUBF, 524 double* bndF, int* varsF, const string& rowName) { 525 SCIP_CONS* cons; 526 std::vector<SCIP_VAR*> v(n + nF); 527 std::vector<SCIP_BOUNDTYPE> bt(n + nF); 528 std::vector<SCIP_Real> bs(n + nF); 529 530 for (int j = 0; j < n; ++j) { 531 v[j] = _scipVars[vars[j]]; 532 bt[j] = (fUB[j] != 0.0) ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER; 533 bs[j] = bnd[j]; 534 } 535 for (int j = 0; j < nF; ++j) { 536 v[n + j] = _scipVars[varsF[j]]; 537 bt[n + j] = (fUBF[j] != 0.0) ? SCIP_BOUNDTYPE_UPPER : SCIP_BOUNDTYPE_LOWER; 538 bs[n + j] = bndF[j]; 539 } 540 541 SCIP_PLUGIN_CALL(_plugin->SCIPcreateConsBasicBounddisjunction( 542 _scip, &cons, rowName.c_str(), v.size(), v.data(), bt.data(), bs.data())); 543 SCIP_PLUGIN_CALL(_plugin->SCIPaddCons(_scip, cons)); 544 SCIP_PLUGIN_CALL(_plugin->SCIPreleaseCons(_scip, &cons)); 545} 546 547void MIPScipWrapper::addCumulative(int nnz, int* rmatind, double* d, double* r, double b, 548 const string& rowName) { 549 SCIP_CONS* cons; 550 vector<SCIP_VAR*> ab(nnz); 551 vector<int> nd(nnz); 552 vector<int> nr(nnz); 553 554 for (int j = 0; j < nnz; ++j) { 555 ab[j] = _scipVars[rmatind[j]]; 556 nd[j] = (int)round(d[j]); 557 nr[j] = (int)round(r[j]); 558 } 559 560 SCIP_PLUGIN_CALL(_plugin->SCIPcreateConsBasicCumulative( 561 _scip, &cons, rowName.c_str(), nnz, ab.data(), nd.data(), nr.data(), (int)round(b))); 562 563 SCIP_PLUGIN_CALL(_plugin->SCIPaddCons(_scip, cons)); 564 SCIP_PLUGIN_CALL(_plugin->SCIPreleaseCons(_scip, &cons)); 565} 566 567void MIPScipWrapper::addTimes(int x, int y, int z, const string& rowName) { 568 /// As x*y - z == 0 569 double zCoef = -1.0; 570 double xyCoef = 1.0; 571 SCIP_CONS* cons; 572 std::array<SCIP_VAR*, 3> zxy = {_scipVars[z], _scipVars[x], _scipVars[y]}; 573 574 SCIP_PLUGIN_CALL(_plugin->SCIPcreateConsBasicQuadratic( 575 _scip, &cons, rowName.c_str(), 1, &zxy[0], &zCoef, 1, &zxy[1], &zxy[2], &xyCoef, 0.0, 0.0)); 576 SCIP_PLUGIN_CALL(_plugin->SCIPaddCons(_scip, cons)); 577 SCIP_PLUGIN_CALL(_plugin->SCIPreleaseCons(_scip, &cons)); 578} 579 580/// SolutionCallback ------------------------------------------------------------------------ 581 582/// From event_bestsol.c: 583#define EVENTHDLR_NAME "bestsol" 584#define EVENTHDLR_DESC "event handler for best solutions found" 585 586// Dirty way of accessing SCIP functions inside C callbacks 587static ScipPlugin* _cb_plugin; 588 589/** initialization method of event handler (called after problem was transformed) */ 590static SCIP_DECL_EVENTINIT(eventInitBestsol) { /*lint --e{715}*/ 591 assert(scip != nullptr); 592 assert(eventhdlr != nullptr); 593 assert(strcmp(_cb_plugin->SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); 594 595 /* notify SCIP that your event handler wants to react on the event type best solution found */ 596 SCIP_PLUGIN_CALL_R(_cb_plugin, _cb_plugin->SCIPcatchEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, 597 eventhdlr, nullptr, nullptr)); 598 599 return SCIP_OKAY; 600} 601 602/** deinitialization method of event handler (called before transformed problem is freed) */ 603static SCIP_DECL_EVENTEXIT(eventExitBestsol) { /*lint --e{715}*/ 604 assert(scip != nullptr); 605 assert(eventhdlr != nullptr); 606 assert(strcmp(_cb_plugin->SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); 607 608 /* notify SCIP that your event handler wants to drop the event type best solution found */ 609 SCIP_PLUGIN_CALL_R(_cb_plugin, _cb_plugin->SCIPdropEvent(scip, SCIP_EVENTTYPE_BESTSOLFOUND, 610 eventhdlr, nullptr, -1)); 611 612 return SCIP_OKAY; 613} 614 615static MIPWrapper::CBUserInfo* cbuiPtr = nullptr; 616static SCIP_VAR** _scipVarsPtr = nullptr; 617 618/** execution method of event handler */ 619static SCIP_DECL_EVENTEXEC(eventExecBestsol) { /*lint --e{715}*/ 620 SCIP_SOL* bestsol; 621 SCIP_Real objVal; 622 int newincumbent = 0; 623 624 assert(eventhdlr != nullptr); 625 assert(strcmp(_cb_plugin->SCIPeventhdlrGetName(eventhdlr), EVENTHDLR_NAME) == 0); 626 assert(event != nullptr); 627 assert(scip != nullptr); 628 assert(_cb_plugin->SCIPeventGetType(event) == SCIP_EVENTTYPE_BESTSOLFOUND); 629 630 SCIPdebugMessage("exec method of event handler for best solution found\n"); 631 632 bestsol = _cb_plugin->SCIPgetBestSol(scip); 633 assert(bestsol != nullptr); 634 objVal = _cb_plugin->SCIPgetSolOrigObj(scip, bestsol); 635 636 if (cbuiPtr == nullptr) { 637 return SCIP_OKAY; 638 } 639 640 if (fabs(cbuiPtr->pOutput->objVal - objVal) > 1e-12 * (1.0 + fabs(objVal))) { 641 newincumbent = 1; 642 cbuiPtr->pOutput->objVal = objVal; 643 cbuiPtr->pOutput->status = MIPWrapper::SAT; 644 cbuiPtr->pOutput->statusName = "feasible from a callback"; 645 } 646 647 if (newincumbent != 0 && _scipVarsPtr != nullptr) { 648 assert(cbuiPtr->pOutput->x); 649 SCIP_PLUGIN_CALL_R( 650 _cb_plugin, _cb_plugin->SCIPgetSolVals(scip, bestsol, cbuiPtr->pOutput->nCols, _scipVarsPtr, 651 (double*)cbuiPtr->pOutput->x)); 652 // wrap_assert(!retcode, "Failed to get variable values."); 653 cbuiPtr->pOutput->nNodes = static_cast<int>(_cb_plugin->SCIPgetNNodes(scip)); 654 cbuiPtr->pOutput->nOpenNodes = _cb_plugin->SCIPgetNNodesLeft(scip); 655 cbuiPtr->pOutput->bestBound = _cb_plugin->SCIPgetDualbound(scip); 656 657 cbuiPtr->pOutput->dCPUTime = -1; 658 659 /// Call the user function: 660 if (cbuiPtr->solcbfn != nullptr) { 661 (*cbuiPtr->solcbfn)(*cbuiPtr->pOutput, cbuiPtr->psi); 662 } 663 } 664 665 return SCIP_OKAY; 666} 667 668/** includes event handler for best solution found */ 669SCIP_RETCODE MIPScipWrapper::includeEventHdlrBestsol() { 670 SCIP_EVENTHDLRDATA* eventhdlrdata; 671 SCIP_EVENTHDLR* eventhdlr; 672 eventhdlrdata = nullptr; 673 674 eventhdlr = nullptr; 675 676 _cb_plugin = _plugin; // So that callbacks can access plugin functions 677 678 /* create event handler for events on watched variables */ 679 SCIP_PLUGIN_CALL_R( 680 _plugin, _plugin->SCIPincludeEventhdlrBasic(_scip, &eventhdlr, EVENTHDLR_NAME, EVENTHDLR_DESC, 681 eventExecBestsol, eventhdlrdata)); 682 assert(eventhdlr != nullptr); 683 684 /// Not for sub-SCIPs 685 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsetEventhdlrInit(_scip, eventhdlr, eventInitBestsol)); 686 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsetEventhdlrExit(_scip, eventhdlr, eventExitBestsol)); 687 688 return SCIP_OKAY; 689} 690 691MIPScipWrapper::Status MIPScipWrapper::convertStatus(SCIP_STATUS scipStatus) { 692 Status s = Status::UNKNOWN; 693 /* Converting the status. */ 694 switch (scipStatus) { 695 case SCIP_STATUS_OPTIMAL: 696 s = Status::OPT; 697 output.statusName = "Optimal"; 698 assert(_plugin->SCIPgetNSolsFound(_scip)); 699 break; 700 case SCIP_STATUS_INFEASIBLE: 701 s = Status::UNSAT; 702 output.statusName = "Infeasible"; 703 break; 704 // case SCIP_MIP_OPTIMAL_INFEAS: 705 case SCIP_STATUS_INFORUNBD: 706 s = Status::UNSATorUNBND; 707 output.statusName = "Infeasible or unbounded"; 708 break; 709 // case SCIP_MIP_SOL_LIM: 710 // s = Status::SAT; 711 // wrap_assert(SCIP_getsolnpoolnumsolns(env, lp), "Feasibility reported but pool 712 // empty?", false); break; 713 case SCIP_STATUS_UNBOUNDED: 714 s = Status::UNBND; 715 output.statusName = "Unbounded"; 716 break; 717 // case SCIP_STATUSMIP_ABORT_INFEAS: 718 // case SCIP_MIP_FAIL_INFEAS: 719 // s = Status::ERROR; 720 // break; 721 default: 722 // case SCIP_MIP_OPTIMAL_TOL: 723 // case SCIP_MIP_ABORT_RELAXATION_UNBOUNDED: 724 if (_plugin->SCIPgetNSols(_scip) != 0) { 725 s = Status::SAT; 726 output.statusName = "Feasible"; 727 } else { 728 s = Status::UNKNOWN; 729 output.statusName = "Unknown"; 730 } 731 } 732 return s; 733} 734 735SCIP_DECL_MESSAGEWARNING(printMsg) { cerr << msg << flush; } 736 737SCIP_RETCODE MIPScipWrapper::solveSCIP() { // Move into ancestor? 738 739 /////////////// Last-minute solver options ////////////////// 740 if (_options->nThreads > 0) 741 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsetIntParam(_scip, "lp/threads", _options->nThreads)); 742 743 if (_options->nTimeout > 0) 744 SCIP_PLUGIN_CALL_R(_plugin, 745 _plugin->SCIPsetRealParam(_scip, "limits/time", 746 static_cast<double>(_options->nTimeout) / 1000.0)); 747 748 if (_options->nWorkMemLimit > 0) 749 SCIP_PLUGIN_CALL_R(_plugin, 750 _plugin->SCIPsetRealParam(_scip, "limits/memory", _options->nWorkMemLimit)); 751 752 if (_options->absGap >= 0.0) 753 SCIP_PLUGIN_CALL_R(_plugin, 754 _plugin->SCIPsetRealParam(_scip, "limits/absgap", _options->absGap)); 755 if (_options->relGap >= 0.0) 756 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsetRealParam(_scip, "limits/gap", _options->relGap)); 757 if (_options->intTol >= 0.0) 758 SCIP_PLUGIN_CALL_R(_plugin, 759 _plugin->SCIPsetRealParam(_scip, "numerics/feastol", _options->intTol)); 760 761 // retcode = SCIP_setintparam (env, SCIP_PARAM_ClockType, 1); // CPU time 762 // wrap_assert(!retcode, " SCIP Warning: Failure to measure CPU time.", false); 763 764 if (!_options->sExportModel.empty()) { 765 // std::cerr <<" Exporting LP model to " << sExportModel << " ..." << std::endl; 766 SCIP_PLUGIN_CALL_R( 767 _plugin, _plugin->SCIPwriteOrigProblem(_scip, _options->sExportModel.c_str(), nullptr, 0)); 768 } 769 770 /* Turn on output to the screen - after model export */ 771 if (!fVerbose) { 772 // SCIP_PLUGIN_CALL(SCIPsetMessagehdlr(_scip, nullptr)); No LP export then 773 _plugin->SCIPsetMessagehdlrQuiet(_scip, TRUE); 774 } else { 775 SCIP_MESSAGEHDLR* pHndl = nullptr; 776 SCIP_PLUGIN_CALL_R( 777 _plugin, _plugin->SCIPmessagehdlrCreate(&pHndl, FALSE, nullptr, FALSE, printMsg, printMsg, 778 printMsg, nullptr, nullptr)); 779 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsetMessagehdlr(_scip, pHndl)); 780 } 781 782 // assert(_scipVars.size() == colObj.size()); 783 int cur_numcols = _scipVars.size(); // No, we create negated indicators: getNCols(); 784 assert(cur_numcols == colObj.size()); 785 assert(cur_numcols == _scipVars.size()); 786 787 /// Solution callback 788 output.nCols = colObj.size(); 789 _x.resize(output.nCols); 790 output.x = &_x[0]; 791 if (_options->flagIntermediate && cbui.solcbfn != nullptr && cbuiPtr == nullptr) { 792 /* include event handler for best solution found */ 793 SCIP_PLUGIN_CALL_R(_plugin, includeEventHdlrBestsol()); 794 cbuiPtr = &cbui; // not thread-safe... TODO 795 _scipVarsPtr = &_scipVars[0]; 796 // retcode = SCIP_setinfocallbackfunc (env, solcallback, &cbui); 797 // wrap_assert(!retcode, "Failed to set solution callback", false); 798 } 799 800 // Process extra flags options 801 for (auto& it : _options->extraParams) { 802 auto name = it.first.substr(7); 803 std::replace(name.begin(), name.end(), '_', '/'); 804 auto* param = _plugin->SCIPgetParam(_scip, name.c_str()); 805 if (param == nullptr) { 806 continue; 807 } 808 auto type = _plugin->SCIPparamGetType(param); 809 switch (type) { 810 case SCIP_ParamType::SCIP_PARAMTYPE_BOOL: 811 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPchgBoolParam(_scip, param, it.second == "true")); 812 break; 813 case SCIP_ParamType::SCIP_PARAMTYPE_CHAR: 814 if (!it.second.empty()) { 815 SCIP_PLUGIN_CALL_R(_plugin, 816 _plugin->SCIPchgCharParam(_scip, param, it.second.c_str()[0])); 817 } 818 break; 819 case SCIP_ParamType::SCIP_PARAMTYPE_INT: 820 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPchgIntParam(_scip, param, stoi(it.second))); 821 break; 822 case SCIP_ParamType::SCIP_PARAMTYPE_LONGINT: 823 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPchgLongintParam(_scip, param, stoll(it.second))); 824 break; 825 case SCIP_ParamType::SCIP_PARAMTYPE_REAL: 826 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPchgRealParam(_scip, param, stod(it.second))); 827 break; 828 case SCIP_ParamType::SCIP_PARAMTYPE_STRING: 829 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPchgStringParam(_scip, param, it.second.c_str())); 830 break; 831 default: 832 break; 833 } 834 } 835 836 if (!_options->sReadParams.empty()) { 837 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPreadParams(_scip, _options->sReadParams.c_str())); 838 } 839 840 if (!_options->sWriteParams.empty()) { 841 SCIP_PLUGIN_CALL_R(_plugin, 842 _plugin->SCIPwriteParams(_scip, _options->sReadParams.c_str(), TRUE, FALSE)); 843 } 844 845 cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); 846 output.dCPUTime = clock(); 847 848 /* Optimize the problem and obtain solution. */ 849 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsolve(_scip)); 850 // wrap_assert( !retcode, "Failed to optimize MIP." ); 851 852 output.dWallTime = 853 std::chrono::duration<double>(std::chrono::steady_clock::now() - output.dWallTime0).count(); 854 output.dCPUTime = (clock() - output.dCPUTime) / CLOCKS_PER_SEC; 855 856 cbuiPtr = nullptr; /// cleanup 857 _scipVarsPtr = nullptr; 858 859 SCIP_STATUS solstat = _plugin->SCIPgetStatus(_scip); 860 output.status = convertStatus(solstat); 861 // output.statusName = SCIP_getstatstring (env, solstat, scip_status_buffer); 862 863 /// Continuing to fill the output object: 864 output.objVal = _plugin->SCIPgetPrimalbound(_scip); 865 output.bestBound = _plugin->SCIPgetDualbound(_scip); 866 // wrap_assert(!retcode, "Failed to get the best bound.", false); 867 if (Status::OPT == output.status || Status::SAT == output.status) { 868 // wrap_assert( !retcode, "No MIP objective value available." ); 869 870 _x.resize(cur_numcols); 871 output.x = &_x[0]; 872 SCIP_PLUGIN_CALL_R(_plugin, 873 _plugin->SCIPgetSolVals(_scip, _plugin->SCIPgetBestSol(_scip), cur_numcols, 874 &_scipVars[0], (double*)output.x)); 875 if (cbui.solcbfn != nullptr && (!_options->flagIntermediate || !cbui.printed)) { 876 cbui.solcbfn(output, cbui.psi); 877 } 878 } 879 output.nNodes = static_cast<int>(_plugin->SCIPgetNTotalNodes(_scip)); 880 output.nOpenNodes = _plugin->SCIPgetNNodesLeft(_scip); // SCIP_getnodeleftcnt (env, lp); 881 882 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPfreeTransform(_scip)); 883 884 return SCIP_OKAY; 885} 886 887SCIP_RETCODE MIPScipWrapper::setObjSenseSCIP(int s) { 888 SCIP_PLUGIN_CALL_R(_plugin, _plugin->SCIPsetObjsense( 889 _scip, s > 0 ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE)); 890 return SCIP_OKAY; 891}