OR-Tools  9.0
cbc_interface.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
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 //
15 
16 #include <cstdint>
17 #include <limits>
18 #include <memory>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/status/status.h"
24 #include "absl/strings/str_format.h"
26 #include "ortools/base/hash.h"
28 #include "ortools/base/logging.h"
29 #include "ortools/base/timer.h"
31 
32 #if defined(USE_CBC)
33 
34 #undef PACKAGE
35 #undef VERSION
36 #include "CbcConfig.h"
37 #include "CbcMessage.hpp"
38 #include "CbcModel.hpp"
39 #include "CoinModel.hpp"
40 #include "OsiClpSolverInterface.hpp"
41 
42 // Heuristics
43 
44 namespace operations_research {
45 
47  public:
48  // Constructor that takes a name for the underlying glpk solver.
49  explicit CBCInterface(MPSolver* const solver);
50  ~CBCInterface() override;
51 
52  // ----- Reset -----
53  void Reset() override;
54 
55  // Sets the optimization direction (min/max).
56  void SetOptimizationDirection(bool maximize) override;
57 
58  // ----- Parameters -----
59 
60  absl::Status SetNumThreads(int num_threads) override {
61  CHECK_GE(num_threads, 1);
62  num_threads_ = num_threads;
63  return absl::OkStatus();
64  }
65 
66  // ----- Solve -----
67  // Solve the problem using the parameter values specified.
68  MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
69 
70  // TODO(user): separate the solve from the model extraction.
71  virtual void ExtractModel() {}
72 
73  // Query problem type.
74  bool IsContinuous() const override { return false; }
75  bool IsLP() const override { return false; }
76  bool IsMIP() const override { return true; }
77 
78  // Modify bounds.
79  void SetVariableBounds(int var_index, double lb, double ub) override;
80  void SetVariableInteger(int var_index, bool integer) override;
81  void SetConstraintBounds(int row_index, double lb, double ub) override;
82 
83  // Add constraint incrementally.
84  void AddRowConstraint(MPConstraint* const ct) override;
85  // Add variable incrementally.
86  void AddVariable(MPVariable* const var) override;
87  // Change a coefficient in a constraint.
88  void SetCoefficient(MPConstraint* const constraint,
89  const MPVariable* const variable, double new_value,
90  double old_value) override {
92  }
93  // Clear a constraint from all its terms.
94  void ClearConstraint(MPConstraint* const constraint) override {
96  }
97 
98  // Change a coefficient in the linear objective.
99  void SetObjectiveCoefficient(const MPVariable* const variable,
100  double coefficient) override {
102  }
103  // Change the constant term in the linear objective.
104  void SetObjectiveOffset(double value) override { sync_status_ = MUST_RELOAD; }
105  // Clear the objective from all its terms.
106  void ClearObjective() override { sync_status_ = MUST_RELOAD; }
107 
108  // Number of simplex iterations
109  int64_t iterations() const override;
110  // Number of branch-and-bound nodes. Only available for discrete problems.
111  int64_t nodes() const override;
112 
113  // Returns the basis status of a row.
114  MPSolver::BasisStatus row_status(int constraint_index) const override {
115  LOG(FATAL) << "Basis status only available for continuous problems";
116  return MPSolver::FREE;
117  }
118  // Returns the basis status of a column.
119  MPSolver::BasisStatus column_status(int variable_index) const override {
120  LOG(FATAL) << "Basis status only available for continuous problems";
121  return MPSolver::FREE;
122  }
123 
124  void ExtractNewVariables() override {}
125  void ExtractNewConstraints() override {}
126  void ExtractObjective() override {}
127 
128  std::string SolverVersion() const override { return "Cbc " CBC_VERSION; }
129 
130  // TODO(user): Maybe we should expose the CbcModel build from osi_
131  // instead, but a new CbcModel is built every time Solve is called,
132  // so it is not possible right now.
133  void* underlying_solver() override { return reinterpret_cast<void*>(&osi_); }
134 
135  private:
136  // Reset best objective bound to +/- infinity depending on the
137  // optimization direction.
138  void ResetBestObjectiveBound();
139 
140  // Set all parameters in the underlying solver.
141  void SetParameters(const MPSolverParameters& param) override;
142  // Set each parameter in the underlying solver.
143  void SetRelativeMipGap(double value) override;
144  void SetPrimalTolerance(double value) override;
145  void SetDualTolerance(double value) override;
146  void SetPresolveMode(int value) override;
147  void SetScalingMode(int value) override;
148  void SetLpAlgorithm(int value) override;
149 
150  OsiClpSolverInterface osi_;
151  // TODO(user): remove and query number of iterations directly from CbcModel
152  int64_t iterations_;
153  int64_t nodes_;
154  // Special way to handle the relative MIP gap parameter.
155  double relative_mip_gap_;
156  int num_threads_ = 1;
157 };
158 
159 // ----- Solver -----
160 
161 // Creates a LP/MIP instance with the specified name and minimization objective.
163  : MPSolverInterface(solver),
164  iterations_(0),
165  nodes_(0),
166  relative_mip_gap_(MPSolverParameters::kDefaultRelativeMipGap) {
167  osi_.setStrParam(OsiProbName, solver_->name_);
168  osi_.setObjSense(1);
169 }
170 
172 
173 // Reset the solver.
175  osi_.reset();
176  osi_.setObjSense(maximize_ ? -1 : 1);
177  osi_.setStrParam(OsiProbName, solver_->name_);
179 }
180 
181 void CBCInterface::ResetBestObjectiveBound() {
182  if (maximize_) {
183  best_objective_bound_ = std::numeric_limits<double>::infinity();
184  } else {
185  best_objective_bound_ = -std::numeric_limits<double>::infinity();
186  }
187 }
188 
192  osi_.setObjSense(maximize ? -1 : 1);
193  } else {
195  }
196 }
197 
198 namespace {
199 // CBC adds a "dummy" variable with index 0 to represent the objective offset.
200 int MPSolverVarIndexToCbcVarIndex(int var_index) { return var_index + 1; }
201 } // namespace
202 
203 void CBCInterface::SetVariableBounds(int var_index, double lb, double ub) {
206  osi_.setColBounds(MPSolverVarIndexToCbcVarIndex(var_index), lb, ub);
207  } else {
209  }
210 }
211 
212 void CBCInterface::SetVariableInteger(int var_index, bool integer) {
214  // TODO(user) : Check if this is actually a change.
216  if (integer) {
217  osi_.setInteger(MPSolverVarIndexToCbcVarIndex(var_index));
218  } else {
219  osi_.setContinuous(MPSolverVarIndexToCbcVarIndex(var_index));
220  }
221  } else {
223  }
224 }
225 
226 void CBCInterface::SetConstraintBounds(int index, double lb, double ub) {
229  osi_.setRowBounds(index, lb, ub);
230  } else {
232  }
233 }
234 
237 }
238 
241 }
242 
243 // Solve the LP/MIP. Returns true only if the optimal solution was revealed.
244 // Returns the status of the search.
246  // CBC requires unique variable and constraint names. By using Lookup*, we
247  // generate variable and constraint indices and ensure the duplicate name
248  // crash will happen here with a readable error message.
249  if (!solver_->variables_.empty()) {
250  solver_->LookupVariableOrNull(solver_->variables_[0]->name());
251  }
252  if (!solver_->constraints_.empty()) {
253  solver_->LookupConstraintOrNull(solver_->constraints_[0]->name());
254  }
255 
256  WallTimer timer;
257  timer.Start();
258 
259  // Note that CBC does not provide any incrementality.
262  Reset();
263  }
264 
265  // Special case if the model is empty since CBC is not able to
266  // handle this special case by itself.
267  if (solver_->variables_.empty() && solver_->constraints_.empty()) {
272  return result_status_;
273  }
274 
275  // Finish preparing the problem.
276  // Define variables.
277  switch (sync_status_) {
278  case MUST_RELOAD: {
279  Reset();
280  CoinModel build;
281  // Create dummy variable for objective offset.
282  build.addColumn(0, nullptr, nullptr, 1.0, 1.0,
283  solver_->Objective().offset(), "dummy", false);
284  const int nb_vars = solver_->variables_.size();
285  for (int i = 0; i < nb_vars; ++i) {
286  MPVariable* const var = solver_->variables_[i];
287  set_variable_as_extracted(i, true);
288  const double obj_coeff = solver_->Objective().GetCoefficient(var);
289  if (var->name().empty()) {
290  build.addColumn(0, nullptr, nullptr, var->lb(), var->ub(), obj_coeff,
291  nullptr, var->integer());
292  } else {
293  build.addColumn(0, nullptr, nullptr, var->lb(), var->ub(), obj_coeff,
294  var->name().c_str(), var->integer());
295  }
296  }
297 
298  // Define constraints.
299  int max_row_length = 0;
300  for (int i = 0; i < solver_->constraints_.size(); ++i) {
301  MPConstraint* const ct = solver_->constraints_[i];
303  if (ct->coefficients_.size() > max_row_length) {
304  max_row_length = ct->coefficients_.size();
305  }
306  }
307  std::unique_ptr<int[]> indices(new int[max_row_length]);
308  std::unique_ptr<double[]> coefs(new double[max_row_length]);
309 
310  for (int i = 0; i < solver_->constraints_.size(); ++i) {
311  MPConstraint* const ct = solver_->constraints_[i];
312  const int size = ct->coefficients_.size();
313  int j = 0;
314  for (const auto& entry : ct->coefficients_) {
315  const int index = MPSolverVarIndexToCbcVarIndex(entry.first->index());
316  indices[j] = index;
317  coefs[j] = entry.second;
318  j++;
319  }
320  if (ct->name().empty()) {
321  build.addRow(size, indices.get(), coefs.get(), ct->lb(), ct->ub());
322  } else {
323  build.addRow(size, indices.get(), coefs.get(), ct->lb(), ct->ub(),
324  ct->name().c_str());
325  }
326  }
327  osi_.loadFromCoinModel(build);
328  break;
329  }
330  case MODEL_SYNCHRONIZED: {
331  break;
332  }
333  case SOLUTION_SYNCHRONIZED: {
334  break;
335  }
336  }
337 
338  // Changing optimization direction through OSI so that the model file
339  // (written through OSI) has the correct optimization duration.
340  osi_.setObjSense(maximize_ ? -1 : 1);
341 
343  VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get());
344 
345  ResetBestObjectiveBound();
346 
347  // Solve
348  CbcModel model(osi_);
349 
350  // Set log level.
351  CoinMessageHandler message_handler;
352  model.passInMessageHandler(&message_handler);
353  if (quiet_) {
354  message_handler.setLogLevel(0, 0); // Coin messages
355  message_handler.setLogLevel(1, 0); // Clp messages
356  message_handler.setLogLevel(2, 0); // Presolve messages
357  message_handler.setLogLevel(3, 0); // Cgl messages
358  } else {
359  message_handler.setLogLevel(0, 1); // Coin messages
360  message_handler.setLogLevel(1, 1); // Clp messages
361  message_handler.setLogLevel(2, 1); // Presolve messages
362  message_handler.setLogLevel(3, 1); // Cgl messages
363  }
364 
365  // Time limit.
366  if (solver_->time_limit() != 0) {
367  VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
368  model.setMaximumSeconds(solver_->time_limit_in_secs());
369  }
370 
371  // And solve.
372  timer.Restart();
373 
374  // Here we use the default function from the command-line CBC solver.
375  // This enables to activate all the features and get the same performance
376  // as the CBC stand-alone executable. The syntax is ugly, however.
377  SetParameters(param);
378  // Always turn presolve on (it's the CBC default and it consistently
379  // improves performance).
380  model.setTypePresolve(0);
381  // Special way to set the relative MIP gap parameter as it cannot be set
382  // through callCbc.
383  model.setAllowableFractionGap(relative_mip_gap_);
384  // NOTE: Trailing space is required to avoid buffer overflow in cbc.
385  int return_status =
386  num_threads_ == 1
387  ? callCbc("-solve ", model)
388  : callCbc(absl::StrCat("-threads ", num_threads_, " -solve "), model);
389  const int kBadReturnStatus = 777;
390  CHECK_NE(kBadReturnStatus, return_status); // Should never happen according
391  // to the CBC source
392 
393  VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
394 
395  // Check the status: optimal, infeasible, etc.
396  int tmp_status = model.status();
397 
398  VLOG(1) << "cbc result status: " << tmp_status;
399  /* Final status of problem
400  (info from cbc/.../CbcSolver.cpp,
401  See http://cs?q="cbc+status"+file:CbcSolver.cpp)
402  Some of these can be found out by is...... functions
403  -1 before branchAndBound
404  0 finished - check isProvenOptimal or isProvenInfeasible to see
405  if solution found
406  (or check value of best solution)
407  1 stopped - on maxnodes, maxsols, maxtime
408  2 difficulties so run was abandoned
409  (5 event user programmed event occurred)
410  */
411  switch (tmp_status) {
412  case 0:
413  // Order of tests counts; if model.isContinuousUnbounded() returns true,
414  // then so does model.isProvenInfeasible()!
415  if (model.isProvenOptimal()) {
417  } else if (model.isContinuousUnbounded()) {
419  } else if (model.isProvenInfeasible()) {
421  } else if (model.isAbandoned()) {
423  } else {
425  }
426  break;
427  case 1:
428  if (model.bestSolution() != nullptr) {
430  } else {
432  }
433  break;
434  default:
436  break;
437  }
438 
441  // Get the results
442  objective_value_ = model.getObjValue();
443  VLOG(1) << "objective=" << objective_value_;
444  const double* const values = model.bestSolution();
445  if (values != nullptr) {
446  // if optimal or feasible solution is found.
447  for (int i = 0; i < solver_->variables_.size(); ++i) {
448  MPVariable* const var = solver_->variables_[i];
449  const int var_index = MPSolverVarIndexToCbcVarIndex(var->index());
450  const double val = values[var_index];
451  var->set_solution_value(val);
452  VLOG(3) << var->name() << "=" << val;
453  }
454  } else {
455  VLOG(1) << "No feasible solution found.";
456  }
457  }
458 
459  iterations_ = model.getIterationCount();
460  nodes_ = model.getNodeCount();
461  best_objective_bound_ = model.getBestPossibleObjValue();
462  VLOG(1) << "best objective bound=" << best_objective_bound_;
463 
465 
466  return result_status_;
467 }
468 
469 // ------ Query statistics on the solution and the solve ------
470 
471 int64_t CBCInterface::iterations() const {
473  return iterations_;
474 }
475 
476 int64_t CBCInterface::nodes() const {
478  return nodes_;
479 }
480 
481 // ----- Parameters -----
482 
483 // The support for parameters in CBC is intentionally sparse. There is
484 // a memory leak in callCbc that prevents to pass parameters through
485 // it, so handling parameters would require an comprehensive rewrite
486 // of the code. I will improve the parameter support only if there is
487 // a relevant use case.
488 
489 void CBCInterface::SetParameters(const MPSolverParameters& param) {
490  SetCommonParameters(param);
491  SetMIPParameters(param);
492 }
493 
494 void CBCInterface::SetRelativeMipGap(double value) {
495  relative_mip_gap_ = value;
496 }
497 
498 void CBCInterface::SetPrimalTolerance(double value) {
499  // Skip the warning for the default value as it coincides with
500  // the default value in CBC.
503  }
504 }
505 
506 void CBCInterface::SetDualTolerance(double value) {
507  // Skip the warning for the default value as it coincides with
508  // the default value in CBC.
511  }
512 }
513 
514 void CBCInterface::SetPresolveMode(int value) {
515  switch (value) {
517  // CBC presolve is always on.
518  break;
519  }
520  default: {
522  }
523  }
524 }
525 
526 void CBCInterface::SetScalingMode(int value) {
528 }
529 
530 void CBCInterface::SetLpAlgorithm(int value) {
532 }
533 
535  return new CBCInterface(solver);
536 }
537 
538 } // namespace operations_research
539 #endif // #if defined(USE_CBC)
#define CHECK_GE(val1, val2)
Definition: base/logging.h:709
#define CHECK_NE(val1, val2)
Definition: base/logging.h:706
#define LOG(severity)
Definition: base/logging.h:423
#define VLOG(verboselevel)
Definition: base/logging.h:986
void Start()
Definition: timer.h:31
void Restart()
Definition: timer.h:35
double Get() const
Definition: timer.h:45
void AddRowConstraint(MPConstraint *const ct) override
bool IsContinuous() const override
void SetConstraintBounds(int row_index, double lb, double ub) override
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
void ClearConstraint(MPConstraint *const constraint) override
void SetObjectiveCoefficient(const MPVariable *const variable, double coefficient) override
void SetCoefficient(MPConstraint *const constraint, const MPVariable *const variable, double new_value, double old_value) override
MPSolver::BasisStatus row_status(int constraint_index) const override
void SetVariableInteger(int var_index, bool integer) override
void SetObjectiveOffset(double value) override
std::string SolverVersion() const override
absl::Status SetNumThreads(int num_threads) override
void AddVariable(MPVariable *const var) override
CBCInterface(MPSolver *const solver)
int64_t nodes() const override
void SetVariableBounds(int var_index, double lb, double ub) override
int64_t iterations() const override
void SetOptimizationDirection(bool maximize) override
MPSolver::BasisStatus column_status(int variable_index) const override
The class for constraints of a Mathematical Programming (MP) model.
double GetCoefficient(const MPVariable *const var) const
Gets the coefficient of a given variable in the objective.
double offset() const
Gets the constant term in the objective.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
MPVariable * LookupVariableOrNull(const std::string &var_name) const
Looks up a variable by name, and returns nullptr if it does not exist.
const MPObjective & Objective() const
Returns the objective object.
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
MPConstraint * LookupConstraintOrNull(const std::string &constraint_name) const
Looks up a constraint by name, and returns nullptr if it does not exist.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
void SetUnsupportedDoubleParam(MPSolverParameters::DoubleParam param)
void set_constraint_as_extracted(int ct_index, bool extracted)
void SetMIPParameters(const MPSolverParameters &param)
static constexpr int64_t kUnknownNumberOfNodes
static constexpr int64_t kUnknownNumberOfIterations
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
void set_variable_as_extracted(int var_index, bool extracted)
void SetCommonParameters(const MPSolverParameters &param)
This class stores parameter settings for LP and MIP solvers.
@ INCREMENTALITY_OFF
Start solve from scratch.
@ DUAL_TOLERANCE
Advanced usage: tolerance for dual feasibility of basic solutions.
@ PRIMAL_TOLERANCE
Advanced usage: tolerance for primal feasibility of basic solutions.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
@ PRESOLVE
Advanced usage: presolve mode.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
The class for variables of a Mathematical Programming (MP) model.
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
const int FATAL
Definition: log_severity.h:32
Collection of objects used to extend the Constraint Solver library.
MPSolverInterface * BuildCBCInterface(MPSolver *const solver)
int index
Definition: pack.cc:509
int64_t coefficient