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