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