/* * main authors: * Karsten Lehmann */ /* this source code form is subject to the terms of the mozilla public * license, v. 2.0. if a copy of the mpl was not distributed with this * file, you can obtain one at http://mozilla.org/mpl/2.0/. */ #include "minizinc/solvers/MIP/MIP_xpress_wrap.hh" #include "minizinc/config.hh" #include "minizinc/exception.hh" #include "minizinc/file_utils.hh" #include "minizinc/utils.hh" #include #include #include #include #include #include #include #include #include struct UserSolutionCallbackData { MIPWrapper::CBUserInfo* info; XPRBprob* problem; vector* variables; XpressPlugin* plugin; }; class XpressException : public runtime_error { public: XpressException(const string& msg) : runtime_error(" MIPxpressWrapper: " + msg) {} }; XpressPlugin::XpressPlugin() : Plugin(XpressPlugin::dlls()) { loadDll(); } XpressPlugin::XpressPlugin(const std::string& dll_file) : Plugin(dll_file) { loadDll(); } void XpressPlugin::loadDll() { load_symbol(XPRSinit); load_symbol(XPRSfree); load_symbol(XPRSgetversion); load_symbol(XPRSgetlicerrmsg); load_symbol(XPRBgetXPRSprob); load_symbol(XPRBsetmsglevel); load_symbol(XPRSsetlogfile); load_symbol(XPRSsetintcontrol); load_symbol(XPRSsetdblcontrol); load_symbol(XPRBgetsol); load_symbol(XPRSgetintattrib); load_symbol(XPRSgetdblattrib); load_symbol(XPRBbegincb); load_symbol(XPRBsync); load_symbol(XPRBendcb); load_symbol(XPRBsetterm); load_symbol(XPRBnewvar); load_symbol(XPRBnewctr); load_symbol(XPRBsetctrtype); load_symbol(XPRBexportprob); load_symbol(XPRBgetbounds); load_symbol(XPRBsetobj); load_symbol(XPRBmipoptimize); load_symbol(XPRBsetsense); load_symbol(XPRSsetcbintsol); load_symbol(XPRBsetub); load_symbol(XPRBsetlb); load_symbol(XPRBsetindicator); load_symbol(XPRBnewsol); load_symbol(XPRBsetsolvar); load_symbol(XPRBaddmipsol); load_symbol(XPRBnewprob); load_symbol(XPRBdelprob); load_symbol(XPRSgetcontrolinfo); load_symbol(XPRSgetintcontrol); load_symbol(XPRSgetintcontrol64); load_symbol(XPRSgetdblcontrol); load_symbol(XPRSgetstrcontrol); load_symbol(XPRSsetintcontrol64); load_symbol(XPRSgetstringcontrol); load_symbol(XPRSsetstrcontrol); } const std::vector& XpressPlugin::dlls() { static std::vector ret = { #ifdef _WIN32 "xprs", "C:\\xpressmp\\bin\\xprs.dll" #elif __APPLE__ "libxprs", "/Applications/FICO Xpress/xpressmp/lib/libxprs.dylib" #else "libxprs", "/opt/xpressmp/lib/libxprs.so" #endif }; return ret; } void MIPxpressWrapper::openXpress() { checkDLL(); _problem = _plugin->XPRBnewprob(nullptr); _xpressObj = _plugin->XPRBnewctr(_problem, nullptr, XB_N); } void MIPxpressWrapper::closeXpress() { _plugin->XPRBdelprob(_problem); _plugin->XPRSfree(); delete _plugin; } void MIPxpressWrapper::checkDLL() { if (!_factoryOptions.xpressDll.empty()) { _plugin = new XpressPlugin(_factoryOptions.xpressDll); } else { _plugin = new XpressPlugin(); } std::vector paths; if (!_factoryOptions.xprsPassword.empty()) { paths.push_back(_factoryOptions.xprsPassword); } else { paths.emplace_back(""); // Try builtin xpress dirs auto dir = MiniZinc::FileUtils::dir_name(_plugin->path()); auto file = dir + "/../bin/xpauth.xpr"; if (!dir.empty() && MiniZinc::FileUtils::file_exists(file)) { paths.push_back(file); // Try the bin dir license file if it exists } } for (const auto& path : paths) { int ret = _plugin->XPRSinit(path.empty() ? nullptr : path.c_str()); if (ret == 0) { return; } // Return code of 32 means student licence, but otherwise it's an error if (ret == 32) { if (_options->verbose) { char message[512]; _plugin->XPRSgetlicerrmsg(message, 512); std::cerr << message << std::endl; } return; } } char message[512]; _plugin->XPRSgetlicerrmsg(message, 512); throw XpressException(message); } string MIPxpressWrapper::getDescription(FactoryOptions& factoryOpt, MiniZinc::SolverInstanceBase::Options* opt) { ostringstream oss; oss << " MIP wrapper for FICO Xpress Optimiser version " << getVersion(factoryOpt, opt); oss << ". Compiled " __DATE__ " " __TIME__; return oss.str(); } string MIPxpressWrapper::getVersion(FactoryOptions& factoryOpt, MiniZinc::SolverInstanceBase::Options* opt) { try { auto* p = factoryOpt.xpressDll.empty() ? new XpressPlugin : new XpressPlugin(factoryOpt.xpressDll); char v[16]; p->XPRSgetversion(v); delete p; return v; } catch (MiniZinc::Plugin::PluginError&) { return ""; } } vector MIPxpressWrapper::getRequiredFlags(FactoryOptions& factoryOpt) { Options opts; FactoryOptions triedFactoryOpts; vector ret; // TODO: This is more complex than it should be // We only know if --xpress-password is required if we have the DLL available // So we have to try the user supplied --xpress-dll if given while (true) { try { // Try opening without considering factory options MIPxpressWrapper w(triedFactoryOpts, &opts); return ret; } catch (MiniZinc::Plugin::PluginError&) { ret.emplace_back("--xpress-dll"); // The DLL needs to be given if (triedFactoryOpts.xpressDll == factoryOpt.xpressDll) { return ret; } triedFactoryOpts.xpressDll = factoryOpt.xpressDll; } catch (XpressException&) { ret.emplace_back("--xpress-password"); // The license needs to be given if (triedFactoryOpts.xprsPassword == factoryOpt.xprsPassword) { return ret; } triedFactoryOpts.xprsPassword = factoryOpt.xprsPassword; } } } vector MIPxpressWrapper::getFactoryFlags() { return {"--xpress-dll", "--xpress-password"}; }; string MIPxpressWrapper::getId() { return "xpress"; } string MIPxpressWrapper::getName() { return "Xpress"; } vector MIPxpressWrapper::getTags() { return {"mip", "float", "api"}; } vector MIPxpressWrapper::getStdFlags() { return {"-i", "-s", "-p", "-r"}; } vector MIPxpressWrapper::getExtraFlags( FactoryOptions& factoryOpt) { try { Options opts; MIPxpressWrapper p(factoryOpt, &opts); auto* prb = p._plugin->XPRBgetXPRSprob(p._problem); // Using string parameter names because there doesn't seem to be a way to recover // the name from a parameter ID number static std::vector all_params = { "algaftercrossover", "algafternetwork", "autoperturb", "backtrack", "backtracktie", "baralg", "barcrash", "bardualstop", "barfreescale", "bargapstop", "bargaptarget", "barindeflimit", "bariterlimit", "barkernel", "barobjscale", "barorder", "barorderthreads", "baroutput", "barpresolveops", "barprimalstop", "barregularize", "barrhsscale", "barsolution", "barstart", "barstartweight", "barstepstop", "barthreads", "barcores", "bigm", "bigmmethod", "branchchoice", "branchdisj", "branchstructural", "breadthfirst", "cachesize", "callbackfrommasterthread", "choleskyalg", "choleskytol", "conflictcuts", "concurrentthreads", "corespercpu", "covercuts", "cpuplatform", "cputime", "crash", "crossover", "crossoveraccuracytol", "crossoveriterlimit", "crossoverops", "crossoverthreads", "cstyle", "cutdepth", "cutfactor", "cutfreq", "cutstrategy", "cutselect", "defaultalg", "densecollimit", "deterministic", "dualgradient", "dualize", "dualizeops", "dualperturb", "dualstrategy", "dualthreads", "eigenvaluetol", "elimfillin", "elimtol", "etatol", "extracols", "extraelems", "extramipents", "extrapresolve", "extraqcelements", "extraqcrows", "extrarows", "extrasetelems", "extrasets", "feasibilitypump", "feastol", "feastoltarget", "forceoutput", "forceparalleldual", "globalfilebias", "globalfileloginterval", "gomcuts", "heurbeforelp", "heurdepth", "heurdiveiterlimit", "heurdiverandomize", "heurdivesoftrounding", "heurdivespeedup", "heurdivestrategy", "heurforcespecialobj", "heurfreq", "heurmaxsol", "heurnodes", "heursearcheffort", "heursearchfreq", "heursearchrootcutfreq", "heursearchrootselect", "heursearchtreeselect", "heurstrategy", "heurthreads", "historycosts", "ifcheckconvexity", "indlinbigm", "indprelinbigm", "invertfreq", "invertmin", "keepbasis", "keepnrows", "l1cache", "linelength", "lnpbest", "lnpiterlimit", "lpflags", "lpiterlimit", "lprefineiterlimit", "localchoice", "lpfolding", "lplog", "lplogdelay", "lplogstyle", "lpthreads", "markowitztol", "matrixtol", "maxchecksonmaxcuttime", "maxchecksonmaxtime", "maxmcoeffbufferelems", "maxcuttime", "maxglobalfilesize", "maxiis", "maximpliedbound", "maxlocalbacktrack", "maxmemoryhard", "maxmemorysoft", "maxmiptasks", "maxmipsol", "maxnode", "maxpagelines", "maxscalefactor", "maxtime", "mipabscutoff", "mipabsgapnotify", "mipabsgapnotifybound", "mipabsgapnotifyobj", "mipabsstop", "mipaddcutoff", "mipdualreductions", "mipfracreduce", "mipkappafreq", "miplog", "mippresolve", "miprampup", "miqcpalg", "miprefineiterlimit", "miprelcutoff", "miprelgapnotify", "miprelstop", "mipterminationmethod", "mipthreads", "miptol", "miptoltarget", "mps18compatible", "mpsboundname", "mpsecho", "mpsformat", "mpsobjname", "mpsrangename", "mpsrhsname", "mutexcallbacks", "netcuts", "nodeselection", "objscalefactor", "optimalitytol", "optimalitytoltarget", "outputlog", "outputmask", "outputtol", "penalty", "perturb", "pivottol", "ppfactor", "preanalyticcenter", "prebasisred", "prebndredcone", "prebndredquad", "precoefelim", "precomponents", "precomponentseffort", "preconedecomp", "preconvertseparable", "predomcol", "predomrow", "preduprow", "preelimquad", "preimplications", "prelindep", "preobjcutdetect", "prepermute", "prepermuteseed", "preprobing", "preprotectdual", "presolve", "presolvemaxgrow", "presolveops", "presolvepasses", "presort", "pricingalg", "primalops", "primalperturb", "primalunshift", "pseudocost", "qccuts", "qcrootalg", "qsimplexops", "quadraticunshift", //"randomseed", "refactor", "refineops", "relaxtreememorylimit", "relpivottol", "repairindefiniteq", "repairinfeasmaxtime", "resourcestrategy", "rootpresolve", "sbbest", "sbeffort", "sbestimate", "sbiterlimit", "sbselect", "scaling", "sifting", "sleeponthreadwait", "sosreftol", "symmetry", "symselect", //"threads", "trace", "treecompression", "treecovercuts", "treecutselect", "treediagnostics", "treegomcuts", "treememorylimit", "treememorysavingtarget", "treepresolve", "treepresolve_keepbasis", "treeqccuts", "tunerhistory", "tunermaxtime", "tunermethod", "tunermethodfile", "tunermode", "tuneroutput", "tuneroutputpath", "tunerpermute", "tunerrootalg", "tunersessionname", "tunertarget", "tunerthreads", "usersolheuristic", "varselection", //"version" }; std::vector res; for (auto param : all_params) { int n; int t; p._plugin->XPRSgetcontrolinfo(prb, param.c_str(), &n, &t); MiniZinc::SolverConfig::ExtraFlag::FlagType param_type; std::string param_default; switch (t) { case XPRS_TYPE_INT: { int d; p._plugin->XPRSgetintcontrol(prb, n, &d); param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; param_default = to_string(d); break; } case XPRS_TYPE_INT64: { XPRSint64 d; p._plugin->XPRSgetintcontrol64(prb, n, &d); param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_INT; param_default = to_string(d); break; } case XPRS_TYPE_DOUBLE: { double d; p._plugin->XPRSgetdblcontrol(prb, n, &d); param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_FLOAT; param_default = to_string(d); break; } case XPRS_TYPE_STRING: { int l; p._plugin->XPRSgetstringcontrol(prb, n, nullptr, 0, &l); char* d = (char*)malloc(l); p._plugin->XPRSgetstringcontrol(prb, n, d, l, &l); param_type = MiniZinc::SolverConfig::ExtraFlag::FlagType::T_STRING; param_default = d; break; } default: continue; } // TODO: Some of these parameters have min/max or are categorical, but there's no way // to programatically get the possible values. We could manually maintain it, but it's // probably not worth doing right now. std::vector param_range; // unused for now res.emplace_back("--xpress-" + param, param, param_type, param_range, param_default); } return res; } catch (MiniZinc::Plugin::PluginError&) { return {}; } catch (XpressException&) { return {}; } return {}; } void MIPxpressWrapper::Options::printHelp(ostream& os) { os << "XPRESS MIP wrapper options:" << std::endl << "--msgLevel print solver output, default: 0" << std::endl << "--logFile log file" << std::endl << "--solver-time-limit stop search after N milliseconds, if negative, it " "will only stop if at least one solution was found" << std::endl << "-n , --numSolutions stop search after N solutions" << std::endl << "--writeModel write model to " << std::endl << "--writeModelFormat [lp|mps] the file format of the written model(lp " "or mps), default: lp" << std::endl << "--absGap absolute gap |primal-dual| to stop, default: " << 0 << std::endl << "--relGap relative gap |primal-dual|/ to stop, " "default: " << 0.0001 << std::endl << "-i print intermediate solution, default: false" << std::endl << "-r , --seed , --random-seed " << std::endl << " random seed, integer" << "-p , --parallel use N threads" << std::endl << "--xpress-dll Xpress DLL file (xprs.dll/libxprs.so/libxprs.dylib)" << std::endl << "--xpress-password directory where xpauth.xpr is located (optional)" << std::endl << std::endl; } bool MIPxpressWrapper::FactoryOptions::processOption(int& i, std::vector& argv, const std::string& workingDir) { MiniZinc::CLOParser cop(i, argv); if (cop.get("--xpress-dll", &xpressDll)) { // NOLINT: Allow repeated empty if } else if (cop.get("--xpress-password", &xprsPassword)) { // NOLINT: Allow repeated empty if } else { return false; } return true; } bool MIPxpressWrapper::Options::processOption(int& i, std::vector& argv, const std::string& workingDir) { MiniZinc::CLOParser cop(i, argv); std::string buffer; if (cop.get("--msgLevel", &msgLevel)) { // NOLINT: Allow repeated empty if } else if (cop.get("--logFile", &buffer)) { logFile = MiniZinc::FileUtils::file_path(buffer, workingDir); } else if (cop.get("--solver-time-limit", &timeout)) { // NOLINT: Allow repeated empty if } else if (cop.get("-n --numSolutions", &numSolutions)) { // NOLINT: Allow repeated empty if } else if (cop.get("--writeModel", &buffer)) { writeModelFile = MiniZinc::FileUtils::file_path(buffer, workingDir); } else if (cop.get("--writeModelFormat", &writeModelFormat)) { // NOLINT: Allow repeated empty if } else if (cop.get("--relGap", &relGap)) { // NOLINT: Allow repeated empty if } else if (cop.get("--absGap", &absGap)) { // NOLINT: Allow repeated empty if } else if (cop.get("-i")) { intermediateSolutions = true; } else if (cop.get("-p --parallel", &numThreads)) { } else if (cop.get("-r --seed --random-seed", &randomSeed)) { } else { return false; } return true; } void MIPxpressWrapper::setOptions() { XPRSprob xprsProblem = _plugin->XPRBgetXPRSprob(_problem); _plugin->XPRBsetmsglevel(_problem, _options->msgLevel); _plugin->XPRSsetlogfile(xprsProblem, _options->logFile.c_str()); if (_options->timeout > 1000 || _options->timeout < -1000) { _plugin->XPRSsetintcontrol(xprsProblem, XPRS_MAXTIME, static_cast(_options->timeout / 1000)); } _plugin->XPRSsetintcontrol(xprsProblem, XPRS_MAXMIPSOL, _options->numSolutions); _plugin->XPRSsetdblcontrol(xprsProblem, XPRS_MIPABSSTOP, _options->absGap); _plugin->XPRSsetdblcontrol(xprsProblem, XPRS_MIPRELSTOP, _options->relGap); if (_options->numThreads > 0) { _plugin->XPRSsetintcontrol(xprsProblem, XPRS_THREADS, _options->numThreads); } if (_options->randomSeed != 0) { _plugin->XPRSsetintcontrol(xprsProblem, XPRS_RANDOMSEED, _options->randomSeed); } for (auto& it : _options->extraParams) { auto name = it.first.substr(9); int n; int t; _plugin->XPRSgetcontrolinfo(xprsProblem, name.c_str(), &n, &t); switch (t) { case XPRS_TYPE_INT: _plugin->XPRSsetintcontrol(xprsProblem, n, stoi(it.second)); break; case XPRS_TYPE_INT64: _plugin->XPRSsetintcontrol64(xprsProblem, n, stoll(it.second)); break; case XPRS_TYPE_DOUBLE: _plugin->XPRSsetdblcontrol(xprsProblem, n, stod(it.second)); break; case XPRS_TYPE_STRING: _plugin->XPRSsetstrcontrol(xprsProblem, n, it.second.c_str()); break; default: throw XpressException("Unknown type for parameter " + name); } } } static MIPWrapper::Status convert_status(int xpressStatus) { switch (xpressStatus) { case XPRB_MIP_OPTIMAL: return MIPWrapper::Status::OPT; case XPRB_MIP_INFEAS: return MIPWrapper::Status::UNSAT; case XPRB_MIP_UNBOUNDED: return MIPWrapper::Status::UNBND; case XPRB_MIP_NO_SOL_FOUND: return MIPWrapper::Status::UNKNOWN; case XPRB_MIP_NOT_LOADED: return MIPWrapper::Status::__ERROR; default: return MIPWrapper::Status::UNKNOWN; } } static string get_status_name(int xpressStatus) { string rt = "Xpress stopped with status: "; switch (xpressStatus) { case XPRB_MIP_OPTIMAL: return rt + "Optimal"; case XPRB_MIP_INFEAS: return rt + "Infeasible"; case XPRB_MIP_UNBOUNDED: return rt + "Unbounded"; case XPRB_MIP_NO_SOL_FOUND: return rt + "No solution found"; case XPRB_MIP_NOT_LOADED: return rt + "No problem loaded or error"; default: return rt + "Unknown status"; } } static void set_output_variables(XpressPlugin* plugin, MIPxpressWrapper::Output* output, vector* variables) { size_t nCols = variables->size(); auto* x = (double*)malloc(nCols * sizeof(double)); for (size_t ii = 0; ii < nCols; ii++) { x[ii] = plugin->XPRBgetsol((*variables)[ii]); } output->x = x; } static void set_output_attributes(XpressPlugin* plugin, MIPxpressWrapper::Output* output, XPRSprob xprsProblem) { int xpressStatus = 0; plugin->XPRSgetintattrib(xprsProblem, XPRS_MIPSTATUS, &xpressStatus); output->status = convert_status(xpressStatus); output->statusName = get_status_name(xpressStatus); plugin->XPRSgetdblattrib(xprsProblem, XPRS_MIPOBJVAL, &output->objVal); plugin->XPRSgetdblattrib(xprsProblem, XPRS_BESTBOUND, &output->bestBound); plugin->XPRSgetintattrib(xprsProblem, XPRS_NODES, &output->nNodes); plugin->XPRSgetintattrib(xprsProblem, XPRS_ACTIVENODES, &output->nOpenNodes); output->dWallTime = std::chrono::duration(std::chrono::steady_clock::now() - output->dWallTime0).count(); output->dCPUTime = double(std::clock() - output->cCPUTime0) / CLOCKS_PER_SEC; } static void XPRS_CC user_sol_notify_callback(XPRSprob xprsProblem, void* userData) { auto* data = (UserSolutionCallbackData*)userData; MIPWrapper::CBUserInfo* info = data->info; set_output_attributes(data->plugin, info->pOutput, xprsProblem); data->plugin->XPRBbegincb(*(data->problem), xprsProblem); data->plugin->XPRBsync(*(data->problem), XPRB_XPRS_SOL); set_output_variables(data->plugin, info->pOutput, data->variables); data->plugin->XPRBendcb(*(data->problem)); if (info->solcbfn != nullptr) { (*info->solcbfn)(*info->pOutput, info->psi); } } void MIPxpressWrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt, string* names) { if (obj == nullptr || lb == nullptr || ub == nullptr || vt == nullptr || names == nullptr) { throw XpressException("invalid input"); } for (size_t i = 0; i < n; ++i) { char* var_name = (char*)names[i].c_str(); int var_type = convertVariableType(vt[i]); XPRBvar var = _plugin->XPRBnewvar(_problem, var_type, var_name, lb[i], ub[i]); _variables.push_back(var); _plugin->XPRBsetterm(_xpressObj, var, obj[i]); } } void MIPxpressWrapper::addRow(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, int mask, const string& rowName) { addConstraint(nnz, rmatind, rmatval, sense, rhs, mask, rowName); } XPRBctr MIPxpressWrapper::addConstraint(int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, int mask, const string& rowName) { _nRows++; XPRBctr constraint = _plugin->XPRBnewctr(_problem, rowName.c_str(), convertConstraintType(sense)); for (int i = 0; i < nnz; ++i) { _plugin->XPRBsetterm(constraint, _variables[rmatind[i]], rmatval[i]); } _plugin->XPRBsetterm(constraint, nullptr, rhs); return constraint; } void MIPxpressWrapper::writeModelIfRequested() { int format = XPRB_LP; if (_options->writeModelFormat == "lp") { format = XPRB_LP; } else if (_options->writeModelFormat == "mps") { format = XPRB_MPS; } if (!_options->writeModelFile.empty()) { _plugin->XPRBexportprob(_problem, format, _options->writeModelFile.c_str()); } } void MIPxpressWrapper::addDummyConstraint() { if (getNCols() == 0) { return; } XPRBctr constraint = _plugin->XPRBnewctr(_problem, "dummy_constraint", XPRB_L); _plugin->XPRBsetterm(constraint, _variables[0], 1); double ub; _plugin->XPRBgetbounds(_variables[0], nullptr, &ub); _plugin->XPRBsetterm(constraint, nullptr, ub); } void MIPxpressWrapper::solve() { if (getNRows() == 0) { addDummyConstraint(); } setOptions(); writeModelIfRequested(); setUserSolutionCallback(); _plugin->XPRBsetobj(_problem, _xpressObj); cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now(); cbui.pOutput->cCPUTime0 = output.dCPUTime = std::clock(); if (_plugin->XPRBmipoptimize(_problem, "c") == 1) { throw XpressException("error while solving"); } set_output_variables(_plugin, &output, &_variables); set_output_attributes(_plugin, &output, _plugin->XPRBgetXPRSprob(_problem)); if (!_options->intermediateSolutions && cbui.solcbfn != nullptr) { cbui.solcbfn(output, cbui.psi); } } void MIPxpressWrapper::setUserSolutionCallback() { if (!_options->intermediateSolutions) { return; } auto* data = new UserSolutionCallbackData{&cbui, &_problem, &_variables, _plugin}; _plugin->XPRSsetcbintsol(_plugin->XPRBgetXPRSprob(_problem), user_sol_notify_callback, data); } void MIPxpressWrapper::setObjSense(int s) { _plugin->XPRBsetsense(_problem, convertObjectiveSense(s)); } void MIPxpressWrapper::setVarLB(int iVar, double lb) { _plugin->XPRBsetlb(_variables[iVar], lb); } void MIPxpressWrapper::setVarUB(int iVar, double ub) { _plugin->XPRBsetub(_variables[iVar], ub); } void MIPxpressWrapper::setVarBounds(int iVar, double lb, double ub) { setVarLB(iVar, lb); setVarUB(iVar, ub); } void MIPxpressWrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind, double* rmatval, LinConType sense, double rhs, const string& rowName) { if (bVal != 0 && bVal != 1) { throw XpressException("indicator bval not in 0/1"); } XPRBctr constraint = addConstraint(nnz, rmatind, rmatval, sense, rhs, 0, rowName); _plugin->XPRBsetindicator(constraint, 2 * bVal - 1, _variables[iBVar]); } bool MIPxpressWrapper::addWarmStart(const std::vector& vars, const std::vector& vals) { XPRBsol warmstart = _plugin->XPRBnewsol(_problem); for (size_t ii = 0; ii < vars.size(); ii++) { _plugin->XPRBsetsolvar(warmstart, _variables[vars[ii]], vals[ii]); } return (_plugin->XPRBaddmipsol(_problem, warmstart, nullptr) == 0); } int MIPxpressWrapper::convertConstraintType(LinConType sense) { switch (sense) { case MIPWrapper::LQ: return XPRB_L; case MIPWrapper::EQ: return XPRB_E; case MIPWrapper::GQ: return XPRB_G; default: throw XpressException("unkown constraint sense"); } } int MIPxpressWrapper::convertVariableType(VarType varType) { switch (varType) { case REAL: return XPRB_PL; case INT: return XPRB_UI; case BINARY: return XPRB_BV; default: throw XpressException("unknown variable type"); } } int MIPxpressWrapper::convertObjectiveSense(int s) { switch (s) { case 1: return XPRB_MAXIM; case -1: return XPRB_MINIM; default: throw XpressException("unknown objective sense"); } }