OR-Tools  9.2
clp_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 <algorithm>
17#include <cstdint>
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "absl/base/attributes.h"
23#include "absl/memory/memory.h"
24#include "absl/strings/match.h"
25#include "absl/strings/str_format.h"
27#include "ortools/base/hash.h"
30#include "ortools/base/timer.h"
32
33#if defined(USE_CLP) || defined(USE_CBC)
34
35#undef PACKAGE
36#undef VERSION
37#include "ClpConfig.h"
38#include "ClpMessage.hpp"
39#include "ClpSimplex.hpp"
40#include "CoinBuild.hpp"
41
42namespace operations_research {
43
45 public:
46 // Constructor that takes a name for the underlying CLP solver.
47 explicit CLPInterface(MPSolver* const solver);
48 ~CLPInterface() override;
49
50 // Sets the optimization direction (min/max).
51 void SetOptimizationDirection(bool maximize) override;
52
53 // ----- Solve -----
54 // Solve the problem using the parameter values specified.
55 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
56
57 // ----- Model modifications and extraction -----
58 // Resets extracted model
59 void Reset() override;
60
61 // Modify bounds.
62 void SetVariableBounds(int var_index, double lb, double ub) override;
63 void SetVariableInteger(int var_index, bool integer) override;
64 void SetConstraintBounds(int row_index, double lb, double ub) override;
65
66 // Add constraint incrementally.
67 void AddRowConstraint(MPConstraint* const ct) override;
68 // Add variable incrementally.
69 void AddVariable(MPVariable* const var) override;
70 // Change a coefficient in a constraint.
71 void SetCoefficient(MPConstraint* const constraint,
72 const MPVariable* const variable, double new_value,
73 double old_value) override;
74 // Clear a constraint from all its terms.
75 void ClearConstraint(MPConstraint* const constraint) override;
76
77 // Change a coefficient in the linear objective.
78 void SetObjectiveCoefficient(const MPVariable* const variable,
79 double coefficient) override;
80 // Change the constant term in the linear objective.
81 void SetObjectiveOffset(double offset) override;
82 // Clear the objective from all its terms.
83 void ClearObjective() override;
84
85 // ------ Query statistics on the solution and the solve ------
86 // Number of simplex iterations
87 int64_t iterations() const override;
88 // Number of branch-and-bound nodes. Only available for discrete problems.
89 int64_t nodes() const override;
90
91 // Returns the basis status of a row.
92 MPSolver::BasisStatus row_status(int constraint_index) const override;
93 // Returns the basis status of a column.
94 MPSolver::BasisStatus column_status(int variable_index) const override;
95
96 // ----- Misc -----
97 // Query problem type.
98 bool IsContinuous() const override { return true; }
99 bool IsLP() const override { return true; }
100 bool IsMIP() const override { return false; }
101
102 void ExtractNewVariables() override;
103 void ExtractNewConstraints() override;
104 void ExtractObjective() override;
105
106 std::string SolverVersion() const override { return "Clp " CLP_VERSION; }
107
108 void* underlying_solver() override {
109 return reinterpret_cast<void*>(clp_.get());
110 }
111
112 private:
113 // Create dummy variable to be able to create empty constraints.
114 void CreateDummyVariableForEmptyConstraints();
115
116 // Set all parameters in the underlying solver.
117 void SetParameters(const MPSolverParameters& param) override;
118 // Reset to their default value the parameters for which CLP has a
119 // stateful API. To be called after the solve so that the next solve
120 // starts from a clean parameter state.
121 void ResetParameters();
122 // Set each parameter in the underlying solver.
123 void SetRelativeMipGap(double value) override;
124 void SetPrimalTolerance(double value) override;
125 void SetDualTolerance(double value) override;
126 void SetPresolveMode(int value) override;
127 void SetScalingMode(int value) override;
128 void SetLpAlgorithm(int value) override;
129
130 // Transforms basis status from CLP enum to MPSolver::BasisStatus.
131 MPSolver::BasisStatus TransformCLPBasisStatus(
132 ClpSimplex::Status clp_basis_status) const;
133
134 std::unique_ptr<ClpSimplex> clp_; // TODO(user) : remove pointer.
135 std::unique_ptr<ClpSolve> options_; // For parameter setting.
136};
137
138// ----- Solver -----
139
140// Creates a LP/MIP instance with the specified name and minimization objective.
142 : MPSolverInterface(solver), clp_(new ClpSimplex), options_(new ClpSolve) {
143 clp_->setStrParam(ClpProbName, solver_->name_);
144 clp_->setOptimizationDirection(1);
145}
146
148
150 clp_ = absl::make_unique<ClpSimplex>();
151 clp_->setOptimizationDirection(maximize_ ? -1 : 1);
153}
154
155// ------ Model modifications and extraction -----
156
157namespace {
158// Variable indices are shifted by 1 internally because of the dummy "objective
159// offset" variable (with internal index 0).
160int MPSolverVarIndexToClpVarIndex(int var_index) { return var_index + 1; }
161} // namespace
162
163// Not cached
166 clp_->setOptimizationDirection(maximize ? -1 : 1);
167}
168
169void CLPInterface::SetVariableBounds(int var_index, double lb, double ub) {
171 if (variable_is_extracted(var_index)) {
172 // Not cached if the variable has been extracted
174 clp_->setColumnBounds(MPSolverVarIndexToClpVarIndex(var_index), lb, ub);
175 } else {
177 }
178}
179
180// Ignore as CLP does not solve models with integer variables
181void CLPInterface::SetVariableInteger(int var_index, bool integer) {}
182
183void CLPInterface::SetConstraintBounds(int index, double lb, double ub) {
186 // Not cached if the row has been extracted
188 clp_->setRowBounds(index, lb, ub);
189 } else {
191 }
192}
193
195 const MPVariable* const variable,
196 double new_value, double old_value) {
198 if (constraint_is_extracted(constraint->index()) &&
199 variable_is_extracted(variable->index())) {
200 // The modification of the coefficient for an extracted row and
201 // variable is not cached.
202 DCHECK_LE(constraint->index(), last_constraint_index_);
204 clp_->modifyCoefficient(constraint->index(),
205 MPSolverVarIndexToClpVarIndex(variable->index()),
206 new_value);
207 } else {
208 // The modification of an unextracted row or variable is cached
209 // and handled in ExtractModel.
211 }
212}
213
214// Not cached
217 // Constraint may not have been extracted yet.
218 if (!constraint_is_extracted(constraint->index())) return;
219 for (const auto& entry : constraint->coefficients_) {
220 DCHECK(variable_is_extracted(entry.first->index()));
221 clp_->modifyCoefficient(constraint->index(),
222 MPSolverVarIndexToClpVarIndex(entry.first->index()),
223 0.0);
224 }
225}
226
227// Cached
229 double coefficient) {
231 if (variable_is_extracted(variable->index())) {
232 clp_->setObjectiveCoefficient(
233 MPSolverVarIndexToClpVarIndex(variable->index()), coefficient);
234 } else {
236 }
237}
238
239// Cached
241 // Constant term. Use -offset instead of +offset because CLP does
242 // not follow conventions.
244 clp_->setObjectiveOffset(-offset);
245}
246
247// Clear objective of all its terms.
250 // Clear linear terms
251 for (const auto& entry : solver_->objective_->coefficients_) {
252 const int mpsolver_var_index = entry.first->index();
253 // Variable may have not been extracted yet.
254 if (!variable_is_extracted(mpsolver_var_index)) {
256 } else {
257 clp_->setObjectiveCoefficient(
258 MPSolverVarIndexToClpVarIndex(mpsolver_var_index), 0.0);
259 }
260 }
261 // Clear constant term.
262 clp_->setObjectiveOffset(0.0);
263}
264
267}
268
271}
272
273void CLPInterface::CreateDummyVariableForEmptyConstraints() {
274 clp_->setColumnBounds(kDummyVariableIndex, 0.0, 0.0);
275 clp_->setObjectiveCoefficient(kDummyVariableIndex, 0.0);
276 // Workaround for peculiar signature of setColumnName. Note that we do need
277 // std::string here, and not 'string', which aren't the same as of 2013-12
278 // (this will change later).
279 std::string dummy = "dummy"; // We do need to create this temporary variable.
280 clp_->setColumnName(kDummyVariableIndex, dummy);
281}
282
283// Define new variables and add them to existing constraints.
285 // Define new variables
286 int total_num_vars = solver_->variables_.size();
287 if (total_num_vars > last_variable_index_) {
289 // Faster extraction when nothing has been extracted yet.
290 clp_->resize(0, total_num_vars + 1);
291 CreateDummyVariableForEmptyConstraints();
292 for (int i = 0; i < total_num_vars; ++i) {
293 MPVariable* const var = solver_->variables_[i];
295 if (!var->name().empty()) {
296 std::string name = var->name();
297 clp_->setColumnName(MPSolverVarIndexToClpVarIndex(i), name);
298 }
299 clp_->setColumnBounds(MPSolverVarIndexToClpVarIndex(i), var->lb(),
300 var->ub());
301 }
302 } else {
303 // TODO(user): This could perhaps be made slightly faster by
304 // iterating through old constraints, constructing by hand the
305 // column-major representation of the addition to them and call
306 // clp_->addColumns. But this is good enough for now.
307 // Create new variables.
308 for (int j = last_variable_index_; j < total_num_vars; ++j) {
309 MPVariable* const var = solver_->variables_[j];
312 // The true objective coefficient will be set later in ExtractObjective.
313 double tmp_obj_coef = 0.0;
314 clp_->addColumn(0, nullptr, nullptr, var->lb(), var->ub(),
315 tmp_obj_coef);
316 if (!var->name().empty()) {
317 std::string name = var->name();
318 clp_->setColumnName(MPSolverVarIndexToClpVarIndex(j), name);
319 }
320 }
321 // Add new variables to existing constraints.
322 for (int i = 0; i < last_constraint_index_; i++) {
323 MPConstraint* const ct = solver_->constraints_[i];
324 const int ct_index = ct->index();
325 for (const auto& entry : ct->coefficients_) {
326 const int mpsolver_var_index = entry.first->index();
327 DCHECK(variable_is_extracted(mpsolver_var_index));
328 if (mpsolver_var_index >= last_variable_index_) {
329 clp_->modifyCoefficient(
330 ct_index, MPSolverVarIndexToClpVarIndex(mpsolver_var_index),
331 entry.second);
332 }
333 }
334 }
335 }
336 }
337}
338
339// Define new constraints on old and new variables.
341 int total_num_rows = solver_->constraints_.size();
342 if (last_constraint_index_ < total_num_rows) {
343 // Find the length of the longest row.
344 int max_row_length = 0;
345 for (int i = last_constraint_index_; i < total_num_rows; ++i) {
346 MPConstraint* const ct = solver_->constraints_[i];
348 set_constraint_as_extracted(ct->index(), true);
349 if (ct->coefficients_.size() > max_row_length) {
350 max_row_length = ct->coefficients_.size();
351 }
352 }
353 // Make space for dummy variable.
354 max_row_length = std::max(1, max_row_length);
355 std::unique_ptr<int[]> indices(new int[max_row_length]);
356 std::unique_ptr<double[]> coefs(new double[max_row_length]);
357 CoinBuild build_object;
358 // Add each new constraint.
359 for (int i = last_constraint_index_; i < total_num_rows; ++i) {
360 MPConstraint* const ct = solver_->constraints_[i];
362 int size = ct->coefficients_.size();
363 if (size == 0) {
364 // Add dummy variable to be able to build the constraint.
365 indices[0] = kDummyVariableIndex;
366 coefs[0] = 1.0;
367 size = 1;
368 }
369 int j = 0;
370 for (const auto& entry : ct->coefficients_) {
371 const int mpsolver_var_index = entry.first->index();
372 DCHECK(variable_is_extracted(mpsolver_var_index));
373 indices[j] = MPSolverVarIndexToClpVarIndex(mpsolver_var_index);
374 coefs[j] = entry.second;
375 j++;
376 }
377 build_object.addRow(size, indices.get(), coefs.get(), ct->lb(), ct->ub());
378 }
379 // Add and name the rows.
380 clp_->addRows(build_object);
381 for (int i = last_constraint_index_; i < total_num_rows; ++i) {
382 MPConstraint* const ct = solver_->constraints_[i];
383 if (!ct->name().empty()) {
384 std::string name = ct->name();
385 clp_->setRowName(ct->index(), name);
386 }
387 }
388 }
389}
390
392 // Linear objective: set objective coefficients for all variables
393 // (some might have been modified)
394 for (const auto& entry : solver_->objective_->coefficients_) {
395 clp_->setObjectiveCoefficient(
396 MPSolverVarIndexToClpVarIndex(entry.first->index()), entry.second);
397 }
398
399 // Constant term. Use -offset instead of +offset because CLP does
400 // not follow conventions.
401 clp_->setObjectiveOffset(-solver_->Objective().offset());
402}
403
404// Extracts model and solve the LP/MIP. Returns the status of the search.
406 try {
407 WallTimer timer;
408 timer.Start();
409
412 Reset();
413 }
414
415 // Set log level.
416 CoinMessageHandler message_handler;
417 clp_->passInMessageHandler(&message_handler);
418 if (quiet_) {
419 message_handler.setLogLevel(1, 0);
420 clp_->setLogLevel(0);
421 } else {
422 message_handler.setLogLevel(1, 1);
423 clp_->setLogLevel(1);
424 }
425
426 // Special case if the model is empty since CLP is not able to
427 // handle this special case by itself.
428 if (solver_->variables_.empty() && solver_->constraints_.empty()) {
432 return result_status_;
433 }
434
435 ExtractModel();
436 VLOG(1) << absl::StrFormat("Model built in %.3f seconds.", timer.Get());
437
438 // Time limit.
439 if (solver_->time_limit() != 0) {
440 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
441 clp_->setMaximumSeconds(solver_->time_limit_in_secs());
442 } else {
443 clp_->setMaximumSeconds(-1.0);
444 }
445
446 // Start from a fresh set of default parameters and set them to
447 // specified values.
448 options_ = absl::make_unique<ClpSolve>();
449 SetParameters(param);
450
451 // Solve
452 timer.Restart();
453 clp_->initialSolve(*options_);
454 VLOG(1) << absl::StrFormat("Solved in %.3f seconds.", timer.Get());
455
456 // Check the status: optimal, infeasible, etc.
457 int tmp_status = clp_->status();
458 VLOG(1) << "clp result status: " << tmp_status;
459 switch (tmp_status) {
460 case CLP_SIMPLEX_FINISHED:
462 break;
463 case CLP_SIMPLEX_INFEASIBLE:
465 break;
466 case CLP_SIMPLEX_UNBOUNDED:
468 break;
469 case CLP_SIMPLEX_STOPPED:
471 break;
472 default:
474 break;
475 }
476
479 // Get the results
480 objective_value_ = clp_->objectiveValue();
481 VLOG(1) << "objective=" << objective_value_;
482 const double* const values = clp_->getColSolution();
483 const double* const reduced_costs = clp_->getReducedCost();
484 for (int i = 0; i < solver_->variables_.size(); ++i) {
485 MPVariable* const var = solver_->variables_[i];
486 const int clp_var_index = MPSolverVarIndexToClpVarIndex(var->index());
487 const double val = values[clp_var_index];
488 var->set_solution_value(val);
489 VLOG(3) << var->name() << ": value = " << val;
490 double reduced_cost = reduced_costs[clp_var_index];
491 var->set_reduced_cost(reduced_cost);
492 VLOG(4) << var->name() << ": reduced cost = " << reduced_cost;
493 }
494 const double* const dual_values = clp_->getRowPrice();
495 for (int i = 0; i < solver_->constraints_.size(); ++i) {
496 MPConstraint* const ct = solver_->constraints_[i];
497 const int constraint_index = ct->index();
498 const double dual_value = dual_values[constraint_index];
499 ct->set_dual_value(dual_value);
500 VLOG(4) << "row " << ct->index() << " dual value = " << dual_value;
501 }
502 }
503
504 ResetParameters();
506 return result_status_;
507 } catch (CoinError& e) {
508 LOG(WARNING) << "Caught exception in Coin LP: " << e.message();
510 return result_status_;
511 }
512}
513
514MPSolver::BasisStatus CLPInterface::TransformCLPBasisStatus(
515 ClpSimplex::Status clp_basis_status) const {
516 switch (clp_basis_status) {
517 case ClpSimplex::isFree:
518 return MPSolver::FREE;
519 case ClpSimplex::basic:
520 return MPSolver::BASIC;
521 case ClpSimplex::atUpperBound:
523 case ClpSimplex::atLowerBound:
525 case ClpSimplex::superBasic:
526 return MPSolver::FREE;
527 case ClpSimplex::isFixed:
529 default:
530 LOG(FATAL) << "Unknown CLP basis status";
531 return MPSolver::FREE;
532 }
533}
534
535// ------ Query statistics on the solution and the solve ------
536
539 return clp_->getIterationCount();
540}
541
542int64_t CLPInterface::nodes() const {
543 LOG(DFATAL) << "Number of nodes only available for discrete problems";
545}
546
548 DCHECK_LE(0, constraint_index);
549 DCHECK_GT(last_constraint_index_, constraint_index);
550 const ClpSimplex::Status clp_basis_status =
551 clp_->getRowStatus(constraint_index);
552 return TransformCLPBasisStatus(clp_basis_status);
553}
554
556 DCHECK_LE(0, variable_index);
557 DCHECK_GT(last_variable_index_, variable_index);
558 const ClpSimplex::Status clp_basis_status =
559 clp_->getColumnStatus(MPSolverVarIndexToClpVarIndex(variable_index));
560 return TransformCLPBasisStatus(clp_basis_status);
561}
562
563// ------ Parameters ------
564
565void CLPInterface::SetParameters(const MPSolverParameters& param) {
566 SetCommonParameters(param);
567}
568
569void CLPInterface::ResetParameters() {
570 clp_->setPrimalTolerance(MPSolverParameters::kDefaultPrimalTolerance);
571 clp_->setDualTolerance(MPSolverParameters::kDefaultDualTolerance);
572}
573
574void CLPInterface::SetRelativeMipGap(double value) {
575 LOG(WARNING) << "The relative MIP gap is only available "
576 << "for discrete problems.";
577}
578
579void CLPInterface::SetPrimalTolerance(double value) {
580 clp_->setPrimalTolerance(value);
581}
582
583void CLPInterface::SetDualTolerance(double value) {
584 clp_->setDualTolerance(value);
585}
586
587void CLPInterface::SetPresolveMode(int value) {
588 switch (value) {
590 options_->setPresolveType(ClpSolve::presolveOff);
591 break;
592 }
594 options_->setPresolveType(ClpSolve::presolveOn);
595 break;
596 }
597 default: {
599 }
600 }
601}
602
603void CLPInterface::SetScalingMode(int value) {
605}
606
607void CLPInterface::SetLpAlgorithm(int value) {
608 switch (value) {
610 options_->setSolveType(ClpSolve::useDual);
611 break;
612 }
614 options_->setSolveType(ClpSolve::usePrimal);
615 break;
616 }
618 options_->setSolveType(ClpSolve::useBarrier);
619 break;
620 }
621 default: {
623 value);
624 }
625 }
626}
627
629 return new CLPInterface(solver);
630}
631
632} // namespace operations_research
633#endif // #if defined(USE_CBC) || defined(USE_CLP)
int64_t max
Definition: alldiff_cst.cc:140
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:889
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:888
#define DCHECK_GT(val1, val2)
Definition: base/logging.h:892
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:890
#define LOG(severity)
Definition: base/logging.h:417
#define DCHECK(condition)
Definition: base/logging.h:886
#define VLOG(verboselevel)
Definition: base/logging.h:980
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
CLPInterface(MPSolver *const solver)
void SetVariableInteger(int var_index, bool integer) override
void SetObjectiveOffset(double offset) override
std::string SolverVersion() const override
void AddVariable(MPVariable *const var) override
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.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
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 ...
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
const MPObjective & Objective() const
Returns the objective object.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
void set_constraint_as_extracted(int ct_index, bool extracted)
bool constraint_is_extracted(int ct_index) const
static constexpr int64_t kUnknownNumberOfNodes
bool variable_is_extracted(int var_index) const
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.
@ 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.
int index() const
Returns the index of the variable in the MPSolver::variables_.
const std::string name
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
const int WARNING
Definition: log_severity.h:31
const int FATAL
Definition: log_severity.h:32
Collection of objects used to extend the Constraint Solver library.
MPSolverInterface * BuildCLPInterface(MPSolver *const solver)
int index
Definition: pack.cc:509
int64_t coefficient