1
0
This repository has been archived on 2025-03-06. You can view files and clone it, but cannot push or open issues or pull requests.

679 lines
25 KiB
C++

/*
* main authors:
* Karsten Lehmann <karsten@satalia.com>
*/
/* 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 <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
struct UserSolutionCallbackData {
MIPWrapper::CBUserInfo* info;
XPRBprob* problem;
vector<XPRBvar>* 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<std::string>& XpressPlugin::dlls() {
static std::vector<std::string> 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<std::string> 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 "<unknown version>";
}
}
vector<string> MIPxpressWrapper::getRequiredFlags(FactoryOptions& factoryOpt) {
Options opts;
FactoryOptions triedFactoryOpts;
vector<string> 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<string> MIPxpressWrapper::getFactoryFlags() {
return {"--xpress-dll", "--xpress-password"};
};
string MIPxpressWrapper::getId() { return "xpress"; }
string MIPxpressWrapper::getName() { return "Xpress"; }
vector<string> MIPxpressWrapper::getTags() { return {"mip", "float", "api"}; }
vector<string> MIPxpressWrapper::getStdFlags() { return {"-i", "-s", "-p", "-r"}; }
vector<MiniZinc::SolverConfig::ExtraFlag> 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<std::string> 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<MiniZinc::SolverConfig::ExtraFlag> 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<std::string> 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 <n> print solver output, default: 0" << std::endl
<< "--logFile <file> log file" << std::endl
<< "--solver-time-limit <N> stop search after N milliseconds, if negative, it "
"will only stop if at least one solution was found"
<< std::endl
<< "-n <N>, --numSolutions <N> stop search after N solutions" << std::endl
<< "--writeModel <file> write model to <file>" << std::endl
<< "--writeModelFormat [lp|mps] the file format of the written model(lp "
"or mps), default: lp"
<< std::endl
<< "--absGap <d> absolute gap |primal-dual| to stop, default: " << 0 << std::endl
<< "--relGap <d> relative gap |primal-dual|/<solver-dep> to stop, "
"default: "
<< 0.0001 << std::endl
<< "-i print intermediate solution, default: false" << std::endl
<< "-r <N>, --seed <N>, --random-seed <N>" << std::endl
<< " random seed, integer"
<< "-p <N>, --parallel <N> use N threads" << std::endl
<< "--xpress-dll <file> Xpress DLL file (xprs.dll/libxprs.so/libxprs.dylib)" << std::endl
<< "--xpress-password <dir> directory where xpauth.xpr is located (optional)" << std::endl
<< std::endl;
}
bool MIPxpressWrapper::FactoryOptions::processOption(int& i, std::vector<std::string>& 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<std::string>& 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<int>(_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_STATUS;
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<XPRBvar>* 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<double>(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<VarId>& vars,
const std::vector<double>& 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");
}
}