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/utils.hh"
15
16#include <cmath>
17#include <cstring>
18#include <ctime>
19#include <fstream>
20#include <iomanip>
21#include <iostream>
22#include <sstream>
23#include <stdexcept>
24#include <string>
25
26struct UserSolutionCallbackData {
27 MIP_wrapper::CBUserInfo* info;
28 XPRBprob* problem;
29 vector<XPRBvar>* variables;
30};
31
32class XpressException : public runtime_error {
33public:
34 XpressException(string msg) : runtime_error(" MIP_xpress_wrapper: " + msg) {}
35};
36
37string MIP_xpress_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) {
38 char v[16];
39 XPRSgetversion(v);
40 ostringstream oss;
41 oss << " MIP wrapper for FICO Xpress Optimiser version " << v;
42 oss << ". Compiled " __DATE__ " " __TIME__;
43 return oss.str();
44}
45
46string MIP_xpress_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) {
47 char v[16];
48 XPRSgetversion(v);
49 return v;
50}
51
52string MIP_xpress_wrapper::needDllFlag() { return ""; }
53
54string MIP_xpress_wrapper::getId() { return "xpress"; }
55
56string MIP_xpress_wrapper::getName() { return "Xpress"; }
57
58vector<string> MIP_xpress_wrapper::getTags() { return {"mip", "float", "api"}; }
59
60vector<string> MIP_xpress_wrapper::getStdFlags() { return {"-a", "-n", "-s"}; }
61
62void MIP_xpress_wrapper::Options::printHelp(ostream& os) {
63 os << "XPRESS MIP wrapper options:" << std::endl
64 << "--msgLevel <n> print solver output, default: 0" << std::endl
65 << "--logFile <file> log file" << std::endl
66 << "--solver-time-limit <N> stop search after N milliseconds, if negative, it "
67 "will only stop if at least one solution was found"
68 << std::endl
69 << "-n <N>, --numSolutions <N> stop search after N solutions" << std::endl
70 << "--writeModel <file> write model to <file>" << std::endl
71 << "--writeModelFormat [lp|mps] the file format of the written model(lp "
72 "or mps), default: lp"
73 << std::endl
74 << "--absGap <d> absolute gap |primal-dual| to stop, default: " << 0 << std::endl
75 << "--relGap <d> relative gap |primal-dual|/<solver-dep> to stop, "
76 "default: "
77 << 0.0001 << std::endl
78 << "-a, --printAllSolutions print intermediate solution, default: false" << std::endl
79 << std::endl;
80}
81
82bool MIP_xpress_wrapper::Options::processOption(int& i, std::vector<std::string>& argv) {
83 MiniZinc::CLOParser cop(i, argv);
84 if (cop.get("--msgLevel", &msgLevel)) {
85 } else if (cop.get("--logFile", &logFile)) {
86 } else if (cop.get("--solver-time-limit", &timeout)) {
87 } else if (cop.get("-n --numSolutions", &numSolutions)) {
88 } else if (cop.get("--writeModel", &writeModelFile)) {
89 } else if (cop.get("--writeModelFormat", &writeModelFormat)) {
90 } else if (cop.get("--relGap", &relGap)) {
91 } else if (cop.get("--absGap", &absGap)) {
92 } else if (string(argv[i]) == "--printAllSolutions" || string(argv[i]) == "-a") {
93 printAllSolutions = true;
94 } else
95 return false;
96 return true;
97}
98
99void MIP_xpress_wrapper::setOptions() {
100 XPRSprob xprsProblem = problem.getXPRSprob();
101
102 problem.setMsgLevel(options->msgLevel);
103
104 XPRSsetlogfile(xprsProblem, options->logFile.c_str());
105 if (options->timeout > 1000 || options->timeout < -1000) {
106 XPRSsetintcontrol(xprsProblem, XPRS_MAXTIME, static_cast<int>(options->timeout / 1000));
107 }
108 XPRSsetintcontrol(xprsProblem, XPRS_MAXMIPSOL, options->numSolutions);
109 XPRSsetdblcontrol(xprsProblem, XPRS_MIPABSSTOP, options->absGap);
110 XPRSsetdblcontrol(xprsProblem, XPRS_MIPRELSTOP, options->relGap);
111}
112
113static MIP_wrapper::Status convertStatus(int xpressStatus) {
114 switch (xpressStatus) {
115 case XPRB_MIP_OPTIMAL:
116 return MIP_wrapper::Status::OPT;
117 case XPRB_MIP_INFEAS:
118 return MIP_wrapper::Status::UNSAT;
119 case XPRB_MIP_UNBOUNDED:
120 return MIP_wrapper::Status::UNBND;
121 case XPRB_MIP_NO_SOL_FOUND:
122 return MIP_wrapper::Status::UNKNOWN;
123 case XPRB_MIP_NOT_LOADED:
124 return MIP_wrapper::Status::__ERROR;
125 default:
126 return MIP_wrapper::Status::UNKNOWN;
127 }
128}
129
130static string getStatusName(int xpressStatus) {
131 string rt = "Xpress stopped with status: ";
132 switch (xpressStatus) {
133 case XPRB_MIP_OPTIMAL:
134 return rt + "Optimal";
135 case XPRB_MIP_INFEAS:
136 return rt + "Infeasible";
137 case XPRB_MIP_UNBOUNDED:
138 return rt + "Unbounded";
139 case XPRB_MIP_NO_SOL_FOUND:
140 return rt + "No solution found";
141 case XPRB_MIP_NOT_LOADED:
142 return rt + "No problem loaded or error";
143 default:
144 return rt + "Unknown status";
145 }
146}
147
148static void setOutputVariables(MIP_xpress_wrapper::Output* output, vector<XPRBvar>* variables) {
149 size_t nCols = variables->size();
150 double* x = (double*)malloc(nCols * sizeof(double));
151 for (size_t ii = 0; ii < nCols; ii++) {
152 x[ii] = (*variables)[ii].getSol();
153 }
154 output->x = x;
155}
156
157static void setOutputAttributes(MIP_xpress_wrapper::Output* output, XPRSprob xprsProblem) {
158 int xpressStatus = 0;
159 XPRSgetintattrib(xprsProblem, XPRS_MIPSTATUS, &xpressStatus);
160 output->status = convertStatus(xpressStatus);
161 output->statusName = getStatusName(xpressStatus);
162
163 XPRSgetdblattrib(xprsProblem, XPRS_MIPOBJVAL, &output->objVal);
164 XPRSgetdblattrib(xprsProblem, XPRS_BESTBOUND, &output->bestBound);
165
166 XPRSgetintattrib(xprsProblem, XPRS_NODES, &output->nNodes);
167 XPRSgetintattrib(xprsProblem, XPRS_ACTIVENODES, &output->nOpenNodes);
168
169 output->dWallTime =
170 std::chrono::duration<double>(std::chrono::steady_clock::now() - output->dWallTime0).count();
171 output->dCPUTime = double(std::clock() - output->cCPUTime0) / CLOCKS_PER_SEC;
172}
173
174static void XPRS_CC userSolNotifyCallback(XPRSprob xprsProblem, void* userData) {
175 UserSolutionCallbackData* data = (UserSolutionCallbackData*)userData;
176 MIP_wrapper::CBUserInfo* info = data->info;
177
178 setOutputAttributes(info->pOutput, xprsProblem);
179
180 data->problem->beginCB(xprsProblem);
181 data->problem->sync(XPRB_XPRS_SOL);
182 setOutputVariables(info->pOutput, data->variables);
183 data->problem->endCB();
184
185 if (info->solcbfn) {
186 (*info->solcbfn)(*info->pOutput, info->ppp);
187 }
188}
189
190void MIP_xpress_wrapper::doAddVars(size_t n, double* obj, double* lb, double* ub, VarType* vt,
191 string* names) {
192 if (obj == nullptr || lb == nullptr || ub == nullptr || vt == nullptr || names == nullptr) {
193 throw XpressException("invalid input");
194 }
195 for (size_t i = 0; i < n; ++i) {
196 char* var_name = (char*)names[i].c_str();
197 int var_type = convertVariableType(vt[i]);
198 XPRBvar var = problem.newVar(var_name, var_type, lb[i], ub[i]);
199 variables.push_back(var);
200 xpressObj.setTerm(obj[i], var);
201 }
202}
203
204void MIP_xpress_wrapper::addRow(int nnz, int* rmatind, double* rmatval, LinConType sense,
205 double rhs, int mask, string rowName) {
206 addConstraint(nnz, rmatind, rmatval, sense, rhs, mask, rowName);
207}
208
209XPRBctr MIP_xpress_wrapper::addConstraint(int nnz, int* rmatind, double* rmatval, LinConType sense,
210 double rhs, int mask, string rowName) {
211 nRows++;
212 XPRBctr constraint = problem.newCtr(rowName.c_str());
213 for (int i = 0; i < nnz; ++i) {
214 constraint.setTerm(variables[rmatind[i]], rmatval[i]);
215 }
216 constraint.setTerm(rhs);
217
218 if (constraint.setType(convertConstraintType(sense)) == 1) {
219 throw XpressException("error while setting sense of constraint");
220 }
221
222 return constraint;
223}
224
225void MIP_xpress_wrapper::writeModelIfRequested() {
226 int format = XPRB_LP;
227 if (options->writeModelFormat == "lp") {
228 format = XPRB_LP;
229 } else if (options->writeModelFormat == "mps") {
230 format = XPRB_MPS;
231 }
232 if (!options->writeModelFile.empty()) {
233 problem.exportProb(format, options->writeModelFile.c_str());
234 }
235}
236
237void MIP_xpress_wrapper::addDummyConstraint() {
238 if (getNCols() == 0) {
239 return;
240 }
241
242 XPRBctr constraint = problem.newCtr("dummy_constraint");
243 constraint.setTerm(variables[0], 1);
244 constraint.setType(convertConstraintType(LinConType::LQ));
245 constraint.setTerm(variables[0].getUB());
246}
247
248void MIP_xpress_wrapper::solve() {
249 if (getNRows() == 0) {
250 addDummyConstraint();
251 }
252
253 setOptions();
254 writeModelIfRequested();
255 setUserSolutionCallback();
256
257 problem.setObj(xpressObj);
258
259 cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now();
260 cbui.pOutput->cCPUTime0 = output.dCPUTime = std::clock();
261
262 if (problem.mipOptimize("c") == 1) {
263 throw XpressException("error while solving");
264 }
265
266 setOutputVariables(&output, &variables);
267 setOutputAttributes(&output, problem.getXPRSprob());
268
269 if (!options->printAllSolutions && cbui.solcbfn) {
270 cbui.solcbfn(output, cbui.ppp);
271 }
272}
273
274void MIP_xpress_wrapper::setUserSolutionCallback() {
275 if (!options->printAllSolutions) {
276 return;
277 }
278
279 UserSolutionCallbackData* data = new UserSolutionCallbackData{&cbui, &problem, &variables};
280
281 XPRSsetcbintsol(problem.getXPRSprob(), userSolNotifyCallback, data);
282}
283
284void MIP_xpress_wrapper::setObjSense(int s) { problem.setSense(convertObjectiveSense(s)); }
285
286void MIP_xpress_wrapper::setVarLB(int iVar, double lb) { variables[iVar].setLB(lb); }
287
288void MIP_xpress_wrapper::setVarUB(int iVar, double ub) { variables[iVar].setUB(ub); }
289
290void MIP_xpress_wrapper::setVarBounds(int iVar, double lb, double ub) {
291 setVarLB(iVar, lb);
292 setVarUB(iVar, ub);
293}
294
295void MIP_xpress_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind,
296 double* rmatval, LinConType sense, double rhs,
297 string rowName) {
298 if (bVal != 0 && bVal != 1) {
299 throw XpressException("indicator bval not in 0/1");
300 }
301 XPRBctr constraint = addConstraint(nnz, rmatind, rmatval, sense, rhs, 0, rowName);
302 constraint.setIndicator(2 * bVal - 1, variables[iBVar]);
303}
304
305bool MIP_xpress_wrapper::addWarmStart(const std::vector<VarId>& vars,
306 const std::vector<double> vals) {
307 XPRBsol warmstart = problem.newSol();
308 for (size_t ii = 0; ii < vars.size(); ii++) {
309 warmstart.setVar(variables[vars[ii]], vals[ii]);
310 }
311 return 1 - problem.addMIPSol(warmstart);
312}
313
314int MIP_xpress_wrapper::convertConstraintType(LinConType sense) {
315 switch (sense) {
316 case MIP_wrapper::LQ:
317 return XPRB_L;
318 case MIP_wrapper::EQ:
319 return XPRB_E;
320 case MIP_wrapper::GQ:
321 return XPRB_G;
322 default:
323 throw XpressException("unkown constraint sense");
324 }
325}
326
327int MIP_xpress_wrapper::convertVariableType(VarType varType) {
328 switch (varType) {
329 case REAL:
330 return XPRB_PL;
331 case INT:
332 return XPRB_UI;
333 case BINARY:
334 return XPRB_BV;
335 default:
336 throw XpressException("unknown variable type");
337 }
338}
339
340int MIP_xpress_wrapper::convertObjectiveSense(int s) {
341 switch (s) {
342 case 1:
343 return XPRB_MAXIM;
344 case -1:
345 return XPRB_MINIM;
346 default:
347 throw XpressException("unknown objective sense");
348 }
349}