this repo has no description
at develop 11 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/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}