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.

350 lines
11 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/utils.hh"
#include <cmath>
#include <cstring>
#include <ctime>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
struct UserSolutionCallbackData {
MIP_wrapper::CBUserInfo* info;
XPRBprob* problem;
vector<XPRBvar>* variables;
};
class XpressException : public runtime_error {
public:
XpressException(string msg) : runtime_error(" MIP_xpress_wrapper: " + msg) {}
};
string MIP_xpress_wrapper::getDescription(MiniZinc::SolverInstanceBase::Options* opt) {
char v[16];
XPRSgetversion(v);
ostringstream oss;
oss << " MIP wrapper for FICO Xpress Optimiser version " << v;
oss << ". Compiled " __DATE__ " " __TIME__;
return oss.str();
}
string MIP_xpress_wrapper::getVersion(MiniZinc::SolverInstanceBase::Options* opt) {
char v[16];
XPRSgetversion(v);
return v;
}
string MIP_xpress_wrapper::needDllFlag() { return ""; }
string MIP_xpress_wrapper::getId() { return "xpress"; }
string MIP_xpress_wrapper::getName() { return "Xpress"; }
vector<string> MIP_xpress_wrapper::getTags() { return {"mip", "float", "api"}; }
vector<string> MIP_xpress_wrapper::getStdFlags() { return {"-a", "-n", "-s"}; }
void MIP_xpress_wrapper::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
<< "-a, --printAllSolutions print intermediate solution, default: false" << std::endl
<< std::endl;
}
bool MIP_xpress_wrapper::Options::processOption(int& i, std::vector<std::string>& argv) {
MiniZinc::CLOParser cop(i, argv);
if (cop.get("--msgLevel", &msgLevel)) {
} else if (cop.get("--logFile", &logFile)) {
} else if (cop.get("--solver-time-limit", &timeout)) {
} else if (cop.get("-n --numSolutions", &numSolutions)) {
} else if (cop.get("--writeModel", &writeModelFile)) {
} else if (cop.get("--writeModelFormat", &writeModelFormat)) {
} else if (cop.get("--relGap", &relGap)) {
} else if (cop.get("--absGap", &absGap)) {
} else if (string(argv[i]) == "--printAllSolutions" || string(argv[i]) == "-a") {
printAllSolutions = true;
} else
return false;
return true;
}
void MIP_xpress_wrapper::setOptions() {
XPRSprob xprsProblem = problem.getXPRSprob();
problem.setMsgLevel(options->msgLevel);
XPRSsetlogfile(xprsProblem, options->logFile.c_str());
if (options->timeout > 1000 || options->timeout < -1000) {
XPRSsetintcontrol(xprsProblem, XPRS_MAXTIME, static_cast<int>(options->timeout / 1000));
}
XPRSsetintcontrol(xprsProblem, XPRS_MAXMIPSOL, options->numSolutions);
XPRSsetdblcontrol(xprsProblem, XPRS_MIPABSSTOP, options->absGap);
XPRSsetdblcontrol(xprsProblem, XPRS_MIPRELSTOP, options->relGap);
}
static MIP_wrapper::Status convertStatus(int xpressStatus) {
switch (xpressStatus) {
case XPRB_MIP_OPTIMAL:
return MIP_wrapper::Status::OPT;
case XPRB_MIP_INFEAS:
return MIP_wrapper::Status::UNSAT;
case XPRB_MIP_UNBOUNDED:
return MIP_wrapper::Status::UNBND;
case XPRB_MIP_NO_SOL_FOUND:
return MIP_wrapper::Status::UNKNOWN;
case XPRB_MIP_NOT_LOADED:
return MIP_wrapper::Status::__ERROR;
default:
return MIP_wrapper::Status::UNKNOWN;
}
}
static string getStatusName(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 setOutputVariables(MIP_xpress_wrapper::Output* output, vector<XPRBvar>* variables) {
size_t nCols = variables->size();
double* x = (double*)malloc(nCols * sizeof(double));
for (size_t ii = 0; ii < nCols; ii++) {
x[ii] = (*variables)[ii].getSol();
}
output->x = x;
}
static void setOutputAttributes(MIP_xpress_wrapper::Output* output, XPRSprob xprsProblem) {
int xpressStatus = 0;
XPRSgetintattrib(xprsProblem, XPRS_MIPSTATUS, &xpressStatus);
output->status = convertStatus(xpressStatus);
output->statusName = getStatusName(xpressStatus);
XPRSgetdblattrib(xprsProblem, XPRS_MIPOBJVAL, &output->objVal);
XPRSgetdblattrib(xprsProblem, XPRS_BESTBOUND, &output->bestBound);
XPRSgetintattrib(xprsProblem, XPRS_NODES, &output->nNodes);
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 userSolNotifyCallback(XPRSprob xprsProblem, void* userData) {
UserSolutionCallbackData* data = (UserSolutionCallbackData*)userData;
MIP_wrapper::CBUserInfo* info = data->info;
setOutputAttributes(info->pOutput, xprsProblem);
data->problem->beginCB(xprsProblem);
data->problem->sync(XPRB_XPRS_SOL);
setOutputVariables(info->pOutput, data->variables);
data->problem->endCB();
if (info->solcbfn) {
(*info->solcbfn)(*info->pOutput, info->ppp);
}
}
void MIP_xpress_wrapper::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 = problem.newVar(var_name, var_type, lb[i], ub[i]);
variables.push_back(var);
xpressObj.setTerm(obj[i], var);
}
}
void MIP_xpress_wrapper::addRow(int nnz, int* rmatind, double* rmatval, LinConType sense,
double rhs, int mask, string rowName) {
addConstraint(nnz, rmatind, rmatval, sense, rhs, mask, rowName);
}
XPRBctr MIP_xpress_wrapper::addConstraint(int nnz, int* rmatind, double* rmatval, LinConType sense,
double rhs, int mask, string rowName) {
nRows++;
XPRBctr constraint = problem.newCtr(rowName.c_str());
for (int i = 0; i < nnz; ++i) {
constraint.setTerm(variables[rmatind[i]], rmatval[i]);
}
constraint.setTerm(rhs);
if (constraint.setType(convertConstraintType(sense)) == 1) {
throw XpressException("error while setting sense of constraint");
}
return constraint;
}
void MIP_xpress_wrapper::writeModelIfRequested() {
int format = XPRB_LP;
if (options->writeModelFormat == "lp") {
format = XPRB_LP;
} else if (options->writeModelFormat == "mps") {
format = XPRB_MPS;
}
if (!options->writeModelFile.empty()) {
problem.exportProb(format, options->writeModelFile.c_str());
}
}
void MIP_xpress_wrapper::addDummyConstraint() {
if (getNCols() == 0) {
return;
}
XPRBctr constraint = problem.newCtr("dummy_constraint");
constraint.setTerm(variables[0], 1);
constraint.setType(convertConstraintType(LinConType::LQ));
constraint.setTerm(variables[0].getUB());
}
void MIP_xpress_wrapper::solve() {
if (getNRows() == 0) {
addDummyConstraint();
}
setOptions();
writeModelIfRequested();
setUserSolutionCallback();
problem.setObj(xpressObj);
cbui.pOutput->dWallTime0 = output.dWallTime0 = std::chrono::steady_clock::now();
cbui.pOutput->cCPUTime0 = output.dCPUTime = std::clock();
if (problem.mipOptimize("c") == 1) {
throw XpressException("error while solving");
}
setOutputVariables(&output, &variables);
setOutputAttributes(&output, problem.getXPRSprob());
if (!options->printAllSolutions && cbui.solcbfn) {
cbui.solcbfn(output, cbui.ppp);
}
}
void MIP_xpress_wrapper::setUserSolutionCallback() {
if (!options->printAllSolutions) {
return;
}
UserSolutionCallbackData* data = new UserSolutionCallbackData{&cbui, &problem, &variables};
XPRSsetcbintsol(problem.getXPRSprob(), userSolNotifyCallback, data);
}
void MIP_xpress_wrapper::setObjSense(int s) { problem.setSense(convertObjectiveSense(s)); }
void MIP_xpress_wrapper::setVarLB(int iVar, double lb) { variables[iVar].setLB(lb); }
void MIP_xpress_wrapper::setVarUB(int iVar, double ub) { variables[iVar].setUB(ub); }
void MIP_xpress_wrapper::setVarBounds(int iVar, double lb, double ub) {
setVarLB(iVar, lb);
setVarUB(iVar, ub);
}
void MIP_xpress_wrapper::addIndicatorConstraint(int iBVar, int bVal, int nnz, int* rmatind,
double* rmatval, LinConType sense, double rhs,
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);
constraint.setIndicator(2 * bVal - 1, variables[iBVar]);
}
bool MIP_xpress_wrapper::addWarmStart(const std::vector<VarId>& vars,
const std::vector<double> vals) {
XPRBsol warmstart = problem.newSol();
for (size_t ii = 0; ii < vars.size(); ii++) {
warmstart.setVar(variables[vars[ii]], vals[ii]);
}
return 1 - problem.addMIPSol(warmstart);
}
int MIP_xpress_wrapper::convertConstraintType(LinConType sense) {
switch (sense) {
case MIP_wrapper::LQ:
return XPRB_L;
case MIP_wrapper::EQ:
return XPRB_E;
case MIP_wrapper::GQ:
return XPRB_G;
default:
throw XpressException("unkown constraint sense");
}
}
int MIP_xpress_wrapper::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 MIP_xpress_wrapper::convertObjectiveSense(int s) {
switch (s) {
case 1:
return XPRB_MAXIM;
case -1:
return XPRB_MINIM;
default:
throw XpressException("unknown objective sense");
}
}