OR-Tools  8.1
cplex_interface.cc
Go to the documentation of this file.
1 // Copyright 2014 IBM Corporation
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // Initial version of this code was written by Daniel Junglas (IBM)
15 
16 #include <limits>
17 #include <memory>
18 
19 #include "absl/strings/str_format.h"
21 #include "ortools/base/logging.h"
22 #include "ortools/base/timer.h"
24 
25 #if defined(USE_CPLEX)
26 
27 extern "C" {
28 #include "ilcplex/cplexx.h"
29 // This is an undocumented function, setting the objective offset
30 // is not supported everywhere (for example it may not be exported if a
31 // model is written to a file), but it works in the cases we need here.
32 CPXLIBAPI int CPXPUBLIC CPXEsetobjoffset(CPXCENVptr, CPXLPptr, double);
33 }
34 
35 // In case we need to return a double but don't have a value for that
36 // we just return a NaN.
37 #if !defined(CPX_NAN)
38 #define CPX_NAN std::numeric_limits<double>::quiet_NaN()
39 #endif
40 
41 // The argument to this macro is the invocation of a CPXX function that
42 // returns a status. If the function returns non-zero the macro aborts
43 // the program with an appropriate error message.
44 #define CHECK_STATUS(s) \
45  do { \
46  int const status_ = s; \
47  CHECK_EQ(0, status_); \
48  } while (0)
49 
50 namespace operations_research {
51 
52 using std::unique_ptr;
53 
54 // For a model that is extracted to an instance of this class there is a
55 // 1:1 corresponence between MPVariable instances and CPLEX columns: the
56 // index of an extracted variable is the column index in the CPLEX model.
57 // Similiar for instances of MPConstraint: the index of the constraint in
58 // the model is the row index in the CPLEX model.
59 class CplexInterface : public MPSolverInterface {
60  public:
61  // NOTE: 'mip' specifies the type of the problem (either continuous or
62  // mixed integer. This type is fixed for the lifetime of the
63  // instance. There are no dynamic changes to the model type.
64  explicit CplexInterface(MPSolver* const solver, bool mip);
65  ~CplexInterface();
66 
67  // Sets the optimization direction (min/max).
68  virtual void SetOptimizationDirection(bool maximize);
69 
70  // ----- Solve -----
71  // Solve the problem using the parameter values specified.
72  virtual MPSolver::ResultStatus Solve(MPSolverParameters const& param);
73 
74  // ----- Model modifications and extraction -----
75  // Resets extracted model
76  virtual void Reset();
77 
78  virtual void SetVariableBounds(int var_index, double lb, double ub);
79  virtual void SetVariableInteger(int var_index, bool integer);
80  virtual void SetConstraintBounds(int row_index, double lb, double ub);
81 
82  virtual void AddRowConstraint(MPConstraint* const ct);
83  virtual void AddVariable(MPVariable* const var);
84  virtual void SetCoefficient(MPConstraint* const constraint,
85  MPVariable const* const variable,
86  double new_value, double old_value);
87 
88  // Clear a constraint from all its terms.
89  virtual void ClearConstraint(MPConstraint* const constraint);
90  // Change a coefficient in the linear objective
91  virtual void SetObjectiveCoefficient(MPVariable const* const variable,
92  double coefficient);
93  // Change the constant term in the linear objective.
94  virtual void SetObjectiveOffset(double value);
95  // Clear the objective from all its terms.
96  virtual void ClearObjective();
97 
98  // ------ Query statistics on the solution and the solve ------
99  // Number of simplex iterations
100  virtual int64 iterations() const;
101  // Number of branch-and-bound nodes. Only available for discrete problems.
102  virtual int64 nodes() const;
103  // Best objective bound. Only available for discrete problems.
104  virtual double best_objective_bound() const;
105 
106  // Returns the basis status of a row.
107  virtual MPSolver::BasisStatus row_status(int constraint_index) const;
108  // Returns the basis status of a column.
109  virtual MPSolver::BasisStatus column_status(int variable_index) const;
110 
111  // ----- Misc -----
112 
113  // Query problem type.
114  // Remember that problem type is a static property that is set
115  // in the constructor and never changed.
116  virtual bool IsContinuous() const { return IsLP(); }
117  virtual bool IsLP() const { return !mMip; }
118  virtual bool IsMIP() const { return mMip; }
119 
120  virtual void ExtractNewVariables();
121  virtual void ExtractNewConstraints();
122  virtual void ExtractObjective();
123 
124  virtual std::string SolverVersion() const;
125 
126  virtual void* underlying_solver() { return reinterpret_cast<void*>(mLp); }
127 
128  virtual double ComputeExactConditionNumber() const {
129  if (!IsContinuous()) {
130  LOG(DFATAL) << "ComputeExactConditionNumber not implemented for"
131  << " CPLEX_MIXED_INTEGER_PROGRAMMING";
132  return CPX_NAN;
133  }
134 
135  if (CheckSolutionIsSynchronized()) {
136  double kappa = CPX_NAN;
137  CHECK_STATUS(CPXXgetdblquality(mEnv, mLp, &kappa, CPX_EXACT_KAPPA));
138  return kappa;
139  }
140  LOG(DFATAL) << "Cannot get exact condition number without solution";
141  return CPX_NAN;
142  }
143 
144  protected:
145  // Set all parameters in the underlying solver.
146  virtual void SetParameters(MPSolverParameters const& param);
147  // Set each parameter in the underlying solver.
148  virtual void SetRelativeMipGap(double value);
149  virtual void SetPrimalTolerance(double value);
150  virtual void SetDualTolerance(double value);
151  virtual void SetPresolveMode(int value);
152  virtual void SetScalingMode(int value);
153  virtual void SetLpAlgorithm(int value);
154 
155  virtual bool ReadParameterFile(std::string const& filename);
156  virtual std::string ValidFileExtensionForParameterFile() const;
157 
158  private:
159  // Mark modeling object "out of sync". This implicitly invalidates
160  // solution information as well. It is the counterpart of
161  // MPSolverInterface::InvalidateSolutionSynchronization
162  void InvalidateModelSynchronization() {
163  mCstat = 0;
164  mRstat = 0;
165  sync_status_ = MUST_RELOAD;
166  }
167 
168  // Transform CPLEX basis status to MPSolver basis status.
169  static MPSolver::BasisStatus xformBasisStatus(int cplex_basis_status);
170 
171  private:
172  CPXLPptr mLp;
173  CPXENVptr mEnv;
174  bool const mMip;
175  // Incremental extraction.
176  // Without incremental extraction we have to re-extract the model every
177  // time we perform a solve. Due to the way the Reset() function is
178  // implemented, this will lose MIP start or basis information from a
179  // previous solve. On the other hand, if there is a significant changes
180  // to the model then just re-extracting everything is usually faster than
181  // keeping the low-level modeling object in sync with the high-level
182  // variables/constraints.
183  // Note that incremental extraction is particularly expensive in function
184  // ExtractNewVariables() since there we must scan _all_ old constraints
185  // and update them with respect to the new variables.
186  bool const supportIncrementalExtraction;
187 
188  // Use slow and immediate updates or try to do bulk updates.
189  // For many updates to the model we have the option to either perform
190  // the update immediately with a potentially slow operation or to
191  // just mark the low-level modeling object out of sync and re-extract
192  // the model later.
193  enum SlowUpdates {
194  SlowSetCoefficient = 0x0001,
195  SlowClearConstraint = 0x0002,
196  SlowSetObjectiveCoefficient = 0x0004,
197  SlowClearObjective = 0x0008,
198  SlowSetConstraintBounds = 0x0010,
199  SlowSetVariableInteger = 0x0020,
200  SlowSetVariableBounds = 0x0040,
201  SlowUpdatesAll = 0xffff
202  } const slowUpdates;
203  // CPLEX has no method to query the basis status of a single variable.
204  // Hence we query the status only once and cache the array. This is
205  // much faster in case the basis status of more than one row/column
206  // is required.
207  unique_ptr<int[]> mutable mCstat;
208  unique_ptr<int[]> mutable mRstat;
209 
210  // Setup the right-hand side of a constraint from its lower and upper bound.
211  static void MakeRhs(double lb, double ub, double& rhs, char& sense,
212  double& range);
213 };
214 
215 // Creates a LP/MIP instance.
216 CplexInterface::CplexInterface(MPSolver* const solver, bool mip)
217  : MPSolverInterface(solver),
218  mEnv(0),
219  mLp(0),
220  mMip(mip),
221  slowUpdates(static_cast<SlowUpdates>(SlowSetObjectiveCoefficient |
222  SlowClearObjective)),
223  supportIncrementalExtraction(false),
224  mCstat(),
225  mRstat() {
226  int status;
227 
228  mEnv = CPXXopenCPLEX(&status);
229  CHECK_STATUS(status);
230  DCHECK(mEnv != nullptr); // should not be NULL if status=0
231 
232  char const* name = solver_->name_.c_str();
233  mLp = CPXXcreateprob(mEnv, &status, name);
234  CHECK_STATUS(status);
235  DCHECK(mLp != nullptr); // should not be NULL if status=0
236 
237  CHECK_STATUS(CPXXchgobjsen(mEnv, mLp, maximize_ ? CPX_MAX : CPX_MIN));
238  if (mMip) CHECK_STATUS(CPXXchgprobtype(mEnv, mLp, CPXPROB_MILP));
239 }
240 
241 CplexInterface::~CplexInterface() {
242  CHECK_STATUS(CPXXfreeprob(mEnv, &mLp));
243  CHECK_STATUS(CPXXcloseCPLEX(&mEnv));
244 }
245 
246 std::string CplexInterface::SolverVersion() const {
247  // We prefer CPXXversionnumber() over CPXXversion() since the
248  // former will never pose any encoding issues.
249  int version = 0;
250  CHECK_STATUS(CPXXversionnumber(mEnv, &version));
251 
252  int const major = version / 1000000;
253  version -= major * 1000000;
254  int const release = version / 10000;
255  version -= release * 10000;
256  int const mod = version / 100;
257  version -= mod * 100;
258  int const fix = version;
259 
260  return absl::StrFormat("CPLEX library version %d.%02d.%02d.%02d", major,
261  release, mod, fix);
262 }
263 
264 // ------ Model modifications and extraction -----
265 
266 void CplexInterface::Reset() {
267  // Instead of explicitly clearing all modeling objects we
268  // just delete the problem object and allocate a new one.
269  CHECK_STATUS(CPXXfreeprob(mEnv, &mLp));
270 
271  int status;
272  const char* const name = solver_->name_.c_str();
273  mLp = CPXXcreateprob(mEnv, &status, name);
274  CHECK_STATUS(status);
275  DCHECK(mLp != nullptr); // should not be NULL if status=0
276 
277  CHECK_STATUS(CPXXchgobjsen(mEnv, mLp, maximize_ ? CPX_MAX : CPX_MIN));
278  if (mMip) CHECK_STATUS(CPXXchgprobtype(mEnv, mLp, CPXPROB_MILP));
279 
280  ResetExtractionInformation();
281  mCstat = 0;
282  mRstat = 0;
283 }
284 
285 void CplexInterface::SetOptimizationDirection(bool maximize) {
286  InvalidateSolutionSynchronization();
287  CPXXchgobjsen(mEnv, mLp, maximize ? CPX_MAX : CPX_MIN);
288 }
289 
290 void CplexInterface::SetVariableBounds(int var_index, double lb, double ub) {
291  InvalidateSolutionSynchronization();
292 
293  // Changing the bounds of a variable is fast. However, doing this for
294  // many variables may still be slow. So we don't perform the update by
295  // default. However, if we support incremental extraction
296  // (supportIncrementalExtraction is true) then we MUST perform the
297  // update here or we will lose it.
298 
299  if (!supportIncrementalExtraction && !(slowUpdates & SlowSetVariableBounds)) {
300  InvalidateModelSynchronization();
301  } else {
302  if (variable_is_extracted(var_index)) {
303  // Variable has already been extracted, so we must modify the
304  // modeling object.
305  DCHECK_LT(var_index, last_variable_index_);
306  char const lu[2] = {'L', 'U'};
307  double const bd[2] = {lb, ub};
308  CPXDIM const idx[2] = {var_index, var_index};
309  CHECK_STATUS(CPXXchgbds(mEnv, mLp, 2, idx, lu, bd));
310  } else {
311  // Variable is not yet extracted. It is sufficient to just mark
312  // the modeling object "out of sync"
313  InvalidateModelSynchronization();
314  }
315  }
316 }
317 
318 // Modifies integrality of an extracted variable.
319 void CplexInterface::SetVariableInteger(int var_index, bool integer) {
320  InvalidateSolutionSynchronization();
321 
322  // NOTE: The type of the model (continuous or mixed integer) is
323  // defined once and for all in the constructor. There are no
324  // dynamic changes to the model type.
325 
326  // Changing the type of a variable should be fast. Still, doing all
327  // updates in one big chunk right before solve() is usually faster.
328  // However, if we support incremental extraction
329  // (supportIncrementalExtraction is true) then we MUST change the
330  // type of extracted variables here.
331 
332  if (!supportIncrementalExtraction &&
333  !(slowUpdates && SlowSetVariableInteger)) {
334  InvalidateModelSynchronization();
335  } else {
336  if (mMip) {
337  if (variable_is_extracted(var_index)) {
338  // Variable is extracted. Change the type immediately.
339  // TODO: Should we check the current type and don't do anything
340  // in case the type does not change?
341  DCHECK_LE(var_index, CPXXgetnumcols(mEnv, mLp));
342  char const type = integer ? CPX_INTEGER : CPX_CONTINUOUS;
343  CHECK_STATUS(CPXXchgctype(mEnv, mLp, 1, &var_index, &type));
344  } else
345  InvalidateModelSynchronization();
346  } else {
347  LOG(DFATAL)
348  << "Attempt to change variable to integer in non-MIP problem!";
349  }
350  }
351 }
352 
353 // Setup the right-hand side of a constraint.
354 void CplexInterface::MakeRhs(double lb, double ub, double& rhs, char& sense,
355  double& range) {
356  if (lb == ub) {
357  // Both bounds are equal -> this is an equality constraint
358  rhs = lb;
359  range = 0.0;
360  sense = 'E';
361  } else if (lb > -CPX_INFBOUND && ub < CPX_INFBOUND) {
362  // Both bounds are finite -> this is a ranged constraint
363  // The value of a ranged constraint is allowed to be in
364  // [ rhs[i], rhs[i]+rngval[i] ]
365  // see also the reference documentation for CPXXnewrows()
366  if (ub < lb) {
367  // The bounds for the constraint are contradictory. CPLEX models
368  // a range constraint l <= ax <= u as
369  // ax = l + v
370  // where v is an auxiliary variable the range of which is controlled
371  // by l and u: if l < u then v in [0, u-l]
372  // else v in [u-l, 0]
373  // (the range is specified as the rngval[] argument to CPXXnewrows).
374  // Thus CPLEX cannot represent range constraints with contradictory
375  // bounds and we must error out here.
376  CHECK_STATUS(CPXERR_BAD_ARGUMENT);
377  }
378  rhs = lb;
379  range = ub - lb;
380  sense = 'R';
381  } else if (ub < CPX_INFBOUND ||
382  (std::abs(ub) == CPX_INFBOUND && std::abs(lb) > CPX_INFBOUND)) {
383  // Finite upper, infinite lower bound -> this is a <= constraint
384  rhs = ub;
385  range = 0.0;
386  sense = 'L';
387  } else if (lb > -CPX_INFBOUND ||
388  (std::abs(lb) == CPX_INFBOUND && std::abs(ub) > CPX_INFBOUND)) {
389  // Finite lower, infinite upper bound -> this is a >= constraint
390  rhs = lb;
391  range = 0.0;
392  sense = 'G';
393  } else {
394  // Lower and upper bound are both infinite.
395  // This is used for example in .mps files to specify alternate
396  // objective functions.
397  // Note that the case lb==ub was already handled above, so we just
398  // pick the bound with larger magnitude and create a constraint for it.
399  // Note that we replace the infinite bound by CPX_INFBOUND since
400  // bounds with larger magnitude may cause other CPLEX functions to
401  // fail (for example the export to LP files).
402  DCHECK_GT(std::abs(lb), CPX_INFBOUND);
403  DCHECK_GT(std::abs(ub), CPX_INFBOUND);
404  if (std::abs(lb) > std::abs(ub)) {
405  rhs = (lb < 0) ? -CPX_INFBOUND : CPX_INFBOUND;
406  sense = 'G';
407  } else {
408  rhs = (ub < 0) ? -CPX_INFBOUND : CPX_INFBOUND;
409  sense = 'L';
410  }
411  range = 0.0;
412  }
413 }
414 
415 void CplexInterface::SetConstraintBounds(int index, double lb, double ub) {
416  InvalidateSolutionSynchronization();
417 
418  // Changing rhs, sense, or range of a constraint is not too slow.
419  // Still, doing all the updates in one large operation is faster.
420  // Note however that if we do not want to re-extract the full model
421  // for each solve (supportIncrementalExtraction is true) then we MUST
422  // update the constraint here, otherwise we lose this update information.
423 
424  if (!supportIncrementalExtraction &&
425  !(slowUpdates & SlowSetConstraintBounds)) {
426  InvalidateModelSynchronization();
427  } else {
428  if (constraint_is_extracted(index)) {
429  // Constraint is already extracted, so we must update its bounds
430  // and its type.
431  DCHECK(mLp != NULL);
432  char sense;
433  double range, rhs;
434  MakeRhs(lb, ub, rhs, sense, range);
435  CHECK_STATUS(CPXXchgrhs(mEnv, mLp, 1, &index, &lb));
436  CHECK_STATUS(CPXXchgsense(mEnv, mLp, 1, &index, &sense));
437  CHECK_STATUS(CPXXchgrngval(mEnv, mLp, 1, &index, &range));
438  } else {
439  // Constraint is not yet extracted. It is sufficient to mark the
440  // modeling object as "out of sync"
441  InvalidateModelSynchronization();
442  }
443  }
444 }
445 
446 void CplexInterface::AddRowConstraint(MPConstraint* const ct) {
447  // This is currently only invoked when a new constraint is created,
448  // see MPSolver::MakeRowConstraint().
449  // At this point we only have the lower and upper bounds of the
450  // constraint. We could immediately call CPXXaddrows() here but it is
451  // usually much faster to handle the fully populated constraint in
452  // ExtractNewConstraints() right before the solve.
453  InvalidateModelSynchronization();
454 }
455 
456 void CplexInterface::AddVariable(MPVariable* const ct) {
457  // This is currently only invoked when a new variable is created,
458  // see MPSolver::MakeVar().
459  // At this point the variable does not appear in any constraints or
460  // the objective function. We could invoke CPXXaddcols() to immediately
461  // create the variable here but it is usually much faster to handle the
462  // fully setup variable in ExtractNewVariables() right before the solve.
463  InvalidateModelSynchronization();
464 }
465 
466 void CplexInterface::SetCoefficient(MPConstraint* const constraint,
467  MPVariable const* const variable,
468  double new_value, double) {
469  InvalidateSolutionSynchronization();
470 
471  // Changing a single coefficient in the matrix is potentially pretty
472  // slow since that coefficient has to be found in the sparse matrix
473  // representation. So by default we don't perform this update immediately
474  // but instead mark the low-level modeling object "out of sync".
475  // If we want to support incremental extraction then we MUST perform
476  // the modification immediately or we will lose it.
477 
478  if (!supportIncrementalExtraction && !(slowUpdates & SlowSetCoefficient)) {
479  InvalidateModelSynchronization();
480  } else {
481  int const row = constraint->index();
482  int const col = variable->index();
483  if (constraint_is_extracted(row) && variable_is_extracted(col)) {
484  // If row and column are both extracted then we can directly
485  // update the modeling object
486  DCHECK_LE(row, last_constraint_index_);
487  DCHECK_LE(col, last_variable_index_);
488  CHECK_STATUS(CPXXchgcoef(mEnv, mLp, row, col, new_value));
489  } else {
490  // If either row or column is not yet extracted then we can
491  // defer the update to ExtractModel()
492  InvalidateModelSynchronization();
493  }
494  }
495 }
496 
497 void CplexInterface::ClearConstraint(MPConstraint* const constraint) {
498  CPXDIM const row = constraint->index();
499  if (!constraint_is_extracted(row))
500  // There is nothing to do if the constraint was not even extracted.
501  return;
502 
503  // Clearing a constraint means setting all coefficients in the corresponding
504  // row to 0 (we cannot just delete the row since that would renumber all
505  // the constraints/rows after it).
506  // Modifying coefficients in the matrix is potentially pretty expensive
507  // since they must be found in the sparse matrix representation. That is
508  // why by default we do not modify the coefficients here but only mark
509  // the low-level modeling object "out of sync".
510 
511  if (!(slowUpdates & SlowClearConstraint)) {
512  InvalidateModelSynchronization();
513  } else {
514  InvalidateSolutionSynchronization();
515 
516  CPXDIM const len = constraint->coefficients_.size();
517  unique_ptr<CPXDIM[]> rowind(new CPXDIM[len]);
518  unique_ptr<CPXDIM[]> colind(new CPXDIM[len]);
519  unique_ptr<double[]> val(new double[len]);
520  CPXDIM j = 0;
521  const auto& coeffs = constraint->coefficients_;
522  for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
523  CPXDIM const col = it->first->index();
524  if (variable_is_extracted(col)) {
525  rowind[j] = row;
526  colind[j] = col;
527  val[j] = 0.0;
528  ++j;
529  }
530  }
531  if (j)
532  CHECK_STATUS(
533  CPXXchgcoeflist(mEnv, mLp, j, rowind.get(), colind.get(), val.get()));
534  }
535 }
536 
537 void CplexInterface::SetObjectiveCoefficient(MPVariable const* const variable,
538  double coefficient) {
539  CPXDIM const col = variable->index();
540  if (!variable_is_extracted(col))
541  // Nothing to do if variable was not even extracted
542  return;
543 
544  InvalidateSolutionSynchronization();
545 
546  // The objective function is stored as a dense vector, so updating a
547  // single coefficient is O(1). So by default we update the low-level
548  // modeling object here.
549  // If we support incremental extraction then we have no choice but to
550  // perform the update immediately.
551 
552  if (supportIncrementalExtraction ||
553  (slowUpdates & SlowSetObjectiveCoefficient)) {
554  CHECK_STATUS(CPXXchgobj(mEnv, mLp, 1, &col, &coefficient));
555  } else
556  InvalidateModelSynchronization();
557 }
558 
559 void CplexInterface::SetObjectiveOffset(double value) {
560  // Changing the objective offset is O(1), so we always do it immediately.
561  InvalidateSolutionSynchronization();
562  CHECK_STATUS(CPXEsetobjoffset(mEnv, mLp, value));
563 }
564 
565 void CplexInterface::ClearObjective() {
566  InvalidateSolutionSynchronization();
567 
568  // Since the objective function is stored as a dense vector updating
569  // it is O(n), so we usually perform the update immediately.
570  // If we want to support incremental extraction then we have no choice
571  // but to perform the update immediately.
572 
573  if (supportIncrementalExtraction || (slowUpdates & SlowClearObjective)) {
574  CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
575  unique_ptr<CPXDIM[]> ind(new CPXDIM[cols]);
576  unique_ptr<double[]> zero(new double[cols]);
577  CPXDIM j = 0;
578  const auto& coeffs = solver_->objective_->coefficients_;
579  for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
580  CPXDIM const idx = it->first->index();
581  // We only need to reset variables that have been extracted.
582  if (variable_is_extracted(idx)) {
583  DCHECK_LT(idx, cols);
584  ind[j] = idx;
585  zero[j] = 0.0;
586  ++j;
587  }
588  }
589  if (j > 0) CHECK_STATUS(CPXXchgobj(mEnv, mLp, j, ind.get(), zero.get()));
590  CHECK_STATUS(CPXEsetobjoffset(mEnv, mLp, 0.0));
591  } else
592  InvalidateModelSynchronization();
593 }
594 
595 // ------ Query statistics on the solution and the solve ------
596 
597 int64 CplexInterface::iterations() const {
598  int iter;
599  if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfIterations;
600  if (mMip)
601  return static_cast<int64>(CPXXgetmipitcnt(mEnv, mLp));
602  else
603  return static_cast<int64>(CPXXgetitcnt(mEnv, mLp));
604 }
605 
606 int64 CplexInterface::nodes() const {
607  if (mMip) {
608  if (!CheckSolutionIsSynchronized()) return kUnknownNumberOfNodes;
609  return static_cast<int64>(CPXXgetnodecnt(mEnv, mLp));
610  } else {
611  LOG(DFATAL) << "Number of nodes only available for discrete problems";
612  return kUnknownNumberOfNodes;
613  }
614 }
615 
616 // Returns the best objective bound. Only available for discrete problems.
617 double CplexInterface::best_objective_bound() const {
618  if (mMip) {
619  if (!CheckSolutionIsSynchronized() || !CheckBestObjectiveBoundExists())
620  // trivial_worst_objective_bound() returns sense*infinity,
621  // that is meaningful even for infeasible problems
622  return trivial_worst_objective_bound();
623  if (solver_->variables_.size() == 0 && solver_->constraints_.size() == 0) {
624  // For an empty model the best objective bound is just the offset.
625  return solver_->Objective().offset();
626  } else {
627  double value = CPX_NAN;
628  CHECK_STATUS(CPXXgetbestobjval(mEnv, mLp, &value));
629  return value;
630  }
631  } else {
632  LOG(DFATAL) << "Best objective bound only available for discrete problems";
633  return trivial_worst_objective_bound();
634  }
635 }
636 
637 // Transform a CPLEX basis status to an MPSolver basis status.
638 MPSolver::BasisStatus CplexInterface::xformBasisStatus(int cplex_basis_status) {
639  switch (cplex_basis_status) {
640  case CPX_AT_LOWER:
642  case CPX_BASIC:
643  return MPSolver::BASIC;
644  case CPX_AT_UPPER:
646  case CPX_FREE_SUPER:
647  return MPSolver::FREE;
648  default:
649  LOG(DFATAL) << "Unknown CPLEX basis status";
650  return MPSolver::FREE;
651  }
652 }
653 
654 // Returns the basis status of a row.
655 MPSolver::BasisStatus CplexInterface::row_status(int constraint_index) const {
656  if (mMip) {
657  LOG(FATAL) << "Basis status only available for continuous problems";
658  return MPSolver::FREE;
659  }
660 
661  if (CheckSolutionIsSynchronized()) {
662  if (!mRstat) {
663  CPXDIM const rows = CPXXgetnumrows(mEnv, mLp);
664  unique_ptr<int[]> data(new int[rows]);
665  mRstat.swap(data);
666  CHECK_STATUS(CPXXgetbase(mEnv, mLp, 0, mRstat.get()));
667  }
668  } else
669  mRstat = 0;
670 
671  if (mRstat)
672  return xformBasisStatus(mRstat[constraint_index]);
673  else {
674  LOG(FATAL) << "Row basis status not available";
675  return MPSolver::FREE;
676  }
677 }
678 
679 // Returns the basis status of a column.
680 MPSolver::BasisStatus CplexInterface::column_status(int variable_index) const {
681  if (mMip) {
682  LOG(FATAL) << "Basis status only available for continuous problems";
683  return MPSolver::FREE;
684  }
685 
686  if (CheckSolutionIsSynchronized()) {
687  if (!mCstat) {
688  CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
689  unique_ptr<int[]> data(new int[cols]);
690  mCstat.swap(data);
691  CHECK_STATUS(CPXXgetbase(mEnv, mLp, mCstat.get(), 0));
692  }
693  } else
694  mCstat = 0;
695 
696  if (mCstat)
697  return xformBasisStatus(mCstat[variable_index]);
698  else {
699  LOG(FATAL) << "Column basis status not available";
700  return MPSolver::FREE;
701  }
702 }
703 
704 // Extract all variables that have not yet been extracted.
705 void CplexInterface::ExtractNewVariables() {
706  // NOTE: The code assumes that a linear expression can never contain
707  // non-zero duplicates.
708 
709  InvalidateSolutionSynchronization();
710 
711  if (!supportIncrementalExtraction) {
712  // Without incremental extraction ExtractModel() is always called
713  // to extract the full model.
714  CHECK(last_variable_index_ == 0 ||
715  last_variable_index_ == solver_->variables_.size());
716  CHECK(last_constraint_index_ == 0 ||
717  last_constraint_index_ == solver_->constraints_.size());
718  }
719 
720  int const last_extracted = last_variable_index_;
721  int const var_count = solver_->variables_.size();
722  CPXDIM newcols = var_count - last_extracted;
723  if (newcols > 0) {
724  // There are non-extracted variables. Extract them now.
725 
726  unique_ptr<double[]> obj(new double[newcols]);
727  unique_ptr<double[]> lb(new double[newcols]);
728  unique_ptr<double[]> ub(new double[newcols]);
729  unique_ptr<char[]> ctype(new char[newcols]);
730  unique_ptr<const char*[]> colname(new const char*[newcols]);
731 
732  bool have_names = false;
733  for (int j = 0, varidx = last_extracted; j < newcols; ++j, ++varidx) {
734  MPVariable const* const var = solver_->variables_[varidx];
735  lb[j] = var->lb();
736  ub[j] = var->ub();
737  ctype[j] = var->integer() ? CPX_INTEGER : CPX_CONTINUOUS;
738  colname[j] = var->name().empty() ? 0 : var->name().c_str();
739  have_names = have_names || var->name().empty();
740  obj[j] = solver_->objective_->GetCoefficient(var);
741  }
742 
743  // Arrays for modifying the problem are setup. Update the index
744  // of variables that will get extracted now. Updating indices
745  // _before_ the actual extraction makes things much simpler in
746  // case we support incremental extraction.
747  // In case of error we just reset the indeces.
748  std::vector<MPVariable*> const& variables = solver_->variables();
749  for (int j = last_extracted; j < var_count; ++j) {
750  CHECK(!variable_is_extracted(variables[j]->index()));
751  set_variable_as_extracted(variables[j]->index(), true);
752  }
753 
754  try {
755  bool use_newcols = true;
756 
757  if (supportIncrementalExtraction) {
758  // If we support incremental extraction then we must
759  // update existing constraints with the new variables.
760  // To do that we use CPXXaddcols() to actually create the
761  // variables. This is supposed to be faster than combining
762  // CPXXnewcols() and CPXXchgcoeflist().
763 
764  // For each column count the size of the intersection with
765  // existing constraints.
766  unique_ptr<CPXDIM[]> collen(new CPXDIM[newcols]);
767  for (CPXDIM j = 0; j < newcols; ++j) collen[j] = 0;
768  CPXNNZ nonzeros = 0;
769  // TODO: Use a bitarray to flag the constraints that actually
770  // intersect new variables?
771  for (int i = 0; i < last_constraint_index_; ++i) {
772  MPConstraint const* const ct = solver_->constraints_[i];
773  CHECK(constraint_is_extracted(ct->index()));
774  const auto& coeffs = ct->coefficients_;
775  for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
776  int const idx = it->first->index();
777  if (variable_is_extracted(idx) && idx > last_variable_index_) {
778  collen[idx - last_variable_index_]++;
779  ++nonzeros;
780  }
781  }
782  }
783 
784  if (nonzeros > 0) {
785  // At least one of the new variables did intersect with an
786  // old constraint. We have to create the new columns via
787  // CPXXaddcols().
788  use_newcols = false;
789  unique_ptr<CPXNNZ[]> begin(new CPXNNZ[newcols + 2]);
790  unique_ptr<CPXDIM[]> cmatind(new CPXDIM[nonzeros]);
791  unique_ptr<double[]> cmatval(new double[nonzeros]);
792 
793  // Here is how cmatbeg[] is setup:
794  // - it is initialized as
795  // [ 0, 0, collen[0], collen[0]+collen[1], ... ]
796  // so that cmatbeg[j+1] tells us where in cmatind[] and
797  // cmatval[] we need to put the next nonzero for column
798  // j
799  // - after nonzeros have been setup the array looks like
800  // [ 0, collen[0], collen[0]+collen[1], ... ]
801  // so that it is the correct input argument for CPXXaddcols
802  CPXNNZ* cmatbeg = begin.get();
803  cmatbeg[0] = 0;
804  cmatbeg[1] = 0;
805  ++cmatbeg;
806  for (CPXDIM j = 0; j < newcols; ++j)
807  cmatbeg[j + 1] = cmatbeg[j] + collen[j];
808 
809  for (int i = 0; i < last_constraint_index_; ++i) {
810  MPConstraint const* const ct = solver_->constraints_[i];
811  CPXDIM const row = ct->index();
812  const auto& coeffs = ct->coefficients_;
813  for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
814  int const idx = it->first->index();
815  if (variable_is_extracted(idx) && idx > last_variable_index_) {
816  CPXNNZ const nz = cmatbeg[idx]++;
817  cmatind[nz] = idx;
818  cmatval[nz] = it->second;
819  }
820  }
821  }
822  --cmatbeg;
823  CHECK_STATUS(CPXXaddcols(mEnv, mLp, newcols, nonzeros, obj.get(),
824  cmatbeg, cmatind.get(), cmatval.get(),
825  lb.get(), ub.get(),
826  have_names ? colname.get() : 0));
827  }
828  }
829  if (use_newcols) {
830  // Either incremental extraction is not supported or none of
831  // the new variables did intersect an existing constraint.
832  // We can just use CPXXnewcols() to create the new variables.
833  CHECK_STATUS(CPXXnewcols(mEnv, mLp, newcols, obj.get(), lb.get(),
834  ub.get(), mMip ? ctype.get() : 0,
835  have_names ? colname.get() : 0));
836  } else {
837  // Incremental extraction: we must update the ctype of the
838  // newly created variables (CPXXaddcols() does not allow
839  // specifying the ctype)
840  if (mMip) {
841  // Query the actual number of columns in case we did not
842  // manage to extract all columns.
843  int const cols = CPXXgetnumcols(mEnv, mLp);
844  unique_ptr<CPXDIM[]> ind(new CPXDIM[newcols]);
845  for (int j = last_extracted; j < cols; ++j)
846  ind[j - last_extracted] = j;
847  CHECK_STATUS(CPXXchgctype(mEnv, mLp, cols - last_extracted, ind.get(),
848  ctype.get()));
849  }
850  }
851  } catch (...) {
852  // Undo all changes in case of error.
853  CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
854  if (cols > last_extracted)
855  (void)CPXXdelcols(mEnv, mLp, last_extracted, cols - 1);
856  std::vector<MPVariable*> const& variables = solver_->variables();
857  int const size = variables.size();
858  for (int j = last_extracted; j < size; ++j)
859  set_variable_as_extracted(j, false);
860  throw;
861  }
862  }
863 }
864 
865 // Extract constraints that have not yet been extracted.
866 void CplexInterface::ExtractNewConstraints() {
867  // NOTE: The code assumes that a linear expression can never contain
868  // non-zero duplicates.
869 
870  if (!supportIncrementalExtraction) {
871  // Without incremental extraction ExtractModel() is always called
872  // to extract the full model.
873  CHECK(last_variable_index_ == 0 ||
874  last_variable_index_ == solver_->variables_.size());
875  CHECK(last_constraint_index_ == 0 ||
876  last_constraint_index_ == solver_->constraints_.size());
877  }
878 
879  CPXDIM const offset = last_constraint_index_;
880  CPXDIM const total = solver_->constraints_.size();
881 
882  if (total > offset) {
883  // There are constraints that are not yet extracted.
884 
885  InvalidateSolutionSynchronization();
886 
887  CPXDIM newCons = total - offset;
888  CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
889  DCHECK_EQ(last_variable_index_, cols);
890  CPXDIM const chunk = 10; // max number of rows to add in one shot
891 
892  // Update indices of new constraints _before_ actually extracting
893  // them. In case of error we will just reset the indices.
894  for (CPXDIM c = offset; c < total; ++c)
895  set_constraint_as_extracted(c, true);
896 
897  try {
898  unique_ptr<CPXDIM[]> rmatind(new CPXDIM[cols]);
899  unique_ptr<double[]> rmatval(new double[cols]);
900  unique_ptr<CPXNNZ[]> rmatbeg(new CPXNNZ[chunk]);
901  unique_ptr<char[]> sense(new char[chunk]);
902  unique_ptr<double[]> rhs(new double[chunk]);
903  unique_ptr<char const*[]> name(new char const*[chunk]);
904  unique_ptr<double[]> rngval(new double[chunk]);
905  unique_ptr<CPXDIM[]> rngind(new CPXDIM[chunk]);
906  bool haveRanges = false;
907 
908  // Loop over the new constraints, collecting rows for up to
909  // CHUNK constraints into the arrays so that adding constraints
910  // is faster.
911  for (CPXDIM c = 0; c < newCons; /* nothing */) {
912  // Collect up to CHUNK constraints into the arrays.
913  CPXDIM nextRow = 0;
914  CPXNNZ nextNz = 0;
915  for (/* nothing */; c < newCons && nextRow < chunk; ++c, ++nextRow) {
916  MPConstraint const* const ct = solver_->constraints_[offset + c];
917 
918  // Stop if there is not enough room in the arrays
919  // to add the current constraint.
920  if (nextNz + ct->coefficients_.size() > cols) {
921  DCHECK_GT(nextRow, 0);
922  break;
923  }
924 
925  // Setup right-hand side of constraint.
926  MakeRhs(ct->lb(), ct->ub(), rhs[nextRow], sense[nextRow],
927  rngval[nextRow]);
928  haveRanges = haveRanges || (rngval[nextRow] != 0.0);
929  rngind[nextRow] = offset + c;
930 
931  // Setup left-hand side of constraint.
932  rmatbeg[nextRow] = nextNz;
933  const auto& coeffs = ct->coefficients_;
934  for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
935  CPXDIM const idx = it->first->index();
936  if (variable_is_extracted(idx)) {
937  DCHECK_LT(nextNz, cols);
938  DCHECK_LT(idx, cols);
939  rmatind[nextNz] = idx;
940  rmatval[nextNz] = it->second;
941  ++nextNz;
942  }
943  }
944 
945  // Finally the name of the constraint.
946  name[nextRow] = ct->name().empty() ? 0 : ct->name().c_str();
947  }
948  if (nextRow > 0) {
949  CHECK_STATUS(CPXXaddrows(mEnv, mLp, 0, nextRow, nextNz, rhs.get(),
950  sense.get(), rmatbeg.get(), rmatind.get(),
951  rmatval.get(), 0, name.get()));
952  if (haveRanges) {
953  CHECK_STATUS(
954  CPXXchgrngval(mEnv, mLp, nextRow, rngind.get(), rngval.get()));
955  }
956  }
957  }
958  } catch (...) {
959  // Undo all changes in case of error.
960  CPXDIM const rows = CPXXgetnumrows(mEnv, mLp);
961  if (rows > offset) (void)CPXXdelrows(mEnv, mLp, offset, rows - 1);
962  std::vector<MPConstraint*> const& constraints = solver_->constraints();
963  int const size = constraints.size();
964  for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
965  throw;
966  }
967  }
968 }
969 
970 // Extract the objective function.
971 void CplexInterface::ExtractObjective() {
972  // NOTE: The code assumes that the objective expression does not contain
973  // any non-zero duplicates.
974 
975  CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
976  DCHECK_EQ(last_variable_index_, cols);
977 
978  unique_ptr<CPXDIM[]> ind(new CPXDIM[cols]);
979  unique_ptr<double[]> val(new double[cols]);
980  for (CPXDIM j = 0; j < cols; ++j) {
981  ind[j] = j;
982  val[j] = 0.0;
983  }
984 
985  const auto& coeffs = solver_->objective_->coefficients_;
986  for (auto it = coeffs.begin(); it != coeffs.end(); ++it) {
987  CPXDIM const idx = it->first->index();
988  if (variable_is_extracted(idx)) {
989  DCHECK_LT(idx, cols);
990  val[idx] = it->second;
991  }
992  }
993 
994  CHECK_STATUS(CPXXchgobj(mEnv, mLp, cols, ind.get(), val.get()));
995  CHECK_STATUS(CPXEsetobjoffset(mEnv, mLp, solver_->Objective().offset()));
996 }
997 
998 // ------ Parameters -----
999 
1000 void CplexInterface::SetParameters(const MPSolverParameters& param) {
1001  SetCommonParameters(param);
1002  if (mMip) SetMIPParameters(param);
1003 }
1004 
1005 void CplexInterface::SetRelativeMipGap(double value) {
1006  if (mMip) {
1007  CHECK_STATUS(CPXXsetdblparam(mEnv, CPX_PARAM_EPGAP, value));
1008  } else {
1009  LOG(WARNING) << "The relative MIP gap is only available "
1010  << "for discrete problems.";
1011  }
1012 }
1013 
1014 void CplexInterface::SetPrimalTolerance(double value) {
1015  CHECK_STATUS(CPXXsetdblparam(mEnv, CPX_PARAM_EPRHS, value));
1016 }
1017 
1018 void CplexInterface::SetDualTolerance(double value) {
1019  CHECK_STATUS(CPXXsetdblparam(mEnv, CPX_PARAM_EPOPT, value));
1020 }
1021 
1022 void CplexInterface::SetPresolveMode(int value) {
1023  MPSolverParameters::PresolveValues const presolve =
1025 
1026  switch (presolve) {
1028  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_PREIND, CPX_OFF));
1029  return;
1031  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_PREIND, CPX_ON));
1032  return;
1033  }
1034  SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, value);
1035 }
1036 
1037 // Sets the scaling mode.
1038 void CplexInterface::SetScalingMode(int value) {
1039  MPSolverParameters::ScalingValues const scaling =
1041 
1042  switch (scaling) {
1044  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_SCAIND, -1));
1045  break;
1047  // TODO: 0 is equilibrium scaling (the default), CPLEX also supports
1048  // 1 aggressive scaling
1049  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_SCAIND, 0));
1050  break;
1051  }
1052 }
1053 
1054 // Sets the LP algorithm : primal, dual or barrier. Note that CPLEX offers other
1055 // LP algorithm (e.g. network) and automatic selection
1056 void CplexInterface::SetLpAlgorithm(int value) {
1057  MPSolverParameters::LpAlgorithmValues const algorithm =
1059 
1060  int alg = CPX_ALG_NONE;
1061 
1062  switch (algorithm) {
1064  alg = CPX_ALG_DUAL;
1065  break;
1067  alg = CPX_ALG_PRIMAL;
1068  break;
1070  alg = CPX_ALG_BARRIER;
1071  break;
1072  }
1073 
1074  if (alg == CPX_ALG_NONE)
1075  SetIntegerParamToUnsupportedValue(MPSolverParameters::LP_ALGORITHM, value);
1076  else {
1077  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_LPMETHOD, alg));
1078  if (mMip) {
1079  // For MIP we have to change two more parameters to specify the
1080  // algorithm that is used to solve LP relaxations.
1081  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_STARTALG, alg));
1082  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_SUBALG, alg));
1083  }
1084  }
1085 }
1086 
1087 bool CplexInterface::ReadParameterFile(std::string const& filename) {
1088  // Return true on success and false on error.
1089  return CPXXreadcopyparam(mEnv, filename.c_str()) == 0;
1090 }
1091 
1092 std::string CplexInterface::ValidFileExtensionForParameterFile() const {
1093  return ".prm";
1094 }
1095 
1096 MPSolver::ResultStatus CplexInterface::Solve(MPSolverParameters const& param) {
1097  int status;
1098 
1099  // Delete chached information
1100  mCstat = 0;
1101  mRstat = 0;
1102 
1103  WallTimer timer;
1104  timer.Start();
1105 
1106  // Set incrementality
1109  param.GetIntegerParam(MPSolverParameters::INCREMENTALITY));
1110  switch (inc) {
1112  Reset(); /* This should not be required but re-extracting everything
1113  * may be faster, so we do it. */
1114  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_ADVIND, 0));
1115  break;
1117  CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_ADVIND, 2));
1118  break;
1119  }
1120 
1121  // Extract the model to be solved.
1122  // If we don't support incremental extraction and the low-level modeling
1123  // is out of sync then we have to re-extract everything. Note that this
1124  // will lose MIP starts or advanced basis information from a previous
1125  // solve.
1126  if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
1127  ExtractModel();
1128  VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
1129 
1130  // Set log level.
1131  CHECK_STATUS(
1132  CPXXsetintparam(mEnv, CPX_PARAM_SCRIND, quiet() ? CPX_OFF : CPX_ON));
1133 
1134  // Set parameters.
1135  // NOTE: We must invoke SetSolverSpecificParametersAsString() _first_.
1136  // Its current implementation invokes ReadParameterFile() which in
1137  // turn invokes CPXXreadcopyparam(). The latter will _overwrite_
1138  // all current parameter settings in the environment.
1139  solver_->SetSolverSpecificParametersAsString(
1140  solver_->solver_specific_parameter_string_);
1141  SetParameters(param);
1142  if (solver_->time_limit()) {
1143  VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1144  CHECK_STATUS(
1145  CPXXsetdblparam(mEnv, CPX_PARAM_TILIM, solver_->time_limit() * 1e-3));
1146  }
1147 
1148  // Solve.
1149  // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
1150  // still allow us to query useful information.
1151  timer.Restart();
1152  if (mMip) {
1153  status = CPXXmipopt(mEnv, mLp);
1154  } else {
1155  status = CPXXlpopt(mEnv, mLp);
1156  }
1157 
1158  // Disable screen output right after solve
1159  (void)CPXXsetintparam(mEnv, CPX_PARAM_SCRIND, CPX_OFF);
1160 
1161  if (status) {
1162  VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
1163  // NOTE: We do not return immediately since there may be information
1164  // to grab (for example an incumbent)
1165  } else {
1166  VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
1167  }
1168 
1169  int const cpxstat = CPXXgetstat(mEnv, mLp);
1170  VLOG(1) << absl::StrFormat("CPLEX solution status %d.", cpxstat);
1171 
1172  // Figure out what solution we have.
1173  int solnmethod, solntype, pfeas, dfeas;
1174  CHECK_STATUS(CPXXsolninfo(mEnv, mLp, &solnmethod, &solntype, &pfeas, &dfeas));
1175  bool const feasible = pfeas != 0;
1176 
1177  // Get problem dimensions for solution queries below.
1178  CPXDIM const rows = CPXXgetnumrows(mEnv, mLp);
1179  CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
1180  DCHECK_EQ(rows, solver_->constraints_.size());
1181  DCHECK_EQ(cols, solver_->variables_.size());
1182 
1183  // Capture objective function value.
1184  objective_value_ = CPX_NAN;
1185  if (pfeas) CHECK_STATUS(CPXXgetobjval(mEnv, mLp, &objective_value_));
1186  VLOG(1) << "objective = " << objective_value_;
1187 
1188  // Capture primal and dual solutions
1189  if (mMip) {
1190  // If there is a primal feasible solution then capture it.
1191  if (pfeas) {
1192  if (cols > 0) {
1193  unique_ptr<double[]> x(new double[cols]);
1194  CHECK_STATUS(CPXXgetx(mEnv, mLp, x.get(), 0, cols - 1));
1195  for (int i = 0; i < solver_->variables_.size(); ++i) {
1196  MPVariable* const var = solver_->variables_[i];
1197  var->set_solution_value(x[i]);
1198  VLOG(3) << var->name() << ": value =" << x[i];
1199  }
1200  }
1201  } else {
1202  for (int i = 0; i < solver_->variables_.size(); ++i)
1203  solver_->variables_[i]->set_solution_value(CPX_NAN);
1204  }
1205 
1206  // MIP does not have duals
1207  for (int i = 0; i < solver_->variables_.size(); ++i)
1208  solver_->variables_[i]->set_reduced_cost(CPX_NAN);
1209  for (int i = 0; i < solver_->constraints_.size(); ++i)
1210  solver_->constraints_[i]->set_dual_value(CPX_NAN);
1211  } else {
1212  // Continuous problem.
1213  if (cols > 0) {
1214  unique_ptr<double[]> x(new double[cols]);
1215  unique_ptr<double[]> dj(new double[cols]);
1216  if (pfeas) CHECK_STATUS(CPXXgetx(mEnv, mLp, x.get(), 0, cols - 1));
1217  if (dfeas) CHECK_STATUS(CPXXgetdj(mEnv, mLp, dj.get(), 0, cols - 1));
1218  for (int i = 0; i < solver_->variables_.size(); ++i) {
1219  MPVariable* const var = solver_->variables_[i];
1220  var->set_solution_value(x[i]);
1221  bool value = false, dual = false;
1222 
1223  if (pfeas) {
1224  var->set_solution_value(x[i]);
1225  value = true;
1226  } else
1227  var->set_solution_value(CPX_NAN);
1228  if (dfeas) {
1229  var->set_reduced_cost(dj[i]);
1230  dual = true;
1231  } else
1232  var->set_reduced_cost(CPX_NAN);
1233  VLOG(3) << var->name() << ":"
1234  << (value ? absl::StrFormat(" value = %f", x[i]) : "")
1235  << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : "");
1236  }
1237  }
1238 
1239  if (rows > 0) {
1240  unique_ptr<double[]> pi(new double[rows]);
1241  if (dfeas) CHECK_STATUS(CPXXgetpi(mEnv, mLp, pi.get(), 0, rows - 1));
1242  for (int i = 0; i < solver_->constraints_.size(); ++i) {
1243  MPConstraint* const ct = solver_->constraints_[i];
1244  bool dual = false;
1245  if (dfeas) {
1246  ct->set_dual_value(pi[i]);
1247  dual = true;
1248  } else
1249  ct->set_dual_value(CPX_NAN);
1250  VLOG(4) << "row " << ct->index() << ":"
1251  << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
1252  }
1253  }
1254  }
1255 
1256  // Map CPLEX status to more generic solution status in MPSolver
1257  switch (cpxstat) {
1258  case CPX_STAT_OPTIMAL:
1259  case CPXMIP_OPTIMAL:
1260  result_status_ = MPSolver::OPTIMAL;
1261  break;
1262  case CPXMIP_OPTIMAL_TOL:
1263  // To be consistent with the other solvers.
1264  result_status_ = MPSolver::OPTIMAL;
1265  break;
1266  case CPX_STAT_INFEASIBLE:
1267  case CPXMIP_INFEASIBLE:
1268  result_status_ = MPSolver::INFEASIBLE;
1269  break;
1270  case CPX_STAT_UNBOUNDED:
1271  case CPXMIP_UNBOUNDED:
1272  result_status_ = MPSolver::UNBOUNDED;
1273  break;
1274  case CPX_STAT_INForUNBD:
1275  case CPXMIP_INForUNBD:
1276  result_status_ = MPSolver::INFEASIBLE;
1277  break;
1278  default:
1279  result_status_ = feasible ? MPSolver::FEASIBLE : MPSolver::ABNORMAL;
1280  break;
1281  }
1282 
1283  sync_status_ = SOLUTION_SYNCHRONIZED;
1284  return result_status_;
1285 }
1286 
1287 MPSolverInterface* BuildCplexInterface(bool mip, MPSolver* const solver) {
1288  return new CplexInterface(solver, mip);
1289 }
1290 
1291 } // namespace operations_research
1292 #endif // #if defined(USE_CPLEX)
var
IntVar * var
Definition: expr_array.cc:1858
integral_types.h
DCHECK_LT
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:888
VLOG
#define VLOG(verboselevel)
Definition: base/logging.h:978
operations_research::PropagationBaseObject::name
virtual std::string name() const
Object naming.
Definition: constraint_solver.cc:2505
LOG
#define LOG(severity)
Definition: base/logging.h:420
WallTimer::Get
double Get() const
Definition: timer.h:45
operations_research::MPSolver::OPTIMAL
@ OPTIMAL
optimal.
Definition: linear_solver.h:429
operations_research::MPSolverParameters::ScalingValues
ScalingValues
Advanced usage: Scaling options.
Definition: linear_solver.h:1421
operations_research::MPSolverParameters::LP_ALGORITHM
@ LP_ALGORITHM
Algorithm to solve linear programs.
Definition: linear_solver.h:1383
FATAL
const int FATAL
Definition: log_severity.h:32
operations_research::MPSolver::UNBOUNDED
@ UNBOUNDED
proven unbounded.
Definition: linear_solver.h:435
logging.h
DCHECK_GT
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:890
value
int64 value
Definition: demon_profiler.cc:43
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
WARNING
const int WARNING
Definition: log_severity.h:31
operations_research::MPSolverParameters::SCALING_OFF
@ SCALING_OFF
Scaling is off.
Definition: linear_solver.h:1423
operations_research::MPSolver::ABNORMAL
@ ABNORMAL
abnormal, i.e., error of some kind.
Definition: linear_solver.h:437
WallTimer::Restart
void Restart()
Definition: timer.h:35
int64
int64_t int64
Definition: integral_types.h:34
index
int index
Definition: pack.cc:508
operations_research::MPSolverParameters::PresolveValues
PresolveValues
For each categorical parameter, enumeration of possible values.
Definition: linear_solver.h:1391
operations_research::MPSolverParameters::PRESOLVE_ON
@ PRESOLVE_ON
Presolve is on.
Definition: linear_solver.h:1395
operations_research::MPSolverParameters::SCALING_ON
@ SCALING_ON
Scaling is on.
Definition: linear_solver.h:1425
WallTimer::Start
void Start()
Definition: timer.h:31
operations_research::MPSolverParameters::INCREMENTALITY_ON
@ INCREMENTALITY_ON
Reuse results from previous solve as much as the underlying solver allows.
Definition: linear_solver.h:1417
operations_research::MPSolver::BASIC
@ BASIC
Definition: linear_solver.h:647
timer.h
operations_research::MPSolverParameters::PRESOLVE
@ PRESOLVE
Advanced usage: presolve mode.
Definition: linear_solver.h:1381
operations_research::MPSolver::FREE
@ FREE
Definition: linear_solver.h:643
operations_research::MPSolver::BasisStatus
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
Definition: linear_solver.h:642
operations_research::Solver::constraints
int constraints() const
Counts the number of constraints that have been added to the solver before the search.
Definition: constraint_solver.h:2860
ct
const Constraint * ct
Definition: demon_profiler.cc:42
WallTimer
Definition: timer.h:23
operations_research::MPSolverParameters::INCREMENTALITY_OFF
@ INCREMENTALITY_OFF
Start solve from scratch.
Definition: linear_solver.h:1411
operations_research::MPSolver::AT_UPPER_BOUND
@ AT_UPPER_BOUND
Definition: linear_solver.h:645
DCHECK
#define DCHECK(condition)
Definition: base/logging.h:884
operations_research::MPSolverParameters::IncrementalityValues
IncrementalityValues
Advanced usage: Incrementality options.
Definition: linear_solver.h:1409
operations_research::MPSolver::ResultStatus
ResultStatus
The status of solving the problem.
Definition: linear_solver.h:427
operations_research::MPSolverParameters::LpAlgorithmValues
LpAlgorithmValues
LP algorithm to use.
Definition: linear_solver.h:1399
coefficient
int64 coefficient
Definition: routing_search.cc:972
col
ColIndex col
Definition: markowitz.cc:176
row
RowIndex row
Definition: markowitz.cc:175
operations_research::MPSolverParameters::INCREMENTALITY
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
Definition: linear_solver.h:1385
DCHECK_EQ
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:885
maximize_
const bool maximize_
Definition: search.cc:2499
operations_research::sat::Solve
CpSolverResponse Solve(const CpModelProto &model_proto)
Solves the given CpModelProto and returns an instance of CpSolverResponse.
Definition: cp_model_solver.cc:3206
linear_solver.h
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
operations_research::MPSolver::INFEASIBLE
@ INFEASIBLE
proven infeasible.
Definition: linear_solver.h:433
absl
Definition: cleanup.h:22
operations_research::MPSolverParameters::PRIMAL
@ PRIMAL
Primal simplex.
Definition: linear_solver.h:1403
operations_research::MPSolverParameters::PRESOLVE_OFF
@ PRESOLVE_OFF
Presolve is off.
Definition: linear_solver.h:1393
DCHECK_LE
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:887
operations_research::MPSolver::AT_LOWER_BOUND
@ AT_LOWER_BOUND
Definition: linear_solver.h:644
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
name
const std::string name
Definition: default_search.cc:808
operations_research::MPSolverParameters::DUAL
@ DUAL
Dual simplex.
Definition: linear_solver.h:1401
operations_research::MPSolver::FEASIBLE
@ FEASIBLE
feasible, or stopped by limit.
Definition: linear_solver.h:431
operations_research::MPSolverParameters::BARRIER
@ BARRIER
Barrier algorithm.
Definition: linear_solver.h:1405