cplex_interface.cpp
1 /*
2  * This file is part of CasADi.
3  *
4  * CasADi -- A symbolic framework for dynamic optimization.
5  * Copyright (C) 2010-2023 Joel Andersson, Joris Gillis, Moritz Diehl, Kobe Bergmans
6  * KU Leuven. All rights reserved.
7  * Copyright (C) 2011-2014 Greg Horn
8  *
9  * CasADi is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 3 of the License, or (at your option) any later version.
13  *
14  * CasADi is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with CasADi; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22  *
23  */
24 
25 #include "cplex_interface.hpp"
26 #include "casadi/core/casadi_misc.hpp"
27 #include "casadi/core/nlp_tools.hpp"
28 #include <ctime>
29 #include <cstdio>
30 #include <cstdlib>
31 #include <sstream>
32 #include <vector>
33 
34 namespace casadi {
35  extern "C"
36  int CASADI_CONIC_CPLEX_EXPORT
37  casadi_register_conic_cplex(Conic::Plugin* plugin) {
38  plugin->creator = CplexInterface::creator;
39  plugin->name = "cplex";
40  plugin->doc = CplexInterface::meta_doc.c_str();
41  plugin->version = CASADI_VERSION;
42  plugin->options = &CplexInterface::options_;
43  plugin->deserialize = &CplexInterface::deserialize;
44  #ifdef CPLEX_ADAPTOR
45  char buffer[400];
46  int ret = cplex_adaptor_load(buffer, sizeof(buffer));
47  if (ret!=0) {
48  casadi_warning("Failed to load CPLEX adaptor: " + std::string(buffer) + ".");
49  return 1;
50  }
51  #endif
52  return 0;
53  }
54 
55  extern "C"
56  void CASADI_CONIC_CPLEX_EXPORT casadi_load_conic_cplex() {
58  }
59 
60  CplexInterface::CplexInterface(const std::string& name,
61  const std::map<std::string, Sparsity>& st)
62  : Conic(name, st) {
63  }
64 
66  = {{&Conic::options_},
67  {{"cplex",
68  {OT_DICT,
69  "Options to be passed to CPLEX"}},
70  {"qp_method",
71  {OT_INT,
72  "Determines which CPLEX algorithm to use."}},
73  {"dump_to_file",
74  {OT_BOOL,
75  "Dumps QP to file in CPLEX format."}},
76  {"dump_filename",
77  {OT_STRING,
78  "The filename to dump to."}},
79  {"tol",
80  {OT_DOUBLE,
81  "Tolerance of solver"}},
82  {"dep_check",
83  {OT_INT,
84  "Detect redundant constraints."}},
85  {"warm_start",
86  {OT_BOOL,
87  "Use warm start with simplex methods (affects only the simplex methods)."}},
88  {"mip_start",
89  {OT_BOOL,
90  "Hot start integers with x0 [Default false]."}},
91  {"sos_groups",
93  "Definition of SOS groups by indices."}},
94  {"sos_weights",
96  "Weights corresponding to SOS entries."}},
97  {"sos_types",
98  {OT_INTVECTOR,
99  "Specify 1 or 2 for each SOS group."}},
100  {"version_suffix",
101  {OT_STRING,
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."}}
105  }
106  };
107 
108  void CplexInterface::init(const Dict& opts) {
109  // Call the init method of the base class
110  Conic::init(opts);
111 
112  // Default options
113  qp_method_ = 0;
114  dump_to_file_ = false;
115  dump_filename_ = "qp.dat";
116  tol_ = 1e-6;
117  dep_check_ = 0;
118  warm_start_ = false;
119  mip_start_ = false;
120 
121  std::vector< std::vector<casadi_int> > sos_groups;
122  std::vector< std::vector<double> > sos_weights;
123  std::vector<casadi_int> sos_types;
124 
125  // Read options
126  for (auto&& op : opts) {
127  if (op.first=="cplex") {
128  opts_ = op.second;
129  } else if (op.first=="qp_method") {
130  qp_method_ = op.second;
131  } else if (op.first=="dump_to_file") {
132  dump_to_file_ = op.second;
133  } else if (op.first=="dump_filename") {
134  dump_filename_ = op.second.to_string();
135  } else if (op.first=="tol") {
136  tol_ = op.second;
137  } else if (op.first=="dep_check") {
138  dep_check_ = op.second;
139  } else if (op.first=="warm_start") {
140  warm_start_ = op.second;
141  } else if (op.first=="mip_start") {
142  mip_start_ = op.second;
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();
149  }
150  }
151 
152  // Validaty SOS constraints
153  check_sos(nx_, sos_groups, sos_weights, sos_types);
154 
155  // Populate SOS structures
157  if (!sos_weights.empty())
158  flatten_nested_vector(sos_weights, sos_weights_);
159 
160  for (casadi_int type : sos_types)
161  sos_types_.push_back(type==1 ? '1' : '2');
162 
163  // Are we solving a mixed-integer problem?
164  mip_ = !discrete_.empty()
165  && std::find(discrete_.begin(), discrete_.end(), true)!=discrete_.end();
166 
167  // Type of variable
168  if (mip_) {
169  ctype_.resize(nx_);
170  for (casadi_int i=0; i<nx_; ++i) {
171  ctype_[i] = discrete_[i] ? 'I' : 'C';
172  }
173  }
174 
175  // Initialize SDP to SOCP memory
177 
178  // Allocate work vectors
179  alloc_w(nx_, true); // g
180  alloc_w(nx_, true); // lbx
181  alloc_w(nx_, true); // ubx
182  alloc_w(na_, true); // lba
183  alloc_w(na_, true); // uba
184  alloc_w(nnz_in(CONIC_H), true); // H
185  alloc_w(nnz_in(CONIC_A), true); // A
186  alloc_w(nx_, true); // x
187  alloc_w(nx_, true); // lam_x
188  alloc_w(na_, true); // lam_a
189  }
190 
191  int param_by_name(CPXENVptr env, const std::string &name) {
192  int whichparam;
193  if (CPXXgetparamnum(env, name.c_str(), &whichparam)) {
194  casadi_error("No such CPLEX parameter: " + name);
195  }
196  return whichparam;
197  }
198 
199  int CplexInterface::init_mem(void* mem) const {
200  if (Conic::init_mem(mem)) return 1;
201  if (!mem) return 1;
202  auto m = static_cast<CplexMemory*>(mem);
203 
204  // Start CPLEX
205  int status;
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);
212  }
213 
214  // Set parameters to their default values
215  if (CPXXsetdefaults(m->env)) {
216  casadi_error("CPXXsetdefaults failed");
217  }
218 
219  // Enable output by default
220  int screen_output = 0;
221  try {
222  screen_output = param_by_name(m->env, "CPXPARAM_ScreenOutput");
223  } catch (std::exception& e) {
224  screen_output = param_by_name(m->env, "CPX_PARAM_SCRIND");
225  }
226  if (CPXXsetintparam(m->env, screen_output, CPX_ON)) {
227  casadi_error("Failure setting CPX_PARAM_SCRIND");
228  }
229 
230  // Optimality tolerance
231  if (CPXXsetdblparam(m->env, param_by_name(m->env, "CPX_PARAM_EPOPT"), tol_)) {
232  casadi_error("Failure setting CPX_PARAM_EPOPT");
233  }
234 
235  // Feasibility tolerance
236  if (CPXXsetdblparam(m->env, param_by_name(m->env, "CPX_PARAM_EPRHS"), tol_)) {
237  casadi_error("Failure setting CPX_PARAM_EPRHS");
238  }
239 
240  // We start with barrier if crossover was chosen.
241  if (CPXXsetintparam(m->env, param_by_name(m->env, "CPX_PARAM_QPMETHOD"),
242  qp_method_ == 7 ? 4 : qp_method_)) {
243  casadi_error("Failure setting CPX_PARAM_QPMETHOD");
244  }
245 
246  // Setting dependency check option
247  if (CPXXsetintparam(m->env, param_by_name(m->env, "CPX_PARAM_DEPIND"), dep_check_)) {
248  casadi_error("Failure setting CPX_PARAM_DEPIND");
249  }
250 
251  // Setting crossover algorithm
252  if (qp_method_ == 7) {
253  if (CPXXsetintparam(m->env, param_by_name(m->env, "CPX_PARAM_BARCROSSALG"), 1)) {
254  casadi_error("Failure setting CPX_PARAM_BARCROSSALG");
255  }
256  }
257 
258  // Set parameters
259  for (auto&& op : opts_) {
260  // Get parameter index
261  int whichparam;
262  if (CPXXgetparamnum(m->env, op.first.c_str(), &whichparam)) {
263  casadi_error("No such CPLEX parameter: " + op.first);
264  }
265 
266  // Get type of parameter
267  int paramtype;
268  if (CPXXgetparamtype(m->env, whichparam, &paramtype)) {
269  casadi_error("CPXXgetparamtype failed");
270  }
271 
272  // Pass to CPLEX
273  switch (paramtype) {
274  case CPX_PARAMTYPE_NONE:
275  casadi_error("CPX_PARAMTYPE_NONE unsupported");
276  break;
277  case CPX_PARAMTYPE_INT:
278  status = CPXXsetintparam(m->env, whichparam, op.second);
279  break;
280  case CPX_PARAMTYPE_DOUBLE:
281  status = CPXXsetdblparam(m->env, whichparam, op.second);
282  break;
283  case CPX_PARAMTYPE_STRING:
284  status = CPXXsetstrparam(m->env, whichparam,
285  static_cast<std::string>(op.second).c_str());
286  break;
287  case CPX_PARAMTYPE_LONG:
288  status = CPXXsetlongparam(m->env, whichparam,
289  static_cast<CPXLONG>(static_cast<casadi_int>(op.second)));
290  break;
291  default:
292  casadi_error("Unknown CPLEX parameter type (" + str(paramtype) + ") for " + op.first);
293  }
294  // Error handling
295  if (status) {
296  casadi_error("Failure setting option " + op.first);
297  }
298  }
299 
300  // Doing allocation of CPLEX data
301  // Objective is to be minimized
302  m->objsen = CPX_MIN;
303 
304  // Allocation of data
305  // Type of constraint
306  m->sense.resize(na_);
307  // Right-hand side of constraints
308  m->rhs.resize(na_);
309  // Range value for lower AND upper bounded constraints
310  m->rngval.resize(na_);
311  // Basis for primal variables
312  m->cstat.resize(nx_);
313  m->rstat.resize(na_);
314 
315  // Matrix A, count the number of elements per column
316  m->matcnt.resize(A_.size2());
317  transform(A_.colind()+1, A_.colind() + A_.size2()+1, A_.colind(), m->matcnt.begin(),
318  std::minus<casadi_int>());
319 
320  // Matrix H, count the number of elements per column
321  m->qmatcnt.resize(H_.size2());
322  transform(H_.colind()+1, H_.colind() + H_.size2()+1, H_.colind(), m->qmatcnt.begin(),
323  std::minus<casadi_int>());
324 
325  // Create problem object
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");
329 
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());
334 
335  const SDPToSOCPMem& sm = sdp_to_socp_mem_;
336 
337  m->socp_qind.resize(sm.indval_size);
338  m->socp_qval.resize(sm.indval_size);
339  m->socp_lbound.resize(sm.map_Q.size2());
340  m->socp_lval.resize(sm.map_Q.nnz());
341  m->socp_colind.resize(sm.map_Q.size2()+1);
342  m->socp_row.resize(sm.map_Q.nnz());
343  m->socp_lbx.resize(sm.r.back());
344 
345  copy_vector(A_.colind(), m->a_colind);
346  copy_vector(A_.row(), m->a_row);
347  copy_vector(H_.colind(), m->h_colind);
348  copy_vector(H_.row(), m->h_row);
349 
350  copy_vector(sm.map_Q.sparsity().colind(), m->socp_colind);
351  copy_vector(sm.map_Q.sparsity().row(), m->socp_row);
352 
353  m->add_stat("preprocessing");
354  m->add_stat("solver");
355  m->add_stat("postprocessing");
356  return 0;
357  }
358 
359 
361  solve(const double** arg, double** res, casadi_int* iw, double* w, void* mem) const {
362  auto m = static_cast<CplexMemory*>(mem);
363  const SDPToSOCPMem& sm = sdp_to_socp_mem_;
364 
365  m->fstats.at("preprocessing").tic();
366 
367  // Problem has not been solved at this point
368  m->return_status = -1;
369 
370  // Get inputs
371  double* g=w; w += nx_;
372  casadi_copy(arg[CONIC_G], nx_, g);
373  double* lbx=w; w += nx_;
374  casadi_copy(arg[CONIC_LBX], nx_, lbx);
375  double* ubx=w; w += nx_;
376  casadi_copy(arg[CONIC_UBX], nx_, ubx);
377  double* lba=w; w += na_;
378  casadi_copy(arg[CONIC_LBA], na_, lba);
379  double* uba=w; w += na_;
380  casadi_copy(arg[CONIC_UBA], na_, uba);
381  double* H=w; w += nnz_in(CONIC_H);
382  casadi_copy(arg[CONIC_H], nnz_in(CONIC_H), H);
383  double* A=w; w += nnz_in(CONIC_A);
384  casadi_copy(arg[CONIC_A], nnz_in(CONIC_A), A);
385  double* x=w; w += nx_;
386  casadi_copy(arg[CONIC_X0], nx_, x);
387  double* lam_x=w; w += nx_;
388  casadi_copy(arg[CONIC_LAM_X0], nx_, lam_x);
389  // Flip the sign of the multipliers
390  casadi_scal(nx_, -1., lam_x);
391 
392  // Temporaries
393  double* lam_a=w; w += na_;
394 
395  // We change method in crossover
396  if (m->is_warm && qp_method_ == 7) {
397  (void)CPXXsetintparam(m->env, param_by_name(m->env, "CPX_PARAM_QPMETHOD"), 1);
398  }
399 
400  for (casadi_int i = 0; i < na_; ++i) {
401  // CPX_INFBOUND
402 
403  // Equality
404  if (uba[i] - lba[i] < 1e-20) {
405  m->sense[i] = 'E';
406  m->rhs[i] = lba[i];
407  m->rngval[i] = 0.;
408  } else if (lba[i] < -CPX_INFBOUND) {
409  // Ineq - no lower bound
410  m->sense[i] = 'L';
411  m->rhs[i] = uba[i];
412  m->rngval[i] = 0.;
413  } else if (uba[i] > CPX_INFBOUND) {
414  // Ineq - no upper bound
415  m->sense[i] = 'G';
416  m->rhs[i] = lba[i];
417  m->rngval[i] = 0.;
418  } else { // Inew both upper and lower bounds
419  m->sense[i] = 'R';
420  m->rhs[i] = lba[i];
421  m->rngval[i] = uba[i] - lba[i];
422  }
423  }
424 
425  // Copying objective, constraints, and bounds.
426  const CPXNNZ* matbeg = get_ptr(m->a_colind);
427  const CPXDIM* matind = get_ptr(m->a_row);
428 
429  const double* matval = A;
430  const double* obj = g;
431  const double* lb = lbx;
432  const double* ub = ubx;
433  if (CPXXcopylp(m->env, m->lp, nx_, na_, m->objsen, obj, get_ptr(m->rhs), get_ptr(m->sense),
434  matbeg, get_ptr(m->matcnt), matind, matval, lb, ub, get_ptr(m->rngval))) {
435  casadi_error("CPXXcopylp failed");
436  }
437 
438 
439  // Add SOS constraints when applicable
440  if (!sos_ind_.empty()) {
441  if (CPXXaddsos(m->env, m->lp,
442  sos_beg_.size()-1, sos_ind_.size(),
445  casadi_error("CPXXaddsos failed");
446  }
447  }
448 
449  if (nnz_in(CONIC_H) > 0) {
450  // Preparing coefficient matrix Q
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");
456  }
457  }
458 
459  // =================
460  // BEGIN SOCP BLOCK
461  // =================
462 
463  // Prepare lower bounds for helper variables for SOCP
464  casadi_int j=0;
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;
468  }
469  m->socp_lbx[j++] = 0;
470  }
471 
472  // Add helper variables for SOCP
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");
476  }
477 
478  // SOCP helper constraints
479  const Sparsity& sp = sm.map_Q.sparsity();
480  const casadi_int* colind = sp.colind();
481  const casadi_int* row = sp.row();
482  const casadi_int* data = sm.map_Q.ptr();
483 
484  const double* p = arg[CONIC_P];
485  const double* q = arg[CONIC_Q];
486 
487  casadi_int numnz = 0;
488  // Loop over columns
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]];
491  // Loop over rows
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;
495  numnz++;
496  }
497  }
498 
499  // Adding SOCP helper constraints
500  if (CPXXaddrows(m->env, m->lp, 0, sp.size2(), sp.nnz(), get_ptr(m->socp_lbound), nullptr,
501  get_ptr(m->socp_colind), get_ptr(m->socp_row), get_ptr(m->socp_lval),
502  nullptr, nullptr)) {
503  casadi_error("CPXXaddrows failed");
504  }
505 
506  // Loop over blocks
507  for (casadi_int i=0; i<sm.r.size()-1; ++i) {
508  casadi_int block_size = sm.r[i+1]-sm.r[i];
509 
510  // Indicate x'x - y^2 <= 0
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;
514  }
515 
516  if (CPXXaddqconstr(m->env, m->lp, 0, block_size,
517  0, 'L', nullptr, nullptr,
518  get_ptr(m->socp_qind), get_ptr(m->socp_qind), get_ptr(m->socp_qval),
519  nullptr)) {
520  casadi_error("CPXXaddqconstr failed");
521  }
522  }
523 
524  // =================
525  // END SOCP BLOCK
526  // =================
527 
528  // Warm-starting if possible
529  if (qp_method_ != 0 && qp_method_ != 4 && m->is_warm) {
530  // TODO(Joel): Initialize slacks and dual variables of bound constraints
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");
534  }
535  } else {
536  if (CPXXcopystart(m->env, m->lp, nullptr, nullptr, x,
537  nullptr, lam_x, nullptr)) {
538  casadi_error("CPXXcopystart failed");
539  }
540  }
541 
542  // Solution
543  double f;
544  std::vector<double> slack(na_);
545  int solstat;
546 
547  if (mip_) {
548  // Pass type of variables
549  if (CPXXcopyctype(m->env, m->lp, &ctype_[0])) {
550  casadi_error("CPXXcopyctype failed");
551  }
552 
553  if (mip_start_) {
554  // Add a single MIP start based on x0
555  const CPXNNZ beg[] = {0};
556  std::vector<int> varindices;
557  std::vector<double> values;
558 
559  for (casadi_int i=0; i<nx_; ++i) {
560  if (!discrete_.empty() && discrete_.at(i)) {
561  varindices.push_back(i);
562  values.push_back(x[i]);
563  }
564  }
565 
566  casadi_assert_dev(!varindices.empty());
567 
568  if (CPXXaddmipstarts(m->env, m->lp, 1, varindices.size(), &beg[0],
569  get_ptr(varindices), get_ptr(values),
570  nullptr, nullptr)) {
571  casadi_error("CPXXaddmipstarts failed");
572  }
573  }
574 
575  if (dump_to_file_) {
576  if (CPXXwriteprob(m->env, m->lp, dump_filename_.c_str(), "LP")) {
577  casadi_error("CPXXwriteprob failed");
578  }
579  }
580 
581  m->fstats.at("preprocessing").toc();
582  m->fstats.at("solver").tic();
583  // Optimize
584  if (CPXXmipopt(m->env, m->lp)) {
585  casadi_error("CPXXmipopt failed");
586  }
587  m->fstats.at("solver").toc();
588  m->fstats.at("postprocessing").tic();
589 
590  int stat = CPXXgetstat(m->env, m->lp);
591  if (stat == CPX_STAT_INFEASIBLE) {
592  m->d_qp.unified_return_status = SOLVER_RET_INFEASIBLE;
593  }
594 
595  // Get objective value
596  if (CPXXgetobjval(m->env, m->lp, &f)) {
597  casadi_error("CPXXgetobjval failed");
598  }
599 
600  // Get primal solution
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");
604  }
605 
606  // Get slacks
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");
610  }
611 
612  // Not a number as dual variables (not calculated with MIQP algorithm)
613  casadi_fill(lam_a, na_, nan);
614  casadi_fill(lam_x, nx_, nan);
615 
616  } else {
617  if (dump_to_file_) {
618  if (CPXXwriteprob(m->env, m->lp, dump_filename_.c_str(), "LP")) {
619  casadi_error("CPXXwriteprob failed");
620  }
621  }
622 
623  m->fstats.at("preprocessing").toc();
624  m->fstats.at("solver").tic();
625  // Optimize
626  if (CPXXqpopt(m->env, m->lp)) {
627  casadi_error("CPXXqpopt failed");
628  }
629 
630  int stat = CPXXgetstat(m->env, m->lp);
631  if (stat == CPX_STAT_INFEASIBLE) {
632  m->d_qp.unified_return_status = SOLVER_RET_INFEASIBLE;
633  }
634 
635  m->fstats.at("solver").toc();
636  m->fstats.at("postprocessing").tic();
637 
638  casadi_int problem_type = CPXXgetprobtype(m->env, m->lp);
639 
640  // The solver switched to a MIQP
641  if (problem_type == CPXPROB_MIQP) {
642 
643  // Get objective value
644  if (CPXXgetobjval(m->env, m->lp, &f)) {
645  casadi_error("CPXXgetobjval failed");
646  }
647 
648  // Get primal solution
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");
652  }
653 
654  // Not a number as dual variables (not calculated with MIQP algorithm)
655  casadi_fill(lam_a, na_, nan);
656  casadi_fill(lam_x, nx_, nan);
657  if (verbose_) casadi_message("CPLEX does not compute dual variables for nonconvex QPs");
658 
659  } else {
660 
661  // Retrieving solution
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) {
664  m->d_qp.unified_return_status = SOLVER_RET_INFEASIBLE;
665  }
666 
667  if (sol_ret) {
668  m->d_qp.success = false;
669  }
670  }
671  }
672 
673  // Retrieving the basis
674  if (qp_method_ != 0 && qp_method_ != 4) {
675  (void)CPXXgetbase(m->env, m->lp, get_ptr(m->cstat), get_ptr(m->rstat));
676  }
677 
678  // Flip the sign of the multipliers
679  casadi_scal(na_, -1., lam_a);
680  casadi_scal(nx_, -1., lam_x);
681 
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;
687 
688  if (verbose_) {
689  char status_string[CPXMESSAGEBUFSIZE];
690  CPXXgetstatstring(m->env, m->return_status, status_string);
691  casadi_message(std::string("CPLEX return status: ") + status_string);
692  }
693 
694  // Next time we warm start
695  if (warm_start_) {
696  m->is_warm = true;
697  }
698 
699  // Get the outputs
700  if (res[CONIC_COST]) *res[CONIC_COST] = f;
701  casadi_copy(lam_a, na_, res[CONIC_LAM_A]);
702  casadi_copy(lam_x, nx_, res[CONIC_LAM_X]);
703  casadi_copy(x, nx_, res[CONIC_X]);
704 
705  m->fstats.at("postprocessing").toc();
706 
707  return 0;
708  }
709 
711  clear_mem();
712  }
713 
714  Dict CplexInterface::get_stats(void* mem) const {
715  Dict stats = Conic::get_stats(mem);
716  auto m = static_cast<CplexMemory*>(mem);
717 
718  char status_string[CPXMESSAGEBUFSIZE];
719  CPXXgetstatstring(m->env, m->return_status, status_string);
720  stats["return_status"] = std::string(status_string);
721 
722  return stats;
723  }
724 
725 
727  // Setting warm-start flag
728  this->is_warm = false;
729 
730  // Set pointer to zero to avoid deleting a nonexisting instance
731  this->env = nullptr;
732  this->lp = nullptr;
733  }
734 
736  // Return flag
737  casadi_int status;
738 
739  // Only free if Cplex problem if it has been allocated
740  if (this->lp!=nullptr) {
741  status = CPXXfreeprob(this->env, &this->lp);
742  if (status!=0) {
743  uerr() << "CPXXfreeprob failed, error code " << status << ".\n";
744  }
745  this->lp = nullptr;
746  }
747 
748  // Closing down license
749  if (this->env!=nullptr) {
750  status = CPXXcloseCPLEX(&this->env);
751  if (status!=0) {
752  uerr() << "CPXXcloseCPLEX failed, error code " << status << ".\n";
753  }
754  this->env = nullptr;
755  }
756  }
757 
759  s.version("CplexInterface", 1);
760  s.unpack("CplexInterface::opts", opts_);
761  s.unpack("CplexInterface::qp_method", qp_method_);
762  s.unpack("CplexInterface::dump_to_file", dump_to_file_);
763  s.unpack("CplexInterface::tol", tol_);
764  s.unpack("CplexInterface::dep_check", dep_check_);
765  s.unpack("CplexInterface::warm_start", warm_start_);
766  s.unpack("CplexInterface::mip_start", mip_start_);
767  s.unpack("CplexInterface::mip", mip_);
768  s.unpack("CplexInterface::ctype", ctype_);
769  s.unpack("CplexInterface::sos_weights", sos_weights_);
770  s.unpack("CplexInterface::sos_beg", sos_beg_);
771  s.unpack("CplexInterface::sos_ind", sos_ind_);
772  s.unpack("CplexInterface::sos_types", sos_types_);
774  }
775 
778 
779  s.version("CplexInterface", 1);
780  s.pack("CplexInterface::opts", opts_);
781  s.pack("CplexInterface::qp_method", qp_method_);
782  s.pack("CplexInterface::dump_to_file", dump_to_file_);
783  s.pack("CplexInterface::tol", tol_);
784  s.pack("CplexInterface::dep_check", dep_check_);
785  s.pack("CplexInterface::warm_start", warm_start_);
786  s.pack("CplexInterface::mip_start", mip_start_);
787  s.pack("CplexInterface::mip", mip_);
788  s.pack("CplexInterface::ctype", ctype_);
789  s.pack("CplexInterface::sos_weights", sos_weights_);
790  s.pack("CplexInterface::sos_beg", sos_beg_);
791  s.pack("CplexInterface::sos_ind", sos_ind_);
792  s.pack("CplexInterface::sos_types", sos_types_);
794  }
795 
796 } // end namespace casadi
Internal class.
Definition: conic_impl.hpp:44
static const Options options_
Options.
Definition: conic_impl.hpp:83
casadi_int nx_
Number of decision variables.
Definition: conic_impl.hpp:169
int init_mem(void *mem) const override
Initalize memory block.
Definition: conic.cpp:451
casadi_int na_
The number of constraints (counting both equality and inequality) == A.size1()
Definition: conic_impl.hpp:172
Sparsity H_
Problem structure.
Definition: conic_impl.hpp:166
void init(const Dict &opts) override
Initialize.
Definition: conic.cpp:412
void deserialize(DeserializingStream &s, SDPToSOCPMem &m)
Definition: conic.cpp:729
std::vector< bool > discrete_
Options.
Definition: conic_impl.hpp:161
void serialize_body(SerializingStream &s) const override
Serialize an object without type information.
Definition: conic.cpp:738
Dict get_stats(void *mem) const override
Get all statistics.
Definition: conic.cpp:711
void sdp_to_socp_init(SDPToSOCPMem &mem) const
SDP to SOCP conversion initialization.
Definition: conic.cpp:580
void serialize(SerializingStream &s, const SDPToSOCPMem &m) const
Definition: conic.cpp:721
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::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.
Scalar * ptr()
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.
General sparsity class.
Definition: sparsity.hpp:106
casadi_int nnz() const
Get the number of (structural) non-zeros.
Definition: sparsity.cpp:148
casadi_int size2() const
Get the number of columns.
Definition: sparsity.cpp:128
const casadi_int * row() const
Get a reference to row-vector,.
Definition: sparsity.cpp:164
const casadi_int * colind() const
Get a reference to the colindex of all column element (see class description)
Definition: sparsity.cpp:168
The casadi namespace.
Definition: archiver.cpp:28
int param_by_name(CPXENVptr env, const std::string &name)
std::ostream & uerr()
void copy_vector(const std::vector< S > &s, std::vector< D > &d)
@ CONIC_UBA
dense, (nc x 1)
Definition: conic.hpp:181
@ CONIC_X0
dense, (n x 1)
Definition: conic.hpp:187
@ CONIC_A
The matrix A: sparse, (nc x n) - product with x must be dense.
Definition: conic.hpp:177
@ CONIC_G
The vector g: dense, (n x 1)
Definition: conic.hpp:175
@ CONIC_Q
The matrix Q: sparse symmetric, (np^2 x n)
Definition: conic.hpp:193
@ CONIC_LBA
dense, (nc x 1)
Definition: conic.hpp:179
@ CONIC_UBX
dense, (n x 1)
Definition: conic.hpp:185
@ CONIC_H
Definition: conic.hpp:173
@ CONIC_LBX
dense, (n x 1)
Definition: conic.hpp:183
@ CONIC_LAM_X0
dense
Definition: conic.hpp:189
@ CONIC_P
The matrix P: sparse symmetric, (np x np)
Definition: conic.hpp:195
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)
@ OT_INTVECTOR
@ OT_DOUBLEVECTORVECTOR
@ OT_INTVECTORVECTOR
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.
Definition: nlp_tools.hpp:79
GenericType::Dict Dict
C++ equivalent of Python's dict or MATLAB's struct.
const double inf
infinity
Definition: calculus.hpp:50
void CASADI_CONIC_CPLEX_EXPORT casadi_load_conic_cplex()
const double nan
Not a number.
Definition: calculus.hpp:53
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.
@ SOLVER_RET_INFEASIBLE
@ CONIC_X
The primal solution.
Definition: conic.hpp:201
@ CONIC_LAM_A
The dual solution corresponding to linear bounds.
Definition: conic.hpp:205
@ CONIC_COST
The optimal cost.
Definition: conic.hpp:203
@ CONIC_LAM_X
The dual solution corresponding to simple bounds.
Definition: conic.hpp:207
SDP to SOCP conversion memory.
Definition: conic_impl.hpp:178
std::vector< casadi_int > r
Definition: conic_impl.hpp:180
std::vector< casadi_int > map_P
Definition: conic_impl.hpp:190
bool is_warm
Indicates if we have to warm-start.
CplexMemory()
Constructor.
CPXENVptr env
CPLEX environment.
Options metadata for a class.
Definition: options.hpp:40