25 #include "xpress_interface.hpp"
26 #include "casadi/core/nlp_tools.hpp"
27 #include "casadi/core/casadi_misc.hpp"
29 #include <xpress_runtime_str.h>
45 int CASADI_CONIC_XPRESS_EXPORT
48 plugin->name =
"xpress";
50 plugin->version = CASADI_VERSION;
63 const std::map<std::string, Sparsity>& st)
71 "Options to be passed to FICO Xpress. Each entry's key is the "
72 "Xpress control name (e.g. \"OUTPUTLOG\", \"MAXTIME\", \"MIPRELSTOP\"); "
73 "the value's type must match the control's type (int / double / string)."
77 "Definition of SOS groups by indices."}},
80 "Weights corresponding to SOS entries."}},
83 "Specify 1 or 2 for each SOS group."}},
86 "Pass x0 as an initial integer solution hint to Xpress via "
87 "XPRSaddmipsol before the MIP solve [Default false]."}},
90 "Write solver log to this file path via XPRSsetlogfile. "
91 "Useful for capturing output (e.g. warm-start acceptance messages) "
92 "in automated tests. Empty string (default) disables file logging."}},
95 "When the problem is infeasible, compute an IIS immediately after "
96 "the solve and expose it in get_stats() as iis_rows / iis_cols. "
97 "IIS computation can be expensive; set to false to skip it "
110 std::vector< std::vector<casadi_int> > sos_groups;
111 std::vector< std::vector<double> > sos_weights;
112 std::vector<casadi_int> sos_types;
115 for (
auto&& op : opts) {
116 if (op.first==
"xpress") {
118 }
else if (op.first==
"sos_groups") {
119 sos_groups = op.second.to_int_vector_vector();
120 }
else if (op.first==
"sos_weights") {
121 sos_weights = op.second.to_double_vector_vector();
122 }
else if (op.first==
"sos_types") {
123 sos_types = op.second.to_int_vector();
124 }
else if (op.first==
"mip_start") {
126 }
else if (op.first==
"log_file") {
128 }
else if (op.first==
"compute_iis") {
137 if (!sos_groups.empty()) {
138 std::vector<casadi_int> beg, ind;
141 sos_setstart_.assign(beg.begin(), beg.end());
142 sos_setind_.assign(ind.begin(), ind.end());
143 sos_settype_.resize(sos_types.size());
144 for (
size_t k = 0; k < sos_types.size(); ++k) {
145 sos_settype_[k] =
static_cast<char>(
'0' + sos_types[k]);
152 if (has_socp_) build_socp_config();
168 colinda_.resize(
A_.
size2() + 1);
169 rowa_.resize(
A_.
nnz());
177 qobj_nz_idx_.clear();
179 const casadi_int* Hr =
H_.
row();
180 for (casadi_int j = 0; j <
H_.
size2(); ++j) {
181 for (casadi_int k = Hc[j]; k < Hc[j + 1]; ++k) {
182 casadi_int i = Hr[k];
184 qobj_col1_.push_back(
static_cast<int>(i));
185 qobj_col2_.push_back(
static_cast<int>(j));
186 qobj_nz_idx_.push_back(
static_cast<int>(k));
193 coltype_.assign(
nx_,
'C');
194 for (casadi_int i = 0; i <
nx_; ++i) {
207 p_.
nquad =
static_cast<int>(qobj_col1_.size());
210 p_.
n_sos_sets =
static_cast<int>(sos_settype_.size());
211 p_.
n_sos_elems =
static_cast<int>(sos_setind_.size());
217 p_.
socp = has_socp_ ? &socp_ :
nullptr;
220 casadi_xpress_setup(&p_);
227 const std::string& local_name,
const std::vector<int>& v) {
229 g <<
"p." << prob_field <<
" = 0;\n";
231 g.
constant_copy(local_name, vector_static_cast<casadi_int>(v),
"int");
232 g <<
"p." << prob_field <<
" = " << local_name <<
";\n";
237 g <<
"p.qp = &p_qp;\n";
244 g <<
"p.nquad = " << qobj_col1_.size() <<
";\n";
246 if (coltype_.empty()) {
247 g <<
"p.coltype = 0;\n";
249 g <<
"p.coltype = " << g.
constant(coltype_) <<
";\n";
252 if (sos_settype_.empty()) {
253 g <<
"p.n_sos_sets = 0;\n";
254 g <<
"p.n_sos_elems = 0;\n";
255 g <<
"p.sos_settype = 0;\n";
256 g <<
"p.sos_setstart = 0;\n";
257 g <<
"p.sos_setind = 0;\n";
258 g <<
"p.sos_refval = 0;\n";
262 g <<
"p.sos_settype = " << g.
constant(sos_settype_) <<
";\n";
263 g <<
"p.sos_refval = " << g.
constant(sos_refval_) <<
";\n";
264 g <<
"p.n_sos_sets = " << sos_settype_.size() <<
";\n";
265 g <<
"p.n_sos_elems = " << sos_setind_.size() <<
";\n";
269 g.
local(
"socp_p",
"struct casadi_socp_prob");
270 g <<
"socp_p.n_blocks = " << socp_.
n_blocks <<
";\n";
271 g <<
"socp_p.n_lifted = " << socp_.
n_lifted <<
";\n";
272 g <<
"socp_p.nx = " << socp_.
nx <<
";\n";
273 g <<
"socp_p.r = " << g.
constant(socp_r_) <<
";\n";
274 g <<
"socp_p.mq_colind = " << g.
constant(socp_mq_colind_) <<
";\n";
275 g <<
"socp_p.mq_row = " << g.
constant(socp_mq_row_) <<
";\n";
276 g <<
"socp_p.mq_data = " << g.
constant(socp_mq_data_) <<
";\n";
277 g <<
"socp_p.map_P = " << g.
constant(socp_map_P_) <<
";\n";
278 g <<
"casadi_socp_setup(&socp_p);\n";
279 g <<
"p.socp = &socp_p;\n";
281 g <<
"p.socp = 0;\n";
284 g <<
"p.mip_start = " << (
mip_start_ ? 1 : 0) <<
";\n";
285 g <<
"casadi_xpress_setup(&p);\n";
289 g <<
"casadi_xpress_init_mem(&" +
codegen_mem(g) +
");\n";
294 g <<
"casadi_xpress_free_mem(&" +
codegen_mem(g) +
");\n";
309 g.
local(
"d",
"struct casadi_xpress_data*");
311 g.
local(
"p",
"struct casadi_xpress_prob");
314 g <<
"d->prob = &p;\n";
315 g <<
"d->qp = &d_qp;\n";
316 g <<
"casadi_xpress_set_work(d, &arg, &res, &iw, &w);\n";
319 g <<
"d->socp.q = arg[" <<
CONIC_Q <<
"];\n";
320 g <<
"d->socp.p = arg[" <<
CONIC_P <<
"];\n";
326 g <<
"XPRSsetintcontrol(d->xprob, " << XPRS_CROSSOVER <<
", 1);\n";
332 for (
auto&& op :
opts_) {
334 g <<
" int xprs_id, xprs_type;\n";
335 g <<
" XPRSgetcontrolinfo(d->xprob, " << g.
constant(op.first)
336 <<
", &xprs_id, &xprs_type);\n";
337 if (op.second.is_double()) {
338 g <<
" XPRSsetdblcontrol(d->xprob, xprs_id, "
339 << g.
constant(op.second.to_double()) <<
");\n";
340 }
else if (op.second.is_int() || op.second.is_bool()) {
341 g <<
" XPRSsetintcontrol(d->xprob, xprs_id, "
342 <<
static_cast<int>(op.second.to_int()) <<
");\n";
343 }
else if (op.second.is_string()) {
344 g <<
" XPRSsetstrcontrol(d->xprob, xprs_id, "
345 << g.
constant(op.second.to_string()) <<
");\n";
347 casadi_error(
"Unsupported option type for '" + op.first +
"'.");
352 g <<
"casadi_xpress_solve(d, arg, res, iw, w);\n";
354 g <<
"if (!d_qp.success) {\n";
356 g <<
" return -1000;\n";
358 g <<
" return -1;\n";
364 void XpressInterface::build_socp_config() {
369 casadi_int n_eq = sm.map_Q.size2();
370 socp_mq_colind_.assign(sm.map_Q.colind(), sm.map_Q.colind() + n_eq + 1);
371 socp_mq_row_.assign(sm.map_Q.row(), sm.map_Q.row() + sm.map_Q.nnz());
372 socp_mq_data_.assign(sm.map_Q.ptr(), sm.map_Q.ptr() + sm.map_Q.nnz());
373 socp_map_P_ = sm.map_P;
375 socp_.
n_blocks =
static_cast<casadi_int
>(sm.r.size() - 1);
383 casadi_socp_setup(&socp_);
394 "Check that the XPRESS environment variable points to a valid license "
395 "or that a license server is reachable.");
398 if (casadi_xpress_init_mem(&m->d))
return 1;
401 m->add_stat(
"solver");
402 m->add_stat(
"postprocessing");
409 casadi_xpress_free_mem(&m->d);
415 casadi_int*& iw,
double*& w)
const {
424 casadi_xpress_set_work(&m->d, &arg, &res, &iw, &w);
437 XPRS_WARN(XPRSsetlogfile, m->d.xprob,
log_file_.c_str());
441 m->iis_valid =
false;
448 XPRS_WARN(XPRSsetintcontrol, m->d.xprob, XPRS_CROSSOVER, 1);
452 for (
auto&& op :
opts_) {
455 int rc = XPRSgetcontrolinfo(m->d.xprob, op.first.c_str(), &
id, &type);
456 casadi_assert(rc == 0,
457 "Xpress: unknown control '" + op.first +
"' (XPRSgetcontrolinfo rc=" +
461 rc = XPRSsetintcontrol(m->d.xprob,
id, op.second.to_int());
463 case XPRS_TYPE_INT64:
464 rc = XPRSsetintcontrol64(m->d.xprob,
id,
465 static_cast<XPRSint64
>(op.second.to_int()));
467 case XPRS_TYPE_DOUBLE:
468 rc = XPRSsetdblcontrol(m->d.xprob,
id, op.second.to_double());
470 case XPRS_TYPE_STRING: {
471 std::string v = op.second.to_string();
472 rc = XPRSsetstrcontrol(m->d.xprob,
id, v.c_str());
476 casadi_error(
"Xpress: unsupported control type for '" + op.first +
"'.");
478 casadi_assert(rc == 0,
479 "Xpress: failed to set control '" + op.first +
"' (rc=" +
str(rc) +
").");
484 solve(
const double** arg,
double** res, casadi_int* iw,
double* w,
void* mem)
const {
487 m->
fstats.at(
"solver").tic();
488 int rc = casadi_xpress_solve(&m->d, arg, res, iw, w);
489 m->fstats.at(
"solver").toc();
493 bool lp_infeas = (m->d.lp_status == XPRS_LP_INFEAS);
494 bool mip_infeas = (m->d.mip_status == XPRS_MIP_INFEAS);
495 if (
compute_iis_ && (lp_infeas || mip_infeas) && m->d.xprob) {
498 if (XPRSiisfirst(m->d.xprob, 2, &iis_status) == 0 && iis_status == 0) {
499 int nrows = 0, ncols = 0;
502 if (XPRSgetiisdata(m->d.xprob, 1, &nrows, &ncols,
503 nullptr,
nullptr,
nullptr,
nullptr,
504 nullptr,
nullptr,
nullptr,
nullptr) == 0) {
505 std::vector<int> rows(nrows), cols(ncols);
506 std::vector<char> contype(nrows), bndtype(ncols);
507 if (XPRSgetiisdata(m->d.xprob, 1, &nrows, &ncols,
508 nrows > 0 ? rows.data() :
nullptr,
509 ncols > 0 ? cols.data() :
nullptr,
510 nrows > 0 ? contype.data() :
nullptr,
511 ncols > 0 ? bndtype.data() :
nullptr,
512 nullptr,
nullptr,
nullptr,
nullptr) == 0) {
513 m->iis_rows.assign(rows.begin(), rows.end());
514 m->iis_cols.assign(cols.begin(), cols.end());
515 m->iis_row_types.assign(contype.begin(), contype.end());
516 m->iis_col_bound_types.assign(bndtype.begin(), bndtype.end());
519 XPRS_LOG_ERROR(XPRSgetiisdata, m->d.xprob,
"Warning");
522 XPRS_LOG_ERROR(XPRSgetiisdata, m->d.xprob,
"Warning");
538 stats[
"lp_status"] = m->d.lp_status;
539 stats[
"mip_status"] = m->d.mip_status;
540 stats[
"simplex_iter"] = m->d.simplex_iter;
541 stats[
"barrier_iter"] = m->d.barrier_iter;
542 stats[
"mip_nodes"] = m->d.mip_nodes;
543 stats[
"objective"] = m->d.obj_val;
547 stats[
"iis_rows"] = m->iis_rows;
548 stats[
"iis_cols"] = m->iis_cols;
549 stats[
"iis_row_types"] = m->iis_row_types;
550 stats[
"iis_col_bound_types"] = m->iis_col_bound_types;
557 int v = s.
version(
"XpressInterface", 1, 2);
567 s.
unpack(
"XpressInterface::sos_settype", sos_settype_);
568 s.
unpack(
"XpressInterface::sos_setstart", sos_setstart_);
569 s.
unpack(
"XpressInterface::sos_setind", sos_setind_);
570 s.
unpack(
"XpressInterface::sos_refval", sos_refval_);
572 if (has_socp_) build_socp_config();
580 s.
version(
"XpressInterface", 2);
585 s.
pack(
"XpressInterface::sos_settype", sos_settype_);
586 s.
pack(
"XpressInterface::sos_setstart", sos_setstart_);
587 s.
pack(
"XpressInterface::sos_setind", sos_setind_);
588 s.
pack(
"XpressInterface::sos_refval", sos_refval_);
Helper class for C code generation.
std::string constant(const std::vector< casadi_int > &v)
Represent an array constant; adding it when new.
void local(const std::string &name, const std::string &type, const std::string &ref="")
Declare a local variable.
void init_local(const std::string &name, const std::string &def)
Specify the default value for a local variable.
std::string sanitize_source(const std::string &src, const std::vector< std::string > &inst, bool add_shorthand=true)
Sanitize source files for codegen.
void add_include(const std::string &new_include, bool relative_path=false, const std::string &use_ifdef=std::string())
Add an include file optionally using a relative path "..." instead of an absolute path <....
void constant_copy(const std::string &var_name, const std::vector< casadi_int > &v, const std::string &type="casadi_int")
Represent an array constant; adding it when new.
std::stringstream auxiliaries
void add_auxiliary(Auxiliary f, const std::vector< std::string > &inst={"casadi_real"})
Add a built-in auxiliary function.
static const Options options_
Options.
casadi_int nx_
Number of decision variables.
int init_mem(void *mem) const override
Initalize memory block.
Sparsity H_
Problem structure.
void init(const Dict &opts) override
Initialize.
std::vector< bool > discrete_
Options.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
Dict get_stats(void *mem) const override
Get all statistics.
casadi_qp_prob< double > p_qp_
void qp_codegen_body(CodeGenerator &g) const
Generate code for the function body.
void sdp_to_socp_init(SDPToSOCPMem &mem) const
SDP to SOCP conversion initialization.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
void alloc_iw(size_t sz_iw, bool persistent=false)
Ensure required length of iw field.
void alloc_res(size_t sz_res, bool persistent=false)
Ensure required length of res field.
void alloc_arg(size_t sz_arg, bool persistent=false)
Ensure required length of arg field.
std::string codegen_mem(CodeGenerator &g, const std::string &index="mem") const
Get thread-local memory object.
size_t sz_res() const
Get required length of res field.
size_t sz_w() const
Get required length of w field.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
size_t sz_arg() const
Get required length of arg field.
size_t sz_iw() const
Get required length of iw field.
bool is_null() const
Is a null pointer?
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool error_on_fail_
Throw an exception on failure?
void clear_mem()
Clear all memory (called from destructor)
Helper class for Serialization.
void version(const std::string &name, int v)
void pack(const Sparsity &e)
Serializes an object to the output stream.
casadi_int nnz() const
Get the number of (structural) non-zeros.
casadi_int size2() const
Get the number of columns.
const casadi_int * row() const
Get a reference to row-vector,.
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Dict get_stats(void *mem) const override
Get all statistics.
void free_mem(void *mem) const override
Free memory block.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
int init_mem(void *mem) const override
Initalize memory block.
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
void set_work(void *mem, const double **&arg, double **&res, casadi_int *&iw, double *&w) const override
Set the (persistent) work vectors.
void codegen_init_mem(CodeGenerator &g) const override
Codegen init for init_mem.
void codegen_body(CodeGenerator &g) const override
Generate code for the function body.
static const std::string meta_doc
A documentation string.
~XpressInterface() override
Destructor.
void codegen_free_mem(CodeGenerator &g) const override
Codegen for free_mem.
static const Options options_
Options.
void init(const Dict &opts) override
Initialize.
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
XpressInterface(const std::string &name, const std::map< std::string, Sparsity > &st)
Constructor using sparsity patterns.
Dict opts_
All Xpress options.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
static int s_xpress_init_rc
static std::once_flag s_xpress_init_flag
void copy_vector(const std::vector< S > &s, std::vector< D > &d)
@ CONIC_Q
The matrix Q: sparse symmetric, (np^2 x n)
@ CONIC_P
The matrix P: sparse symmetric, (np x np)
void flatten_nested_vector(const std::vector< std::vector< T > > &nested, std::vector< S > &flat)
Flatten a nested std::vector tot a single flattened vector.
static void codegen_int_field(CodeGenerator &g, const std::string &prob_field, const std::string &local_name, const std::vector< int > &v)
std::string str(const T &v)
String representation, any type.
void check_sos(casadi_int nx, const std::vector< std::vector< T > > &groups, std::vector< std::vector< double > > &weights, std::vector< casadi_int > &types)
Check sos structure and generate defaults.
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
int CASADI_CONIC_XPRESS_EXPORT casadi_register_conic_xpress(Conic::Plugin *plugin)
void CASADI_CONIC_XPRESS_EXPORT casadi_load_conic_xpress()
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
static void xpress_global_init()
Options metadata for a class.
std::map< std::string, FStats > fstats
void add_stat(const std::string &s)
casadi_xpress_data< double > d
const casadi_int * mq_colind
const casadi_int * mq_row
const casadi_int * mq_data
const double * sos_refval
const casadi_qp_prob< T1 > * qp
const casadi_socp_prob< T1 > * socp