this repo has no description
at develop 26 kB view raw
1/* 2 * main authors: 3 * Karsten Lehmann <karsten@satalia.com> 4 */ 5 6/* this source code form is subject to the terms of the mozilla public 7 * license, v. 2.0. if a copy of the mpl was not distributed with this 8 * file, you can obtain one at http://mozilla.org/mpl/2.0/. */ 9 10#include "minizinc/solvers/MIP/MIP_xpress_wrap.hh" 11 12#include "minizinc/config.hh" 13#include "minizinc/exception.hh" 14#include "minizinc/file_utils.hh" 15#include "minizinc/utils.hh" 16 17#include <cmath> 18#include <cstring> 19#include <ctime> 20#include <fstream> 21#include <iomanip> 22#include <iostream> 23#include <sstream> 24#include <stdexcept> 25#include <string> 26 27struct UserSolutionCallbackData { 28 MIPWrapper::CBUserInfo* info; 29 XPRBprob* problem; 30 vector<XPRBvar>* variables; 31 XpressPlugin* plugin; 32}; 33 34class XpressException : public runtime_error { 35public: 36 XpressException(const string& msg) : runtime_error(" MIPxpressWrapper: " + msg) {} 37}; 38 39XpressPlugin::XpressPlugin() : Plugin(XpressPlugin::dlls()) { loadDll(); } 40 41XpressPlugin::XpressPlugin(const std::string& dll_file) : Plugin(dll_file) { loadDll(); } 42 43void XpressPlugin::loadDll() { 44 load_symbol(XPRSinit); 45 load_symbol(XPRSfree); 46 load_symbol(XPRSgetversion); 47 load_symbol(XPRSgetlicerrmsg); 48 load_symbol(XPRBgetXPRSprob); 49 load_symbol(XPRBsetmsglevel); 50 load_symbol(XPRSsetlogfile); 51 load_symbol(XPRSsetintcontrol); 52 load_symbol(XPRSsetdblcontrol); 53 load_symbol(XPRBgetsol); 54 load_symbol(XPRSgetintattrib); 55 load_symbol(XPRSgetdblattrib); 56 load_symbol(XPRBbegincb); 57 load_symbol(XPRBsync); 58 load_symbol(XPRBendcb); 59 load_symbol(XPRBsetterm); 60 load_symbol(XPRBnewvar); 61 load_symbol(XPRBnewctr); 62 load_symbol(XPRBsetctrtype); 63 load_symbol(XPRBexportprob); 64 load_symbol(XPRBgetbounds); 65 load_symbol(XPRBsetobj); 66 load_symbol(XPRBmipoptimize); 67 load_symbol(XPRBsetsense); 68 load_symbol(XPRSsetcbintsol); 69 load_symbol(XPRBsetub); 70 load_symbol(XPRBsetlb); 71 load_symbol(XPRBsetindicator); 72 load_symbol(XPRBnewsol); 73 load_symbol(XPRBsetsolvar); 74 load_symbol(XPRBaddmipsol); 75 load_symbol(XPRBnewprob); 76 load_symbol(XPRBdelprob); 77 load_symbol(XPRSgetcontrolinfo); 78 load_symbol(XPRSgetintcontrol); 79 load_symbol(XPRSgetintcontrol64); 80 load_symbol(XPRSgetdblcontrol); 81 load_symbol(XPRSgetstrcontrol); 82 load_symbol(XPRSsetintcontrol64); 83 load_symbol(XPRSgetstringcontrol); 84 load_symbol(XPRSsetstrcontrol); 85} 86 87const std::vector<std::string>& XpressPlugin::dlls() { 88 static std::vector<std::string> ret = { 89#ifdef _WIN32 90 "xprs", "C:\\xpressmp\\bin\\xprs.dll" 91#elif __APPLE__ 92 "libxprs", "/Applications/FICO Xpress/xpressmp/lib/libxprs.dylib" 93#else 94 "libxprs", "/opt/xpressmp/lib/libxprs.so" 95#endif 96 }; 97 return ret; 98} 99 100void MIPxpressWrapper::openXpress() { 101 checkDLL(); 102 _problem = _plugin->XPRBnewprob(nullptr); 103 _xpressObj = _plugin->XPRBnewctr(_problem, nullptr, XB_N); 104} 105 106void MIPxpressWrapper::closeXpress() { 107 _plugin->XPRBdelprob(_problem); 108 _plugin->XPRSfree(); 109 delete _plugin; 110} 111 112void MIPxpressWrapper::checkDLL() { 113 if (!_factoryOptions.xpressDll.empty()) { 114 _plugin = new XpressPlugin(_factoryOptions.xpressDll); 115 } else { 116 _plugin = new XpressPlugin(); 117 } 118 119 std::vector<std::string> paths; 120 if (!_factoryOptions.xprsPassword.empty()) { 121 paths.push_back(_factoryOptions.xprsPassword); 122 } else { 123 paths.emplace_back(""); // Try builtin xpress dirs 124 auto dir = MiniZinc::FileUtils::dir_name(_plugin->path()); 125 auto file = dir + "/../bin/xpauth.xpr"; 126 if (!dir.empty() && MiniZinc::FileUtils::file_exists(file)) { 127 paths.push_back(file); // Try the bin dir license file if it exists 128 } 129 } 130 131 for (const auto& path : paths) { 132 int ret = _plugin->XPRSinit(path.empty() ? nullptr : path.c_str()); 133 if (ret == 0) { 134 return; 135 } 136 // Return code of 32 means student licence, but otherwise it's an error 137 if (ret == 32) { 138 if (_options->verbose) { 139 char message[512]; 140 _plugin->XPRSgetlicerrmsg(message, 512); 141 std::cerr << message << std::endl; 142 } 143 return; 144 } 145 } 146 147 char message[512]; 148 _plugin->XPRSgetlicerrmsg(message, 512); 149 throw XpressException(message); 150} 151 152string MIPxpressWrapper::getDescription(FactoryOptions& factoryOpt, 153 MiniZinc::SolverInstanceBase::Options* opt) { 154 ostringstream oss; 155 oss << " MIP wrapper for FICO Xpress Optimiser version " << getVersion(factoryOpt, opt); 156 oss << ". Compiled " __DATE__ " " __TIME__; 157 return oss.str(); 158} 159 160string MIPxpressWrapper::getVersion(FactoryOptions& factoryOpt, 161 MiniZinc::SolverInstanceBase::Options* opt) { 162 try { 163 auto* p = 164 factoryOpt.xpressDll.empty() ? new XpressPlugin : new XpressPlugin(factoryOpt.xpressDll); 165 char v[16]; 166 p->XPRSgetversion(v); 167 delete p; 168 return v; 169 } catch (MiniZinc::Plugin::PluginError&) { 170 return "<unknown version>"; 171 } 172} 173 174vector<string> MIPxpressWrapper::getRequiredFlags(FactoryOptions& factoryOpt) { 175 Options opts; 176 FactoryOptions triedFactoryOpts; 177 vector<string> ret; 178 // TODO: This is more complex than it should be 179 // We only know if --xpress-password is required if we have the DLL available 180 // So we have to try the user supplied --xpress-dll if given 181 while (true) { 182 try { 183 // Try opening without considering factory options 184 MIPxpressWrapper w(triedFactoryOpts, &opts); 185 return ret; 186 } catch (MiniZinc::Plugin::PluginError&) { 187 ret.emplace_back("--xpress-dll"); // The DLL needs to be given 188 if (triedFactoryOpts.xpressDll == factoryOpt.xpressDll) { 189 return ret; 190 } 191 triedFactoryOpts.xpressDll = factoryOpt.xpressDll; 192 } catch (XpressException&) { 193 ret.emplace_back("--xpress-password"); // The license needs to be given 194 if (triedFactoryOpts.xprsPassword == factoryOpt.xprsPassword) { 195 return ret; 196 } 197 triedFactoryOpts.xprsPassword = factoryOpt.xprsPassword; 198 } 199 } 200} 201 202vector<string> MIPxpressWrapper::getFactoryFlags() { 203 return {"--xpress-dll", "--xpress-password"}; 204}; 205 206string MIPxpressWrapper::getId() { return "xpress"; } 207 208string MIPxpressWrapper::getName() { return "Xpress"; } 209 210vector<string> MIPxpressWrapper::getTags() { return {"mip", "float", "api"}; } 211 212vector<string> MIPxpressWrapper::getStdFlags() { return {"-i", "-s", "-p", "-r"}; } 213 214vector<MiniZinc::SolverConfig::ExtraFlag> MIPxpressWrapper::getExtraFlags( 215 FactoryOptions& factoryOpt) { 216 try { 217 Options opts; 218 MIPxpressWrapper p(factoryOpt, &opts); 219 220 auto* prb = p._plugin->XPRBgetXPRSprob(p._problem); 221 // Using string parameter names because there doesn't seem to be a way to recover 222 // the name from a parameter ID number 223 static std::vector<std::string> all_params = { 224 "algaftercrossover", "algafternetwork", "autoperturb", "backtrack", "backtracktie", 225 "baralg", "barcrash", "bardualstop", "barfreescale", "bargapstop", "bargaptarget", 226 "barindeflimit", "bariterlimit", "barkernel", "barobjscale", "barorder", "barorderthreads", 227 "baroutput", "barpresolveops", "barprimalstop", "barregularize", "barrhsscale", 228 "barsolution", "barstart", "barstartweight", "barstepstop", "barthreads", "barcores", 229 "bigm", "bigmmethod", "branchchoice", "branchdisj", "branchstructural", "breadthfirst", 230 "cachesize", "callbackfrommasterthread", "choleskyalg", "choleskytol", "conflictcuts", 231 "concurrentthreads", "corespercpu", "covercuts", "cpuplatform", "cputime", "crash", 232 "crossover", "crossoveraccuracytol", "crossoveriterlimit", "crossoverops", 233 "crossoverthreads", "cstyle", "cutdepth", "cutfactor", "cutfreq", "cutstrategy", 234 "cutselect", "defaultalg", "densecollimit", "deterministic", "dualgradient", "dualize", 235 "dualizeops", "dualperturb", "dualstrategy", "dualthreads", "eigenvaluetol", "elimfillin", 236 "elimtol", "etatol", "extracols", "extraelems", "extramipents", "extrapresolve", 237 "extraqcelements", "extraqcrows", "extrarows", "extrasetelems", "extrasets", 238 "feasibilitypump", "feastol", "feastoltarget", "forceoutput", "forceparalleldual", 239 "globalfilebias", "globalfileloginterval", "gomcuts", "heurbeforelp", "heurdepth", 240 "heurdiveiterlimit", "heurdiverandomize", "heurdivesoftrounding", "heurdivespeedup", 241 "heurdivestrategy", "heurforcespecialobj", "heurfreq", "heurmaxsol", "heurnodes", 242 "heursearcheffort", "heursearchfreq", "heursearchrootcutfreq", "heursearchrootselect", 243 "heursearchtreeselect", "heurstrategy", "heurthreads", "historycosts", "ifcheckconvexity", 244 "indlinbigm", "indprelinbigm", "invertfreq", "invertmin", "keepbasis", "keepnrows", 245 "l1cache", "linelength", "lnpbest", "lnpiterlimit", "lpflags", "lpiterlimit", 246 "lprefineiterlimit", "localchoice", "lpfolding", "lplog", "lplogdelay", "lplogstyle", 247 "lpthreads", "markowitztol", "matrixtol", "maxchecksonmaxcuttime", "maxchecksonmaxtime", 248 "maxmcoeffbufferelems", "maxcuttime", "maxglobalfilesize", "maxiis", "maximpliedbound", 249 "maxlocalbacktrack", "maxmemoryhard", "maxmemorysoft", "maxmiptasks", "maxmipsol", 250 "maxnode", "maxpagelines", "maxscalefactor", "maxtime", "mipabscutoff", "mipabsgapnotify", 251 "mipabsgapnotifybound", "mipabsgapnotifyobj", "mipabsstop", "mipaddcutoff", 252 "mipdualreductions", "mipfracreduce", "mipkappafreq", "miplog", "mippresolve", "miprampup", 253 "miqcpalg", "miprefineiterlimit", "miprelcutoff", "miprelgapnotify", "miprelstop", 254 "mipterminationmethod", "mipthreads", "miptol", "miptoltarget", "mps18compatible", 255 "mpsboundname", "mpsecho", "mpsformat", "mpsobjname", "mpsrangename", "mpsrhsname", 256 "mutexcallbacks", "netcuts", "nodeselection", "objscalefactor", "optimalitytol", 257 "optimalitytoltarget", "outputlog", "outputmask", "outputtol", "penalty", "perturb", 258 "pivottol", "ppfactor", "preanalyticcenter", "prebasisred", "prebndredcone", 259 "prebndredquad", "precoefelim", "precomponents", "precomponentseffort", "preconedecomp", 260 "preconvertseparable", "predomcol", "predomrow", "preduprow", "preelimquad", 261 "preimplications", "prelindep", "preobjcutdetect", "prepermute", "prepermuteseed", 262 "preprobing", "preprotectdual", "presolve", "presolvemaxgrow", "presolveops", 263 "presolvepasses", "presort", "pricingalg", "primalops", "primalperturb", "primalunshift", 264 "pseudocost", "qccuts", "qcrootalg", "qsimplexops", "quadraticunshift", 265 //"randomseed", 266 "refactor", "refineops", "relaxtreememorylimit", "relpivottol", "repairindefiniteq", 267 "repairinfeasmaxtime", "resourcestrategy", "rootpresolve", "sbbest", "sbeffort", 268 "sbestimate", "sbiterlimit", "sbselect", "scaling", "sifting", "sleeponthreadwait", 269 "sosreftol", "symmetry", "symselect", 270 //"threads", 271 "trace", "treecompression", "treecovercuts", "treecutselect", "treediagnostics", 272 "treegomcuts", "treememorylimit", "treememorysavingtarget", "treepresolve", 273 "treepresolve_keepbasis", "treeqccuts", "tunerhistory", "tunermaxtime", "tunermethod", 274 "tunermethodfile", "tunermode", "tuneroutput", "tuneroutputpath", "tunerpermute", 275 "tunerrootalg", "tunersessionname", "tunertarget", "tunerthreads", "usersolheuristic", 276 "varselection", 277 //"version" 278 }; 279 std::vector<MiniZinc::SolverConfig::ExtraFlag> res; 280 for (auto param : all_params) { 281 int n; 282 int t; 283 p._plugin->XPRSgetcontrolinfo(prb, param.c_str(), &n, &t); 284 MiniZinc::SolverConfig::ExtraFlag::FlagType param_type; 285 std::string param_default; 286 switch (t) { 287 case XPRS_TYPE_INT: { 288 int d; 289 p._plugin->XPRSgetintcontrol(prb, n, &d); 290 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; 291 param_default = to_string(d); 292 break; 293 } 294 case XPRS_TYPE_INT64: { 295 XPRSint64 d; 296 p._plugin->XPRSgetintcontrol64(prb, n, &d); 297 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; 298 param_default = to_string(d); 299 break; 300 } 301 case XPRS_TYPE_DOUBLE: { 302 double d; 303 p._plugin->XPRSgetdblcontrol(prb, n, &d); 304 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_FLOAT; 305 param_default = to_string(d); 306 break; 307 } 308 case XPRS_TYPE_STRING: { 309 int l; 310 p._plugin->XPRSgetstringcontrol(prb, n, nullptr, 0, &l); 311 char* d = (char*)malloc(l); 312 p._plugin->XPRSgetstringcontrol(prb, n, d, l, &l); 313 param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_STRING; 314 param_default = d; 315 break; 316 } 317 default: 318 continue; 319 } 320 // TODO: Some of these parameters have min/max or are categorical, but there's no way 321 // to programatically get the possible values. We could manually maintain it, but it's 322 // probably not worth doing right now. 323 std::vector<std::string> param_range; // unused for now 324 res.emplace_back("--xpress-" + param, param, param_type, param_range, param_default); 325 } 326 return res; 327 } catch (MiniZinc::Plugin::PluginError&) { 328 return {}; 329 } catch (XpressException&) { 330 return {}; 331 } 332 return {}; 333} 334 335void MIPxpressWrapper::Options::printHelp(ostream& os) { 336 os << "XPRESS MIP wrapper options:" << std::endl 337 << "--msgLevel <n> print solver output, default: 0" << std::endl 338 << "--logFile <file> log file" << std::endl 339 << "--solver-time-limit <N> stop search after N milliseconds, if negative, it " 340 "will only stop if at least one solution was found" 341 << std::endl 342 << "-n <N>, --numSolutions <N> stop search after N solutions" << std::endl 343 << "--writeModel <file> write model to <file>" << std::endl 344 << "--writeModelFormat [lp|mps] the file format of the written model(lp " 345 "or mps), default: lp" 346 << std::endl 347 << "--absGap <d> absolute gap |primal-dual| to stop, default: " << 0 << std::endl 348 << "--relGap <d> relative gap |primal-dual|/<solver-dep> to stop, " 349 "default: " 350 << 0.0001 << std::endl 351 << "-i print intermediate solution, default: false" << std::endl 352 << "-r <N>, --seed <N>, --random-seed <N>" << std::endl 353 << " random seed, integer" 354 << "-p <N>, --parallel <N> use N threads" << std::endl 355 << "--xpress-dll <file> Xpress DLL file (xprs.dll/libxprs.so/libxprs.dylib)" << std::endl 356 << "--xpress-password <dir> directory where xpauth.xpr is located (optional)" << std::endl 357 << std::endl; 358} 359 360bool MIPxpressWrapper::FactoryOptions::processOption(int& i, std::vector<std::string>& argv, 361 const std::string& workingDir) { 362 MiniZinc::CLOParser cop(i, argv); 363 if (cop.get("--xpress-dll", &xpressDll)) { // NOLINT: Allow repeated empty if 364 } else if (cop.get("--xpress-password", &xprsPassword)) { // NOLINT: Allow repeated empty if 365 } else { 366 return false; 367 } 368 return true; 369} 370 371bool MIPxpressWrapper::Options::processOption(int& i, std::vector<std::string>& argv, 372 const std::string& workingDir) { 373 MiniZinc::CLOParser cop(i, argv); 374 std::string buffer; 375 if (cop.get("--msgLevel", &msgLevel)) { // NOLINT: Allow repeated empty if 376 } else if (cop.get("--logFile", &buffer)) { 377 logFile = MiniZinc::FileUtils::file_path(buffer, workingDir); 378 } else if (cop.get("--solver-time-limit", &timeout)) { // NOLINT: Allow repeated empty if 379 } else if (cop.get("-n --numSolutions", &numSolutions)) { // NOLINT: Allow repeated empty if 380 } else if (cop.get("--writeModel", &buffer)) { 381 writeModelFile = MiniZinc::FileUtils::file_path(buffer, workingDir); 382 } else if (cop.get("--writeModelFormat", &writeModelFormat)) { // NOLINT: Allow repeated empty if 383 } else if (cop.get("--relGap", &relGap)) { // NOLINT: Allow repeated empty if 384 } else if (cop.get("--absGap", &absGap)) { // NOLINT: Allow repeated empty if 385 } else if (cop.get("-i")) { 386 intermediateSolutions = true; 387 } else if (cop.get("-p --parallel", &numThreads)) { 388 } else if (cop.get("-r --seed --random-seed", &randomSeed)) { 389 } else { 390 return false; 391 } 392 return true; 393} 394 395void MIPxpressWrapper::setOptions() { 396 XPRSprob xprsProblem = _plugin->XPRBgetXPRSprob(_problem); 397 398 _plugin->XPRBsetmsglevel(_problem, _options->msgLevel); 399 400 _plugin->XPRSsetlogfile(xprsProblem, _options->logFile.c_str()); 401 if (_options->timeout > 1000 || _options->timeout < -1000) { 402 _plugin->XPRSsetintcontrol(xprsProblem, XPRS_MAXTIME, 403 static_cast<int>(_options->timeout / 1000)); 404 } 405 _plugin->XPRSsetintcontrol(xprsProblem, XPRS_MAXMIPSOL, _options->numSolutions); 406 _plugin->XPRSsetdblcontrol(xprsProblem, XPRS_MIPABSSTOP, _options->absGap); 407 _plugin->XPRSsetdblcontrol(xprsProblem, XPRS_MIPRELSTOP, _options->relGap); 408 409 if (_options->numThreads > 0) { 410 _plugin->XPRSsetintcontrol(xprsProblem, XPRS_THREADS, _options->numThreads); 411 } 412 413 if (_options->randomSeed != 0) { 414 _plugin->XPRSsetintcontrol(xprsProblem, XPRS_RANDOMSEED, _options->randomSeed); 415 } 416 417 for (auto& it : _options->extraParams) { 418 auto name = it.first.substr(9); 419 int n; 420 int t; 421 _plugin->XPRSgetcontrolinfo(xprsProblem, name.c_str(), &n, &t); 422 switch (t) { 423 case XPRS_TYPE_INT: 424 _plugin->XPRSsetintcontrol(xprsProblem, n, stoi(it.second)); 425 break; 426 case XPRS_TYPE_INT64: 427 _plugin->XPRSsetintcontrol64(xprsProblem, n, stoll(it.second)); 428 break; 429 case XPRS_TYPE_DOUBLE: 430 _plugin->XPRSsetdblcontrol(xprsProblem, n, stod(it.second)); 431 break; 432 case XPRS_TYPE_STRING: 433 _plugin->XPRSsetstrcontrol(xprsProblem, n, it.second.c_str()); 434 break; 435 default: 436 throw XpressException("Unknown type for parameter " + name); 437 } 438 } 439} 440 441static MIPWrapper::Status convert_status(int xpressStatus) { 442 switch (xpressStatus) { 443 case XPRB_MIP_OPTIMAL: 444 return MIPWrapper::Status::OPT; 445 case XPRB_MIP_INFEAS: 446 return MIPWrapper::Status::UNSAT; 447 case XPRB_MIP_UNBOUNDED: 448 return MIPWrapper::Status::UNBND; 449 case XPRB_MIP_NO_SOL_FOUND: 450 return MIPWrapper::Status::UNKNOWN; 451 case XPRB_MIP_NOT_LOADED: 452 return MIPWrapper::Status::__ERROR; 453 default: 454 return MIPWrapper::Status::UNKNOWN; 455 } 456} 457 458static string get_status_name(int xpressStatus) { 459 string rt = "Xpress stopped with status: "; 460 switch (xpressStatus) { 461 case XPRB_MIP_OPTIMAL: 462 return rt + "Optimal"; 463 case XPRB_MIP_INFEAS: 464 return rt + "Infeasible"; 465 case XPRB_MIP_UNBOUNDED: 466 return rt + "Unbounded"; 467 case XPRB_MIP_NO_SOL_FOUND: 468 return rt + "No solution found"; 469 case XPRB_MIP_NOT_LOADED: 470 return rt + "No problem loaded or error"; 471 default: 472 return rt + "Unknown status"; 473 } 474} 475 476static void set_output_variables(XpressPlugin* plugin, MIPxpressWrapper::Output* output, 477 vector<XPRBvar>* variables) { 478 size_t nCols = variables->size(); 479 auto* x = (double*)malloc(nCols * sizeof(double)); 480 for (size_t ii = 0; ii < nCols; ii++) { 481 x[ii] = plugin->XPRBgetsol((*variables)[ii]); 482 } 483 output->x = x; 484} 485 486static void set_output_attributes(XpressPlugin* plugin, MIPxpressWrapper::Output* output, 487 XPRSprob xprsProblem) { 488 int xpressStatus = 0; 489 plugin->XPRSgetintattrib(xprsProblem, XPRS_MIPSTATUS, &xpressStatus); 490 output->status = convert_status(xpressStatus); 491 output->statusName = get_status_name(xpressStatus); 492 493 plugin->XPRSgetdblattrib(xprsProblem, XPRS_MIPOBJVAL, &output->objVal); 494 plugin->XPRSgetdblattrib(xprsProblem, XPRS_BESTBOUND, &output->bestBound); 495 496 plugin->XPRSgetintattrib(xprsProblem, XPRS_NODES, &output->nNodes); 497 plugin->XPRSgetintattrib(xprsProblem, XPRS_ACTIVENODES, &output->nOpenNodes); 498 499 output->dWallTime = 500 std::chrono::duration<double>(std::chrono::steady_clock::now() - output->dWallTime0).count(); 501 output->dCPUTime = double(std::clock() - output->cCPUTime0) / CLOCKS_PER_SEC; 502} 503 504static void XPRS_CC user_sol_notify_callback(XPRSprob xprsProblem, void* userData) { 505 auto* data = (UserSolutionCallbackData*)userData; 506 MIPWrapper::CBUserInfo* info = data->info; 507 508 set_output_attributes(data->plugin, info->pOutput, xprsProblem); 509 510 data->plugin->XPRBbegincb(*(data->problem), xprsProblem); 511 data->plugin->XPRBsync(*(data->problem), XPRB_XPRS_SOL); 512 set_output_variables(data->plugin, info->pOutput, data->variables); 513 data->plugin->XPRBendcb(*(data->problem)); 514 515 if (info->solcbfn != nullptr) { 516 (*info->solcbfn)(*info->pOutput, info->psi); 517 } 518} 519 520void MIPxpressWrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, 521 string* names) { 522 if (obj == nullptr || lb == nullptr || ub == nullptr || vt == nullptr || names == nullptr) { 523 throw XpressException("invalid input"); 524 } 525 for (size_t i = 0; i < n; ++i) { 526 char* var_name = (char*)names[i].c_str(); 527 int var_type = convertVariableType(vt[i]); 528 XPRBvar var = _plugin->XPRBnewvar(_problem, var_type, var_name, lb[i], ub[i]); 529 _variables.push_back(var); 530 _plugin->XPRBsetterm(_xpressObj, var, obj[i]); 531 } 532} 533 534void MIPxpressWrapper::addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, 535 int mask, const string& rowName) { 536 addConstraint(nnz, rmatind, rmatval, sense, rhs, mask, rowName); 537} 538 539XPRBctr MIPxpressWrapper::addConstraint(int nnz, int* rmatind, double* rmatval, LinConType sense, 540 double rhs, int mask, const string& rowName) { 541 _nRows++; 542 XPRBctr constraint = _plugin->XPRBnewctr(_problem, rowName.c_str(), convertConstraintType(sense)); 543 for (int i = 0; i < nnz; ++i) { 544 _plugin->XPRBsetterm(constraint, _variables[rmatind[i]], rmatval[i]); 545 } 546 _plugin->XPRBsetterm(constraint, nullptr, rhs); 547 548 return constraint; 549} 550 551void MIPxpressWrapper::writeModelIfRequested() { 552 int format = XPRB_LP; 553 if (_options->writeModelFormat == "lp") { 554 format = XPRB_LP; 555 } else if (_options->writeModelFormat == "mps") { 556 format = XPRB_MPS; 557 } 558 if (!_options->writeModelFile.empty()) { 559 _plugin->XPRBexportprob(_problem, format, _options->writeModelFile.c_str()); 560 } 561} 562 563void MIPxpressWrapper::addDummyConstraint() { 564 if (getNCols() == 0) { 565 return; 566 } 567 568 XPRBctr constraint = _plugin->XPRBnewctr(_problem, "dummy_constraint", XPRB_L); 569 _plugin->XPRBsetterm(constraint, _variables[0], 1); 570 double ub; 571 _plugin->XPRBgetbounds(_variables[0], nullptr, &ub); 572 _plugin->XPRBsetterm(constraint, nullptr, ub); 573} 574 575void MIPxpressWrapper::solve() { 576 if (getNRows() == 0) { 577 addDummyConstraint(); 578 } 579 580 setOptions(); 581 writeModelIfRequested(); 582 setUserSolutionCallback(); 583 584 _plugin->XPRBsetobj(_problem, _xpressObj); 585 586 cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); 587 cbui.pOutput->cCPUTime0 = output.dCPUTime = std::clock(); 588 589 if (_plugin->XPRBmipoptimize(_problem, "c") == 1) { 590 throw XpressException("error while solving"); 591 } 592 593 set_output_variables(_plugin, &output, &_variables); 594 set_output_attributes(_plugin, &output, _plugin->XPRBgetXPRSprob(_problem)); 595 596 if (!_options->intermediateSolutions && cbui.solcbfn != nullptr) { 597 cbui.solcbfn(output, cbui.psi); 598 } 599} 600 601void MIPxpressWrapper::setUserSolutionCallback() { 602 if (!_options->intermediateSolutions) { 603 return; 604 } 605 606 auto* data = new UserSolutionCallbackData{&cbui, &_problem, &_variables, _plugin}; 607 608 _plugin->XPRSsetcbintsol(_plugin->XPRBgetXPRSprob(_problem), user_sol_notify_callback, data); 609} 610 611void MIPxpressWrapper::setObjSense(int s) { 612 _plugin->XPRBsetsense(_problem, convertObjectiveSense(s)); 613} 614 615void MIPxpressWrapper::setVarLB(int iVar, double lb) { _plugin->XPRBsetlb(_variables[iVar], lb); } 616 617void MIPxpressWrapper::setVarUB(int iVar, double ub) { _plugin->XPRBsetub(_variables[iVar], ub); } 618 619void MIPxpressWrapper::setVarBounds(int iVar, double lb, double ub) { 620 setVarLB(iVar, lb); 621 setVarUB(iVar, ub); 622} 623 624void MIPxpressWrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, 625 double* rmatval, LinConType sense, double rhs, 626 const string& rowName) { 627 if (bVal != 0 && bVal != 1) { 628 throw XpressException("indicator bval not in 0/1"); 629 } 630 XPRBctr constraint = addConstraint(nnz, rmatind, rmatval, sense, rhs, 0, rowName); 631 _plugin->XPRBsetindicator(constraint, 2 * bVal - 1, _variables[iBVar]); 632} 633 634bool MIPxpressWrapper::addWarmStart(const std::vector<VarId>& vars, 635 const std::vector<double>& vals) { 636 XPRBsol warmstart = _plugin->XPRBnewsol(_problem); 637 for (size_t ii = 0; ii < vars.size(); ii++) { 638 _plugin->XPRBsetsolvar(warmstart, _variables[vars[ii]], vals[ii]); 639 } 640 return (_plugin->XPRBaddmipsol(_problem, warmstart, nullptr) == 0); 641} 642 643int MIPxpressWrapper::convertConstraintType(LinConType sense) { 644 switch (sense) { 645 case MIPWrapper::LQ: 646 return XPRB_L; 647 case MIPWrapper::EQ: 648 return XPRB_E; 649 case MIPWrapper::GQ: 650 return XPRB_G; 651 default: 652 throw XpressException("unkown constraint sense"); 653 } 654} 655 656int MIPxpressWrapper::convertVariableType(VarType varType) { 657 switch (varType) { 658 case REAL: 659 return XPRB_PL; 660 case INT: 661 return XPRB_UI; 662 case BINARY: 663 return XPRB_BV; 664 default: 665 throw XpressException("unknown variable type"); 666 } 667} 668 669int MIPxpressWrapper::convertObjectiveSense(int s) { 670 switch (s) { 671 case 1: 672 return XPRB_MAXIM; 673 case -1: 674 return XPRB_MINIM; 675 default: 676 throw XpressException("unknown objective sense"); 677 } 678}