OR-Tools  8.1
integral_solver.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 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 
15 
16 #include <math.h>
17 
18 #include <vector>
19 
20 #include "ortools/bop/bop_solver.h"
22 
23 namespace operations_research {
24 namespace bop {
25 
26 using ::operations_research::glop::ColIndex;
30 using ::operations_research::glop::LinearProgram;
31 using ::operations_research::glop::LPDecomposer;
32 using ::operations_research::glop::RowIndex;
33 using ::operations_research::glop::SparseColumn;
34 using ::operations_research::glop::SparseMatrix;
35 using ::operations_research::sat::LinearBooleanConstraint;
36 using ::operations_research::sat::LinearBooleanProblem;
37 using ::operations_research::sat::LinearObjective;
38 
39 namespace {
40 // TODO(user): Use an existing one or move it to util.
42  const double kTolerance = 1e-10;
43  return std::abs(x - round(x)) <= kTolerance;
44 }
45 
46 // Returns true when all the variables of the problem are Boolean, and all the
47 // constraints have integer coefficients.
48 // TODO(user): Move to SAT util.
49 bool ProblemIsBooleanAndHasOnlyIntegralConstraints(
50  const LinearProgram& linear_problem) {
51  const glop::SparseMatrix& matrix = linear_problem.GetSparseMatrix();
52 
53  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
54  const Fractional lower_bound = linear_problem.variable_lower_bounds()[col];
55  const Fractional upper_bound = linear_problem.variable_upper_bounds()[col];
56 
57  if (lower_bound <= -1.0 || upper_bound >= 2.0) {
58  // Integral variable.
59  return false;
60  }
61 
62  for (const SparseColumn::Entry e : matrix.column(col)) {
63  if (!IsIntegerWithinTolerance(e.coefficient())) {
64  // Floating coefficient.
65  return false;
66  }
67  }
68  }
69  return true;
70 }
71 
72 // Builds a LinearBooleanProblem based on a LinearProgram with all the variables
73 // being booleans and all the constraints having only integral coefficients.
74 // TODO(user): Move to SAT util.
75 void BuildBooleanProblemWithIntegralConstraints(
76  const LinearProgram& linear_problem, const DenseRow& initial_solution,
77  LinearBooleanProblem* boolean_problem,
78  std::vector<bool>* boolean_initial_solution) {
79  CHECK(boolean_problem != nullptr);
80  boolean_problem->Clear();
81 
82  const glop::SparseMatrix& matrix = linear_problem.GetSparseMatrix();
83  // Create Boolean variables.
84  for (ColIndex col(0); col < matrix.num_cols(); ++col) {
85  boolean_problem->add_var_names(linear_problem.GetVariableName(col));
86  }
87  boolean_problem->set_num_variables(matrix.num_cols().value());
88  boolean_problem->set_name(linear_problem.name());
89 
90  // Create constraints.
91  for (RowIndex row(0); row < matrix.num_rows(); ++row) {
92  LinearBooleanConstraint* const constraint =
93  boolean_problem->add_constraints();
94  constraint->set_name(linear_problem.GetConstraintName(row));
95  if (linear_problem.constraint_lower_bounds()[row] != -kInfinity) {
96  constraint->set_lower_bound(
97  linear_problem.constraint_lower_bounds()[row]);
98  }
99  if (linear_problem.constraint_upper_bounds()[row] != kInfinity) {
100  constraint->set_upper_bound(
101  linear_problem.constraint_upper_bounds()[row]);
102  }
103  }
104 
105  // Store the constraint coefficients.
106  for (ColIndex col(0); col < matrix.num_cols(); ++col) {
107  for (const SparseColumn::Entry e : matrix.column(col)) {
108  LinearBooleanConstraint* const constraint =
109  boolean_problem->mutable_constraints(e.row().value());
110  constraint->add_literals(col.value() + 1);
111  constraint->add_coefficients(e.coefficient());
112  }
113  }
114 
115  // Add the unit constraints to fix the variables since the variable bounds
116  // are always [0, 1] in a BooleanLinearProblem.
117  for (ColIndex col(0); col < matrix.num_cols(); ++col) {
118  // TODO(user): double check the rounding, and add unit test for this.
119  const int lb = std::round(linear_problem.variable_lower_bounds()[col]);
120  const int ub = std::round(linear_problem.variable_upper_bounds()[col]);
121  if (lb == ub) {
122  LinearBooleanConstraint* ct = boolean_problem->add_constraints();
123  ct->set_lower_bound(ub);
124  ct->set_upper_bound(ub);
125  ct->add_literals(col.value() + 1);
126  ct->add_coefficients(1.0);
127  }
128  }
129 
130  // Create the minimization objective.
131  std::vector<double> coefficients;
132  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
133  const Fractional coeff = linear_problem.objective_coefficients()[col];
134  if (coeff != 0.0) coefficients.push_back(coeff);
135  }
136  double scaling_factor = 0.0;
137  double relative_error = 0.0;
139  &relative_error);
140  const int64 gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
141  LinearObjective* const objective = boolean_problem->mutable_objective();
142  objective->set_offset(linear_problem.objective_offset() * scaling_factor /
143  gcd);
144 
145  // Note that here we set the scaling factor for the inverse operation of
146  // getting the "true" objective value from the scaled one. Hence the inverse.
147  objective->set_scaling_factor(1.0 / scaling_factor * gcd);
148  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
149  const Fractional coeff = linear_problem.objective_coefficients()[col];
150  const int64 value = static_cast<int64>(round(coeff * scaling_factor)) / gcd;
151  if (value != 0) {
152  objective->add_literals(col.value() + 1);
153  objective->add_coefficients(value);
154  }
155  }
156 
157  // If the problem was a maximization one, we need to modify the objective.
158  if (linear_problem.IsMaximizationProblem()) {
159  sat::ChangeOptimizationDirection(boolean_problem);
160  }
161 
162  // Fill the Boolean initial solution.
163  if (!initial_solution.empty()) {
164  CHECK(boolean_initial_solution != nullptr);
165  CHECK_EQ(boolean_problem->num_variables(), initial_solution.size());
166  boolean_initial_solution->assign(boolean_problem->num_variables(), false);
167  for (int i = 0; i < initial_solution.size(); ++i) {
168  (*boolean_initial_solution)[i] = (initial_solution[ColIndex(i)] != 0);
169  }
170  }
171 }
172 
173 //------------------------------------------------------------------------------
174 // IntegralVariable
175 //------------------------------------------------------------------------------
176 // Model an integral variable using Boolean variables.
177 // TODO(user): Enable discrete representation by value, i.e. use three Boolean
178 // variables when only possible values are 10, 12, 32.
179 // In the same way, when only two consecutive values are possible
180 // use only one Boolean variable with an offset.
181 class IntegralVariable {
182  public:
183  IntegralVariable();
184 
185  // Creates the minimal number of Boolean variables to represent an integral
186  // variable with range [lower_bound, upper_bound]. start_var_index corresponds
187  // to the next available Boolean variable index. If three Boolean variables
188  // are needed to model the integral variable, the used variables will have
189  // indices start_var_index, start_var_index +1, and start_var_index +2.
190  void BuildFromRange(int start_var_index, Fractional lower_bound,
191  Fractional upper_bound);
192 
193  void Clear();
194  void set_offset(int64 offset) { offset_ = offset; }
195  void set_weight(VariableIndex var, int64 weight);
196 
197  int GetNumberOfBooleanVariables() const { return bits_.size(); }
198 
199  const std::vector<VariableIndex>& bits() const { return bits_; }
200  const std::vector<int64>& weights() const { return weights_; }
201  int64 offset() const { return offset_; }
202 
203  // Returns the value of the integral variable based on the Boolean conversion
204  // and the Boolean solution to the problem.
205  int64 GetSolutionValue(const BopSolution& solution) const;
206 
207  // Returns the values of the Boolean variables based on the Boolean conversion
208  // and the integral value of this variable. This only works for variables that
209  // were constructed using BuildFromRange() (for which can_be_reversed_ is
210  // true).
211  std::vector<bool> GetBooleanSolutionValues(int64 integral_value) const;
212 
213  std::string DebugString() const;
214 
215  private:
216  // The value of the integral variable is expressed as
217  // sum_i(weights[i] * Value(bits[i])) + offset.
218  // Note that weights can be negative to represent negative values.
219  std::vector<VariableIndex> bits_;
220  std::vector<int64> weights_;
221  int64 offset_;
222  // True if the values of the boolean variables representing this integral
223  // variable can be deduced from the integral variable's value. Namely, this is
224  // true for variables built using BuildFromRange() but usually false for
225  // variables built using set_weight().
226  bool can_be_reversed_;
227 };
228 
229 IntegralVariable::IntegralVariable()
230  : bits_(), weights_(), offset_(0), can_be_reversed_(true) {}
231 
232 void IntegralVariable::BuildFromRange(int start_var_index,
233  Fractional lower_bound,
234  Fractional upper_bound) {
235  Clear();
236 
237  // Integral variable. Split the variable into the minimum number of bits
238  // required to model the upper bound.
239  CHECK_NE(-kInfinity, lower_bound);
240  CHECK_NE(kInfinity, upper_bound);
241 
242  const int64 integral_lower_bound = static_cast<int64>(ceil(lower_bound));
243  const int64 integral_upper_bound = static_cast<int64>(floor(upper_bound));
244  offset_ = integral_lower_bound;
245  const int64 delta = integral_upper_bound - integral_lower_bound;
246  const int num_used_bits = MostSignificantBitPosition64(delta) + 1;
247  for (int i = 0; i < num_used_bits; ++i) {
248  bits_.push_back(VariableIndex(start_var_index + i));
249  weights_.push_back(1ULL << i);
250  }
251 }
252 
253 void IntegralVariable::Clear() {
254  bits_.clear();
255  weights_.clear();
256  offset_ = 0;
257  can_be_reversed_ = true;
258 }
259 
260 void IntegralVariable::set_weight(VariableIndex var, int64 weight) {
261  bits_.push_back(var);
262  weights_.push_back(weight);
263  can_be_reversed_ = false;
264 }
265 
266 int64 IntegralVariable::GetSolutionValue(const BopSolution& solution) const {
267  int64 value = offset_;
268  for (int i = 0; i < bits_.size(); ++i) {
269  value += weights_[i] * solution.Value(bits_[i]);
270  }
271  return value;
272 }
273 
274 std::vector<bool> IntegralVariable::GetBooleanSolutionValues(
275  int64 integral_value) const {
276  if (can_be_reversed_) {
277  DCHECK(std::is_sorted(weights_.begin(), weights_.end()));
278  std::vector<bool> boolean_values(weights_.size(), false);
279  int64 remaining_value = integral_value - offset_;
280  for (int i = weights_.size() - 1; i >= 0; --i) {
281  if (remaining_value >= weights_[i]) {
282  boolean_values[i] = true;
283  remaining_value -= weights_[i];
284  }
285  }
286  CHECK_EQ(0, remaining_value)
287  << "Couldn't map integral value to boolean variables.";
288  return boolean_values;
289  }
290  return std::vector<bool>();
291 }
292 
293 std::string IntegralVariable::DebugString() const {
294  std::string str;
295  CHECK_EQ(bits_.size(), weights_.size());
296  for (int i = 0; i < bits_.size(); ++i) {
297  str += absl::StrFormat("%d [%d] ", weights_[i], bits_[i].value());
298  }
299  str += absl::StrFormat(" Offset: %d", offset_);
300  return str;
301 }
302 
303 //------------------------------------------------------------------------------
304 // IntegralProblemConverter
305 //------------------------------------------------------------------------------
306 // This class is used to convert a LinearProblem containing integral variables
307 // into a LinearBooleanProblem that Bop can consume.
308 // The converter tries to reuse existing Boolean variables as much as possible,
309 // but there are no guarantees to model all integral variables using the total
310 // minimal number of Boolean variables.
311 // Consider for instance the constraint "x - 2 * y = 0".
312 // Depending on the declaration order, two different outcomes are possible:
313 // - When x is considered first, the converter will generate new variables
314 // for both x and y as we only consider integral weights, i.e. y = x / 2.
315 // - When y is considered first, the converter will reuse Boolean variables
316 // from y to model x as x = 2 * y (integral weight).
317 //
318 // Note that the converter only deals with integral variables, i.e. no
319 // continuous variables.
320 class IntegralProblemConverter {
321  public:
322  IntegralProblemConverter();
323 
324  // Converts the LinearProgram into a LinearBooleanProblem. If an initial
325  // solution is given (i.e. if its size is not zero), converts it into a
326  // Boolean solution.
327  // Returns false when the conversion fails.
328  bool ConvertToBooleanProblem(const LinearProgram& linear_problem,
329  const DenseRow& initial_solution,
330  LinearBooleanProblem* boolean_problem,
331  std::vector<bool>* boolean_initial_solution);
332 
333  // Returns the value of a variable of the original problem based on the
334  // Boolean conversion and the Boolean solution to the problem.
335  int64 GetSolutionValue(ColIndex global_col,
336  const BopSolution& solution) const;
337 
338  private:
339  // Returns true when the linear_problem_ can be converted into a Boolean
340  // problem. Note that floating weights and continuous variables are not
341  // supported.
342  bool CheckProblem(const LinearProgram& linear_problem) const;
343 
344  // Initializes the type of each variable of the linear_problem_.
345  void InitVariableTypes(const LinearProgram& linear_problem,
346  LinearBooleanProblem* boolean_problem);
347 
348  // Converts all variables of the problem.
349  void ConvertAllVariables(const LinearProgram& linear_problem,
350  LinearBooleanProblem* boolean_problem);
351 
352  // Adds all variables constraints, i.e. lower and upper bounds of variables.
353  void AddVariableConstraints(const LinearProgram& linear_problem,
354  LinearBooleanProblem* boolean_problem);
355 
356  // Converts all constraints from LinearProgram to LinearBooleanProblem.
357  void ConvertAllConstraints(const LinearProgram& linear_problem,
358  LinearBooleanProblem* boolean_problem);
359 
360  // Converts the objective from LinearProgram to LinearBooleanProblem.
361  void ConvertObjective(const LinearProgram& linear_problem,
362  LinearBooleanProblem* boolean_problem);
363 
364  // Converts the integral variable represented by col in the linear_problem_
365  // into an IntegralVariable using existing Boolean variables.
366  // Returns false when existing Boolean variables are not enough to model
367  // the integral variable.
368  bool ConvertUsingExistingBooleans(const LinearProgram& linear_problem,
369  ColIndex col,
370  IntegralVariable* integral_var);
371 
372  // Creates the integral_var using the given linear_problem_ constraint.
373  // The constraint is an equality constraint and contains only one integral
374  // variable (already the case in the model or thanks to previous
375  // booleanization of other integral variables), i.e.
376  // bound <= w * integral_var + sum(w_i * b_i) <= bound
377  // The remaining integral variable can then be expressed:
378  // integral_var == (bound + sum(-w_i * b_i)) / w
379  // Note that all divisions by w have to be integral as Bop only deals with
380  // integral coefficients.
381  bool CreateVariableUsingConstraint(const LinearProgram& linear_problem,
382  RowIndex constraint,
383  IntegralVariable* integral_var);
384 
385  // Adds weighted integral variable represented by col to the current dense
386  // constraint.
387  Fractional AddWeightedIntegralVariable(
388  ColIndex col, Fractional weight,
390 
391  // Scales weights and adds all non-zero scaled weights and literals to t.
392  // t is a constraint or the objective.
393  // Returns the bound error due to the scaling.
394  // The weight is scaled using:
395  // static_cast<int64>(round(weight * scaling_factor)) / gcd;
396  template <class T>
397  double ScaleAndSparsifyWeights(
398  double scaling_factor, int64 gcd,
399  const absl::StrongVector<VariableIndex, Fractional>& dense_weights, T* t);
400 
401  // Returns true when at least one element is non-zero.
402  bool HasNonZeroWeights(
403  const absl::StrongVector<VariableIndex, Fractional>& dense_weights) const;
404 
405  bool problem_is_boolean_and_has_only_integral_constraints_;
406 
407  // global_to_boolean_[i] represents the Boolean variable index in Bop; when
408  // negative -global_to_boolean_[i] - 1 represents the index of the
409  // integral variable in integral_variables_.
410  absl::StrongVector</*global_col*/ glop::ColIndex, /*boolean_col*/ int>
411  global_to_boolean_;
412  std::vector<IntegralVariable> integral_variables_;
413  std::vector<ColIndex> integral_indices_;
414  int num_boolean_variables_;
415 
416  enum VariableType { BOOLEAN, INTEGRAL, INTEGRAL_EXPRESSED_AS_BOOLEAN };
418 };
419 
420 IntegralProblemConverter::IntegralProblemConverter()
421  : global_to_boolean_(),
422  integral_variables_(),
423  integral_indices_(),
424  num_boolean_variables_(0),
425  variable_types_() {}
426 
427 bool IntegralProblemConverter::ConvertToBooleanProblem(
428  const LinearProgram& linear_problem, const DenseRow& initial_solution,
429  LinearBooleanProblem* boolean_problem,
430  std::vector<bool>* boolean_initial_solution) {
431  bool use_initial_solution = (initial_solution.size() > 0);
432  if (use_initial_solution) {
433  CHECK_EQ(initial_solution.size(), linear_problem.num_variables())
434  << "The initial solution should have the same number of variables as "
435  "the LinearProgram.";
436  CHECK(boolean_initial_solution != nullptr);
437  }
438  if (!CheckProblem(linear_problem)) {
439  return false;
440  }
441 
442  problem_is_boolean_and_has_only_integral_constraints_ =
443  ProblemIsBooleanAndHasOnlyIntegralConstraints(linear_problem);
444  if (problem_is_boolean_and_has_only_integral_constraints_) {
445  BuildBooleanProblemWithIntegralConstraints(linear_problem, initial_solution,
446  boolean_problem,
447  boolean_initial_solution);
448  return true;
449  }
450 
451  InitVariableTypes(linear_problem, boolean_problem);
452  ConvertAllVariables(linear_problem, boolean_problem);
453  boolean_problem->set_num_variables(num_boolean_variables_);
454  boolean_problem->set_name(linear_problem.name());
455 
456  AddVariableConstraints(linear_problem, boolean_problem);
457  ConvertAllConstraints(linear_problem, boolean_problem);
458  ConvertObjective(linear_problem, boolean_problem);
459 
460  // A BooleanLinearProblem is always in the minimization form.
461  if (linear_problem.IsMaximizationProblem()) {
462  sat::ChangeOptimizationDirection(boolean_problem);
463  }
464 
465  if (use_initial_solution) {
466  boolean_initial_solution->assign(boolean_problem->num_variables(), false);
467  for (ColIndex global_col(0); global_col < global_to_boolean_.size();
468  ++global_col) {
469  const int col = global_to_boolean_[global_col];
470  if (col >= 0) {
471  (*boolean_initial_solution)[col] = (initial_solution[global_col] != 0);
472  } else {
473  const IntegralVariable& integral_variable =
474  integral_variables_[-col - 1];
475  const std::vector<VariableIndex>& boolean_cols =
476  integral_variable.bits();
477  const std::vector<bool>& boolean_values =
478  integral_variable.GetBooleanSolutionValues(
479  round(initial_solution[global_col]));
480  if (!boolean_values.empty()) {
481  CHECK_EQ(boolean_cols.size(), boolean_values.size());
482  for (int i = 0; i < boolean_values.size(); ++i) {
483  const int boolean_col = boolean_cols[i].value();
484  (*boolean_initial_solution)[boolean_col] = boolean_values[i];
485  }
486  }
487  }
488  }
489  }
490 
491  return true;
492 }
493 
494 int64 IntegralProblemConverter::GetSolutionValue(
495  ColIndex global_col, const BopSolution& solution) const {
496  if (problem_is_boolean_and_has_only_integral_constraints_) {
497  return solution.Value(VariableIndex(global_col.value()));
498  }
499 
500  const int pos = global_to_boolean_[global_col];
501  return pos >= 0 ? solution.Value(VariableIndex(pos))
502  : integral_variables_[-pos - 1].GetSolutionValue(solution);
503 }
504 
505 bool IntegralProblemConverter::CheckProblem(
506  const LinearProgram& linear_problem) const {
507  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
508  if (!linear_problem.IsVariableInteger(col)) {
509  LOG(ERROR) << "Variable " << linear_problem.GetVariableName(col)
510  << " is continuous. This is not supported by BOP.";
511  return false;
512  }
513  if (linear_problem.variable_lower_bounds()[col] == -kInfinity) {
514  LOG(ERROR) << "Variable " << linear_problem.GetVariableName(col)
515  << " has no lower bound. This is not supported by BOP.";
516  return false;
517  }
518  if (linear_problem.variable_upper_bounds()[col] == kInfinity) {
519  LOG(ERROR) << "Variable " << linear_problem.GetVariableName(col)
520  << " has no upper bound. This is not supported by BOP.";
521  return false;
522  }
523  }
524  return true;
525 }
526 
527 void IntegralProblemConverter::InitVariableTypes(
528  const LinearProgram& linear_problem,
529  LinearBooleanProblem* boolean_problem) {
530  global_to_boolean_.assign(linear_problem.num_variables().value(), 0);
531  variable_types_.assign(linear_problem.num_variables().value(), INTEGRAL);
532  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
533  const Fractional lower_bound = linear_problem.variable_lower_bounds()[col];
534  const Fractional upper_bound = linear_problem.variable_upper_bounds()[col];
535 
536  if (lower_bound > -1.0 && upper_bound < 2.0) {
537  // Boolean variable.
538  variable_types_[col] = BOOLEAN;
539  global_to_boolean_[col] = num_boolean_variables_;
540  ++num_boolean_variables_;
541  boolean_problem->add_var_names(linear_problem.GetVariableName(col));
542  } else {
543  // Integral variable.
544  variable_types_[col] = INTEGRAL;
545  integral_indices_.push_back(col);
546  }
547  }
548 }
549 
550 void IntegralProblemConverter::ConvertAllVariables(
551  const LinearProgram& linear_problem,
552  LinearBooleanProblem* boolean_problem) {
553  for (const ColIndex col : integral_indices_) {
554  CHECK_EQ(INTEGRAL, variable_types_[col]);
555  IntegralVariable integral_var;
556  if (!ConvertUsingExistingBooleans(linear_problem, col, &integral_var)) {
557  const Fractional lower_bound =
558  linear_problem.variable_lower_bounds()[col];
559  const Fractional upper_bound =
560  linear_problem.variable_upper_bounds()[col];
561  integral_var.BuildFromRange(num_boolean_variables_, lower_bound,
562  upper_bound);
563  num_boolean_variables_ += integral_var.GetNumberOfBooleanVariables();
564  const std::string var_name = linear_problem.GetVariableName(col);
565  for (int i = 0; i < integral_var.bits().size(); ++i) {
566  boolean_problem->add_var_names(var_name + absl::StrFormat("_%d", i));
567  }
568  }
569  integral_variables_.push_back(integral_var);
570  global_to_boolean_[col] = -integral_variables_.size();
571  variable_types_[col] = INTEGRAL_EXPRESSED_AS_BOOLEAN;
572  }
573 }
574 
575 void IntegralProblemConverter::ConvertAllConstraints(
576  const LinearProgram& linear_problem,
577  LinearBooleanProblem* boolean_problem) {
578  // TODO(user): This is the way it's done in glop/proto_utils.cc but having
579  // to transpose looks unnecessary costly.
580  glop::SparseMatrix transpose;
581  transpose.PopulateFromTranspose(linear_problem.GetSparseMatrix());
582 
583  double max_relative_error = 0.0;
584  double max_bound_error = 0.0;
585  double max_scaling_factor = 0.0;
586  double relative_error = 0.0;
587  double scaling_factor = 0.0;
588  std::vector<double> coefficients;
589  for (RowIndex row(0); row < linear_problem.num_constraints(); ++row) {
590  Fractional offset = 0.0;
592  num_boolean_variables_, 0.0);
593  for (const SparseColumn::Entry e : transpose.column(RowToColIndex(row))) {
594  // Cast in ColIndex due to the transpose.
595  offset += AddWeightedIntegralVariable(RowToColIndex(e.row()),
596  e.coefficient(), &dense_weights);
597  }
598  if (!HasNonZeroWeights(dense_weights)) {
599  continue;
600  }
601 
602  // Compute the scaling for non-integral weights.
603  coefficients.clear();
604  for (VariableIndex var(0); var < num_boolean_variables_; ++var) {
605  if (dense_weights[var] != 0.0) {
606  coefficients.push_back(dense_weights[var]);
607  }
608  }
610  &relative_error);
611  const int64 gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
612  max_relative_error = std::max(relative_error, max_relative_error);
613  max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
614 
615  LinearBooleanConstraint* constraint = boolean_problem->add_constraints();
616  constraint->set_name(linear_problem.GetConstraintName(row));
617  const double bound_error =
618  ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, constraint);
619  max_bound_error = std::max(max_bound_error, bound_error);
620 
621  const Fractional lower_bound =
622  linear_problem.constraint_lower_bounds()[row];
623  if (lower_bound != -kInfinity) {
624  const Fractional offset_lower_bound = lower_bound - offset;
625  const double offset_scaled_lower_bound =
626  round(offset_lower_bound * scaling_factor - bound_error);
627  if (offset_scaled_lower_bound >= static_cast<double>(kint64max)) {
628  LOG(WARNING) << "A constraint is trivially unsatisfiable.";
629  return;
630  }
631  if (offset_scaled_lower_bound > -static_cast<double>(kint64max)) {
632  // Otherwise, the constraint is not needed.
633  constraint->set_lower_bound(
634  static_cast<int64>(offset_scaled_lower_bound) / gcd);
635  }
636  }
637  const Fractional upper_bound =
638  linear_problem.constraint_upper_bounds()[row];
639  if (upper_bound != kInfinity) {
640  const Fractional offset_upper_bound = upper_bound - offset;
641  const double offset_scaled_upper_bound =
642  round(offset_upper_bound * scaling_factor + bound_error);
643  if (offset_scaled_upper_bound <= -static_cast<double>(kint64max)) {
644  LOG(WARNING) << "A constraint is trivially unsatisfiable.";
645  return;
646  }
647  if (offset_scaled_upper_bound < static_cast<double>(kint64max)) {
648  // Otherwise, the constraint is not needed.
649  constraint->set_upper_bound(
650  static_cast<int64>(offset_scaled_upper_bound) / gcd);
651  }
652  }
653  }
654 }
655 
656 void IntegralProblemConverter::ConvertObjective(
657  const LinearProgram& linear_problem,
658  LinearBooleanProblem* boolean_problem) {
659  LinearObjective* objective = boolean_problem->mutable_objective();
660  Fractional offset = 0.0;
662  num_boolean_variables_, 0.0);
663  // Compute the objective weights for the binary variable model.
664  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
665  offset += AddWeightedIntegralVariable(
666  col, linear_problem.objective_coefficients()[col], &dense_weights);
667  }
668 
669  // Compute the scaling for non-integral weights.
670  std::vector<double> coefficients;
671  for (VariableIndex var(0); var < num_boolean_variables_; ++var) {
672  if (dense_weights[var] != 0.0) {
673  coefficients.push_back(dense_weights[var]);
674  }
675  }
676  double scaling_factor = 0.0;
677  double max_relative_error = 0.0;
678  double relative_error = 0.0;
680  &relative_error);
681  const int64 gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
682  max_relative_error = std::max(relative_error, max_relative_error);
683  VLOG(1) << "objective relative error: " << relative_error;
684  VLOG(1) << "objective scaling factor: " << scaling_factor / gcd;
685 
686  ScaleAndSparsifyWeights(scaling_factor, gcd, dense_weights, objective);
687 
688  // Note that here we set the scaling factor for the inverse operation of
689  // getting the "true" objective value from the scaled one. Hence the inverse.
690  objective->set_scaling_factor(1.0 / scaling_factor * gcd);
691  objective->set_offset((linear_problem.objective_offset() + offset) *
692  scaling_factor / gcd);
693 }
694 
695 void IntegralProblemConverter::AddVariableConstraints(
696  const LinearProgram& linear_problem,
697  LinearBooleanProblem* boolean_problem) {
698  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
699  const Fractional lower_bound = linear_problem.variable_lower_bounds()[col];
700  const Fractional upper_bound = linear_problem.variable_upper_bounds()[col];
701  const int pos = global_to_boolean_[col];
702  if (pos >= 0) {
703  // Boolean variable.
704  CHECK_EQ(BOOLEAN, variable_types_[col]);
705  const bool is_fixed = (lower_bound > -1.0 && upper_bound < 1.0) ||
706  (lower_bound > 0.0 && upper_bound < 2.0);
707  if (is_fixed) {
708  // Set the variable.
709  const int fixed_value = lower_bound > -1.0 && upper_bound < 1.0 ? 0 : 1;
710  LinearBooleanConstraint* constraint =
711  boolean_problem->add_constraints();
712  constraint->set_lower_bound(fixed_value);
713  constraint->set_upper_bound(fixed_value);
714  constraint->add_literals(pos + 1);
715  constraint->add_coefficients(1);
716  }
717  } else {
718  CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[col]);
719  // Integral variable.
720  if (lower_bound != -kInfinity || upper_bound != kInfinity) {
721  const IntegralVariable& integral_var = integral_variables_[-pos - 1];
722  LinearBooleanConstraint* constraint =
723  boolean_problem->add_constraints();
724  for (int i = 0; i < integral_var.bits().size(); ++i) {
725  constraint->add_literals(integral_var.bits()[i].value() + 1);
726  constraint->add_coefficients(integral_var.weights()[i]);
727  }
728  if (lower_bound != -kInfinity) {
729  constraint->set_lower_bound(static_cast<int64>(ceil(lower_bound)) -
730  integral_var.offset());
731  }
732  if (upper_bound != kInfinity) {
733  constraint->set_upper_bound(static_cast<int64>(floor(upper_bound)) -
734  integral_var.offset());
735  }
736  }
737  }
738  }
739 }
740 
741 bool IntegralProblemConverter::ConvertUsingExistingBooleans(
742  const LinearProgram& linear_problem, ColIndex col,
743  IntegralVariable* integral_var) {
744  CHECK(nullptr != integral_var);
745  CHECK_EQ(INTEGRAL, variable_types_[col]);
746 
747  const SparseMatrix& matrix = linear_problem.GetSparseMatrix();
748  const SparseMatrix& transpose = linear_problem.GetTransposeSparseMatrix();
749  for (const SparseColumn::Entry var_entry : matrix.column(col)) {
750  const RowIndex constraint = var_entry.row();
751  const Fractional lb = linear_problem.constraint_lower_bounds()[constraint];
752  const Fractional ub = linear_problem.constraint_upper_bounds()[constraint];
753  if (lb != ub) {
754  // To replace an integral variable by a weighted sum of Boolean variables,
755  // the constraint has to be an equality.
756  continue;
757  }
758 
759  if (transpose.column(RowToColIndex(constraint)).num_entries() <= 1) {
760  // Can't replace the integer variable by Boolean variables when there are
761  // no Boolean variables.
762  // TODO(user): We could actually simplify the problem when the variable
763  // is constant, but this should be done by the preprocessor,
764  // not here. Consider activating the MIP preprocessing.
765  continue;
766  }
767 
768  bool only_one_integral_variable = true;
769  for (const SparseColumn::Entry constraint_entry :
770  transpose.column(RowToColIndex(constraint))) {
771  const ColIndex var_index = RowToColIndex(constraint_entry.row());
772  if (var_index != col && variable_types_[var_index] == INTEGRAL) {
773  only_one_integral_variable = false;
774  break;
775  }
776  }
777  if (only_one_integral_variable &&
778  CreateVariableUsingConstraint(linear_problem, constraint,
779  integral_var)) {
780  return true;
781  }
782  }
783 
784  integral_var->Clear();
785  return false;
786 }
787 
788 bool IntegralProblemConverter::CreateVariableUsingConstraint(
789  const LinearProgram& linear_problem, RowIndex constraint,
790  IntegralVariable* integral_var) {
791  CHECK(nullptr != integral_var);
792  integral_var->Clear();
793 
794  const SparseMatrix& transpose = linear_problem.GetTransposeSparseMatrix();
796  num_boolean_variables_, 0.0);
797  Fractional scale = 1.0;
798  int64 variable_offset = 0;
799  for (const SparseColumn::Entry constraint_entry :
800  transpose.column(RowToColIndex(constraint))) {
801  const ColIndex col = RowToColIndex(constraint_entry.row());
802  if (variable_types_[col] == INTEGRAL) {
803  scale = constraint_entry.coefficient();
804  } else if (variable_types_[col] == BOOLEAN) {
805  const int pos = global_to_boolean_[col];
806  CHECK_LE(0, pos);
807  dense_weights[VariableIndex(pos)] -= constraint_entry.coefficient();
808  } else {
809  CHECK_EQ(INTEGRAL_EXPRESSED_AS_BOOLEAN, variable_types_[col]);
810  const int pos = global_to_boolean_[col];
811  CHECK_GT(0, pos);
812  const IntegralVariable& local_integral_var =
813  integral_variables_[-pos - 1];
814  variable_offset -=
815  constraint_entry.coefficient() * local_integral_var.offset();
816  for (int i = 0; i < local_integral_var.bits().size(); ++i) {
817  dense_weights[local_integral_var.bits()[i]] -=
818  constraint_entry.coefficient() * local_integral_var.weights()[i];
819  }
820  }
821  }
822 
823  // Rescale using the weight of the integral variable.
824  const Fractional lb = linear_problem.constraint_lower_bounds()[constraint];
825  const Fractional offset = (lb + variable_offset) / scale;
826  if (!IsIntegerWithinTolerance(offset)) {
827  return false;
828  }
829  integral_var->set_offset(static_cast<int64>(offset));
830 
831  for (VariableIndex var(0); var < dense_weights.size(); ++var) {
832  if (dense_weights[var] != 0.0) {
833  const Fractional weight = dense_weights[var] / scale;
835  return false;
836  }
837  integral_var->set_weight(var, static_cast<int64>(weight));
838  }
839  }
840 
841  return true;
842 }
843 
844 Fractional IntegralProblemConverter::AddWeightedIntegralVariable(
845  ColIndex col, Fractional weight,
847  CHECK(nullptr != dense_weights);
848 
849  if (weight == 0.0) {
850  return 0;
851  }
852 
853  Fractional offset = 0;
854  const int pos = global_to_boolean_[col];
855  if (pos >= 0) {
856  // Boolean variable.
857  (*dense_weights)[VariableIndex(pos)] += weight;
858  } else {
859  // Integral variable.
860  const IntegralVariable& integral_var = integral_variables_[-pos - 1];
861  for (int i = 0; i < integral_var.bits().size(); ++i) {
862  (*dense_weights)[integral_var.bits()[i]] +=
863  integral_var.weights()[i] * weight;
864  }
865  offset += weight * integral_var.offset();
866  }
867  return offset;
868 }
869 
870 template <class T>
871 double IntegralProblemConverter::ScaleAndSparsifyWeights(
872  double scaling_factor, int64 gcd,
873  const absl::StrongVector<VariableIndex, Fractional>& dense_weights, T* t) {
874  CHECK(nullptr != t);
875 
876  double bound_error = 0.0;
877  for (VariableIndex var(0); var < dense_weights.size(); ++var) {
878  if (dense_weights[var] != 0.0) {
879  const double scaled_weight = dense_weights[var] * scaling_factor;
880  bound_error += fabs(round(scaled_weight) - scaled_weight);
881  t->add_literals(var.value() + 1);
882  t->add_coefficients(static_cast<int64>(round(scaled_weight)) / gcd);
883  }
884  }
885 
886  return bound_error;
887 }
888 bool IntegralProblemConverter::HasNonZeroWeights(
889  const absl::StrongVector<VariableIndex, Fractional>& dense_weights) const {
890  for (const Fractional weight : dense_weights) {
891  if (weight != 0.0) {
892  return true;
893  }
894  }
895  return false;
896 }
897 
898 bool CheckSolution(const LinearProgram& linear_problem,
899  const glop::DenseRow& variable_values) {
900  glop::DenseColumn constraint_values(linear_problem.num_constraints(), 0);
901 
902  const SparseMatrix& matrix = linear_problem.GetSparseMatrix();
903  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
904  const Fractional lower_bound = linear_problem.variable_lower_bounds()[col];
905  const Fractional upper_bound = linear_problem.variable_upper_bounds()[col];
906  const Fractional value = variable_values[col];
907  if (lower_bound > value || upper_bound < value) {
908  LOG(ERROR) << "Variable " << col << " out of bound: " << value
909  << " should be in " << lower_bound << " .. " << upper_bound;
910  return false;
911  }
912 
913  for (const SparseColumn::Entry entry : matrix.column(col)) {
914  constraint_values[entry.row()] += entry.coefficient() * value;
915  }
916  }
917 
918  for (RowIndex row(0); row < linear_problem.num_constraints(); ++row) {
919  const Fractional lb = linear_problem.constraint_lower_bounds()[row];
920  const Fractional ub = linear_problem.constraint_upper_bounds()[row];
921  const Fractional value = constraint_values[row];
922  if (lb > value || ub < value) {
923  LOG(ERROR) << "Constraint " << row << " out of bound: " << value
924  << " should be in " << lb << " .. " << ub;
925  return false;
926  }
927  }
928 
929  return true;
930 }
931 
932 // Solves the given linear program and returns the solve status.
933 BopSolveStatus InternalSolve(const LinearProgram& linear_problem,
934  const BopParameters& parameters,
935  const DenseRow& initial_solution,
936  TimeLimit* time_limit, DenseRow* variable_values,
937  Fractional* objective_value,
938  Fractional* best_bound) {
939  CHECK(variable_values != nullptr);
940  CHECK(objective_value != nullptr);
941  CHECK(best_bound != nullptr);
942  const bool use_initial_solution = (initial_solution.size() > 0);
943  if (use_initial_solution) {
944  CHECK_EQ(initial_solution.size(), linear_problem.num_variables());
945  }
946 
947  // Those values will only make sense when a solution is found, however we
948  // resize here such that one can access the values even if they don't mean
949  // anything.
950  variable_values->resize(linear_problem.num_variables(), 0);
951 
952  LinearBooleanProblem boolean_problem;
953  std::vector<bool> boolean_initial_solution;
954  IntegralProblemConverter converter;
955  if (!converter.ConvertToBooleanProblem(linear_problem, initial_solution,
956  &boolean_problem,
957  &boolean_initial_solution)) {
958  return BopSolveStatus::INVALID_PROBLEM;
959  }
960 
961  BopSolver bop_solver(boolean_problem);
962  bop_solver.SetParameters(parameters);
963  BopSolveStatus status = BopSolveStatus::NO_SOLUTION_FOUND;
964  if (use_initial_solution) {
965  BopSolution bop_solution(boolean_problem, "InitialSolution");
966  CHECK_EQ(boolean_initial_solution.size(), boolean_problem.num_variables());
967  for (int i = 0; i < boolean_initial_solution.size(); ++i) {
968  bop_solution.SetValue(VariableIndex(i), boolean_initial_solution[i]);
969  }
970  status = bop_solver.SolveWithTimeLimit(bop_solution, time_limit);
971  } else {
972  status = bop_solver.SolveWithTimeLimit(time_limit);
973  }
974  if (status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND ||
975  status == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
976  // Compute objective value.
977  const BopSolution& solution = bop_solver.best_solution();
978  CHECK(solution.IsFeasible());
979 
980  *objective_value = linear_problem.objective_offset();
981  for (ColIndex col(0); col < linear_problem.num_variables(); ++col) {
982  const int64 value = converter.GetSolutionValue(col, solution);
983  (*variable_values)[col] = value;
984  *objective_value += value * linear_problem.objective_coefficients()[col];
985  }
986 
987  CheckSolution(linear_problem, *variable_values);
988 
989  // TODO(user): Check that the scaled best bound from Bop is a valid one
990  // even after conversion. If yes, remove the optimality test.
991  *best_bound = status == BopSolveStatus::OPTIMAL_SOLUTION_FOUND
992  ? *objective_value
993  : bop_solver.GetScaledBestBound();
994  }
995  return status;
996 }
997 
998 void RunOneBop(const BopParameters& parameters, int problem_index,
999  const DenseRow& initial_solution, TimeLimit* time_limit,
1000  LPDecomposer* decomposer, DenseRow* variable_values,
1001  Fractional* objective_value, Fractional* best_bound,
1002  BopSolveStatus* status) {
1003  CHECK(decomposer != nullptr);
1004  CHECK(variable_values != nullptr);
1005  CHECK(objective_value != nullptr);
1006  CHECK(best_bound != nullptr);
1007  CHECK(status != nullptr);
1008 
1009  LinearProgram problem;
1010  decomposer->ExtractLocalProblem(problem_index, &problem);
1011  DenseRow local_initial_solution;
1012  if (initial_solution.size() > 0) {
1013  local_initial_solution =
1014  decomposer->ExtractLocalAssignment(problem_index, initial_solution);
1015  }
1016  // TODO(user): Investigate a better approximation of the time needed to
1017  // solve the problem than just the number of variables.
1018  const double total_num_variables = std::max(
1019  1.0, static_cast<double>(
1020  decomposer->original_problem().num_variables().value()));
1021  const double time_per_variable =
1022  parameters.max_time_in_seconds() / total_num_variables;
1023  const double deterministic_time_per_variable =
1024  parameters.max_deterministic_time() / total_num_variables;
1025  const int local_num_variables = std::max(1, problem.num_variables().value());
1026 
1027  NestedTimeLimit subproblem_time_limit(
1028  time_limit,
1029  std::max(time_per_variable * local_num_variables,
1030  parameters.decomposed_problem_min_time_in_seconds()),
1031  deterministic_time_per_variable * local_num_variables);
1032 
1033  *status = InternalSolve(problem, parameters, local_initial_solution,
1034  subproblem_time_limit.GetTimeLimit(), variable_values,
1035  objective_value, best_bound);
1036 }
1037 } // anonymous namespace
1038 
1039 IntegralSolver::IntegralSolver()
1040  : parameters_(), variable_values_(), objective_value_(0.0) {}
1041 
1042 BopSolveStatus IntegralSolver::Solve(const LinearProgram& linear_problem) {
1043  return Solve(linear_problem, DenseRow());
1044 }
1045 
1047  const LinearProgram& linear_problem, TimeLimit* time_limit) {
1048  return SolveWithTimeLimit(linear_problem, DenseRow(), time_limit);
1049 }
1050 
1052  const LinearProgram& linear_problem,
1053  const DenseRow& user_provided_initial_solution) {
1054  std::unique_ptr<TimeLimit> time_limit =
1055  TimeLimit::FromParameters(parameters_);
1056  return SolveWithTimeLimit(linear_problem, user_provided_initial_solution,
1057  time_limit.get());
1058 }
1059 
1061  const LinearProgram& linear_problem,
1062  const DenseRow& user_provided_initial_solution, TimeLimit* time_limit) {
1063  // We make a copy so that we can clear it if the presolve is active.
1064  DenseRow initial_solution = user_provided_initial_solution;
1065  if (initial_solution.size() > 0) {
1066  CHECK_EQ(initial_solution.size(), linear_problem.num_variables())
1067  << "The initial solution should have the same number of variables as "
1068  "the LinearProgram.";
1069  }
1070 
1071  // Some code path requires to copy the given linear_problem. When this
1072  // happens, we will simply change the target of this pointer.
1073  LinearProgram const* lp = &linear_problem;
1074 
1075  BopSolveStatus status;
1076  if (lp->num_variables() >= parameters_.decomposer_num_variables_threshold()) {
1077  LPDecomposer decomposer;
1078  decomposer.Decompose(lp);
1079  const int num_sub_problems = decomposer.GetNumberOfProblems();
1080  VLOG(1) << "Problem is decomposable into " << num_sub_problems
1081  << " components!";
1082  if (num_sub_problems > 1) {
1083  // The problem can be decomposed. Solve each sub-problem and aggregate the
1084  // result.
1085  std::vector<DenseRow> variable_values(num_sub_problems);
1086  std::vector<Fractional> objective_values(num_sub_problems,
1087  Fractional(0.0));
1088  std::vector<Fractional> best_bounds(num_sub_problems, Fractional(0.0));
1089  std::vector<BopSolveStatus> statuses(num_sub_problems,
1091 
1092  for (int i = 0; i < num_sub_problems; ++i) {
1093  RunOneBop(parameters_, i, initial_solution, time_limit, &decomposer,
1094  &(variable_values[i]), &(objective_values[i]),
1095  &(best_bounds[i]), &(statuses[i]));
1096  }
1097 
1098  // Aggregate results.
1100  objective_value_ = lp->objective_offset();
1101  best_bound_ = 0.0;
1102  for (int i = 0; i < num_sub_problems; ++i) {
1103  objective_value_ += objective_values[i];
1104  best_bound_ += best_bounds[i];
1105  if (statuses[i] == BopSolveStatus::NO_SOLUTION_FOUND ||
1106  statuses[i] == BopSolveStatus::INFEASIBLE_PROBLEM ||
1107  statuses[i] == BopSolveStatus::INVALID_PROBLEM) {
1108  return statuses[i];
1109  }
1110 
1111  if (statuses[i] == BopSolveStatus::FEASIBLE_SOLUTION_FOUND) {
1112  status = BopSolveStatus::FEASIBLE_SOLUTION_FOUND;
1113  }
1114  }
1115  variable_values_ = decomposer.AggregateAssignments(variable_values);
1116  CheckSolution(*lp, variable_values_);
1117  } else {
1118  status =
1119  InternalSolve(*lp, parameters_, initial_solution, time_limit,
1120  &variable_values_, &objective_value_, &best_bound_);
1121  }
1122  } else {
1123  status = InternalSolve(*lp, parameters_, initial_solution, time_limit,
1124  &variable_values_, &objective_value_, &best_bound_);
1125  }
1126 
1127  return status;
1128 }
1129 
1130 } // namespace bop
1131 } // namespace operations_research
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::glop::DenseRow
StrictITIVector< ColIndex, Fractional > DenseRow
Definition: lp_types.h:299
VLOG
#define VLOG(verboselevel)
Definition: base/logging.h:978
max
int64 max
Definition: alldiff_cst.cc:139
coefficients
std::vector< double > coefficients
Definition: sat/lp_utils.cc:497
num_entries
EntryIndex num_entries
Definition: preprocessor.cc:1306
operations_research::ComputeGcdOfRoundedDoubles
int64 ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
Definition: fp_utils.cc:189
LOG
#define LOG(severity)
Definition: base/logging.h:420
absl::StrongVector::size
size_type size() const
Definition: strong_vector.h:147
ERROR
const int ERROR
Definition: log_severity.h:32
value
int64 value
Definition: demon_profiler.cc:43
CHECK_GT
#define CHECK_GT(val1, val2)
Definition: base/logging.h:702
weight
int64 weight
Definition: pack.cc:509
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
WARNING
const int WARNING
Definition: log_severity.h:31
operations_research::TimeLimit::FromParameters
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Creates a time limit object initialized from an object that provides methods max_time_in_seconds() an...
Definition: time_limit.h:159
operations_research::IsIntegerWithinTolerance
bool IsIntegerWithinTolerance(FloatType x, FloatType tolerance)
Definition: fp_utils.h:161
int64
int64_t int64
Definition: integral_types.h:34
operations_research::TimeLimit
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
operations_research::bop::IntegralSolver::variable_values
const glop::DenseRow & variable_values() const
Definition: integral_solver.h:64
offset_
const int64 offset_
Definition: interval.cc:2076
max_scaling_factor
double max_scaling_factor
Definition: sat/lp_utils.cc:492
operations_research::sat::ChangeOptimizationDirection
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
Definition: boolean_problem.cc:208
operations_research::glop::Fractional
double Fractional
Definition: lp_types.h:77
operations_research::glop::kInfinity
const double kInfinity
Definition: lp_types.h:83
operations_research::glop::RowToColIndex
ColIndex RowToColIndex(RowIndex row)
Definition: lp_types.h:48
integral_solver.h
time_limit
SharedTimeLimit * time_limit
Definition: cp_model_solver.cc:2103
bop_solver.h
CHECK_EQ
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
ct
const Constraint * ct
Definition: demon_profiler.cc:42
operations_research::bop::IntegralSolver::SolveWithTimeLimit
ABSL_MUST_USE_RESULT BopSolveStatus SolveWithTimeLimit(const glop::LinearProgram &linear_problem, TimeLimit *time_limit)
operations_research::MostSignificantBitPosition64
int MostSignificantBitPosition64(uint64 n)
Definition: bitset.h:231
DCHECK
#define DCHECK(condition)
Definition: base/logging.h:884
CHECK_LE
#define CHECK_LE(val1, val2)
Definition: base/logging.h:699
lp_decomposer.h
absl::StrongVector
Definition: strong_vector.h:76
col
ColIndex col
Definition: markowitz.cc:176
operations_research::GetBestScalingOfDoublesToInt64
double GetBestScalingOfDoublesToInt64(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, int64 max_absolute_sum)
Definition: fp_utils.cc:168
row
RowIndex row
Definition: markowitz.cc:175
operations_research::glop::DenseColumn
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:328
delta
int64 delta
Definition: resource.cc:1684
operations_research::glop::VariableType
VariableType
Definition: lp_types.h:174
CHECK_NE
#define CHECK_NE(val1, val2)
Definition: base/logging.h:698
operations_research::bop::IntegralSolver::Solve
ABSL_MUST_USE_RESULT BopSolveStatus Solve(const glop::LinearProgram &linear_problem)
operations_research::bop::BopSolveStatus
BopSolveStatus
Definition: bop_types.h:31
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
parameters
SatParameters parameters
Definition: cp_model_fz_solver.cc:108
operations_research::fz::CheckSolution
bool CheckSolution(const Model &model, const std::function< int64(IntegerVariable *)> &evaluator)
Definition: checker.cc:1263
kint64max
static const int64 kint64max
Definition: integral_types.h:62
operations_research::bop::BopSolveStatus::OPTIMAL_SOLUTION_FOUND
@ OPTIMAL_SOLUTION_FOUND