this repo has no description
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}