25 #include "cplex_interface.hpp"
26 #include "casadi/core/casadi_misc.hpp"
27 #include "casadi/core/nlp_tools.hpp"
36 int CASADI_CONIC_CPLEX_EXPORT
39 plugin->name =
"cplex";
41 plugin->version = CASADI_VERSION;
46 int ret = cplex_adaptor_load(buffer,
sizeof(buffer));
48 casadi_warning(
"Failed to load CPLEX adaptor: " + std::string(buffer) +
".");
61 const std::map<std::string, Sparsity>& st)
69 "Options to be passed to CPLEX"}},
72 "Determines which CPLEX algorithm to use."}},
75 "Dumps QP to file in CPLEX format."}},
78 "The filename to dump to."}},
81 "Tolerance of solver"}},
84 "Detect redundant constraints."}},
87 "Use warm start with simplex methods (affects only the simplex methods)."}},
90 "Hot start integers with x0 [Default false]."}},
93 "Definition of SOS groups by indices."}},
96 "Weights corresponding to SOS entries."}},
99 "Specify 1 or 2 for each SOS group."}},
102 "Specify version of cplex to load. "
103 "We will attempt to load libcplex<version_suffix>.[so|dll|dylib]. "
104 "Default value is taken from CPLEX_VERSION env variable."}}
121 std::vector< std::vector<casadi_int> > sos_groups;
122 std::vector< std::vector<double> > sos_weights;
123 std::vector<casadi_int> sos_types;
126 for (
auto&& op : opts) {
127 if (op.first==
"cplex") {
129 }
else if (op.first==
"qp_method") {
131 }
else if (op.first==
"dump_to_file") {
133 }
else if (op.first==
"dump_filename") {
135 }
else if (op.first==
"tol") {
137 }
else if (op.first==
"dep_check") {
139 }
else if (op.first==
"warm_start") {
141 }
else if (op.first==
"mip_start") {
143 }
else if (op.first==
"sos_groups") {
144 sos_groups = op.second.to_int_vector_vector();
145 }
else if (op.first==
"sos_weights") {
146 sos_weights = op.second.to_double_vector_vector();
147 }
else if (op.first==
"sos_types") {
148 sos_types = op.second.to_int_vector();
157 if (!sos_weights.empty())
160 for (casadi_int type : sos_types)
170 for (casadi_int i=0; i<
nx_; ++i) {
193 if (CPXXgetparamnum(env, name.c_str(), &whichparam)) {
194 casadi_error(
"No such CPLEX parameter: " + name);
206 casadi_assert_dev(m->env==
nullptr);
207 m->env = CPXXopenCPLEX(&status);
208 if (m->env==
nullptr) {
209 char errmsg[CPXMESSAGEBUFSIZE];
210 CPXXgeterrorstring(m->env, status, errmsg);
211 casadi_error(std::string(
"Cannot initialize CPLEX environment: ") + errmsg);
215 if (CPXXsetdefaults(m->env)) {
216 casadi_error(
"CPXXsetdefaults failed");
220 int screen_output = 0;
222 screen_output =
param_by_name(m->env,
"CPXPARAM_ScreenOutput");
223 }
catch (std::exception& e) {
226 if (CPXXsetintparam(m->env, screen_output, CPX_ON)) {
227 casadi_error(
"Failure setting CPX_PARAM_SCRIND");
232 casadi_error(
"Failure setting CPX_PARAM_EPOPT");
237 casadi_error(
"Failure setting CPX_PARAM_EPRHS");
241 if (CPXXsetintparam(m->env,
param_by_name(m->env,
"CPX_PARAM_QPMETHOD"),
243 casadi_error(
"Failure setting CPX_PARAM_QPMETHOD");
248 casadi_error(
"Failure setting CPX_PARAM_DEPIND");
253 if (CPXXsetintparam(m->env,
param_by_name(m->env,
"CPX_PARAM_BARCROSSALG"), 1)) {
254 casadi_error(
"Failure setting CPX_PARAM_BARCROSSALG");
259 for (
auto&& op :
opts_) {
262 if (CPXXgetparamnum(m->env, op.first.c_str(), &whichparam)) {
263 casadi_error(
"No such CPLEX parameter: " + op.first);
268 if (CPXXgetparamtype(m->env, whichparam, ¶mtype)) {
269 casadi_error(
"CPXXgetparamtype failed");
274 case CPX_PARAMTYPE_NONE:
275 casadi_error(
"CPX_PARAMTYPE_NONE unsupported");
277 case CPX_PARAMTYPE_INT:
278 status = CPXXsetintparam(m->env, whichparam, op.second);
280 case CPX_PARAMTYPE_DOUBLE:
281 status = CPXXsetdblparam(m->env, whichparam, op.second);
283 case CPX_PARAMTYPE_STRING:
284 status = CPXXsetstrparam(m->env, whichparam,
285 static_cast<std::string
>(op.second).c_str());
287 case CPX_PARAMTYPE_LONG:
288 status = CPXXsetlongparam(m->env, whichparam,
289 static_cast<CPXLONG
>(
static_cast<casadi_int
>(op.second)));
292 casadi_error(
"Unknown CPLEX parameter type (" +
str(paramtype) +
") for " + op.first);
296 casadi_error(
"Failure setting option " + op.first);
306 m->sense.resize(
na_);
310 m->rngval.resize(
na_);
312 m->cstat.resize(
nx_);
313 m->rstat.resize(
na_);
318 std::minus<casadi_int>());
323 std::minus<casadi_int>());
326 casadi_assert_dev(m->lp==
nullptr);
327 m->lp = CPXXcreateprob(m->env, &status,
"QP from CasADi");
328 casadi_assert(m->lp!=
nullptr,
"CPXXcreateprob failed");
330 m->a_colind.resize(
A_.
size2()+1);
331 m->a_row.resize(
A_.
nnz());
332 m->h_colind.resize(
H_.
size2()+1);
333 m->h_row.resize(
H_.
nnz());
340 m->socp_lval.resize(sm.
map_Q.
nnz());
343 m->socp_lbx.resize(sm.
r.back());
353 m->add_stat(
"preprocessing");
354 m->add_stat(
"solver");
355 m->add_stat(
"postprocessing");
361 solve(
const double** arg,
double** res, casadi_int* iw,
double* w,
void* mem)
const {
365 m->fstats.at(
"preprocessing").tic();
368 m->return_status = -1;
371 double* g=w; w +=
nx_;
373 double* lbx=w; w +=
nx_;
375 double* ubx=w; w +=
nx_;
377 double* lba=w; w +=
na_;
379 double* uba=w; w +=
na_;
385 double* x=w; w +=
nx_;
387 double* lam_x=w; w +=
nx_;
393 double* lam_a=w; w +=
na_;
397 (void)CPXXsetintparam(m->env,
param_by_name(m->env,
"CPX_PARAM_QPMETHOD"), 1);
400 for (casadi_int i = 0; i <
na_; ++i) {
404 if (uba[i] - lba[i] < 1e-20) {
408 }
else if (lba[i] < -CPX_INFBOUND) {
413 }
else if (uba[i] > CPX_INFBOUND) {
421 m->rngval[i] = uba[i] - lba[i];
426 const CPXNNZ* matbeg =
get_ptr(m->a_colind);
427 const CPXDIM* matind =
get_ptr(m->a_row);
429 const double* matval = A;
430 const double* obj = g;
431 const double* lb = lbx;
432 const double* ub = ubx;
434 matbeg,
get_ptr(m->matcnt), matind, matval, lb, ub,
get_ptr(m->rngval))) {
435 casadi_error(
"CPXXcopylp failed");
441 if (CPXXaddsos(m->env, m->lp,
445 casadi_error(
"CPXXaddsos failed");
451 const CPXNNZ* qmatbeg =
get_ptr(m->h_colind);
452 const CPXDIM* qmatind =
get_ptr(m->h_row);
453 const double* qmatval = H;
454 if (CPXXcopyquad(m->env, m->lp, qmatbeg,
get_ptr(m->qmatcnt), qmatind, qmatval)) {
455 casadi_error(
"CPXXcopyquad failed");
465 for (casadi_int i=0;i<sm.
r.size()-1;++i) {
466 for (casadi_int k=0;k<sm.
r[i+1]-sm.
r[i]-1;++k) {
467 m->socp_lbx[j++] = -
inf;
469 m->socp_lbx[j++] = 0;
473 if (CPXXnewcols(m->env, m->lp, sm.
r.back(),
nullptr,
474 get_ptr(m->socp_lbx),
nullptr,
nullptr,
nullptr)) {
475 casadi_error(
"CPXXnewcols failed");
480 const casadi_int* colind = sp.
colind();
481 const casadi_int* row = sp.
row();
482 const casadi_int* data = sm.
map_Q.
ptr();
484 const double* p = arg[
CONIC_P];
485 const double* q = arg[
CONIC_Q];
487 casadi_int numnz = 0;
489 for (casadi_int i=0; i<sp.
size2(); ++i) {
490 m->socp_lbound[i] = sm.
map_P[i]==-1 ? 0 : -p[sm.
map_P[i]];
492 for (casadi_int k=colind[i]; k<colind[i+1]; ++k) {
493 casadi_int j = row[k];
494 m->socp_lval[numnz] = (q && j<
nx_) ? q[data[k]] : -1;
500 if (CPXXaddrows(m->env, m->lp, 0, sp.
size2(), sp.
nnz(),
get_ptr(m->socp_lbound),
nullptr,
503 casadi_error(
"CPXXaddrows failed");
507 for (casadi_int i=0; i<sm.
r.size()-1; ++i) {
508 casadi_int block_size = sm.
r[i+1]-sm.
r[i];
511 for (casadi_int j=0;j<block_size;++j) {
512 m->socp_qind[j] =
nx_ + sm.
r[i] + j;
513 m->socp_qval[j] = j<block_size-1 ? 1 : -1;
516 if (CPXXaddqconstr(m->env, m->lp, 0, block_size,
517 0,
'L',
nullptr,
nullptr,
520 casadi_error(
"CPXXaddqconstr failed");
531 if (CPXXcopystart(m->env, m->lp,
get_ptr(m->cstat),
get_ptr(m->rstat), x,
532 nullptr, lam_x,
nullptr)) {
533 casadi_error(
"CPXXcopystart failed");
536 if (CPXXcopystart(m->env, m->lp,
nullptr,
nullptr, x,
537 nullptr, lam_x,
nullptr)) {
538 casadi_error(
"CPXXcopystart failed");
544 std::vector<double> slack(
na_);
549 if (CPXXcopyctype(m->env, m->lp, &
ctype_[0])) {
550 casadi_error(
"CPXXcopyctype failed");
555 const CPXNNZ beg[] = {0};
556 std::vector<int> varindices;
557 std::vector<double> values;
559 for (casadi_int i=0; i<
nx_; ++i) {
561 varindices.push_back(i);
562 values.push_back(x[i]);
566 casadi_assert_dev(!varindices.empty());
568 if (CPXXaddmipstarts(m->env, m->lp, 1, varindices.size(), &beg[0],
571 casadi_error(
"CPXXaddmipstarts failed");
577 casadi_error(
"CPXXwriteprob failed");
581 m->fstats.at(
"preprocessing").toc();
582 m->fstats.at(
"solver").tic();
584 if (CPXXmipopt(m->env, m->lp)) {
585 casadi_error(
"CPXXmipopt failed");
587 m->fstats.at(
"solver").toc();
588 m->fstats.at(
"postprocessing").tic();
590 int stat = CPXXgetstat(m->env, m->lp);
591 if (stat == CPX_STAT_INFEASIBLE) {
596 if (CPXXgetobjval(m->env, m->lp, &f)) {
597 casadi_error(
"CPXXgetobjval failed");
601 casadi_int cur_numcols = CPXXgetnumcols(m->env, m->lp);
602 if (CPXXgetx(m->env, m->lp, x, 0, cur_numcols-1)) {
603 casadi_error(
"CPXXgetx failed");
607 casadi_int cur_numrows = CPXXgetnumrows(m->env, m->lp);
608 if (CPXXgetslack(m->env, m->lp,
get_ptr(slack), 0, cur_numrows-1)) {
609 casadi_error(
"CPXXgetslack failed");
619 casadi_error(
"CPXXwriteprob failed");
623 m->fstats.at(
"preprocessing").toc();
624 m->fstats.at(
"solver").tic();
626 if (CPXXqpopt(m->env, m->lp)) {
627 casadi_error(
"CPXXqpopt failed");
630 int stat = CPXXgetstat(m->env, m->lp);
631 if (stat == CPX_STAT_INFEASIBLE) {
635 m->fstats.at(
"solver").toc();
636 m->fstats.at(
"postprocessing").tic();
638 casadi_int problem_type = CPXXgetprobtype(m->env, m->lp);
641 if (problem_type == CPXPROB_MIQP) {
644 if (CPXXgetobjval(m->env, m->lp, &f)) {
645 casadi_error(
"CPXXgetobjval failed");
649 casadi_int cur_numcols = CPXXgetnumcols(m->env, m->lp);
650 if (CPXXgetx(m->env, m->lp, x, 0, cur_numcols-1)) {
651 casadi_error(
"CPXXgetx failed");
657 if (
verbose_) casadi_message(
"CPLEX does not compute dual variables for nonconvex QPs");
662 int sol_ret = CPXXsolution(m->env, m->lp, &solstat, &f, x, lam_a,
get_ptr(slack), lam_x);
663 if (sol_ret == CPXERR_NO_SOLN || sol_ret == CPXERR_NO_SOLN) {
668 m->d_qp.success =
false;
675 (void)CPXXgetbase(m->env, m->lp,
get_ptr(m->cstat),
get_ptr(m->rstat));
682 m->return_status = CPXXgetstat(m->env, m->lp);
683 m->d_qp.success = m->return_status==CPX_STAT_OPTIMAL;
684 m->d_qp.success |= m->return_status==CPX_STAT_FIRSTORDER;
685 m->d_qp.success |= m->return_status==CPXMIP_OPTIMAL;
686 m->d_qp.success |= m->return_status==CPXMIP_OPTIMAL_TOL;
691 casadi_message(std::string(
"CPLEX return status: ") +
status_string);
705 m->fstats.at(
"postprocessing").toc();
740 if (this->
lp!=
nullptr) {
741 status = CPXXfreeprob(this->
env, &this->
lp);
743 uerr() <<
"CPXXfreeprob failed, error code " << status <<
".\n";
749 if (this->
env!=
nullptr) {
750 status = CPXXcloseCPLEX(&this->
env);
752 uerr() <<
"CPXXcloseCPLEX failed, error code " << status <<
".\n";
759 s.
version(
"CplexInterface", 1);
779 s.
version(
"CplexInterface", 1);
783 s.
pack(
"CplexInterface::tol",
tol_);
787 s.
pack(
"CplexInterface::mip",
mip_);
static const Options options_
Options.
casadi_int nx_
Number of decision variables.
int init_mem(void *mem) const override
Initalize memory block.
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Sparsity H_
Problem structure.
void init(const Dict &opts) override
Initialize.
void deserialize(DeserializingStream &s, SDPToSOCPMem &m)
std::vector< bool > discrete_
Options.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Dict get_stats(void *mem) const override
Get all statistics.
void sdp_to_socp_init(SDPToSOCPMem &mem) const
SDP to SOCP conversion initialization.
void serialize(SerializingStream &s, const SDPToSOCPMem &m) const
static const Options options_
Options.
std::vector< double > sos_weights_
SDPToSOCPMem sdp_to_socp_mem_
SDP to SOCP conversion memory.
std::vector< char > sos_types_
~CplexInterface() override
Destructor.
std::vector< casadi_int > sos_beg_
int solve(const double **arg, double **res, casadi_int *iw, double *w, void *mem) const override
Solve the QP.
static ProtoFunction * deserialize(DeserializingStream &s)
Deserialize with type disambiguation.
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
void init(const Dict &opts) override
Initialize.
static const std::string meta_doc
A documentation string.
static Conic * creator(const std::string &name, const std::map< std::string, Sparsity > &st)
Create a new QP Solver.
std::string dump_filename_
std::vector< int > sos_ind_
Dict get_stats(void *mem) const override
Get all statistics.
CplexInterface(const std::string &name, const std::map< std::string, Sparsity > &st)
Constructor using sparsity patterns.
int init_mem(void *mem) const override
Initalize memory block.
std::vector< char > ctype_
Dict opts_
All CPLEX options.
Helper class for Serialization.
void unpack(Sparsity &e)
Reconstruct an object from the input stream.
void version(const std::string &name, int v)
casadi_int nnz_in() const
Number of input/output nonzeros.
void alloc_w(size_t sz_w, bool persistent=false)
Ensure required length of w field.
casadi_int nnz() const
Get the number of (structural) non-zero elements.
casadi_int size2() const
Get the second dimension (i.e. number of columns)
const Sparsity & sparsity() const
Const access the sparsity - reference to data member.
static void registerPlugin(const Plugin &plugin, bool needs_lock=true)
Register an integrator in the factory.
bool verbose_
Verbose printout.
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)
int param_by_name(CPXENVptr env, const std::string &name)
void copy_vector(const std::vector< S > &s, std::vector< D > &d)
@ CONIC_UBA
dense, (nc x 1)
@ CONIC_A
The matrix A: sparse, (nc x n) - product with x must be dense.
@ CONIC_G
The vector g: dense, (n x 1)
@ CONIC_Q
The matrix Q: sparse symmetric, (np^2 x n)
@ CONIC_LBA
dense, (nc x 1)
@ CONIC_UBX
dense, (n x 1)
@ CONIC_LBX
dense, (n x 1)
@ 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.
void casadi_copy(const T1 *x, casadi_int n, T1 *y)
COPY: y <-x.
void casadi_fill(T1 *x, casadi_int n, T1 alpha)
FILL: x <- alpha.
static std::string status_string(SLEQP_STATUS status)
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.
void CASADI_CONIC_CPLEX_EXPORT casadi_load_conic_cplex()
const double nan
Not a number.
void casadi_scal(casadi_int n, T1 alpha, T1 *x)
SCAL: x <- alpha*x.
int CASADI_CONIC_CPLEX_EXPORT casadi_register_conic_cplex(Conic::Plugin *plugin)
T * get_ptr(std::vector< T > &v)
Get a pointer to the data contained in the vector.
@ CONIC_X
The primal solution.
@ CONIC_LAM_A
The dual solution corresponding to linear bounds.
@ CONIC_COST
The optimal cost.
@ CONIC_LAM_X
The dual solution corresponding to simple bounds.
SDP to SOCP conversion memory.
std::vector< casadi_int > r
std::vector< casadi_int > map_P
~CplexMemory()
Destructor.
bool is_warm
Indicates if we have to warm-start.
CplexMemory()
Constructor.
CPXENVptr env
CPLEX environment.
Options metadata for a class.