Google OR-Tools v9.9
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
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#if defined(USE_CPLEX)
16
17#include <cstdint>
18#include <limits>
19#include <memory>
20
21#include "absl/strings/str_format.h"
22#include "absl/strings/str_split.h"
24#include "ortools/base/timer.h"
26
27extern "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.
32CPXLIBAPI 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
50namespace operations_research {
51
52using 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.
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);
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.
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_t iterations() const;
101 // Number of branch-and-bound nodes. Only available for discrete problems.
102 virtual int64_t nodes() const;
103
104 // Returns the basis status of a row.
106 // Returns the basis status of a column.
107 virtual MPSolver::BasisStatus column_status(int variable_index) const;
108
109 // ----- Misc -----
110
111 // Query problem type.
112 // Remember that problem type is a static property that is set
113 // in the constructor and never changed.
114 virtual bool IsContinuous() const { return IsLP(); }
115 virtual bool IsLP() const { return !mMip; }
116 virtual bool IsMIP() const { return mMip; }
117
119 const std::string& parameters) override;
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
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;
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.
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);
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);
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
242 CHECK_STATUS(CPXXfreeprob(mEnv, &mLp));
243 CHECK_STATUS(CPXXcloseCPLEX(&mEnv));
244}
245
246std::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
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);
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
281 mCstat = 0;
282 mRstat = 0;
283}
284
287 CPXXchgobjsen(mEnv, mLp, maximize ? CPX_MAX : CPX_MIN);
288}
289
290void CplexInterface::SetVariableBounds(int var_index, double lb, double ub) {
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 {
303 // Variable has already been extracted, so we must modify the
304 // modeling object.
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.
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) {
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.
354void 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
415void CplexInterface::SetConstraintBounds(int index, double lb, double ub) {
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 {
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
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
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
467 MPVariable const* const variable,
468 double new_value, double) {
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();
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
498 CPXDIM const row = constraint->index();
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 {
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();
525 rowind[j] = row;
526 colind[j] = col;
527 val[j] = 0.0;
528 ++j;
529 }
530 }
531 if (j)
533 CPXXchgcoeflist(mEnv, mLp, j, rowind.get(), colind.get(), val.get()));
534 }
535}
536
538 double coefficient) {
539 CPXDIM const col = variable->index();
541 // Nothing to do if variable was not even extracted
542 return;
543
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
560 // Changing the objective offset is O(1), so we always do it immediately.
563}
564
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
598 int iter;
600 if (mMip)
601 return static_cast<int64_t>(CPXXgetmipitcnt(mEnv, mLp));
602 else
603 return static_cast<int64_t>(CPXXgetitcnt(mEnv, mLp));
604}
605
606int64_t CplexInterface::nodes() const {
607 if (mMip) {
609 return static_cast<int64_t>(CPXXgetnodecnt(mEnv, mLp));
610 } else {
611 LOG(DFATAL) << "Number of nodes only available for discrete problems";
613 }
614}
615
616// Transform a CPLEX basis status to an MPSolver basis status.
617MPSolver::BasisStatus CplexInterface::xformBasisStatus(int cplex_basis_status) {
618 switch (cplex_basis_status) {
619 case CPX_AT_LOWER:
621 case CPX_BASIC:
622 return MPSolver::BASIC;
623 case CPX_AT_UPPER:
625 case CPX_FREE_SUPER:
626 return MPSolver::FREE;
627 default:
628 LOG(DFATAL) << "Unknown CPLEX basis status";
629 return MPSolver::FREE;
630 }
631}
632
633// Returns the basis status of a row.
635 if (mMip) {
636 LOG(FATAL) << "Basis status only available for continuous problems";
637 return MPSolver::FREE;
638 }
639
641 if (!mRstat) {
642 CPXDIM const rows = CPXXgetnumrows(mEnv, mLp);
643 unique_ptr<int[]> data(new int[rows]);
644 mRstat.swap(data);
645 CHECK_STATUS(CPXXgetbase(mEnv, mLp, 0, mRstat.get()));
646 }
647 } else
648 mRstat = 0;
649
650 if (mRstat)
651 return xformBasisStatus(mRstat[constraint_index]);
652 else {
653 LOG(FATAL) << "Row basis status not available";
654 return MPSolver::FREE;
655 }
656}
657
658// Returns the basis status of a column.
660 if (mMip) {
661 LOG(FATAL) << "Basis status only available for continuous problems";
662 return MPSolver::FREE;
663 }
664
666 if (!mCstat) {
667 CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
668 unique_ptr<int[]> data(new int[cols]);
669 mCstat.swap(data);
670 CHECK_STATUS(CPXXgetbase(mEnv, mLp, mCstat.get(), 0));
671 }
672 } else
673 mCstat = 0;
674
675 if (mCstat)
676 return xformBasisStatus(mCstat[variable_index]);
677 else {
678 LOG(FATAL) << "Column basis status not available";
679 return MPSolver::FREE;
680 }
681}
682
683// Extract all variables that have not yet been extracted.
685 // NOTE: The code assumes that a linear expression can never contain
686 // non-zero duplicates.
687
689
690 if (!supportIncrementalExtraction) {
691 // Without incremental extraction ExtractModel() is always called
692 // to extract the full model.
693 CHECK(last_variable_index_ == 0 ||
694 last_variable_index_ == solver_->variables_.size());
695 CHECK(last_constraint_index_ == 0 ||
696 last_constraint_index_ == solver_->constraints_.size());
697 }
698
699 int const last_extracted = last_variable_index_;
700 int const var_count = solver_->variables_.size();
701 CPXDIM newcols = var_count - last_extracted;
702 if (newcols > 0) {
703 // There are non-extracted variables. Extract them now.
704
705 unique_ptr<double[]> obj(new double[newcols]);
706 unique_ptr<double[]> lb(new double[newcols]);
707 unique_ptr<double[]> ub(new double[newcols]);
708 unique_ptr<char[]> ctype(new char[newcols]);
709 unique_ptr<const char*[]> colname(new const char*[newcols]);
710
711 bool have_names = false;
712 for (int j = 0, varidx = last_extracted; j < newcols; ++j, ++varidx) {
713 MPVariable const* const var = solver_->variables_[varidx];
714 lb[j] = var->lb();
715 ub[j] = var->ub();
716 ctype[j] = var->integer() ? CPX_INTEGER : CPX_CONTINUOUS;
717 colname[j] = var->name().empty() ? 0 : var->name().c_str();
718 have_names = have_names || var->name().empty();
719 obj[j] = solver_->objective_->GetCoefficient(var);
720 }
721
722 // Arrays for modifying the problem are setup. Update the index
723 // of variables that will get extracted now. Updating indices
724 // _before_ the actual extraction makes things much simpler in
725 // case we support incremental extraction.
726 // In case of error we just reset the indeces.
727 std::vector<MPVariable*> const& variables = solver_->variables();
728 for (int j = last_extracted; j < var_count; ++j) {
729 CHECK(!variable_is_extracted(variables[j]->index()));
730 set_variable_as_extracted(variables[j]->index(), true);
731 }
732
733 try {
734 bool use_newcols = true;
735
736 if (supportIncrementalExtraction) {
737 // If we support incremental extraction then we must
738 // update existing constraints with the new variables.
739 // To do that we use CPXXaddcols() to actually create the
740 // variables. This is supposed to be faster than combining
741 // CPXXnewcols() and CPXXchgcoeflist().
742
743 // For each column count the size of the intersection with
744 // existing constraints.
745 unique_ptr<CPXDIM[]> collen(new CPXDIM[newcols]);
746 for (CPXDIM j = 0; j < newcols; ++j) collen[j] = 0;
747 CPXNNZ nonzeros = 0;
748 // TODO: Use a bitarray to flag the constraints that actually
749 // intersect new variables?
750 for (int i = 0; i < last_constraint_index_; ++i) {
751 MPConstraint const* const ct = solver_->constraints_[i];
752 CHECK(constraint_is_extracted(ct->index()));
753 const auto& coeffs = ct->coefficients_;
754 for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
755 int const idx = it->first->index();
757 collen[idx - last_variable_index_]++;
758 ++nonzeros;
759 }
760 }
761 }
762
763 if (nonzeros > 0) {
764 // At least one of the new variables did intersect with an
765 // old constraint. We have to create the new columns via
766 // CPXXaddcols().
767 use_newcols = false;
768 unique_ptr<CPXNNZ[]> begin(new CPXNNZ[newcols + 2]);
769 unique_ptr<CPXDIM[]> cmatind(new CPXDIM[nonzeros]);
770 unique_ptr<double[]> cmatval(new double[nonzeros]);
771
772 // Here is how cmatbeg[] is setup:
773 // - it is initialized as
774 // [ 0, 0, collen[0], collen[0]+collen[1], ... ]
775 // so that cmatbeg[j+1] tells us where in cmatind[] and
776 // cmatval[] we need to put the next nonzero for column
777 // j
778 // - after nonzeros have been setup the array looks like
779 // [ 0, collen[0], collen[0]+collen[1], ... ]
780 // so that it is the correct input argument for CPXXaddcols
781 CPXNNZ* cmatbeg = begin.get();
782 cmatbeg[0] = 0;
783 cmatbeg[1] = 0;
784 ++cmatbeg;
785 for (CPXDIM j = 0; j < newcols; ++j)
786 cmatbeg[j + 1] = cmatbeg[j] + collen[j];
787
788 for (int i = 0; i < last_constraint_index_; ++i) {
789 MPConstraint const* const ct = solver_->constraints_[i];
790 CPXDIM const row = ct->index();
791 const auto& coeffs = ct->coefficients_;
792 for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
793 int const idx = it->first->index();
795 CPXNNZ const nz = cmatbeg[idx]++;
796 cmatind[nz] = idx;
797 cmatval[nz] = it->second;
798 }
799 }
800 }
801 --cmatbeg;
802 CHECK_STATUS(CPXXaddcols(mEnv, mLp, newcols, nonzeros, obj.get(),
803 cmatbeg, cmatind.get(), cmatval.get(),
804 lb.get(), ub.get(),
805 have_names ? colname.get() : 0));
806 }
807 }
808 if (use_newcols) {
809 // Either incremental extraction is not supported or none of
810 // the new variables did intersect an existing constraint.
811 // We can just use CPXXnewcols() to create the new variables.
812 CHECK_STATUS(CPXXnewcols(mEnv, mLp, newcols, obj.get(), lb.get(),
813 ub.get(), mMip ? ctype.get() : 0,
814 have_names ? colname.get() : 0));
815 } else {
816 // Incremental extraction: we must update the ctype of the
817 // newly created variables (CPXXaddcols() does not allow
818 // specifying the ctype)
819 if (mMip) {
820 // Query the actual number of columns in case we did not
821 // manage to extract all columns.
822 int const cols = CPXXgetnumcols(mEnv, mLp);
823 unique_ptr<CPXDIM[]> ind(new CPXDIM[newcols]);
824 for (int j = last_extracted; j < cols; ++j)
825 ind[j - last_extracted] = j;
826 CHECK_STATUS(CPXXchgctype(mEnv, mLp, cols - last_extracted, ind.get(),
827 ctype.get()));
828 }
829 }
830 } catch (...) {
831 // Undo all changes in case of error.
832 CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
833 if (cols > last_extracted)
834 (void)CPXXdelcols(mEnv, mLp, last_extracted, cols - 1);
835 std::vector<MPVariable*> const& variables = solver_->variables();
836 int const size = variables.size();
837 for (int j = last_extracted; j < size; ++j)
839 throw;
840 }
841 }
842}
843
844// Extract constraints that have not yet been extracted.
846 // NOTE: The code assumes that a linear expression can never contain
847 // non-zero duplicates.
848
849 if (!supportIncrementalExtraction) {
850 // Without incremental extraction ExtractModel() is always called
851 // to extract the full model.
852 CHECK(last_variable_index_ == 0 ||
853 last_variable_index_ == solver_->variables_.size());
854 CHECK(last_constraint_index_ == 0 ||
855 last_constraint_index_ == solver_->constraints_.size());
856 }
857
858 CPXDIM const offset = last_constraint_index_;
859 CPXDIM const total = solver_->constraints_.size();
860
861 if (total > offset) {
862 // There are constraints that are not yet extracted.
863
865
866 CPXDIM newCons = total - offset;
867 CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
868 CHECK(last_variable_index_ == 0 || last_variable_index_ == cols);
869 CPXDIM const chunk = 10; // max number of rows to add in one shot
870
871 // Update indices of new constraints _before_ actually extracting
872 // them. In case of error we will just reset the indices.
873 for (CPXDIM c = offset; c < total; ++c)
875
876 try {
877 unique_ptr<CPXDIM[]> rmatind(new CPXDIM[cols]);
878 unique_ptr<double[]> rmatval(new double[cols]);
879 unique_ptr<CPXNNZ[]> rmatbeg(new CPXNNZ[chunk]);
880 unique_ptr<char[]> sense(new char[chunk]);
881 unique_ptr<double[]> rhs(new double[chunk]);
882 unique_ptr<char const*[]> name(new char const*[chunk]);
883 unique_ptr<double[]> rngval(new double[chunk]);
884 unique_ptr<CPXDIM[]> rngind(new CPXDIM[chunk]);
885 bool haveRanges = false;
886
887 // Loop over the new constraints, collecting rows for up to
888 // CHUNK constraints into the arrays so that adding constraints
889 // is faster.
890 for (CPXDIM c = 0; c < newCons; /* nothing */) {
891 // Collect up to CHUNK constraints into the arrays.
892 CPXDIM nextRow = 0;
893 CPXNNZ nextNz = 0;
894 for (/* nothing */; c < newCons && nextRow < chunk; ++c, ++nextRow) {
895 MPConstraint const* const ct = solver_->constraints_[offset + c];
896
897 // Stop if there is not enough room in the arrays
898 // to add the current constraint.
899 if (nextNz + ct->coefficients_.size() > cols) {
900 DCHECK_GT(nextRow, 0);
901 break;
902 }
903
904 // Setup right-hand side of constraint.
905 MakeRhs(ct->lb(), ct->ub(), rhs[nextRow], sense[nextRow],
906 rngval[nextRow]);
907 haveRanges = haveRanges || (rngval[nextRow] != 0.0);
908 rngind[nextRow] = offset + c;
909
910 // Setup left-hand side of constraint.
911 rmatbeg[nextRow] = nextNz;
912 const auto& coeffs = ct->coefficients_;
913 for (auto it(coeffs.begin()); it != coeffs.end(); ++it) {
914 CPXDIM const idx = it->first->index();
915 if (variable_is_extracted(idx)) {
916 DCHECK_LT(nextNz, cols);
917 DCHECK_LT(idx, cols);
918 rmatind[nextNz] = idx;
919 rmatval[nextNz] = it->second;
920 ++nextNz;
921 }
922 }
923
924 // Finally the name of the constraint.
925 name[nextRow] = ct->name().empty() ? 0 : ct->name().c_str();
926 }
927 if (nextRow > 0) {
928 CHECK_STATUS(CPXXaddrows(mEnv, mLp, 0, nextRow, nextNz, rhs.get(),
929 sense.get(), rmatbeg.get(), rmatind.get(),
930 rmatval.get(), 0, name.get()));
931 if (haveRanges) {
933 CPXXchgrngval(mEnv, mLp, nextRow, rngind.get(), rngval.get()));
934 }
935 }
936 }
937 } catch (...) {
938 // Undo all changes in case of error.
939 CPXDIM const rows = CPXXgetnumrows(mEnv, mLp);
940 if (rows > offset) (void)CPXXdelrows(mEnv, mLp, offset, rows - 1);
941 std::vector<MPConstraint*> const& constraints = solver_->constraints();
942 int const size = constraints.size();
943 for (int i = offset; i < size; ++i) set_constraint_as_extracted(i, false);
944 throw;
945 }
946 }
947}
948
949// Extract the objective function.
951 // NOTE: The code assumes that the objective expression does not contain
952 // any non-zero duplicates.
953
954 CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
955 CHECK(last_variable_index_ == 0 || last_variable_index_ == cols);
956
957 unique_ptr<CPXDIM[]> ind(new CPXDIM[cols]);
958 unique_ptr<double[]> val(new double[cols]);
959 for (CPXDIM j = 0; j < cols; ++j) {
960 ind[j] = j;
961 val[j] = 0.0;
962 }
963
964 const auto& coeffs = solver_->objective_->coefficients_;
965 for (auto it = coeffs.begin(); it != coeffs.end(); ++it) {
966 CPXDIM const idx = it->first->index();
967 if (variable_is_extracted(idx)) {
968 DCHECK_LT(idx, cols);
969 val[idx] = it->second;
970 }
971 }
972
973 CHECK_STATUS(CPXXchgobj(mEnv, mLp, cols, ind.get(), val.get()));
975}
976
977// ------ Parameters -----
978
980 SetCommonParameters(param);
981 if (mMip) SetMIPParameters(param);
982}
983
985 if (mMip) {
986 CHECK_STATUS(CPXXsetdblparam(mEnv, CPX_PARAM_EPGAP, value));
987 } else {
988 LOG(WARNING) << "The relative MIP gap is only available "
989 << "for discrete problems.";
990 }
991}
992
994 CHECK_STATUS(CPXXsetdblparam(mEnv, CPX_PARAM_EPRHS, value));
995}
996
998 CHECK_STATUS(CPXXsetdblparam(mEnv, CPX_PARAM_EPOPT, value));
999}
1000
1002 MPSolverParameters::PresolveValues const presolve =
1004
1005 switch (presolve) {
1007 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_PREIND, CPX_OFF));
1008 return;
1010 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_PREIND, CPX_ON));
1011 return;
1012 }
1014}
1015
1016// Sets the scaling mode.
1018 MPSolverParameters::ScalingValues const scaling =
1020
1021 switch (scaling) {
1023 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_SCAIND, -1));
1024 break;
1026 // TODO: 0 is equilibrium scaling (the default), CPLEX also supports
1027 // 1 aggressive scaling
1028 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_SCAIND, 0));
1029 break;
1030 }
1031}
1032
1033// Sets the LP algorithm : primal, dual or barrier. Note that CPLEX offers other
1034// LP algorithm (e.g. network) and automatic selection
1038
1039 int alg = CPX_ALG_NONE;
1040
1041 switch (algorithm) {
1043 alg = CPX_ALG_DUAL;
1044 break;
1046 alg = CPX_ALG_PRIMAL;
1047 break;
1049 alg = CPX_ALG_BARRIER;
1050 break;
1051 }
1052
1053 if (alg == CPX_ALG_NONE)
1055 else {
1056 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_LPMETHOD, alg));
1057 if (mMip) {
1058 // For MIP we have to change two more parameters to specify the
1059 // algorithm that is used to solve LP relaxations.
1060 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_STARTALG, alg));
1061 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_SUBALG, alg));
1062 }
1063 }
1064}
1065
1066bool CplexInterface::ReadParameterFile(std::string const& filename) {
1067 // Return true on success and false on error.
1068 return CPXXreadcopyparam(mEnv, filename.c_str()) == 0;
1069}
1070
1072 return ".prm";
1073}
1074
1076 int status;
1077
1078 // Delete chached information
1079 mCstat = 0;
1080 mRstat = 0;
1081
1082 WallTimer timer;
1083 timer.Start();
1084
1085 // Set incrementality
1089 switch (inc) {
1091 Reset(); /* This should not be required but re-extracting everything
1092 * may be faster, so we do it. */
1093 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_ADVIND, 0));
1094 break;
1096 CHECK_STATUS(CPXXsetintparam(mEnv, CPX_PARAM_ADVIND, 2));
1097 break;
1098 }
1099
1100 // Extract the model to be solved.
1101 // If we don't support incremental extraction and the low-level modeling
1102 // is out of sync then we have to re-extract everything. Note that this
1103 // will lose MIP starts or advanced basis information from a previous
1104 // solve.
1105 if (!supportIncrementalExtraction && sync_status_ == MUST_RELOAD) Reset();
1106 ExtractModel();
1107 VLOG(1) << absl::StrFormat("Model build in %.3f seconds.", timer.Get());
1108
1109 // Set log level.
1111 CPXXsetintparam(mEnv, CPX_PARAM_SCRIND, quiet() ? CPX_OFF : CPX_ON));
1112
1113 if (!solver_->solution_hint_.empty()) {
1114 int const sol_count = solver_->solution_hint_.size();
1115 long long int beg[1] = {0};
1116 int* varindices = new int[sol_count];
1117 double* values = new double[sol_count];
1118
1119 for (int i = 0; i < sol_count; ++i) {
1120 varindices[i] = solver_->solution_hint_[i].first->index();
1121 values[i] = solver_->solution_hint_[i].second;
1122 }
1123 CPXXaddmipstarts(mEnv, mLp, 1, sol_count, beg, varindices, values, NULL,
1124 NULL);
1125 }
1126 // Set parameters.
1127 // NOTE: We must invoke SetSolverSpecificParametersAsString() _first_.
1128 // Its current implementation invokes ReadParameterFile() which in
1129 // turn invokes CPXXreadcopyparam(). The latter will _overwrite_
1130 // all current parameter settings in the environment.
1132 solver_->solver_specific_parameter_string_);
1133 SetParameters(param);
1134 if (solver_->time_limit()) {
1135 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
1137 CPXXsetdblparam(mEnv, CPX_PARAM_TILIM, solver_->time_limit() * 1e-3));
1138 }
1139
1140 // Solve.
1141 // Do not CHECK_STATUS here since some errors (for example CPXERR_NO_MEMORY)
1142 // still allow us to query useful information.
1143 timer.Restart();
1144 if (mMip) {
1145 status = CPXXmipopt(mEnv, mLp);
1146 } else {
1147 status = CPXXlpopt(mEnv, mLp);
1148 }
1149
1150 // Disable screen output right after solve
1151 (void)CPXXsetintparam(mEnv, CPX_PARAM_SCRIND, CPX_OFF);
1152
1153 if (status) {
1154 VLOG(1) << absl::StrFormat("Failed to optimize MIP. Error %d", status);
1155 // NOTE: We do not return immediately since there may be information
1156 // to grab (for example an incumbent)
1157 } else {
1158 VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
1159 }
1160
1161 int const cpxstat = CPXXgetstat(mEnv, mLp);
1162 VLOG(1) << absl::StrFormat("CPLEX solution status %d.", cpxstat);
1163
1164 // Figure out what solution we have.
1165 int solnmethod, solntype, pfeas, dfeas;
1166 CHECK_STATUS(CPXXsolninfo(mEnv, mLp, &solnmethod, &solntype, &pfeas, &dfeas));
1167 bool const feasible = pfeas != 0;
1168
1169 // Get problem dimensions for solution queries below.
1170 CPXDIM const rows = CPXXgetnumrows(mEnv, mLp);
1171 CPXDIM const cols = CPXXgetnumcols(mEnv, mLp);
1172 DCHECK_EQ(rows, solver_->constraints_.size());
1173 DCHECK_EQ(cols, solver_->variables_.size());
1174
1175 // Capture objective function value.
1178 if (feasible) {
1179 CHECK_STATUS(CPXXgetobjval(mEnv, mLp, &objective_value_));
1180 if (mMip) {
1181 CHECK_STATUS(CPXXgetbestobjval(mEnv, mLp, &best_objective_bound_));
1182 }
1183 }
1184 VLOG(1) << "objective=" << objective_value_
1185 << ", bound=" << best_objective_bound_;
1186
1187 // Capture primal and dual solutions
1188 if (mMip) {
1189 // If there is a primal feasible solution then capture it.
1190 if (feasible) {
1191 if (cols > 0) {
1192 unique_ptr<double[]> x(new double[cols]);
1193 CHECK_STATUS(CPXXgetx(mEnv, mLp, x.get(), 0, cols - 1));
1194 for (int i = 0; i < solver_->variables_.size(); ++i) {
1195 MPVariable* const var = solver_->variables_[i];
1196 var->set_solution_value(x[i]);
1197 VLOG(3) << var->name() << ": value =" << x[i];
1198 }
1199 }
1200 } else {
1201 for (int i = 0; i < solver_->variables_.size(); ++i)
1202 solver_->variables_[i]->set_solution_value(CPX_NAN);
1203 }
1204
1205 // MIP does not have duals
1206 for (int i = 0; i < solver_->variables_.size(); ++i)
1207 solver_->variables_[i]->set_reduced_cost(CPX_NAN);
1208 for (int i = 0; i < solver_->constraints_.size(); ++i)
1209 solver_->constraints_[i]->set_dual_value(CPX_NAN);
1210 } else {
1211 // Continuous problem.
1212 if (cols > 0) {
1213 unique_ptr<double[]> x(new double[cols]);
1214 unique_ptr<double[]> dj(new double[cols]);
1215 if (feasible) CHECK_STATUS(CPXXgetx(mEnv, mLp, x.get(), 0, cols - 1));
1216 if (dfeas) CHECK_STATUS(CPXXgetdj(mEnv, mLp, dj.get(), 0, cols - 1));
1217 for (int i = 0; i < solver_->variables_.size(); ++i) {
1218 MPVariable* const var = solver_->variables_[i];
1219 var->set_solution_value(x[i]);
1220 bool value = false, dual = false;
1221
1222 if (feasible) {
1223 var->set_solution_value(x[i]);
1224 value = true;
1225 } else
1226 var->set_solution_value(CPX_NAN);
1227 if (dfeas) {
1228 var->set_reduced_cost(dj[i]);
1229 dual = true;
1230 } else
1231 var->set_reduced_cost(CPX_NAN);
1232 VLOG(3) << var->name() << ":"
1233 << (value ? absl::StrFormat(" value = %f", x[i]) : "")
1234 << (dual ? absl::StrFormat(" reduced cost = %f", dj[i]) : "");
1235 }
1236 }
1237
1238 if (rows > 0) {
1239 unique_ptr<double[]> pi(new double[rows]);
1240 if (dfeas) CHECK_STATUS(CPXXgetpi(mEnv, mLp, pi.get(), 0, rows - 1));
1241 for (int i = 0; i < solver_->constraints_.size(); ++i) {
1242 MPConstraint* const ct = solver_->constraints_[i];
1243 bool dual = false;
1244 if (dfeas) {
1245 ct->set_dual_value(pi[i]);
1246 dual = true;
1247 } else
1248 ct->set_dual_value(CPX_NAN);
1249 VLOG(4) << "row " << ct->index() << ":"
1250 << (dual ? absl::StrFormat(" dual = %f", pi[i]) : "");
1251 }
1252 }
1253 }
1254
1255 // Map CPLEX status to more generic solution status in MPSolver
1256 switch (cpxstat) {
1257 case CPX_STAT_OPTIMAL:
1258 case CPXMIP_OPTIMAL:
1260 break;
1261 case CPXMIP_OPTIMAL_TOL:
1262 // To be consistent with the other solvers.
1264 break;
1265 case CPX_STAT_INFEASIBLE:
1266 case CPXMIP_INFEASIBLE:
1268 break;
1269 case CPX_STAT_UNBOUNDED:
1270 case CPXMIP_UNBOUNDED:
1272 break;
1273 case CPX_STAT_INForUNBD:
1274 case CPXMIP_INForUNBD:
1276 break;
1277 default:
1279 break;
1280 }
1281
1283 return result_status_;
1284}
1285
1287 return new CplexInterface(solver, mip);
1288}
1289
1291 const std::string& parameters) {
1292 if (parameters.empty()) return true;
1293 for (const auto parameter : absl::StrSplit(parameters, absl::ByAnyChar(","),
1294 absl::SkipWhitespace())) {
1295 std::vector<std::string> key_value =
1296 absl::StrSplit(parameter, absl::ByAnyChar("="), absl::SkipWhitespace());
1297 if (key_value.size() != 2) {
1298 LOG(WARNING) << absl::StrFormat(
1299 "Cannot parse parameter '%s'. Expected format is 'parameter/name = "
1300 "value'",
1301 parameter);
1302 continue;
1303 }
1304 std::string identifier = key_value[0];
1305 absl::RemoveExtraAsciiWhitespace(&identifier);
1306
1307 std::string value = key_value[1];
1308 absl::RemoveExtraAsciiWhitespace(&value);
1309
1310 try {
1311 if (identifier.find("LogFile") != std::string::npos) {
1312 CPXXsetlogfilename(mEnv, value.c_str(), "w");
1313 } else {
1314 std::string delimiter = ".";
1315 if (value.find(delimiter) == std::string::npos) {
1316 (void)CPXXsetintparam(mEnv, std::stoi(identifier), std::stoi(value));
1317 } else {
1318 (void)CPXXsetdblparam(mEnv, std::stoi(identifier), std::stod(value));
1319 }
1320 VLOG(2) << absl::StrFormat("Set parameter %s to %s", identifier, value);
1321 }
1322 } catch (...) {
1323 LOG(WARNING) << absl::StrFormat(
1324 "Cannot parse parameter '%s'. Expected format is 'parameter/name = "
1325 "value'",
1326 identifier);
1327 }
1328 }
1329 return true;
1330}
1331
1332} // namespace operations_research
1333#endif // #if defined(USE_CPLEX)
double Get() const
Definition timer.h:46
void Restart()
Definition timer.h:36
void Start()
When Start() is called multiple times, only the most recent is used.
Definition timer.h:32
virtual MPSolver::BasisStatus row_status(int constraint_index) const
Returns the basis status of a row.
virtual void SetOptimizationDirection(bool maximize)
Sets the optimization direction (min/max).
virtual MPSolver::BasisStatus column_status(int variable_index) const
Returns the basis status of a column.
virtual int64_t iterations() const
---— Query statistics on the solution and the solve ---—
bool SetSolverSpecificParametersAsString(const std::string &parameters) override
virtual double ComputeExactConditionNumber() const
virtual void SetVariableBounds(int var_index, double lb, double ub)
Modifies bounds of an extracted variable.
virtual void * underlying_solver()
Returns the underlying solver.
virtual void ClearObjective()
Clear the objective from all its terms.
virtual void Reset()
---— Model modifications and extraction --—
virtual void AddRowConstraint(MPConstraint *const ct)
Adds a linear constraint.
virtual void ClearConstraint(MPConstraint *const constraint)
Clear a constraint from all its terms.
virtual void ExtractNewVariables()
Extract all variables that have not yet been extracted.
virtual void SetVariableInteger(int var_index, bool integer)
Modifies integrality of an extracted variable.
virtual MPSolver::ResultStatus Solve(MPSolverParameters const &param)
virtual void SetCoefficient(MPConstraint *const constraint, MPVariable const *const variable, double new_value, double old_value)
Changes a coefficient in a constraint.
virtual std::string SolverVersion() const
Returns a string describing the underlying solver and its version.
virtual void SetPresolveMode(int value)
virtual int64_t nodes() const
Number of branch-and-bound nodes. Only available for discrete problems.
virtual bool ReadParameterFile(std::string const &filename)
virtual void SetParameters(MPSolverParameters const &param)
Set all parameters in the underlying solver.
virtual bool IsContinuous() const
--— Misc --—
virtual void SetObjectiveOffset(double value)
Change the constant term in the linear objective.
virtual void AddVariable(MPVariable *const var)
Add a variable.
virtual void ExtractNewConstraints()
Extract constraints that have not yet been extracted.
virtual bool IsLP() const
Returns true if the problem is continuous and linear.
virtual void SetConstraintBounds(int row_index, double lb, double ub)
Modify bounds of an extracted variable.
virtual bool IsMIP() const
Returns true if the problem is discrete and linear.
virtual void SetScalingMode(int value)
Sets the scaling mode.
virtual void ExtractObjective()
Extract the objective function.
virtual void SetObjectiveCoefficient(MPVariable const *const variable, double coefficient)
Change a coefficient in the linear objective.
virtual void SetDualTolerance(double value)
CplexInterface(MPSolver *const solver, bool mip)
Creates a LP/MIP instance.
virtual std::string ValidFileExtensionForParameterFile() const
virtual void SetRelativeMipGap(double value)
Set each parameter in the underlying solver.
virtual void SetPrimalTolerance(double value)
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
double offset() const
Gets the constant term in the objective.
void set_variable_as_extracted(int var_index, bool extracted)
static constexpr int64_t kUnknownNumberOfIterations
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
Resets the extraction information.
int last_variable_index_
Index in MPSolver::constraints_ of last variable extracted.
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
Sets a supported integer parameter to an unsupported value.
int last_constraint_index_
Index in MPSolver::variables_ of last constraint extracted.
bool variable_is_extracted(int var_index) const
bool quiet() const
Returns the boolean indicating the verbosity of the solver output.
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
void ExtractModel()
Extracts model stored in MPSolver.
double objective_value_
The value of the objective function.
double best_objective_bound_
The value of the best objective bound. Used only for MIP solvers.
bool maximize_
Optimization direction.
void SetMIPParameters(const MPSolverParameters &param)
Sets MIP specific parameters in the underlying solver.
void SetCommonParameters(const MPSolverParameters &param)
Sets parameters common to LP and MIP in the underlying solver.
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
PresolveValues
For each categorical parameter, enumeration of possible values.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
@ PRESOLVE
Advanced usage: presolve mode.
@ LP_ALGORITHM
Algorithm to solve linear programs.
IncrementalityValues
Advanced usage: Incrementality options.
@ INCREMENTALITY_OFF
Start solve from scratch.
ScalingValues
Advanced usage: Scaling options.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
const std::vector< MPVariable * > & variables() const
@ FEASIBLE
feasible, or stopped by limit.
@ INFEASIBLE
proven infeasible.
@ ABNORMAL
abnormal, i.e., error of some kind.
const MPObjective & Objective() const
bool SetSolverSpecificParametersAsString(const std::string &parameters)
const std::vector< MPConstraint * > & constraints() const
The class for variables of a Mathematical Programming (MP) model.
int index() const
Returns the index of the variable in the MPSolver::variables_.
SatParameters parameters
#define CPX_NAN
CPXLIBAPI int CPXPUBLIC CPXEsetobjoffset(CPXCENVptr, CPXLPptr, double)
Initial version of this code was written by Daniel Junglas (IBM)
#define CHECK_STATUS(s)
const std::string name
A name for logging purposes.
const Constraint * ct
int64_t value
IntVar * var
absl::Status status
Definition g_gurobi.cc:44
int constraint_index
int index
ColIndex col
Definition markowitz.cc:187
RowIndex row
Definition markowitz.cc:186
In SWIG mode, we don't want anything besides these top-level includes.
MPSolverInterface * BuildCplexInterface(bool mip, MPSolver *const solver)
const Variable x
Definition qp_tests.cc:127
int64_t coefficient
int var_index
Definition search.cc:3268
const std::optional< Range > & range
Definition statistics.cc:37
*vec begin()+0)