OR-Tools  8.0
linear_programming_constraint.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 <algorithm>
17 #include <cmath>
18 #include <iterator>
19 #include <limits>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/numeric/int128.h"
29 #include "ortools/base/logging.h"
30 #include "ortools/base/map_util.h"
31 #include "ortools/base/mathutil.h"
32 #include "ortools/base/stl_util.h"
35 #include "ortools/glop/status.h"
39 #include "ortools/sat/integer.h"
42 
43 namespace operations_research {
44 namespace sat {
45 
46 using glop::ColIndex;
47 using glop::Fractional;
48 using glop::RowIndex;
49 
50 const double LinearProgrammingConstraint::kCpEpsilon = 1e-4;
51 const double LinearProgrammingConstraint::kLpEpsilon = 1e-6;
52 
54  if (is_sparse_) {
55  for (const glop::ColIndex col : non_zeros_) {
56  dense_vector_[col] = IntegerValue(0);
57  }
58  dense_vector_.resize(size, IntegerValue(0));
59  } else {
60  dense_vector_.assign(size, IntegerValue(0));
61  }
62  for (const glop::ColIndex col : non_zeros_) {
63  is_zeros_[col] = true;
64  }
65  is_zeros_.resize(size, true);
66  non_zeros_.clear();
67  is_sparse_ = true;
68 }
69 
70 bool ScatteredIntegerVector::Add(glop::ColIndex col, IntegerValue value) {
71  const int64 add = CapAdd(value.value(), dense_vector_[col].value());
72  if (add == kint64min || add == kint64max) return false;
73  dense_vector_[col] = IntegerValue(add);
74  if (is_sparse_ && is_zeros_[col]) {
75  is_zeros_[col] = false;
76  non_zeros_.push_back(col);
77  }
78  return true;
79 }
80 
82  IntegerValue multiplier,
83  const std::vector<std::pair<glop::ColIndex, IntegerValue>>& terms) {
84  const double threshold = 0.1 * static_cast<double>(dense_vector_.size());
85  if (is_sparse_ && static_cast<double>(terms.size()) < threshold) {
86  for (const std::pair<glop::ColIndex, IntegerValue> term : terms) {
87  if (is_zeros_[term.first]) {
88  is_zeros_[term.first] = false;
89  non_zeros_.push_back(term.first);
90  }
91  if (!AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
92  return false;
93  }
94  }
95  if (static_cast<double>(non_zeros_.size()) < threshold) {
96  is_sparse_ = false;
97  }
98  } else {
99  is_sparse_ = false;
100  for (const std::pair<glop::ColIndex, IntegerValue> term : terms) {
101  if (!AddProductTo(multiplier, term.second, &dense_vector_[term.first])) {
102  return false;
103  }
104  }
105  }
106  return true;
107 }
108 
110  const std::vector<IntegerVariable>& integer_variables,
111  IntegerValue upper_bound, LinearConstraint* result) {
112  result->vars.clear();
113  result->coeffs.clear();
114  if (is_sparse_) {
115  std::sort(non_zeros_.begin(), non_zeros_.end());
116  for (const glop::ColIndex col : non_zeros_) {
117  const IntegerValue coeff = dense_vector_[col];
118  if (coeff == 0) continue;
119  result->vars.push_back(integer_variables[col.value()]);
120  result->coeffs.push_back(coeff);
121  }
122  } else {
123  const int size = dense_vector_.size();
124  for (glop::ColIndex col(0); col < size; ++col) {
125  const IntegerValue coeff = dense_vector_[col];
126  if (coeff == 0) continue;
127  result->vars.push_back(integer_variables[col.value()]);
128  result->coeffs.push_back(coeff);
129  }
130  }
131  result->lb = kMinIntegerValue;
132  result->ub = upper_bound;
133 }
134 
135 std::vector<std::pair<glop::ColIndex, IntegerValue>>
137  std::vector<std::pair<glop::ColIndex, IntegerValue>> result;
138  if (is_sparse_) {
139  std::sort(non_zeros_.begin(), non_zeros_.end());
140  for (const glop::ColIndex col : non_zeros_) {
141  const IntegerValue coeff = dense_vector_[col];
142  if (coeff != 0) result.push_back({col, coeff});
143  }
144  } else {
145  const int size = dense_vector_.size();
146  for (glop::ColIndex col(0); col < size; ++col) {
147  const IntegerValue coeff = dense_vector_[col];
148  if (coeff != 0) result.push_back({col, coeff});
149  }
150  }
151  return result;
152 }
153 
154 // TODO(user): make SatParameters singleton too, otherwise changing them after
155 // a constraint was added will have no effect on this class.
157  : constraint_manager_(model),
158  sat_parameters_(*(model->GetOrCreate<SatParameters>())),
159  model_(model),
160  time_limit_(model->GetOrCreate<TimeLimit>()),
161  integer_trail_(model->GetOrCreate<IntegerTrail>()),
162  trail_(model->GetOrCreate<Trail>()),
163  model_heuristics_(model->GetOrCreate<SearchHeuristicsVector>()),
164  integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
165  random_(model->GetOrCreate<ModelRandomGenerator>()),
166  implied_bounds_processor_({}, integer_trail_,
167  model->GetOrCreate<ImpliedBounds>()),
168  dispatcher_(model->GetOrCreate<LinearProgrammingDispatcher>()),
169  expanded_lp_solution_(
170  *model->GetOrCreate<LinearProgrammingConstraintLpSolution>()) {
171  // Tweak the default parameters to make the solve incremental.
172  glop::GlopParameters parameters;
173  parameters.set_use_dual_simplex(true);
174  simplex_.SetParameters(parameters);
175  if (sat_parameters_.use_branching_in_lp() ||
176  sat_parameters_.search_branching() == SatParameters::LP_SEARCH) {
177  compute_reduced_cost_averages_ = true;
178  }
179 
180  // Register our local rev int repository.
181  integer_trail_->RegisterReversibleClass(&rc_rev_int_repository_);
182 }
183 
185  VLOG(1) << "Total number of simplex iterations: "
186  << total_num_simplex_iterations_;
187 }
188 
190  const LinearConstraint& ct) {
191  DCHECK(!lp_constraint_is_registered_);
192  constraint_manager_.Add(ct);
193 
194  // We still create the mirror variable right away though.
195  //
196  // TODO(user): clean this up? Note that it is important that the variable
197  // in lp_data_ never changes though, so we can restart from the current
198  // lp solution and be incremental (even if the constraints changed).
199  for (const IntegerVariable var : ct.vars) {
200  GetOrCreateMirrorVariable(PositiveVariable(var));
201  }
202 }
203 
204 glop::ColIndex LinearProgrammingConstraint::GetOrCreateMirrorVariable(
205  IntegerVariable positive_variable) {
206  DCHECK(VariableIsPositive(positive_variable));
207  const auto it = mirror_lp_variable_.find(positive_variable);
208  if (it == mirror_lp_variable_.end()) {
209  const glop::ColIndex col(integer_variables_.size());
210  implied_bounds_processor_.AddLpVariable(positive_variable);
211  mirror_lp_variable_[positive_variable] = col;
212  integer_variables_.push_back(positive_variable);
213  lp_solution_.push_back(std::numeric_limits<double>::infinity());
214  lp_reduced_cost_.push_back(0.0);
215  (*dispatcher_)[positive_variable] = this;
216 
217  const int index = std::max(positive_variable.value(),
218  NegationOf(positive_variable).value());
219  if (index >= expanded_lp_solution_.size()) {
220  expanded_lp_solution_.resize(index + 1, 0.0);
221  }
222  return col;
223  }
224  return it->second;
225 }
226 
228  IntegerValue coeff) {
229  CHECK(!lp_constraint_is_registered_);
230  objective_is_defined_ = true;
231  IntegerVariable pos_var = VariableIsPositive(ivar) ? ivar : NegationOf(ivar);
232  if (ivar != pos_var) coeff = -coeff;
233 
234  constraint_manager_.SetObjectiveCoefficient(pos_var, coeff);
235  const glop::ColIndex col = GetOrCreateMirrorVariable(pos_var);
236  integer_objective_.push_back({col, coeff});
237  objective_infinity_norm_ =
238  std::max(objective_infinity_norm_, IntTypeAbs(coeff));
239 }
240 
241 // TODO(user): As the search progress, some variables might get fixed. Exploit
242 // this to reduce the number of variables in the LP and in the
243 // ConstraintManager? We might also detect during the search that two variable
244 // are equivalent.
245 //
246 // TODO(user): On TSP/VRP with a lot of cuts, this can take 20% of the overall
247 // running time. We should be able to almost remove most of this from the
248 // profile by being more incremental (modulo LP scaling).
249 //
250 // TODO(user): A longer term idea for LP with a lot of variables is to not
251 // add all variables to each LP solve and do some "sifting". That can be useful
252 // for TSP for instance where the number of edges is large, but only a small
253 // fraction will be used in the optimal solution.
254 bool LinearProgrammingConstraint::CreateLpFromConstraintManager() {
255  // Fill integer_lp_.
256  integer_lp_.clear();
257  infinity_norms_.clear();
258  const auto& all_constraints = constraint_manager_.AllConstraints();
259  for (const auto index : constraint_manager_.LpConstraints()) {
260  const LinearConstraint& ct = all_constraints[index].constraint;
261 
262  integer_lp_.push_back(LinearConstraintInternal());
263  LinearConstraintInternal& new_ct = integer_lp_.back();
264  new_ct.lb = ct.lb;
265  new_ct.ub = ct.ub;
266  const int size = ct.vars.size();
267  IntegerValue infinity_norm(0);
268  if (ct.lb > ct.ub) {
269  VLOG(1) << "Trivial infeasible bound in an LP constraint";
270  return false;
271  }
272  if (ct.lb > kMinIntegerValue) {
273  infinity_norm = std::max(infinity_norm, IntTypeAbs(ct.lb));
274  }
275  if (ct.ub < kMaxIntegerValue) {
276  infinity_norm = std::max(infinity_norm, IntTypeAbs(ct.ub));
277  }
278  for (int i = 0; i < size; ++i) {
279  // We only use positive variable inside this class.
280  IntegerVariable var = ct.vars[i];
281  IntegerValue coeff = ct.coeffs[i];
282  if (!VariableIsPositive(var)) {
283  var = NegationOf(var);
284  coeff = -coeff;
285  }
286  infinity_norm = std::max(infinity_norm, IntTypeAbs(coeff));
287  new_ct.terms.push_back({GetOrCreateMirrorVariable(var), coeff});
288  }
289  infinity_norms_.push_back(infinity_norm);
290 
291  // Important to keep lp_data_ "clean".
292  std::sort(new_ct.terms.begin(), new_ct.terms.end());
293  }
294 
295  // Copy the integer_lp_ into lp_data_.
296  lp_data_.Clear();
297  for (int i = 0; i < integer_variables_.size(); ++i) {
298  CHECK_EQ(glop::ColIndex(i), lp_data_.CreateNewVariable());
299  }
300  for (const auto entry : integer_objective_) {
301  lp_data_.SetObjectiveCoefficient(entry.first, ToDouble(entry.second));
302  }
303  for (const LinearConstraintInternal& ct : integer_lp_) {
304  const ConstraintIndex row = lp_data_.CreateNewConstraint();
305  lp_data_.SetConstraintBounds(row, ToDouble(ct.lb), ToDouble(ct.ub));
306  for (const auto& term : ct.terms) {
307  lp_data_.SetCoefficient(row, term.first, ToDouble(term.second));
308  }
309  }
310  lp_data_.NotifyThatColumnsAreClean();
311 
312  // We scale the LP using the level zero bounds that we later override
313  // with the current ones.
314  //
315  // TODO(user): As part of the scaling, we may also want to shift the initial
316  // variable bounds so that each variable contain the value zero in their
317  // domain. Maybe just once and for all at the beginning.
318  const int num_vars = integer_variables_.size();
319  for (int i = 0; i < num_vars; i++) {
320  const IntegerVariable cp_var = integer_variables_[i];
321  const double lb = ToDouble(integer_trail_->LevelZeroLowerBound(cp_var));
322  const double ub = ToDouble(integer_trail_->LevelZeroUpperBound(cp_var));
323  lp_data_.SetVariableBounds(glop::ColIndex(i), lb, ub);
324  }
325 
326  scaler_.Scale(&lp_data_);
327  UpdateBoundsOfLpVariables();
328 
329  lp_data_.NotifyThatColumnsAreClean();
330  lp_data_.AddSlackVariablesWhereNecessary(false);
331  VLOG(1) << "LP relaxation: " << lp_data_.GetDimensionString() << ". "
332  << constraint_manager_.AllConstraints().size()
333  << " Managed constraints.";
334  return true;
335 }
336 
337 LPSolveInfo LinearProgrammingConstraint::SolveLpForBranching() {
338  LPSolveInfo info;
339  glop::BasisState basis_state = simplex_.GetState();
340 
341  const glop::Status status = simplex_.Solve(lp_data_, time_limit_);
342  total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
343  simplex_.LoadStateForNextSolve(basis_state);
344  if (!status.ok()) {
345  VLOG(1) << "The LP solver encountered an error: " << status.error_message();
346  info.status = glop::ProblemStatus::ABNORMAL;
347  return info;
348  }
349  info.status = simplex_.GetProblemStatus();
350  if (info.status == glop::ProblemStatus::OPTIMAL ||
351  info.status == glop::ProblemStatus::DUAL_FEASIBLE) {
352  // Record the objective bound.
353  info.lp_objective = simplex_.GetObjectiveValue();
354  info.new_obj_bound = IntegerValue(
355  static_cast<int64>(std::ceil(info.lp_objective - kCpEpsilon)));
356  }
357  return info;
358 }
359 
360 void LinearProgrammingConstraint::FillReducedCostReasonIn(
361  const glop::DenseRow& reduced_costs,
362  std::vector<IntegerLiteral>* integer_reason) {
363  integer_reason->clear();
364  const int num_vars = integer_variables_.size();
365  for (int i = 0; i < num_vars; i++) {
366  const double rc = reduced_costs[glop::ColIndex(i)];
367  if (rc > kLpEpsilon) {
368  integer_reason->push_back(
369  integer_trail_->LowerBoundAsLiteral(integer_variables_[i]));
370  } else if (rc < -kLpEpsilon) {
371  integer_reason->push_back(
372  integer_trail_->UpperBoundAsLiteral(integer_variables_[i]));
373  }
374  }
375 
376  integer_trail_->RemoveLevelZeroBounds(integer_reason);
377 }
378 
379 bool LinearProgrammingConstraint::BranchOnVar(IntegerVariable positive_var) {
380  // From the current LP solution, branch on the given var if fractional.
381  DCHECK(lp_solution_is_set_);
382  const double current_value = GetSolutionValue(positive_var);
383  DCHECK_GT(std::abs(current_value - std::round(current_value)), kCpEpsilon);
384 
385  // Used as empty reason in this method.
386  integer_reason_.clear();
387 
388  bool deductions_were_made = false;
389 
390  UpdateBoundsOfLpVariables();
391 
392  const IntegerValue current_obj_lb = integer_trail_->LowerBound(objective_cp_);
393  // This will try to branch in both direction around the LP value of the
394  // given variable and push any deduction done this way.
395 
396  const glop::ColIndex lp_var = GetOrCreateMirrorVariable(positive_var);
397  const double current_lb = ToDouble(integer_trail_->LowerBound(positive_var));
398  const double current_ub = ToDouble(integer_trail_->UpperBound(positive_var));
399  const double factor = scaler_.VariableScalingFactor(lp_var);
400  if (current_value < current_lb || current_value > current_ub) {
401  return false;
402  }
403 
404  // Form LP1 var <= floor(current_value)
405  const double new_ub = std::floor(current_value);
406  lp_data_.SetVariableBounds(lp_var, current_lb * factor, new_ub * factor);
407 
408  LPSolveInfo lower_branch_info = SolveLpForBranching();
409  if (lower_branch_info.status != glop::ProblemStatus::OPTIMAL &&
410  lower_branch_info.status != glop::ProblemStatus::DUAL_FEASIBLE &&
411  lower_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
412  return false;
413  }
414 
415  if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
416  // Push the other branch.
417  const IntegerLiteral deduction = IntegerLiteral::GreaterOrEqual(
418  positive_var, IntegerValue(std::ceil(current_value)));
419  if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
420  return false;
421  }
422  deductions_were_made = true;
423  } else if (lower_branch_info.new_obj_bound <= current_obj_lb) {
424  return false;
425  }
426 
427  // Form LP2 var >= ceil(current_value)
428  const double new_lb = std::ceil(current_value);
429  lp_data_.SetVariableBounds(lp_var, new_lb * factor, current_ub * factor);
430 
431  LPSolveInfo upper_branch_info = SolveLpForBranching();
432  if (upper_branch_info.status != glop::ProblemStatus::OPTIMAL &&
433  upper_branch_info.status != glop::ProblemStatus::DUAL_FEASIBLE &&
434  upper_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
435  return deductions_were_made;
436  }
437 
438  if (upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
439  // Push the other branch if not infeasible.
440  if (lower_branch_info.status != glop::ProblemStatus::DUAL_UNBOUNDED) {
441  const IntegerLiteral deduction = IntegerLiteral::LowerOrEqual(
442  positive_var, IntegerValue(std::floor(current_value)));
443  if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
444  return deductions_were_made;
445  }
446  deductions_were_made = true;
447  }
448  } else if (upper_branch_info.new_obj_bound <= current_obj_lb) {
449  return deductions_were_made;
450  }
451 
452  IntegerValue approximate_obj_lb = kMinIntegerValue;
453 
454  if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED &&
455  upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
456  return integer_trail_->ReportConflict(integer_reason_);
457  } else if (lower_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
458  approximate_obj_lb = upper_branch_info.new_obj_bound;
459  } else if (upper_branch_info.status == glop::ProblemStatus::DUAL_UNBOUNDED) {
460  approximate_obj_lb = lower_branch_info.new_obj_bound;
461  } else {
462  approximate_obj_lb = std::min(lower_branch_info.new_obj_bound,
463  upper_branch_info.new_obj_bound);
464  }
465 
466  // NOTE: On some problems, the approximate_obj_lb could be inexact which add
467  // some tolerance to CP-SAT where currently there is none.
468  if (approximate_obj_lb <= current_obj_lb) return deductions_were_made;
469 
470  // Push the bound to the trail.
471  const IntegerLiteral deduction =
472  IntegerLiteral::GreaterOrEqual(objective_cp_, approximate_obj_lb);
473  if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
474  return deductions_were_made;
475  }
476 
477  return true;
478 }
479 
481  DCHECK(!lp_constraint_is_registered_);
482  lp_constraint_is_registered_ = true;
483  model->GetOrCreate<LinearProgrammingConstraintCollection>()->push_back(this);
484 
485  // Note fdid, this is not really needed by should lead to better cache
486  // locality.
487  std::sort(integer_objective_.begin(), integer_objective_.end());
488 
489  // Set the LP to its initial content.
490  if (!sat_parameters_.add_lp_constraints_lazily()) {
491  constraint_manager_.AddAllConstraintsToLp();
492  }
493  if (!CreateLpFromConstraintManager()) {
494  model->GetOrCreate<SatSolver>()->NotifyThatModelIsUnsat();
495  return;
496  }
497 
498  GenericLiteralWatcher* watcher = model->GetOrCreate<GenericLiteralWatcher>();
499  const int watcher_id = watcher->Register(this);
500  const int num_vars = integer_variables_.size();
501  for (int i = 0; i < num_vars; i++) {
502  watcher->WatchIntegerVariable(integer_variables_[i], watcher_id, i);
503  }
504  if (objective_is_defined_) {
505  watcher->WatchUpperBound(objective_cp_, watcher_id);
506  }
507  watcher->SetPropagatorPriority(watcher_id, 2);
508  watcher->AlwaysCallAtLevelZero(watcher_id);
509 
510  if (integer_variables_.size() >= 20) { // Do not use on small subparts.
511  auto* container = model->GetOrCreate<SearchHeuristicsVector>();
512  container->push_back(HeuristicLPPseudoCostBinary(model));
513  container->push_back(HeuristicLPMostInfeasibleBinary(model));
514  }
515 
516  // Registering it with the trail make sure this class is always in sync when
517  // it is used in the decision heuristics.
518  integer_trail_->RegisterReversibleClass(this);
519  watcher->RegisterReversibleInt(watcher_id, &rev_optimal_constraints_size_);
520 }
521 
523  optimal_constraints_.resize(rev_optimal_constraints_size_);
524  if (lp_solution_is_set_ && level < lp_solution_level_) {
525  lp_solution_is_set_ = false;
526  }
527 
528  // Special case for level zero, we "reload" any previously known optimal
529  // solution from that level.
530  //
531  // TODO(user): Keep all optimal solution in the current branch?
532  // TODO(user): Still try to add cuts/constraints though!
533  if (level == 0 && !level_zero_lp_solution_.empty()) {
534  lp_solution_is_set_ = true;
535  lp_solution_ = level_zero_lp_solution_;
536  lp_solution_level_ = 0;
537  for (int i = 0; i < lp_solution_.size(); i++) {
538  expanded_lp_solution_[integer_variables_[i]] = lp_solution_[i];
539  expanded_lp_solution_[NegationOf(integer_variables_[i])] =
540  -lp_solution_[i];
541  }
542  }
543 }
544 
546  for (const IntegerVariable var : generator.vars) {
547  GetOrCreateMirrorVariable(VariableIsPositive(var) ? var : NegationOf(var));
548  }
549  cut_generators_.push_back(std::move(generator));
550 }
551 
553  const std::vector<int>& watch_indices) {
554  if (!lp_solution_is_set_) return Propagate();
555 
556  // At level zero, if there is still a chance to add cuts or lazy constraints,
557  // we re-run the LP.
558  if (trail_->CurrentDecisionLevel() == 0 && !lp_at_level_zero_is_final_) {
559  return Propagate();
560  }
561 
562  // Check whether the change breaks the current LP solution. If it does, call
563  // Propagate() on the current LP.
564  for (const int index : watch_indices) {
565  const double lb =
566  ToDouble(integer_trail_->LowerBound(integer_variables_[index]));
567  const double ub =
568  ToDouble(integer_trail_->UpperBound(integer_variables_[index]));
569  const double value = lp_solution_[index];
570  if (value < lb - kCpEpsilon || value > ub + kCpEpsilon) return Propagate();
571  }
572 
573  // TODO(user): The saved lp solution is still valid given the current variable
574  // bounds, so the LP optimal didn't change. However we might still want to add
575  // new cuts or new lazy constraints?
576  //
577  // TODO(user): Propagate the last optimal_constraint? Note that we need
578  // to be careful since the reversible int in IntegerSumLE are not registered.
579  // However, because we delete "optimalconstraints" on backtrack, we might not
580  // care.
581  return true;
582 }
583 
584 glop::Fractional LinearProgrammingConstraint::GetVariableValueAtCpScale(
585  glop::ColIndex var) {
586  return scaler_.UnscaleVariableValue(var, simplex_.GetVariableValue(var));
587 }
588 
590  IntegerVariable variable) const {
591  return lp_solution_[gtl::FindOrDie(mirror_lp_variable_, variable).value()];
592 }
593 
595  IntegerVariable variable) const {
596  return lp_reduced_cost_[gtl::FindOrDie(mirror_lp_variable_, variable)
597  .value()];
598 }
599 
600 void LinearProgrammingConstraint::UpdateBoundsOfLpVariables() {
601  const int num_vars = integer_variables_.size();
602  for (int i = 0; i < num_vars; i++) {
603  const IntegerVariable cp_var = integer_variables_[i];
604  const double lb = ToDouble(integer_trail_->LowerBound(cp_var));
605  const double ub = ToDouble(integer_trail_->UpperBound(cp_var));
606  const double factor = scaler_.VariableScalingFactor(glop::ColIndex(i));
607  lp_data_.SetVariableBounds(glop::ColIndex(i), lb * factor, ub * factor);
608  }
609 }
610 
611 bool LinearProgrammingConstraint::SolveLp() {
612  if (trail_->CurrentDecisionLevel() == 0) {
613  lp_at_level_zero_is_final_ = false;
614  }
615 
616  const auto status = simplex_.Solve(lp_data_, time_limit_);
617  total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
618  if (!status.ok()) {
619  VLOG(1) << "The LP solver encountered an error: " << status.error_message();
620  simplex_.ClearStateForNextSolve();
621  return false;
622  }
623  average_degeneracy_.AddData(CalculateDegeneracy());
624  if (average_degeneracy_.CurrentAverage() >= 1000.0) {
625  VLOG(2) << "High average degeneracy: "
626  << average_degeneracy_.CurrentAverage();
627  }
628 
630  lp_solution_is_set_ = true;
631  lp_solution_level_ = trail_->CurrentDecisionLevel();
632  const int num_vars = integer_variables_.size();
633  for (int i = 0; i < num_vars; i++) {
634  const glop::Fractional value =
635  GetVariableValueAtCpScale(glop::ColIndex(i));
636  lp_solution_[i] = value;
637  expanded_lp_solution_[integer_variables_[i]] = value;
638  expanded_lp_solution_[NegationOf(integer_variables_[i])] = -value;
639  }
640 
641  if (lp_solution_level_ == 0) {
642  level_zero_lp_solution_ = lp_solution_;
643  }
644  }
645  return true;
646 }
647 
648 bool LinearProgrammingConstraint::AddCutFromConstraints(
649  const std::string& name,
650  const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers) {
651  // This is initialized to a valid linear contraint (by taking linear
652  // combination of the LP rows) and will be transformed into a cut if
653  // possible.
654  //
655  // TODO(user): For CG cuts, Ideally this linear combination should have only
656  // one fractional variable (basis_col). But because of imprecision, we get a
657  // bunch of fractional entry with small coefficient (relative to the one of
658  // basis_col). We try to handle that in IntegerRoundingCut(), but it might be
659  // better to add small multiple of the involved rows to get rid of them.
660  IntegerValue cut_ub;
661  if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
662  &cut_ub)) {
663  VLOG(1) << "Issue, overflow!";
664  return false;
665  }
666 
667  // Important: because we use integer_multipliers below, we cannot just
668  // divide by GCD or call PreventOverflow() here.
669  //
670  // TODO(user): the conversion col_index -> IntegerVariable is slow and could
671  // in principle be removed. Easy for cuts, but not so much for
672  // implied_bounds_processor_. Note that in theory this could allow us to
673  // use Literal directly without the need to have an IntegerVariable for them.
674  tmp_scattered_vector_.ConvertToLinearConstraint(integer_variables_, cut_ub,
675  &cut_);
676 
677  // Note that the base constraint we use are currently always tight.
678  // It is not a requirement though.
679  if (DEBUG_MODE) {
680  const double norm = ToDouble(ComputeInfinityNorm(cut_));
681  const double activity = ComputeActivity(cut_, expanded_lp_solution_);
682  if (std::abs(activity - ToDouble(cut_.ub)) / norm > 1e-4) {
683  VLOG(1) << "Cut not tight " << activity << " <= " << ToDouble(cut_.ub);
684  return false;
685  }
686  }
687  CHECK(constraint_manager_.DebugCheckConstraint(cut_));
688 
689  // We will create "artificial" variables after this index that will be
690  // substitued back into LP variables afterwards. Also not that we only use
691  // positive variable indices for these new variables, so that algorithm that
692  // take their negation will not mess up the indexing.
693  const IntegerVariable first_new_var(expanded_lp_solution_.size());
694  CHECK_EQ(first_new_var.value() % 2, 0);
695 
696  LinearConstraint copy_in_debug;
697  if (DEBUG_MODE) {
698  copy_in_debug = cut_;
699  }
700 
701  // Unlike for the knapsack cuts, it might not be always beneficial to
702  // process the implied bounds even though it seems to be better in average.
703  //
704  // TODO(user): Perform more experiments, in particular with which bound we use
705  // and if we complement or not before the MIR rounding. Other solvers seems
706  // to try different complementation strategies in a "potprocessing" and we
707  // don't. Try this too.
708  std::vector<ImpliedBoundsProcessor::SlackInfo> ib_slack_infos;
709  implied_bounds_processor_.ProcessUpperBoundedConstraintWithSlackCreation(
710  /*substitute_only_inner_variables=*/false, first_new_var,
711  expanded_lp_solution_, &cut_, &ib_slack_infos);
712  DCHECK(implied_bounds_processor_.DebugSlack(first_new_var, copy_in_debug,
713  cut_, ib_slack_infos));
714 
715  // Fills data for IntegerRoundingCut().
716  //
717  // Note(user): we use the current bound here, so the reasonement will only
718  // produce locally valid cut if we call this at a non-root node. We could
719  // use the level zero bounds if we wanted to generate a globally valid cut
720  // at another level. For now this is only called at level zero anyway.
721  tmp_lp_values_.clear();
722  tmp_var_lbs_.clear();
723  tmp_var_ubs_.clear();
724  for (const IntegerVariable var : cut_.vars) {
725  if (var >= first_new_var) {
726  CHECK(VariableIsPositive(var));
727  const auto& info =
728  ib_slack_infos[(var.value() - first_new_var.value()) / 2];
729  tmp_lp_values_.push_back(info.lp_value);
730  tmp_var_lbs_.push_back(info.lb);
731  tmp_var_ubs_.push_back(info.ub);
732  } else {
733  tmp_lp_values_.push_back(expanded_lp_solution_[var]);
734  tmp_var_lbs_.push_back(integer_trail_->LowerBound(var));
735  tmp_var_ubs_.push_back(integer_trail_->UpperBound(var));
736  }
737  }
738 
739  // Add slack.
740  // definition: integer_lp_[row] + slack_row == bound;
741  const IntegerVariable first_slack(first_new_var +
742  IntegerVariable(2 * ib_slack_infos.size()));
743  tmp_slack_rows_.clear();
744  tmp_slack_bounds_.clear();
745  for (const auto pair : integer_multipliers) {
746  const RowIndex row = pair.first;
747  const IntegerValue coeff = pair.second;
748  const auto status = simplex_.GetConstraintStatus(row);
749  if (status == glop::ConstraintStatus::FIXED_VALUE) continue;
750 
751  tmp_lp_values_.push_back(0.0);
752  cut_.vars.push_back(first_slack +
753  2 * IntegerVariable(tmp_slack_rows_.size()));
754  tmp_slack_rows_.push_back(row);
755  cut_.coeffs.push_back(coeff);
756 
757  const IntegerValue diff(
758  CapSub(integer_lp_[row].ub.value(), integer_lp_[row].lb.value()));
759  if (coeff > 0) {
760  tmp_slack_bounds_.push_back(integer_lp_[row].ub);
761  tmp_var_lbs_.push_back(IntegerValue(0));
762  tmp_var_ubs_.push_back(diff);
763  } else {
764  tmp_slack_bounds_.push_back(integer_lp_[row].lb);
765  tmp_var_lbs_.push_back(-diff);
766  tmp_var_ubs_.push_back(IntegerValue(0));
767  }
768  }
769 
770  bool at_least_one_added = false;
771 
772  // Try cover appraoch to find cut.
773  {
774  if (cover_cut_helper_.TrySimpleKnapsack(cut_, tmp_lp_values_, tmp_var_lbs_,
775  tmp_var_ubs_)) {
776  at_least_one_added |= PostprocessAndAddCut(
777  absl::StrCat(name, "_K"), cover_cut_helper_.Info(), first_new_var,
778  first_slack, ib_slack_infos, cover_cut_helper_.mutable_cut());
779  }
780  }
781 
782  // Try integer rounding heuristic to find cut.
783  {
784  RoundingOptions options;
785  options.max_scaling = sat_parameters_.max_integer_rounding_scaling();
786  integer_rounding_cut_helper_.ComputeCut(options, tmp_lp_values_,
787  tmp_var_lbs_, tmp_var_ubs_,
788  &implied_bounds_processor_, &cut_);
789  at_least_one_added |= PostprocessAndAddCut(
790  name,
791  absl::StrCat("num_lifted_booleans=",
792  integer_rounding_cut_helper_.NumLiftedBooleans()),
793  first_new_var, first_slack, ib_slack_infos, &cut_);
794  }
795  return at_least_one_added;
796 }
797 
798 bool LinearProgrammingConstraint::PostprocessAndAddCut(
799  const std::string& name, const std::string& info,
800  IntegerVariable first_new_var, IntegerVariable first_slack,
801  const std::vector<ImpliedBoundsProcessor::SlackInfo>& ib_slack_infos,
802  LinearConstraint* cut) {
803  // Compute the activity. Warning: the cut no longer have the same size so we
804  // cannot use tmp_lp_values_. Note that the substitution below shouldn't
805  // change the activity by definition.
806  double activity = 0.0;
807  for (int i = 0; i < cut->vars.size(); ++i) {
808  if (cut->vars[i] < first_new_var) {
809  activity +=
810  ToDouble(cut->coeffs[i]) * expanded_lp_solution_[cut->vars[i]];
811  }
812  }
813  const double kMinViolation = 1e-4;
814  const double violation = activity - ToDouble(cut->ub);
815  if (violation < kMinViolation) {
816  VLOG(3) << "Bad cut " << activity << " <= " << ToDouble(cut->ub);
817  return false;
818  }
819 
820  // Substitute any slack left.
821  {
822  int num_slack = 0;
823  tmp_scattered_vector_.ClearAndResize(integer_variables_.size());
824  IntegerValue cut_ub = cut->ub;
825  bool overflow = false;
826  for (int i = 0; i < cut->vars.size(); ++i) {
827  const IntegerVariable var = cut->vars[i];
828 
829  // Simple copy for non-slack variables.
830  if (var < first_new_var) {
831  const glop::ColIndex col =
832  gtl::FindOrDie(mirror_lp_variable_, PositiveVariable(var));
833  if (VariableIsPositive(var)) {
834  tmp_scattered_vector_.Add(col, cut->coeffs[i]);
835  } else {
836  tmp_scattered_vector_.Add(col, -cut->coeffs[i]);
837  }
838  continue;
839  }
840 
841  // Replace slack from bound substitution.
842  if (var < first_slack) {
843  const IntegerValue multiplier = cut->coeffs[i];
844  const int index = (var.value() - first_new_var.value()) / 2;
845  CHECK_LT(index, ib_slack_infos.size());
846 
847  std::vector<std::pair<ColIndex, IntegerValue>> terms;
848  for (const std::pair<IntegerVariable, IntegerValue>& term :
849  ib_slack_infos[index].terms) {
850  terms.push_back(
851  {gtl::FindOrDie(mirror_lp_variable_,
852  PositiveVariable(term.first)),
853  VariableIsPositive(term.first) ? term.second : -term.second});
854  }
855  if (!tmp_scattered_vector_.AddLinearExpressionMultiple(multiplier,
856  terms)) {
857  overflow = true;
858  break;
859  }
860  if (!AddProductTo(multiplier, -ib_slack_infos[index].offset, &cut_ub)) {
861  overflow = true;
862  break;
863  }
864  continue;
865  }
866 
867  // Replace slack from LP constraints.
868  ++num_slack;
869  const int slack_index = (var.value() - first_slack.value()) / 2;
870  const glop::RowIndex row = tmp_slack_rows_[slack_index];
871  const IntegerValue multiplier = -cut->coeffs[i];
872  if (!tmp_scattered_vector_.AddLinearExpressionMultiple(
873  multiplier, integer_lp_[row].terms)) {
874  overflow = true;
875  break;
876  }
877 
878  // Update rhs.
879  if (!AddProductTo(multiplier, tmp_slack_bounds_[slack_index], &cut_ub)) {
880  overflow = true;
881  break;
882  }
883  }
884 
885  if (overflow) {
886  VLOG(1) << "Overflow in slack removal.";
887  return false;
888  }
889 
890  VLOG(3) << " num_slack: " << num_slack;
891  tmp_scattered_vector_.ConvertToLinearConstraint(integer_variables_, cut_ub,
892  cut);
893  }
894 
895  // Display some stats used for investigation of cut generation.
896  const std::string extra_info =
897  absl::StrCat(info, " num_ib_substitutions=", ib_slack_infos.size());
898 
899  const double new_violation =
900  ComputeActivity(*cut, expanded_lp_solution_) - ToDouble(cut_.ub);
901  if (std::abs(violation - new_violation) >= 1e-4) {
902  VLOG(1) << "Violation discrepancy after slack removal. "
903  << " before = " << violation << " after = " << new_violation;
904  }
905 
906  DivideByGCD(cut);
907  return constraint_manager_.AddCut(*cut, name, expanded_lp_solution_,
908  extra_info);
909 }
910 
911 void LinearProgrammingConstraint::AddCGCuts() {
912  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
913  const RowIndex num_rows = lp_data_.num_constraints();
914  for (RowIndex row(0); row < num_rows; ++row) {
915  ColIndex basis_col = simplex_.GetBasis(row);
916  const Fractional lp_value = GetVariableValueAtCpScale(basis_col);
917 
918  // TODO(user): We could just look at the diff with std::floor() in the hope
919  // that when we are just under an integer, the exact computation below will
920  // also be just under it.
921  if (std::abs(lp_value - std::round(lp_value)) < 0.01) continue;
922 
923  // If this variable is a slack, we ignore it. This is because the
924  // corresponding row is not tight under the given lp values.
925  if (basis_col >= integer_variables_.size()) continue;
926 
927  const glop::ScatteredRow& lambda = simplex_.GetUnitRowLeftInverse(row);
928  glop::DenseColumn lp_multipliers(num_rows, 0.0);
929  double magnitude = 0.0;
930  int num_non_zeros = 0;
931  for (RowIndex row(0); row < num_rows; ++row) {
932  lp_multipliers[row] = lambda.values[glop::RowToColIndex(row)];
933  if (std::abs(lp_multipliers[row]) < 1e-12) {
934  lp_multipliers[row] = 0.0;
935  continue;
936  }
937 
938  // There should be no BASIC status, but they could be imprecision
939  // in the GetUnitRowLeftInverse() code? not sure, so better be safe.
940  const auto status = simplex_.GetConstraintStatus(row);
941  if (status == glop::ConstraintStatus::BASIC) {
942  VLOG(1) << "BASIC row not expected! " << lp_multipliers[row];
943  lp_multipliers[row] = 0.0;
944  }
945 
946  magnitude = std::max(magnitude, std::abs(lp_multipliers[row]));
947  if (lp_multipliers[row] != 0.0) ++num_non_zeros;
948  }
949  if (num_non_zeros == 0) continue;
950 
951  Fractional scaling;
952  for (int i = 0; i < 2; ++i) {
953  if (i == 1) {
954  // Try other sign.
955  //
956  // TODO(user): Maybe add an heuristic to know beforehand which sign to
957  // use?
958  for (RowIndex row(0); row < num_rows; ++row) {
959  lp_multipliers[row] = -lp_multipliers[row];
960  }
961  }
962 
963  // TODO(user): We use a lower value here otherwise we might run into
964  // overflow while computing the cut. This should be fixable.
965  const std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
966  ScaleLpMultiplier(/*take_objective_into_account=*/false,
967  lp_multipliers, &scaling, /*max_pow=*/52);
968  AddCutFromConstraints("CG", integer_multipliers);
969  }
970  }
971 }
972 
973 namespace {
974 
975 // For each element of a, adds a random one in b and append the pair to output.
976 void RandomPick(const std::vector<RowIndex>& a, const std::vector<RowIndex>& b,
977  ModelRandomGenerator* random,
978  std::vector<std::pair<RowIndex, RowIndex>>* output) {
979  if (a.empty() || b.empty()) return;
980  for (const RowIndex row : a) {
981  const RowIndex other = b[absl::Uniform<int>(*random, 0, b.size())];
982  if (other != row) {
983  output->push_back({row, other});
984  }
985  }
986 }
987 
988 template <class ListOfTerms>
989 IntegerValue GetCoeff(ColIndex col, const ListOfTerms& terms) {
990  for (const auto& term : terms) {
991  if (term.first == col) return term.second;
992  }
993  return IntegerValue(0);
994 }
995 
996 } // namespace
997 
998 void LinearProgrammingConstraint::AddMirCuts() {
999  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1000 
1001  // Heuristic to generate MIR_n cuts by combining a small number of rows. This
1002  // works greedily and follow more or less the MIR cut description in the
1003  // literature. We have a current cut, and we add one more row to it while
1004  // eliminating a variable of the current cut whose LP value is far from its
1005  // bound.
1006  //
1007  // A notable difference is that we randomize the variable we eliminate and
1008  // the row we use to do so. We still have weights to indicate our preferred
1009  // choices. This allows to generate different cuts when called again and
1010  // again.
1011  //
1012  // TODO(user): We could combine n rows to make sure we eliminate n variables
1013  // far away from their bounds by solving exactly in integer small linear
1014  // system.
1015  gtl::ITIVector<ColIndex, IntegerValue> dense_cut(integer_variables_.size(),
1016  IntegerValue(0));
1017  SparseBitset<ColIndex> non_zeros_(ColIndex(integer_variables_.size()));
1018 
1019  // We compute all the rows that are tight, these will be used as the base row
1020  // for the MIR_n procedure below.
1021  const RowIndex num_rows = lp_data_.num_constraints();
1022  std::vector<std::pair<RowIndex, IntegerValue>> base_rows;
1023  gtl::ITIVector<RowIndex, double> row_weights(num_rows.value(), 0.0);
1024  for (RowIndex row(0); row < num_rows; ++row) {
1025  const auto status = simplex_.GetConstraintStatus(row);
1026  if (status == glop::ConstraintStatus::BASIC) continue;
1027  if (status == glop::ConstraintStatus::FREE) continue;
1028 
1031  base_rows.push_back({row, IntegerValue(1)});
1032  }
1035  base_rows.push_back({row, IntegerValue(-1)});
1036  }
1037 
1038  // For now, we use the dual values for the row "weights".
1039  //
1040  // Note that we use the dual at LP scale so that it make more sense when we
1041  // compare different rows since the LP has been scaled.
1042  //
1043  // TODO(user): In Kati Wolter PhD "Implementation of Cutting Plane
1044  // Separators for Mixed Integer Programs" which describe SCIP's MIR cuts
1045  // implementation (or at least an early version of it), a more complex score
1046  // is used.
1047  //
1048  // Note(user): Because we only consider tight rows under the current lp
1049  // solution (i.e. non-basic rows), most should have a non-zero dual values.
1050  // But there is some degenerate problem where these rows have a really low
1051  // weight (or even zero), and having only weight of exactly zero in
1052  // std::discrete_distribution will result in a crash.
1053  row_weights[row] = std::max(1e-8, std::abs(simplex_.GetDualValue(row)));
1054  }
1055 
1056  std::vector<double> weights;
1058  std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1059  for (const std::pair<RowIndex, IntegerValue>& entry : base_rows) {
1060  // First try to generate a cut directly from this base row (MIR1).
1061  //
1062  // Note(user): We abort on success like it seems to be done in the
1063  // literature. Note that we don't succeed that often in generating an
1064  // efficient cut, so I am not sure aborting will make a big difference
1065  // speedwise. We might generate similar cuts though, but hopefully the cut
1066  // management can deal with that.
1067  integer_multipliers = {entry};
1068  if (AddCutFromConstraints("MIR_1", integer_multipliers)) {
1069  continue;
1070  }
1071 
1072  // Cleanup.
1073  for (const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1074  dense_cut[col] = IntegerValue(0);
1075  }
1076  non_zeros_.SparseClearAll();
1077 
1078  // Copy cut.
1079  const IntegerValue multiplier = entry.second;
1080  for (const std::pair<ColIndex, IntegerValue> term :
1081  integer_lp_[entry.first].terms) {
1082  const ColIndex col = term.first;
1083  const IntegerValue coeff = term.second;
1084  non_zeros_.Set(col);
1085  dense_cut[col] += coeff * multiplier;
1086  }
1087 
1088  used_rows.assign(num_rows.value(), false);
1089  used_rows[entry.first] = true;
1090 
1091  // We will aggregate at most kMaxAggregation more rows.
1092  //
1093  // TODO(user): optim + tune.
1094  const int kMaxAggregation = 5;
1095  for (int i = 0; i < kMaxAggregation; ++i) {
1096  // First pick a variable to eliminate. We currently pick a random one with
1097  // a weight that depend on how far it is from its closest bound.
1098  IntegerValue max_magnitude(0);
1099  weights.clear();
1100  std::vector<ColIndex> col_candidates;
1101  for (const ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1102  if (dense_cut[col] == 0) continue;
1103 
1104  max_magnitude = std::max(max_magnitude, IntTypeAbs(dense_cut[col]));
1105  const int col_degree =
1106  lp_data_.GetSparseColumn(col).num_entries().value();
1107  if (col_degree <= 1) continue;
1109  continue;
1110  }
1111 
1112  const IntegerVariable var = integer_variables_[col.value()];
1113  const double lp_value = expanded_lp_solution_[var];
1114  const double lb = ToDouble(integer_trail_->LowerBound(var));
1115  const double ub = ToDouble(integer_trail_->UpperBound(var));
1116  const double bound_distance = std::min(ub - lp_value, lp_value - lb);
1117  if (bound_distance > 1e-2) {
1118  weights.push_back(bound_distance);
1119  col_candidates.push_back(col);
1120  }
1121  }
1122  if (col_candidates.empty()) break;
1123 
1124  const ColIndex var_to_eliminate =
1125  col_candidates[std::discrete_distribution<>(weights.begin(),
1126  weights.end())(*random_)];
1127 
1128  // What rows can we add to eliminate var_to_eliminate?
1129  std::vector<RowIndex> possible_rows;
1130  weights.clear();
1131  for (const auto entry : lp_data_.GetSparseColumn(var_to_eliminate)) {
1132  const RowIndex row = entry.row();
1133  const auto status = simplex_.GetConstraintStatus(row);
1134  if (status == glop::ConstraintStatus::BASIC) continue;
1135  if (status == glop::ConstraintStatus::FREE) continue;
1136 
1137  // We disallow all the rows that contain a variable that we already
1138  // eliminated (or are about to). This mean that we choose rows that
1139  // form a "triangular" matrix on the position we choose to eliminate.
1140  if (used_rows[row]) continue;
1141  used_rows[row] = true;
1142 
1143  // TODO(user): Instead of using FIXED_VALUE consider also both direction
1144  // when we almost have an equality? that is if the LP constraints bounds
1145  // are close from each others (<1e-6 ?). Initial experiments shows it
1146  // doesn't change much, so I kept this version for now. Note that it
1147  // might just be better to use the side that constrain the current lp
1148  // optimal solution (that we get from the status).
1149  bool add_row = false;
1150  if (status == glop::ConstraintStatus::FIXED_VALUE ||
1152  if (entry.coefficient() > 0.0) {
1153  if (dense_cut[var_to_eliminate] < 0) add_row = true;
1154  } else {
1155  if (dense_cut[var_to_eliminate] > 0) add_row = true;
1156  }
1157  }
1158  if (status == glop::ConstraintStatus::FIXED_VALUE ||
1160  if (entry.coefficient() > 0.0) {
1161  if (dense_cut[var_to_eliminate] > 0) add_row = true;
1162  } else {
1163  if (dense_cut[var_to_eliminate] < 0) add_row = true;
1164  }
1165  }
1166  if (add_row) {
1167  possible_rows.push_back(row);
1168  weights.push_back(row_weights[row]);
1169  }
1170  }
1171  if (possible_rows.empty()) break;
1172 
1173  const RowIndex row_to_combine =
1174  possible_rows[std::discrete_distribution<>(weights.begin(),
1175  weights.end())(*random_)];
1176  const IntegerValue to_combine_coeff =
1177  GetCoeff(var_to_eliminate, integer_lp_[row_to_combine].terms);
1178  CHECK_NE(to_combine_coeff, 0);
1179 
1180  IntegerValue mult1 = -to_combine_coeff;
1181  IntegerValue mult2 = dense_cut[var_to_eliminate];
1182  CHECK_NE(mult2, 0);
1183  if (mult1 < 0) {
1184  mult1 = -mult1;
1185  mult2 = -mult2;
1186  }
1187 
1188  const IntegerValue gcd = IntegerValue(
1189  MathUtil::GCD64(std::abs(mult1.value()), std::abs(mult2.value())));
1190  CHECK_NE(gcd, 0);
1191  mult1 /= gcd;
1192  mult2 /= gcd;
1193 
1194  // Overflow detection.
1195  //
1196  // TODO(user): do that in the possible_rows selection? only problem is
1197  // that we do not have the integer coefficient there...
1198  for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1199  max_magnitude = std::max(max_magnitude, entry.second);
1200  }
1201  if (CapAdd(CapProd(max_magnitude.value(), std::abs(mult1.value())),
1202  CapProd(infinity_norms_[row_to_combine].value(),
1203  std::abs(mult2.value()))) == kint64max) {
1204  break;
1205  }
1206 
1207  for (std::pair<RowIndex, IntegerValue>& entry : integer_multipliers) {
1208  entry.second *= mult1;
1209  }
1210  integer_multipliers.push_back({row_to_combine, mult2});
1211 
1212  // TODO(user): Not supper efficient to recombine the rows.
1213  if (AddCutFromConstraints(absl::StrCat("MIR_", i + 2),
1214  integer_multipliers)) {
1215  break;
1216  }
1217 
1218  // Minor optim: the computation below is only needed if we do one more
1219  // iteration.
1220  if (i + 1 == kMaxAggregation) break;
1221 
1222  for (ColIndex col : non_zeros_.PositionsSetAtLeastOnce()) {
1223  dense_cut[col] *= mult1;
1224  }
1225  for (const std::pair<ColIndex, IntegerValue> term :
1226  integer_lp_[row_to_combine].terms) {
1227  const ColIndex col = term.first;
1228  const IntegerValue coeff = term.second;
1229  non_zeros_.Set(col);
1230  dense_cut[col] += coeff * mult2;
1231  }
1232  }
1233  }
1234 }
1235 
1236 void LinearProgrammingConstraint::AddZeroHalfCuts() {
1237  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1238 
1239  tmp_lp_values_.clear();
1240  tmp_var_lbs_.clear();
1241  tmp_var_ubs_.clear();
1242  for (const IntegerVariable var : integer_variables_) {
1243  tmp_lp_values_.push_back(expanded_lp_solution_[var]);
1244  tmp_var_lbs_.push_back(integer_trail_->LowerBound(var));
1245  tmp_var_ubs_.push_back(integer_trail_->UpperBound(var));
1246  }
1247 
1248  // TODO(user): See if it make sense to try to use implied bounds there.
1249  zero_half_cut_helper_.ProcessVariables(tmp_lp_values_, tmp_var_lbs_,
1250  tmp_var_ubs_);
1251  for (glop::RowIndex row(0); row < integer_lp_.size(); ++row) {
1252  // Even though we could use non-tight row, for now we prefer to use tight
1253  // ones.
1254  const auto status = simplex_.GetConstraintStatus(row);
1255  if (status == glop::ConstraintStatus::BASIC) continue;
1256  if (status == glop::ConstraintStatus::FREE) continue;
1257 
1258  zero_half_cut_helper_.AddOneConstraint(
1259  row, integer_lp_[row].terms, integer_lp_[row].lb, integer_lp_[row].ub);
1260  }
1261  for (const std::vector<std::pair<RowIndex, IntegerValue>>& multipliers :
1262  zero_half_cut_helper_.InterestingCandidates(random_)) {
1263  // TODO(user): Make sure that if the resulting linear coefficients are not
1264  // too high, we do try a "divisor" of two and thus try a true zero-half cut
1265  // instead of just using our best MIR heuristic (which might still be better
1266  // though).
1267  AddCutFromConstraints("ZERO_HALF", multipliers);
1268  }
1269 }
1270 
1271 void LinearProgrammingConstraint::UpdateSimplexIterationLimit(
1272  const int64 min_iter, const int64 max_iter) {
1273  if (sat_parameters_.linearization_level() < 2) return;
1274  const int64 num_degenerate_columns = CalculateDegeneracy();
1275  const int64 num_cols = simplex_.GetProblemNumCols().value();
1276  if (num_cols <= 0) {
1277  return;
1278  }
1279  CHECK_GT(num_cols, 0);
1280  const int64 decrease_factor = (10 * num_degenerate_columns) / num_cols;
1281  if (simplex_.GetProblemStatus() == glop::ProblemStatus::DUAL_FEASIBLE) {
1282  // We reached here probably because we predicted wrong. We use this as a
1283  // signal to increase the iterations or punish less for degeneracy compare
1284  // to the other part.
1285  if (is_degenerate_) {
1286  next_simplex_iter_ /= std::max(int64{1}, decrease_factor);
1287  } else {
1288  next_simplex_iter_ *= 2;
1289  }
1290  } else if (simplex_.GetProblemStatus() == glop::ProblemStatus::OPTIMAL) {
1291  if (is_degenerate_) {
1292  next_simplex_iter_ /= std::max(int64{1}, 2 * decrease_factor);
1293  } else {
1294  // This is the most common case. We use the size of the problem to
1295  // determine the limit and ignore the previous limit.
1296  next_simplex_iter_ = num_cols / 40;
1297  }
1298  }
1299  next_simplex_iter_ =
1300  std::max(min_iter, std::min(max_iter, next_simplex_iter_));
1301 }
1302 
1304  UpdateBoundsOfLpVariables();
1305 
1306  // TODO(user): It seems the time we loose by not stopping early might be worth
1307  // it because we end up with a better explanation at optimality.
1308  glop::GlopParameters parameters = simplex_.GetParameters();
1309  if (/* DISABLES CODE */ (false) && objective_is_defined_) {
1310  // We put a limit on the dual objective since there is no point increasing
1311  // it past our current objective upper-bound (we will already fail as soon
1312  // as we pass it). Note that this limit is properly transformed using the
1313  // objective scaling factor and offset stored in lp_data_.
1314  //
1315  // Note that we use a bigger epsilon here to be sure that if we abort
1316  // because of this, we will report a conflict.
1317  parameters.set_objective_upper_limit(
1318  static_cast<double>(integer_trail_->UpperBound(objective_cp_).value() +
1319  100.0 * kCpEpsilon));
1320  }
1321 
1322  // Put an iteration limit on the work we do in the simplex for this call. Note
1323  // that because we are "incremental", even if we don't solve it this time we
1324  // will make progress towards a solve in the lower node of the tree search.
1325  if (trail_->CurrentDecisionLevel() == 0) {
1326  // TODO(user): Dynamically change the iteration limit for root node as
1327  // well.
1328  parameters.set_max_number_of_iterations(2000);
1329  } else {
1330  parameters.set_max_number_of_iterations(next_simplex_iter_);
1331  }
1332  if (sat_parameters_.use_exact_lp_reason()) {
1333  parameters.set_change_status_to_imprecise(false);
1334  parameters.set_primal_feasibility_tolerance(1e-7);
1335  parameters.set_dual_feasibility_tolerance(1e-7);
1336  }
1337 
1338  simplex_.SetParameters(parameters);
1340  if (!SolveLp()) return true;
1341 
1342  // Add new constraints to the LP and resolve?
1343  const int max_cuts_rounds =
1344  trail_->CurrentDecisionLevel() == 0
1345  ? sat_parameters_.max_cut_rounds_at_level_zero()
1346  : 1;
1347  int cuts_round = 0;
1348  while (simplex_.GetProblemStatus() == glop::ProblemStatus::OPTIMAL &&
1349  cuts_round < max_cuts_rounds) {
1350  // We wait for the first batch of problem constraints to be added before we
1351  // begin to generate cuts.
1352  cuts_round++;
1353  if (!integer_lp_.empty()) {
1354  implied_bounds_processor_.ClearCache();
1355  implied_bounds_processor_.SeparateSomeImpliedBoundCuts(
1356  expanded_lp_solution_);
1357 
1358  // The "generic" cuts are currently part of this class as they are using
1359  // data from the current LP.
1360  //
1361  // TODO(user): Refactor so that they are just normal cut generators?
1362  if (trail_->CurrentDecisionLevel() == 0) {
1363  if (sat_parameters_.add_mir_cuts()) AddMirCuts();
1364  if (sat_parameters_.add_cg_cuts()) AddCGCuts();
1365  if (sat_parameters_.add_zero_half_cuts()) AddZeroHalfCuts();
1366  }
1367 
1368  // Try to add cuts.
1369  if (!cut_generators_.empty() &&
1370  (trail_->CurrentDecisionLevel() == 0 ||
1371  !sat_parameters_.only_add_cuts_at_level_zero())) {
1372  for (const CutGenerator& generator : cut_generators_) {
1373  generator.generate_cuts(expanded_lp_solution_, &constraint_manager_);
1374  }
1375  }
1376 
1377  implied_bounds_processor_.IbCutPool().TransferToManager(
1378  expanded_lp_solution_, &constraint_manager_);
1379  }
1380 
1381  glop::BasisState state = simplex_.GetState();
1382  if (constraint_manager_.ChangeLp(expanded_lp_solution_, &state)) {
1383  simplex_.LoadStateForNextSolve(state);
1384  if (!CreateLpFromConstraintManager()) {
1385  return integer_trail_->ReportConflict({});
1386  }
1387  const double old_obj = simplex_.GetObjectiveValue();
1388  if (!SolveLp()) return true;
1389  if (simplex_.GetProblemStatus() == glop::ProblemStatus::OPTIMAL) {
1390  VLOG(1) << "Relaxation improvement " << old_obj << " -> "
1391  << simplex_.GetObjectiveValue()
1392  << " diff: " << simplex_.GetObjectiveValue() - old_obj
1393  << " level: " << trail_->CurrentDecisionLevel();
1394  }
1395  } else {
1396  if (trail_->CurrentDecisionLevel() == 0) {
1397  lp_at_level_zero_is_final_ = true;
1398  }
1399  break;
1400  }
1401  }
1402 
1403  // A dual-unbounded problem is infeasible. We use the dual ray reason.
1404  if (simplex_.GetProblemStatus() == glop::ProblemStatus::DUAL_UNBOUNDED) {
1405  if (sat_parameters_.use_exact_lp_reason()) {
1406  if (!FillExactDualRayReason()) return true;
1407  } else {
1408  FillReducedCostReasonIn(simplex_.GetDualRayRowCombination(),
1409  &integer_reason_);
1410  }
1411  return integer_trail_->ReportConflict(integer_reason_);
1412  }
1413 
1414  // TODO(user): Update limits for DUAL_UNBOUNDED status as well.
1415  UpdateSimplexIterationLimit(/*min_iter=*/10, /*max_iter=*/1000);
1416 
1417  // Optimality deductions if problem has an objective.
1418  if (objective_is_defined_ &&
1420  simplex_.GetProblemStatus() == glop::ProblemStatus::DUAL_FEASIBLE)) {
1421  // Try to filter optimal objective value. Note that GetObjectiveValue()
1422  // already take care of the scaling so that it returns an objective in the
1423  // CP world.
1424  const double relaxed_optimal_objective = simplex_.GetObjectiveValue();
1425  const IntegerValue approximate_new_lb(
1426  static_cast<int64>(std::ceil(relaxed_optimal_objective - kCpEpsilon)));
1427 
1428  // TODO(user): Maybe do a bit less computation when we cannot propagate
1429  // anything.
1430  if (sat_parameters_.use_exact_lp_reason()) {
1431  if (!ExactLpReasonning()) return false;
1432 
1433  // Display when the inexact bound would have propagated more.
1434  const IntegerValue propagated_lb =
1435  integer_trail_->LowerBound(objective_cp_);
1436  if (approximate_new_lb > propagated_lb) {
1437  VLOG(2) << "LP objective [ " << ToDouble(propagated_lb) << ", "
1438  << ToDouble(integer_trail_->UpperBound(objective_cp_))
1439  << " ] approx_lb += "
1440  << ToDouble(approximate_new_lb - propagated_lb) << " gap: "
1441  << integer_trail_->UpperBound(objective_cp_) - propagated_lb;
1442  }
1443  } else {
1444  FillReducedCostReasonIn(simplex_.GetReducedCosts(), &integer_reason_);
1445  const double objective_cp_ub =
1446  ToDouble(integer_trail_->UpperBound(objective_cp_));
1447  ReducedCostStrengtheningDeductions(objective_cp_ub -
1448  relaxed_optimal_objective);
1449  if (!deductions_.empty()) {
1450  deductions_reason_ = integer_reason_;
1451  deductions_reason_.push_back(
1452  integer_trail_->UpperBoundAsLiteral(objective_cp_));
1453  }
1454 
1455  // Push new objective lb.
1456  if (approximate_new_lb > integer_trail_->LowerBound(objective_cp_)) {
1457  const IntegerLiteral deduction =
1458  IntegerLiteral::GreaterOrEqual(objective_cp_, approximate_new_lb);
1459  if (!integer_trail_->Enqueue(deduction, {}, integer_reason_)) {
1460  return false;
1461  }
1462  }
1463 
1464  // Push reduced cost strengthening bounds.
1465  if (!deductions_.empty()) {
1466  const int trail_index_with_same_reason = integer_trail_->Index();
1467  for (const IntegerLiteral deduction : deductions_) {
1468  if (!integer_trail_->Enqueue(deduction, {}, deductions_reason_,
1469  trail_index_with_same_reason)) {
1470  return false;
1471  }
1472  }
1473  }
1474  }
1475  }
1476 
1477  // Copy more info about the current solution.
1478  if (simplex_.GetProblemStatus() == glop::ProblemStatus::OPTIMAL) {
1479  CHECK(lp_solution_is_set_);
1480 
1481  lp_objective_ = simplex_.GetObjectiveValue();
1482  lp_solution_is_integer_ = true;
1483  const int num_vars = integer_variables_.size();
1484  for (int i = 0; i < num_vars; i++) {
1485  lp_reduced_cost_[i] = scaler_.UnscaleReducedCost(
1486  glop::ColIndex(i), simplex_.GetReducedCost(glop::ColIndex(i)));
1487  if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) >
1488  kCpEpsilon) {
1489  lp_solution_is_integer_ = false;
1490  }
1491  }
1492 
1493  if (compute_reduced_cost_averages_) {
1494  UpdateAverageReducedCosts();
1495  }
1496  }
1497 
1498  if (sat_parameters_.use_branching_in_lp() && objective_is_defined_ &&
1499  trail_->CurrentDecisionLevel() == 0 && !is_degenerate_ &&
1500  lp_solution_is_set_ && !lp_solution_is_integer_ &&
1501  sat_parameters_.linearization_level() >= 2 &&
1502  compute_reduced_cost_averages_ &&
1504  count_since_last_branching_++;
1505  if (count_since_last_branching_ < branching_frequency_) {
1506  return true;
1507  }
1508  count_since_last_branching_ = 0;
1509  bool branching_successful = false;
1510 
1511  // Strong branching on top max_num_branches variable.
1512  const int max_num_branches = 3;
1513  const int num_vars = integer_variables_.size();
1514  std::vector<std::pair<double, IntegerVariable>> branching_vars;
1515  for (int i = 0; i < num_vars; ++i) {
1516  const IntegerVariable var = integer_variables_[i];
1517  const IntegerVariable positive_var = PositiveVariable(var);
1518 
1519  // Skip non fractional variables.
1520  const double current_value = GetSolutionValue(positive_var);
1521  if (std::abs(current_value - std::round(current_value)) <= kCpEpsilon) {
1522  continue;
1523  }
1524 
1525  // Skip ignored variables.
1526  if (integer_trail_->IsCurrentlyIgnored(var)) continue;
1527 
1528  // We can use any metric to select a variable to branch on. Reduced cost
1529  // average is one of the most promissing metric. It captures the history
1530  // of the objective bound improvement in LP due to changes in the given
1531  // variable bounds.
1532  //
1533  // NOTE: We also experimented using PseudoCosts and most recent reduced
1534  // cost as metrics but it doesn't give better results on benchmarks.
1535  const double cost_i = rc_scores_[i];
1536  std::pair<double, IntegerVariable> branching_var =
1537  std::make_pair(-cost_i, positive_var);
1538  auto iterator = std::lower_bound(branching_vars.begin(),
1539  branching_vars.end(), branching_var);
1540 
1541  branching_vars.insert(iterator, branching_var);
1542  if (branching_vars.size() > max_num_branches) {
1543  branching_vars.resize(max_num_branches);
1544  }
1545  }
1546 
1547  for (const std::pair<double, IntegerVariable>& branching_var :
1548  branching_vars) {
1549  const IntegerVariable positive_var = branching_var.second;
1550  VLOG(2) << "Branching on: " << positive_var;
1551  if (BranchOnVar(positive_var)) {
1552  VLOG(2) << "Branching successful.";
1553  branching_successful = true;
1554  } else {
1555  break;
1556  }
1557  }
1558  if (!branching_successful) {
1559  branching_frequency_ *= 2;
1560  }
1561  }
1562  return true;
1563 }
1564 
1565 // Returns kMinIntegerValue in case of overflow.
1566 //
1567 // TODO(user): Because of PreventOverflow(), this should actually never happen.
1568 IntegerValue LinearProgrammingConstraint::GetImpliedLowerBound(
1569  const LinearConstraint& terms) const {
1570  IntegerValue lower_bound(0);
1571  const int size = terms.vars.size();
1572  for (int i = 0; i < size; ++i) {
1573  const IntegerVariable var = terms.vars[i];
1574  const IntegerValue coeff = terms.coeffs[i];
1575  CHECK_NE(coeff, 0);
1576  const IntegerValue bound = coeff > 0 ? integer_trail_->LowerBound(var)
1577  : integer_trail_->UpperBound(var);
1578  if (!AddProductTo(bound, coeff, &lower_bound)) return kMinIntegerValue;
1579  }
1580  return lower_bound;
1581 }
1582 
1583 bool LinearProgrammingConstraint::PossibleOverflow(
1584  const LinearConstraint& constraint) {
1585  IntegerValue lower_bound(0);
1586  const int size = constraint.vars.size();
1587  for (int i = 0; i < size; ++i) {
1588  const IntegerVariable var = constraint.vars[i];
1589  const IntegerValue coeff = constraint.coeffs[i];
1590  CHECK_NE(coeff, 0);
1591  const IntegerValue bound = coeff > 0 ? integer_trail_->LowerBound(var)
1592  : integer_trail_->UpperBound(var);
1593  if (!AddProductTo(bound, coeff, &lower_bound)) {
1594  return true;
1595  }
1596  }
1597  const int64 slack = CapAdd(lower_bound.value(), -constraint.ub.value());
1598  if (slack == kint64min || slack == kint64max) {
1599  return true;
1600  }
1601  return false;
1602 }
1603 
1604 namespace {
1605 
1606 absl::int128 FloorRatio128(absl::int128 x, IntegerValue positive_div) {
1607  absl::int128 div128(positive_div.value());
1608  absl::int128 result = x / div128;
1609  if (result * div128 > x) return result - 1;
1610  return result;
1611 }
1612 
1613 } // namespace
1614 
1615 void LinearProgrammingConstraint::PreventOverflow(LinearConstraint* constraint,
1616  int max_pow) {
1617  // Compute the min/max possible partial sum.
1618  //
1619  // Note that since we currently only use this cut locally, it is okay to
1620  // use the current lb/ub here to decide if we have an overflow or not. Below
1621  // however, we do have to use the level zero lower bound.
1622  double sum_min = std::min(0.0, ToDouble(-constraint->ub));
1623  double sum_max = std::max(0.0, ToDouble(-constraint->ub));
1624  const int size = constraint->vars.size();
1625  for (int i = 0; i < size; ++i) {
1626  const IntegerVariable var = constraint->vars[i];
1627  const double coeff = ToDouble(constraint->coeffs[i]);
1628  const double prod1 = coeff * ToDouble(integer_trail_->LowerBound(var));
1629  const double prod2 = coeff * ToDouble(integer_trail_->UpperBound(var));
1630  sum_min += std::min(0.0, std::min(prod1, prod2));
1631  sum_max += std::max(0.0, std::max(prod1, prod2));
1632  }
1633  const double max_value = std::max(sum_max, -sum_min);
1634 
1635  const IntegerValue divisor(std::ceil(std::ldexp(max_value, -max_pow)));
1636  if (divisor <= 1) return;
1637 
1638  // To be correct, we need to shift all variable so that they are positive.
1639  //
1640  // Important: One might be tempted to think that using the current variable
1641  // bounds is okay here since we only use this to derive cut/constraint that
1642  // only needs to be locally valid. However, in some corner cases (like when
1643  // one term become zero), we might loose the fact that we used one of the
1644  // variable bound to derive the new constraint, so we will miss it in the
1645  // explanation !!
1646  //
1647  // TODO(user): This code is tricky and similar to the one to generate cuts.
1648  // Test and may reduce the duplication? note however that here we use int128
1649  // to deal with potential overflow.
1650  int new_size = 0;
1651  absl::int128 adjust = 0;
1652  for (int i = 0; i < size; ++i) {
1653  const IntegerValue old_coeff = constraint->coeffs[i];
1654  const IntegerValue new_coeff = FloorRatio(old_coeff, divisor);
1655 
1656  // Compute the rhs adjustement.
1657  const absl::int128 remainder =
1658  absl::int128(old_coeff.value()) -
1659  absl::int128(new_coeff.value()) * absl::int128(divisor.value());
1660  adjust +=
1661  remainder *
1662  absl::int128(
1663  integer_trail_->LevelZeroLowerBound(constraint->vars[i]).value());
1664 
1665  if (new_coeff == 0) continue;
1666  constraint->vars[new_size] = constraint->vars[i];
1667  constraint->coeffs[new_size] = new_coeff;
1668  ++new_size;
1669  }
1670  constraint->vars.resize(new_size);
1671  constraint->coeffs.resize(new_size);
1672 
1673  constraint->ub = IntegerValue(static_cast<int64>(
1674  FloorRatio128(absl::int128(constraint->ub.value()) - adjust, divisor)));
1675 }
1676 
1677 // TODO(user): combine this with RelaxLinearReason() to avoid the extra
1678 // magnitude vector and the weird precondition of RelaxLinearReason().
1679 void LinearProgrammingConstraint::SetImpliedLowerBoundReason(
1680  const LinearConstraint& terms, IntegerValue slack) {
1681  integer_reason_.clear();
1682  std::vector<IntegerValue> magnitudes;
1683  const int size = terms.vars.size();
1684  for (int i = 0; i < size; ++i) {
1685  const IntegerVariable var = terms.vars[i];
1686  const IntegerValue coeff = terms.coeffs[i];
1687  CHECK_NE(coeff, 0);
1688  if (coeff > 0) {
1689  magnitudes.push_back(coeff);
1690  integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(var));
1691  } else {
1692  magnitudes.push_back(-coeff);
1693  integer_reason_.push_back(integer_trail_->UpperBoundAsLiteral(var));
1694  }
1695  }
1696  CHECK_GE(slack, 0);
1697  if (slack > 0) {
1698  integer_trail_->RelaxLinearReason(slack, magnitudes, &integer_reason_);
1699  }
1700  integer_trail_->RemoveLevelZeroBounds(&integer_reason_);
1701 }
1702 
1703 // TODO(user): Provide a sparse interface.
1704 std::vector<std::pair<RowIndex, IntegerValue>>
1705 LinearProgrammingConstraint::ScaleLpMultiplier(
1706  bool take_objective_into_account,
1707  const glop::DenseColumn& dense_lp_multipliers, Fractional* scaling,
1708  int max_pow) const {
1709  double max_sum = 0.0;
1710  std::vector<std::pair<RowIndex, Fractional>> cp_multipliers;
1711  for (RowIndex row(0); row < dense_lp_multipliers.size(); ++row) {
1712  const Fractional lp_multi = dense_lp_multipliers[row];
1713 
1714  // We ignore small values since these are likely errors and will not
1715  // contribute much to the new lp constraint anyway.
1716  if (std::abs(lp_multi) < 1e-12) continue;
1717 
1718  // Remove trivial bad cases.
1719  //
1720  // TODO(user): It might be better (when possible) to use the OPTIMAL row
1721  // status since in most situation we do want the constraint we add to be
1722  // tight under the current LP solution. Only for infeasible problem we might
1723  // not have access to the status.
1724  if (lp_multi > 0.0 && integer_lp_[row].ub >= kMaxIntegerValue) {
1725  continue;
1726  }
1727  if (lp_multi < 0.0 && integer_lp_[row].lb <= kMinIntegerValue) {
1728  continue;
1729  }
1730 
1731  const Fractional cp_multi = scaler_.UnscaleDualValue(row, lp_multi);
1732  cp_multipliers.push_back({row, cp_multi});
1733  max_sum += ToDouble(infinity_norms_[row]) * std::abs(cp_multi);
1734  }
1735 
1736  // This behave exactly like if we had another "objective" constraint with
1737  // an lp_multi of 1.0 and a cp_multi of 1.0.
1738  if (take_objective_into_account) {
1739  max_sum += ToDouble(objective_infinity_norm_);
1740  }
1741 
1742  *scaling = 1.0;
1743  std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers;
1744  if (max_sum == 0.0) {
1745  // Empty linear combinaison.
1746  return integer_multipliers;
1747  }
1748 
1749  // We want max_sum * scaling to be <= 2 ^ max_pow and fit on an int64.
1750  // We use a power of 2 as this seems to work better.
1751  const double threshold = std::ldexp(1, max_pow) / max_sum;
1752  if (threshold < 1.0) {
1753  // TODO(user): we currently do not support scaling down, so we just abort
1754  // in this case.
1755  return integer_multipliers;
1756  }
1757  while (2 * *scaling <= threshold) *scaling *= 2;
1758 
1759  // Scale the multipliers by *scaling.
1760  //
1761  // TODO(user): Maybe use int128 to avoid overflow?
1762  for (const auto entry : cp_multipliers) {
1763  const IntegerValue coeff(std::round(entry.second * (*scaling)));
1764  if (coeff != 0) integer_multipliers.push_back({entry.first, coeff});
1765  }
1766  return integer_multipliers;
1767 }
1768 
1769 bool LinearProgrammingConstraint::ComputeNewLinearConstraint(
1770  const std::vector<std::pair<RowIndex, IntegerValue>>& integer_multipliers,
1771  ScatteredIntegerVector* scattered_vector, IntegerValue* upper_bound) const {
1772  // Initialize the new constraint.
1773  *upper_bound = 0;
1774  scattered_vector->ClearAndResize(integer_variables_.size());
1775 
1776  // Compute the new constraint by taking the linear combination given by
1777  // integer_multipliers of the integer constraints in integer_lp_.
1778  for (const std::pair<RowIndex, IntegerValue> term : integer_multipliers) {
1779  const RowIndex row = term.first;
1780  const IntegerValue multiplier = term.second;
1781  CHECK_LT(row, integer_lp_.size());
1782 
1783  // Update the constraint.
1784  if (!scattered_vector->AddLinearExpressionMultiple(
1785  multiplier, integer_lp_[row].terms)) {
1786  return false;
1787  }
1788 
1789  // Update the upper bound.
1790  const IntegerValue bound =
1791  multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
1792  if (!AddProductTo(multiplier, bound, upper_bound)) return false;
1793  }
1794 
1795  return true;
1796 }
1797 
1798 // TODO(user): no need to update the multipliers.
1799 void LinearProgrammingConstraint::AdjustNewLinearConstraint(
1800  std::vector<std::pair<glop::RowIndex, IntegerValue>>* integer_multipliers,
1801  ScatteredIntegerVector* scattered_vector, IntegerValue* upper_bound) const {
1802  const IntegerValue kMaxWantedCoeff(1e18);
1803  for (std::pair<RowIndex, IntegerValue>& term : *integer_multipliers) {
1804  const RowIndex row = term.first;
1805  const IntegerValue multiplier = term.second;
1806  if (multiplier == 0) continue;
1807 
1808  // We will only allow change of the form "multiplier += to_add" with to_add
1809  // in [-negative_limit, positive_limit].
1810  IntegerValue negative_limit = kMaxWantedCoeff;
1811  IntegerValue positive_limit = kMaxWantedCoeff;
1812 
1813  // Make sure we never change the sign of the multiplier, except if the
1814  // row is an equality in which case we don't care.
1815  if (integer_lp_[row].ub != integer_lp_[row].lb) {
1816  if (multiplier > 0) {
1817  negative_limit = std::min(negative_limit, multiplier);
1818  } else {
1819  positive_limit = std::min(positive_limit, -multiplier);
1820  }
1821  }
1822 
1823  // Make sure upper_bound + to_add * row_bound never overflow.
1824  const IntegerValue row_bound =
1825  multiplier > 0 ? integer_lp_[row].ub : integer_lp_[row].lb;
1826  if (row_bound != 0) {
1827  const IntegerValue limit1 = FloorRatio(
1828  std::max(IntegerValue(0), kMaxWantedCoeff - IntTypeAbs(*upper_bound)),
1829  IntTypeAbs(row_bound));
1830  const IntegerValue limit2 =
1831  FloorRatio(kMaxWantedCoeff, IntTypeAbs(row_bound));
1832  if (*upper_bound > 0 == row_bound > 0) { // Same sign.
1833  positive_limit = std::min(positive_limit, limit1);
1834  negative_limit = std::min(negative_limit, limit2);
1835  } else {
1836  negative_limit = std::min(negative_limit, limit1);
1837  positive_limit = std::min(positive_limit, limit2);
1838  }
1839  }
1840 
1841  // If we add the row to the scattered_vector, diff will indicate by how much
1842  // |upper_bound - ImpliedLB(scattered_vector)| will change. That correspond
1843  // to increasing the multiplier by 1.
1844  //
1845  // At this stage, we are not sure computing sum coeff * bound will not
1846  // overflow, so we use floating point numbers. It is fine to do so since
1847  // this is not directly involved in the actual exact constraint generation:
1848  // these variables are just used in an heuristic.
1849  double positive_diff = ToDouble(row_bound);
1850  double negative_diff = ToDouble(row_bound);
1851 
1852  // TODO(user): we could relax a bit some of the condition and allow a sign
1853  // change. It is just trickier to compute the diff when we allow such
1854  // changes.
1855  for (const auto entry : integer_lp_[row].terms) {
1856  const ColIndex col = entry.first;
1857  const IntegerValue coeff = entry.second;
1858  const IntegerValue abs_coef = IntTypeAbs(coeff);
1859  CHECK_NE(coeff, 0);
1860 
1861  const IntegerVariable var = integer_variables_[col.value()];
1862  const IntegerValue lb = integer_trail_->LowerBound(var);
1863  const IntegerValue ub = integer_trail_->UpperBound(var);
1864 
1865  // Moving a variable away from zero seems to improve the bound even
1866  // if it reduces the number of non-zero. Note that this is because of
1867  // this that positive_diff and negative_diff are not the same.
1868  const IntegerValue current = (*scattered_vector)[col];
1869  if (current == 0) {
1870  const IntegerValue overflow_limit(
1871  FloorRatio(kMaxWantedCoeff, abs_coef));
1872  positive_limit = std::min(positive_limit, overflow_limit);
1873  negative_limit = std::min(negative_limit, overflow_limit);
1874  if (coeff > 0) {
1875  positive_diff -= ToDouble(coeff) * ToDouble(lb);
1876  negative_diff -= ToDouble(coeff) * ToDouble(ub);
1877  } else {
1878  positive_diff -= ToDouble(coeff) * ToDouble(ub);
1879  negative_diff -= ToDouble(coeff) * ToDouble(lb);
1880  }
1881  continue;
1882  }
1883 
1884  // We don't want to change the sign of current (except if the variable is
1885  // fixed) or to have an overflow.
1886  //
1887  // Corner case:
1888  // - IntTypeAbs(current) can be larger than kMaxWantedCoeff!
1889  // - The code assumes that 2 * kMaxWantedCoeff do not overflow.
1890  const IntegerValue current_magnitude = IntTypeAbs(current);
1891  const IntegerValue other_direction_limit = FloorRatio(
1892  lb == ub
1893  ? kMaxWantedCoeff + std::min(current_magnitude,
1894  kMaxIntegerValue - kMaxWantedCoeff)
1895  : current_magnitude,
1896  abs_coef);
1897  const IntegerValue same_direction_limit(FloorRatio(
1898  std::max(IntegerValue(0), kMaxWantedCoeff - current_magnitude),
1899  abs_coef));
1900  if (current > 0 == coeff > 0) { // Same sign.
1901  negative_limit = std::min(negative_limit, other_direction_limit);
1902  positive_limit = std::min(positive_limit, same_direction_limit);
1903  } else {
1904  negative_limit = std::min(negative_limit, same_direction_limit);
1905  positive_limit = std::min(positive_limit, other_direction_limit);
1906  }
1907 
1908  // This is how diff change.
1909  const IntegerValue implied = current > 0 ? lb : ub;
1910  if (implied != 0) {
1911  positive_diff -= ToDouble(coeff) * ToDouble(implied);
1912  negative_diff -= ToDouble(coeff) * ToDouble(implied);
1913  }
1914  }
1915 
1916  // Only add a multiple of this row if it tighten the final constraint.
1917  // The positive_diff/negative_diff are supposed to be integer modulo the
1918  // double precision, so we only add a multiple if they seems far away from
1919  // zero.
1920  IntegerValue to_add(0);
1921  if (positive_diff <= -1.0 && positive_limit > 0) {
1922  to_add = positive_limit;
1923  }
1924  if (negative_diff >= 1.0 && negative_limit > 0) {
1925  // Pick this if it is better than the positive sign.
1926  if (to_add == 0 ||
1927  std::abs(ToDouble(negative_limit) * negative_diff) >
1928  std::abs(ToDouble(positive_limit) * positive_diff)) {
1929  to_add = -negative_limit;
1930  }
1931  }
1932  if (to_add != 0) {
1933  term.second += to_add;
1934  *upper_bound += to_add * row_bound;
1935 
1936  // TODO(user): we could avoid checking overflow here, but this is likely
1937  // not in the hot loop.
1938  CHECK(scattered_vector->AddLinearExpressionMultiple(
1939  to_add, integer_lp_[row].terms));
1940  }
1941  }
1942 }
1943 
1944 // The "exact" computation go as follow:
1945 //
1946 // Given any INTEGER linear combination of the LP constraints, we can create a
1947 // new integer constraint that is valid (its computation must not overflow
1948 // though). Lets call this "linear_combination <= ub". We can then always add to
1949 // it the inequality "objective_terms <= objective_var", so we get:
1950 // ImpliedLB(objective_terms + linear_combination) - ub <= objective_var.
1951 // where ImpliedLB() is computed from the variable current bounds.
1952 //
1953 // Now, if we use for the linear combination and approximation of the optimal
1954 // negated dual LP values (by scaling them and rounding them to integer), we
1955 // will get an EXACT objective lower bound that is more or less the same as the
1956 // inexact bound given by the LP relaxation. This allows to derive exact reasons
1957 // for any propagation done by this constraint.
1958 bool LinearProgrammingConstraint::ExactLpReasonning() {
1959  // Clear old reason and deductions.
1960  integer_reason_.clear();
1961  deductions_.clear();
1962  deductions_reason_.clear();
1963 
1964  // The row multipliers will be the negation of the LP duals.
1965  //
1966  // TODO(user): Provide and use a sparse API in Glop to get the duals.
1967  const RowIndex num_rows = simplex_.GetProblemNumRows();
1968  glop::DenseColumn lp_multipliers(num_rows);
1969  for (RowIndex row(0); row < num_rows; ++row) {
1970  lp_multipliers[row] = -simplex_.GetDualValue(row);
1971  }
1972 
1973  Fractional scaling;
1974  std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
1975  ScaleLpMultiplier(/*take_objective_into_account=*/true, lp_multipliers,
1976  &scaling);
1977 
1978  IntegerValue rc_ub;
1979  if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
1980  &rc_ub)) {
1981  VLOG(1) << "Issue while computing the exact LP reason. Aborting.";
1982  return true;
1983  }
1984 
1985  // The "objective constraint" behave like if the unscaled cp multiplier was
1986  // 1.0, so we will multiply it by this number and add it to reduced_costs.
1987  const IntegerValue obj_scale(std::round(scaling));
1988  if (obj_scale == 0) {
1989  VLOG(1) << "Overflow during exact LP reasoning. scaling=" << scaling;
1990  return true;
1991  }
1992  CHECK(tmp_scattered_vector_.AddLinearExpressionMultiple(obj_scale,
1993  integer_objective_));
1994  AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
1995  &rc_ub);
1996 
1997  // Create the IntegerSumLE that will allow to propagate the objective and more
1998  // generally do the reduced cost fixing.
1999  LinearConstraint new_constraint;
2000  tmp_scattered_vector_.ConvertToLinearConstraint(integer_variables_, rc_ub,
2001  &new_constraint);
2002  new_constraint.vars.push_back(objective_cp_);
2003  new_constraint.coeffs.push_back(-obj_scale);
2004  DivideByGCD(&new_constraint);
2005  PreventOverflow(&new_constraint);
2006  DCHECK(!PossibleOverflow(new_constraint));
2007  DCHECK(constraint_manager_.DebugCheckConstraint(new_constraint));
2008 
2009  IntegerSumLE* cp_constraint =
2010  new IntegerSumLE({}, new_constraint.vars, new_constraint.coeffs,
2011  new_constraint.ub, model_);
2012  if (trail_->CurrentDecisionLevel() == 0) {
2013  // Since we will never ask the reason for a constraint at level 0, we just
2014  // keep the last one.
2015  optimal_constraints_.clear();
2016  }
2017  optimal_constraints_.emplace_back(cp_constraint);
2018  rev_optimal_constraints_size_ = optimal_constraints_.size();
2019  return cp_constraint->Propagate();
2020 }
2021 
2022 bool LinearProgrammingConstraint::FillExactDualRayReason() {
2023  Fractional scaling;
2024  std::vector<std::pair<RowIndex, IntegerValue>> integer_multipliers =
2025  ScaleLpMultiplier(/*take_objective_into_account=*/false,
2026  simplex_.GetDualRay(), &scaling);
2027 
2028  IntegerValue new_constraint_ub;
2029  if (!ComputeNewLinearConstraint(integer_multipliers, &tmp_scattered_vector_,
2030  &new_constraint_ub)) {
2031  VLOG(1) << "Isse while computing the exact dual ray reason. Aborting.";
2032  return false;
2033  }
2034 
2035  AdjustNewLinearConstraint(&integer_multipliers, &tmp_scattered_vector_,
2036  &new_constraint_ub);
2037 
2038  LinearConstraint new_constraint;
2039  tmp_scattered_vector_.ConvertToLinearConstraint(
2040  integer_variables_, new_constraint_ub, &new_constraint);
2041  DivideByGCD(&new_constraint);
2042  PreventOverflow(&new_constraint);
2043  DCHECK(!PossibleOverflow(new_constraint));
2044  DCHECK(constraint_manager_.DebugCheckConstraint(new_constraint));
2045 
2046  const IntegerValue implied_lb = GetImpliedLowerBound(new_constraint);
2047  if (implied_lb <= new_constraint.ub) {
2048  VLOG(1) << "LP exact dual ray not infeasible,"
2049  << " implied_lb: " << implied_lb.value() / scaling
2050  << " ub: " << new_constraint.ub.value() / scaling;
2051  return false;
2052  }
2053  const IntegerValue slack = (implied_lb - new_constraint.ub) - 1;
2054  SetImpliedLowerBoundReason(new_constraint, slack);
2055  return true;
2056 }
2057 
2058 int64 LinearProgrammingConstraint::CalculateDegeneracy() {
2059  const glop::ColIndex num_vars = simplex_.GetProblemNumCols();
2060  int num_non_basic_with_zero_rc = 0;
2061  for (glop::ColIndex i(0); i < num_vars; ++i) {
2062  const double rc = simplex_.GetReducedCost(i);
2063  if (rc != 0.0) continue;
2064  if (simplex_.GetVariableStatus(i) == glop::VariableStatus::BASIC) {
2065  continue;
2066  }
2067  num_non_basic_with_zero_rc++;
2068  }
2069  const int64 num_cols = simplex_.GetProblemNumCols().value();
2070  is_degenerate_ = num_non_basic_with_zero_rc >= 0.3 * num_cols;
2071  return num_non_basic_with_zero_rc;
2072 }
2073 
2074 void LinearProgrammingConstraint::ReducedCostStrengtheningDeductions(
2075  double cp_objective_delta) {
2076  deductions_.clear();
2077 
2078  // TRICKY: while simplex_.GetObjectiveValue() use the objective scaling factor
2079  // stored in the lp_data_, all the other functions like GetReducedCost() or
2080  // GetVariableValue() do not.
2081  const double lp_objective_delta =
2082  cp_objective_delta / lp_data_.objective_scaling_factor();
2083  const int num_vars = integer_variables_.size();
2084  for (int i = 0; i < num_vars; i++) {
2085  const IntegerVariable cp_var = integer_variables_[i];
2086  const glop::ColIndex lp_var = glop::ColIndex(i);
2087  const double rc = simplex_.GetReducedCost(lp_var);
2088  const double value = simplex_.GetVariableValue(lp_var);
2089 
2090  if (rc == 0.0) continue;
2091  const double lp_other_bound = value + lp_objective_delta / rc;
2092  const double cp_other_bound =
2093  scaler_.UnscaleVariableValue(lp_var, lp_other_bound);
2094 
2095  if (rc > kLpEpsilon) {
2096  const double ub = ToDouble(integer_trail_->UpperBound(cp_var));
2097  const double new_ub = std::floor(cp_other_bound + kCpEpsilon);
2098  if (new_ub < ub) {
2099  // TODO(user): Because rc > kLpEpsilon, the lower_bound of cp_var
2100  // will be part of the reason returned by FillReducedCostsReason(), but
2101  // we actually do not need it here. Same below.
2102  const IntegerValue new_ub_int(static_cast<IntegerValue>(new_ub));
2103  deductions_.push_back(IntegerLiteral::LowerOrEqual(cp_var, new_ub_int));
2104  }
2105  } else if (rc < -kLpEpsilon) {
2106  const double lb = ToDouble(integer_trail_->LowerBound(cp_var));
2107  const double new_lb = std::ceil(cp_other_bound - kCpEpsilon);
2108  if (new_lb > lb) {
2109  const IntegerValue new_lb_int(static_cast<IntegerValue>(new_lb));
2110  deductions_.push_back(
2111  IntegerLiteral::GreaterOrEqual(cp_var, new_lb_int));
2112  }
2113  }
2114  }
2115 }
2116 
2117 namespace {
2118 
2119 // Add a cut of the form Sum_{outgoing arcs from S} lp >= rhs_lower_bound.
2120 //
2121 // Note that we used to also add the same cut for the incoming arcs, but because
2122 // of flow conservation on these problems, the outgoing flow is always the same
2123 // as the incoming flow, so adding this extra cut doesn't seem relevant.
2124 void AddOutgoingCut(int num_nodes, int subset_size,
2125  const std::vector<bool>& in_subset,
2126  const std::vector<int>& tails,
2127  const std::vector<int>& heads,
2128  const std::vector<Literal>& literals,
2129  const std::vector<double>& literal_lp_values,
2130  int64 rhs_lower_bound,
2131  const gtl::ITIVector<IntegerVariable, double>& lp_values,
2132  LinearConstraintManager* manager, Model* model) {
2133  // A node is said to be optional if it can be excluded from the subcircuit,
2134  // in which case there is a self-loop on that node.
2135  // If there are optional nodes, use extended formula:
2136  // sum(cut) >= 1 - optional_loop_in - optional_loop_out
2137  // where optional_loop_in's node is in subset, optional_loop_out's is out.
2138  // TODO(user): Favor optional loops fixed to zero at root.
2139  int num_optional_nodes_in = 0;
2140  int num_optional_nodes_out = 0;
2141  int optional_loop_in = -1;
2142  int optional_loop_out = -1;
2143  for (int i = 0; i < tails.size(); ++i) {
2144  if (tails[i] != heads[i]) continue;
2145  if (in_subset[tails[i]]) {
2146  num_optional_nodes_in++;
2147  if (optional_loop_in == -1 ||
2148  literal_lp_values[i] < literal_lp_values[optional_loop_in]) {
2149  optional_loop_in = i;
2150  }
2151  } else {
2152  num_optional_nodes_out++;
2153  if (optional_loop_out == -1 ||
2154  literal_lp_values[i] < literal_lp_values[optional_loop_out]) {
2155  optional_loop_out = i;
2156  }
2157  }
2158  }
2159 
2160  // TODO(user): The lower bound for CVRP is computed assuming all nodes must be
2161  // served, if it is > 1 we lower it to one in the presence of optional nodes.
2162  if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2163  CHECK_GE(rhs_lower_bound, 1);
2164  rhs_lower_bound = 1;
2165  }
2166 
2167  LinearConstraintBuilder outgoing(model, IntegerValue(rhs_lower_bound),
2169  double sum_outgoing = 0.0;
2170 
2171  // Add outgoing arcs, compute outgoing flow.
2172  for (int i = 0; i < tails.size(); ++i) {
2173  if (in_subset[tails[i]] && !in_subset[heads[i]]) {
2174  sum_outgoing += literal_lp_values[i];
2175  CHECK(outgoing.AddLiteralTerm(literals[i], IntegerValue(1)));
2176  }
2177  }
2178 
2179  // Support optional nodes if any.
2180  if (num_optional_nodes_in + num_optional_nodes_out > 0) {
2181  // When all optionals of one side are excluded in lp solution, no cut.
2182  if (num_optional_nodes_in == subset_size &&
2183  (optional_loop_in == -1 ||
2184  literal_lp_values[optional_loop_in] > 1.0 - 1e-6)) {
2185  return;
2186  }
2187  if (num_optional_nodes_out == num_nodes - subset_size &&
2188  (optional_loop_out == -1 ||
2189  literal_lp_values[optional_loop_out] > 1.0 - 1e-6)) {
2190  return;
2191  }
2192 
2193  // There is no mandatory node in subset, add optional_loop_in.
2194  if (num_optional_nodes_in == subset_size) {
2195  CHECK(
2196  outgoing.AddLiteralTerm(literals[optional_loop_in], IntegerValue(1)));
2197  sum_outgoing += literal_lp_values[optional_loop_in];
2198  }
2199 
2200  // There is no mandatory node out of subset, add optional_loop_out.
2201  if (num_optional_nodes_out == num_nodes - subset_size) {
2202  CHECK(outgoing.AddLiteralTerm(literals[optional_loop_out],
2203  IntegerValue(1)));
2204  sum_outgoing += literal_lp_values[optional_loop_out];
2205  }
2206  }
2207 
2208  if (sum_outgoing < rhs_lower_bound - 1e-6) {
2209  manager->AddCut(outgoing.Build(), "Circuit", lp_values);
2210  }
2211 }
2212 
2213 } // namespace
2214 
2215 // We roughly follow the algorithm described in section 6 of "The Traveling
2216 // Salesman Problem, A computational Study", David L. Applegate, Robert E.
2217 // Bixby, Vasek Chvatal, William J. Cook.
2218 //
2219 // Note that this is mainly a "symmetric" case algo, but it does still work for
2220 // the asymmetric case.
2222  int num_nodes, const std::vector<int>& tails, const std::vector<int>& heads,
2223  const std::vector<Literal>& literals,
2224  const gtl::ITIVector<IntegerVariable, double>& lp_values,
2225  absl::Span<const int64> demands, int64 capacity,
2226  LinearConstraintManager* manager, Model* model) {
2227  if (num_nodes <= 2) return;
2228 
2229  // We will collect only the arcs with a positive lp_values to speed up some
2230  // computation below.
2231  struct Arc {
2232  int tail;
2233  int head;
2234  double lp_value;
2235  };
2236  std::vector<Arc> relevant_arcs;
2237 
2238  // Sort the arcs by non-increasing lp_values.
2239  std::vector<double> literal_lp_values(literals.size());
2240  std::vector<std::pair<double, int>> arc_by_decreasing_lp_values;
2241  auto* encoder = model->GetOrCreate<IntegerEncoder>();
2242  for (int i = 0; i < literals.size(); ++i) {
2243  double lp_value;
2244  const IntegerVariable direct_view = encoder->GetLiteralView(literals[i]);
2245  if (direct_view != kNoIntegerVariable) {
2246  lp_value = lp_values[direct_view];
2247  } else {
2248  lp_value =
2249  1.0 - lp_values[encoder->GetLiteralView(literals[i].Negated())];
2250  }
2251  literal_lp_values[i] = lp_value;
2252 
2253  if (lp_value < 1e-6) continue;
2254  relevant_arcs.push_back({tails[i], heads[i], lp_value});
2255  arc_by_decreasing_lp_values.push_back({lp_value, i});
2256  }
2257  std::sort(arc_by_decreasing_lp_values.begin(),
2258  arc_by_decreasing_lp_values.end(),
2259  std::greater<std::pair<double, int>>());
2260 
2261  // We will do a union-find by adding one by one the arc of the lp solution
2262  // in the order above. Every intermediate set during this construction will
2263  // be a candidate for a cut.
2264  //
2265  // In parallel to the union-find, to efficiently reconstruct these sets (at
2266  // most num_nodes), we construct a "decomposition forest" of the different
2267  // connected components. Note that we don't exploit any asymmetric nature of
2268  // the graph here. This is exactly the algo 6.3 in the book above.
2269  int num_components = num_nodes;
2270  std::vector<int> parent(num_nodes);
2271  std::vector<int> root(num_nodes);
2272  for (int i = 0; i < num_nodes; ++i) {
2273  parent[i] = i;
2274  root[i] = i;
2275  }
2276  auto get_root_and_compress_path = [&root](int node) {
2277  int r = node;
2278  while (root[r] != r) r = root[r];
2279  while (root[node] != r) {
2280  const int next = root[node];
2281  root[node] = r;
2282  node = next;
2283  }
2284  return r;
2285  };
2286  for (const auto pair : arc_by_decreasing_lp_values) {
2287  if (num_components == 2) break;
2288  const int tail = get_root_and_compress_path(tails[pair.second]);
2289  const int head = get_root_and_compress_path(heads[pair.second]);
2290  if (tail != head) {
2291  // Update the decomposition forest, note that the number of nodes is
2292  // growing.
2293  const int new_node = parent.size();
2294  parent.push_back(new_node);
2295  parent[head] = new_node;
2296  parent[tail] = new_node;
2297  --num_components;
2298 
2299  // It is important that the union-find representative is the same node.
2300  root.push_back(new_node);
2301  root[head] = new_node;
2302  root[tail] = new_node;
2303  }
2304  }
2305 
2306  // For each node in the decomposition forest, try to add a cut for the set
2307  // formed by the nodes and its children. To do that efficiently, we first
2308  // order the nodes so that for each node in a tree, the set of children forms
2309  // a consecutive span in the pre_order vector. This vector just lists the
2310  // nodes in the "pre-order" graph traversal order. The Spans will point inside
2311  // the pre_order vector, it is why we initialize it once and for all.
2312  int new_size = 0;
2313  std::vector<int> pre_order(num_nodes);
2314  std::vector<absl::Span<const int>> subsets;
2315  {
2316  std::vector<absl::InlinedVector<int, 2>> graph(parent.size());
2317  for (int i = 0; i < parent.size(); ++i) {
2318  if (parent[i] != i) graph[parent[i]].push_back(i);
2319  }
2320  std::vector<int> queue;
2321  std::vector<bool> seen(graph.size(), false);
2322  std::vector<int> start_index(parent.size());
2323  for (int i = num_nodes; i < parent.size(); ++i) {
2324  // Note that because of the way we constructed 'parent', the graph is a
2325  // binary tree. This is not required for the correctness of the algorithm
2326  // here though.
2327  CHECK(graph[i].empty() || graph[i].size() == 2);
2328  if (parent[i] != i) continue;
2329 
2330  // Explore the subtree rooted at node i.
2331  CHECK(!seen[i]);
2332  queue.push_back(i);
2333  while (!queue.empty()) {
2334  const int node = queue.back();
2335  if (seen[node]) {
2336  queue.pop_back();
2337  // All the children of node are in the span [start, end) of the
2338  // pre_order vector.
2339  const int start = start_index[node];
2340  if (new_size - start > 1) {
2341  subsets.emplace_back(&pre_order[start], new_size - start);
2342  }
2343  continue;
2344  }
2345  seen[node] = true;
2346  start_index[node] = new_size;
2347  if (node < num_nodes) pre_order[new_size++] = node;
2348  for (const int child : graph[node]) {
2349  if (!seen[child]) queue.push_back(child);
2350  }
2351  }
2352  }
2353  }
2354 
2355  // Compute the total demands in order to know the minimum incoming/outgoing
2356  // flow.
2357  int64 total_demands = 0;
2358  if (!demands.empty()) {
2359  for (const int64 demand : demands) total_demands += demand;
2360  }
2361 
2362  // Process each subsets and add any violated cut.
2363  CHECK_EQ(pre_order.size(), num_nodes);
2364  std::vector<bool> in_subset(num_nodes, false);
2365  for (const absl::Span<const int> subset : subsets) {
2366  CHECK_GT(subset.size(), 1);
2367  CHECK_LT(subset.size(), num_nodes);
2368 
2369  // These fields will be left untouched if demands.empty().
2370  bool contain_depot = false;
2371  int64 subset_demand = 0;
2372 
2373  // Initialize "in_subset" and the subset demands.
2374  for (const int n : subset) {
2375  in_subset[n] = true;
2376  if (!demands.empty()) {
2377  if (n == 0) contain_depot = true;
2378  subset_demand += demands[n];
2379  }
2380  }
2381 
2382  // Compute a lower bound on the outgoing flow.
2383  //
2384  // TODO(user): This lower bound assume all nodes in subset must be served,
2385  // which is not the case. For TSP we do the correct thing in
2386  // AddOutgoingCut() but not for CVRP... Fix!!
2387  //
2388  // TODO(user): It could be very interesting to see if this "min outgoing
2389  // flow" cannot be automatically infered from the constraint in the
2390  // precedence graph. This might work if we assume that any kind of path
2391  // cumul constraint is encoded with constraints:
2392  // [edge => value_head >= value_tail + edge_weight].
2393  // We could take the minimum incoming edge weight per node in the set, and
2394  // use the cumul variable domain to infer some capacity.
2395  int64 min_outgoing_flow = 1;
2396  if (!demands.empty()) {
2397  min_outgoing_flow =
2398  contain_depot
2399  ? (total_demands - subset_demand + capacity - 1) / capacity
2400  : (subset_demand + capacity - 1) / capacity;
2401  }
2402 
2403  // We still need to serve nodes with a demand of zero, and in the corner
2404  // case where all node in subset have a zero demand, the formula above
2405  // result in a min_outgoing_flow of zero.
2406  min_outgoing_flow = std::max(min_outgoing_flow, int64{1});
2407 
2408  // Compute the current outgoing flow out of the subset.
2409  //
2410  // This can take a significant portion of the running time, it is why it is
2411  // faster to do it only on arcs with non-zero lp values which should be in
2412  // linear number rather than the total number of arc which can be quadratic.
2413  //
2414  // TODO(user): For the symmetric case there is an even faster algo. See if
2415  // it can be generalized to the asymmetric one if become needed.
2416  // Reference is algo 6.4 of the "The Traveling Salesman Problem" book
2417  // mentionned above.
2418  double outgoing_flow = 0.0;
2419  for (const auto arc : relevant_arcs) {
2420  if (in_subset[arc.tail] && !in_subset[arc.head]) {
2421  outgoing_flow += arc.lp_value;
2422  }
2423  }
2424 
2425  // Add a cut if the current outgoing flow is not enough.
2426  if (outgoing_flow < min_outgoing_flow - 1e-6) {
2427  AddOutgoingCut(num_nodes, subset.size(), in_subset, tails, heads,
2428  literals, literal_lp_values,
2429  /*rhs_lower_bound=*/min_outgoing_flow, lp_values, manager,
2430  model);
2431  }
2432 
2433  // Sparse clean up.
2434  for (const int n : subset) in_subset[n] = false;
2435  }
2436 }
2437 
2438 namespace {
2439 
2440 // Returns for each literal its integer view, or the view of its negation.
2441 std::vector<IntegerVariable> GetAssociatedVariables(
2442  const std::vector<Literal>& literals, Model* model) {
2443  auto* encoder = model->GetOrCreate<IntegerEncoder>();
2444  std::vector<IntegerVariable> result;
2445  for (const Literal l : literals) {
2446  const IntegerVariable direct_view = encoder->GetLiteralView(l);
2447  if (direct_view != kNoIntegerVariable) {
2448  result.push_back(direct_view);
2449  } else {
2450  result.push_back(encoder->GetLiteralView(l.Negated()));
2451  DCHECK_NE(result.back(), kNoIntegerVariable);
2452  }
2453  }
2454  return result;
2455 }
2456 
2457 } // namespace
2458 
2459 // We use a basic algorithm to detect components that are not connected to the
2460 // rest of the graph in the LP solution, and add cuts to force some arcs to
2461 // enter and leave this component from outside.
2463  int num_nodes, const std::vector<int>& tails, const std::vector<int>& heads,
2464  const std::vector<Literal>& literals, Model* model) {
2465  CutGenerator result;
2466  result.vars = GetAssociatedVariables(literals, model);
2467  result.generate_cuts =
2468  [num_nodes, tails, heads, literals, model](
2469  const gtl::ITIVector<IntegerVariable, double>& lp_values,
2470  LinearConstraintManager* manager) {
2472  num_nodes, tails, heads, literals, lp_values,
2473  /*demands=*/{}, /*capacity=*/0, manager, model);
2474  };
2475  return result;
2476 }
2477 
2479  const std::vector<int>& tails,
2480  const std::vector<int>& heads,
2481  const std::vector<Literal>& literals,
2482  const std::vector<int64>& demands,
2483  int64 capacity, Model* model) {
2484  CutGenerator result;
2485  result.vars = GetAssociatedVariables(literals, model);
2486  result.generate_cuts =
2487  [num_nodes, tails, heads, demands, capacity, literals, model](
2488  const gtl::ITIVector<IntegerVariable, double>& lp_values,
2489  LinearConstraintManager* manager) {
2490  SeparateSubtourInequalities(num_nodes, tails, heads, literals,
2491  lp_values, demands, capacity, manager,
2492  model);
2493  };
2494  return result;
2495 }
2496 
2497 std::function<LiteralIndex()>
2499  IntegerTrail* integer_trail = integer_trail_;
2500  IntegerEncoder* integer_encoder = model->GetOrCreate<IntegerEncoder>();
2501  // Gather all 0-1 variables that appear in some LP.
2502  std::vector<IntegerVariable> variables;
2503  for (IntegerVariable var : integer_variables_) {
2504  if (integer_trail_->LowerBound(var) == 0 &&
2505  integer_trail_->UpperBound(var) == 1) {
2506  variables.push_back(var);
2507  }
2508  }
2509  VLOG(1) << "HeuristicLPMostInfeasibleBinary has " << variables.size()
2510  << " variables.";
2511 
2512  return [this, variables, integer_trail, integer_encoder]() {
2513  const double kEpsilon = 1e-6;
2514  // Find most fractional value.
2515  IntegerVariable fractional_var = kNoIntegerVariable;
2516  double fractional_distance_best = -1.0;
2517  for (const IntegerVariable var : variables) {
2518  // Skip ignored and fixed variables.
2519  if (integer_trail_->IsCurrentlyIgnored(var)) continue;
2520  const IntegerValue lb = integer_trail_->LowerBound(var);
2521  const IntegerValue ub = integer_trail_->UpperBound(var);
2522  if (lb == ub) continue;
2523 
2524  // Check variable's support is fractional.
2525  const double lp_value = this->GetSolutionValue(var);
2526  const double fractional_distance =
2527  std::min(std::ceil(lp_value - kEpsilon) - lp_value,
2528  lp_value - std::floor(lp_value + kEpsilon));
2529  if (fractional_distance < kEpsilon) continue;
2530 
2531  // Keep variable if it is farther from integrality than the previous.
2532  if (fractional_distance > fractional_distance_best) {
2533  fractional_var = var;
2534  fractional_distance_best = fractional_distance;
2535  }
2536  }
2537 
2538  if (fractional_var != kNoIntegerVariable) {
2539  return integer_encoder
2541  IntegerLiteral::GreaterOrEqual(fractional_var, IntegerValue(1)))
2542  .Index();
2543  }
2544  return kNoLiteralIndex;
2545  };
2546 }
2547 
2548 std::function<LiteralIndex()>
2550  // Gather all 0-1 variables that appear in this LP.
2551  std::vector<IntegerVariable> variables;
2552  for (IntegerVariable var : integer_variables_) {
2553  if (integer_trail_->LowerBound(var) == 0 &&
2554  integer_trail_->UpperBound(var) == 1) {
2555  variables.push_back(var);
2556  }
2557  }
2558  VLOG(1) << "HeuristicLPPseudoCostBinary has " << variables.size()
2559  << " variables.";
2560 
2561  // Store average of reduced cost from 1 to 0. The best heuristic only sets
2562  // variables to one and cares about cost to zero, even though classic
2563  // pseudocost will use max_var min(cost_to_one[var], cost_to_zero[var]).
2564  const int num_vars = variables.size();
2565  std::vector<double> cost_to_zero(num_vars, 0.0);
2566  std::vector<int> num_cost_to_zero(num_vars);
2567  int num_calls = 0;
2568 
2569  IntegerEncoder* integer_encoder = model->GetOrCreate<IntegerEncoder>();
2570  return [=]() mutable {
2571  const double kEpsilon = 1e-6;
2572 
2573  // Every 10000 calls, decay pseudocosts.
2574  num_calls++;
2575  if (num_calls == 10000) {
2576  for (int i = 0; i < num_vars; i++) {
2577  cost_to_zero[i] /= 2;
2578  num_cost_to_zero[i] /= 2;
2579  }
2580  num_calls = 0;
2581  }
2582 
2583  // Accumulate pseudo-costs of all unassigned variables.
2584  for (int i = 0; i < num_vars; i++) {
2585  const IntegerVariable var = variables[i];
2586  // Skip ignored and fixed variables.
2587  if (integer_trail_->IsCurrentlyIgnored(var)) continue;
2588  const IntegerValue lb = integer_trail_->LowerBound(var);
2589  const IntegerValue ub = integer_trail_->UpperBound(var);
2590  if (lb == ub) continue;
2591 
2592  const double rc = this->GetSolutionReducedCost(var);
2593  // Skip reduced costs that are nonzero because of numerical issues.
2594  if (std::abs(rc) < kEpsilon) continue;
2595 
2596  const double value = std::round(this->GetSolutionValue(var));
2597  if (value == 1.0 && rc < 0.0) {
2598  cost_to_zero[i] -= rc;
2599  num_cost_to_zero[i]++;
2600  }
2601  }
2602 
2603  // Select noninstantiated variable with highest pseudo-cost.
2604  int selected_index = -1;
2605  double best_cost = 0.0;
2606  for (int i = 0; i < num_vars; i++) {
2607  const IntegerVariable var = variables[i];
2608  // Skip ignored and fixed variables.
2609  if (integer_trail_->IsCurrentlyIgnored(var)) continue;
2610  if (integer_trail_->IsFixed(var)) continue;
2611 
2612  if (num_cost_to_zero[i] > 0 &&
2613  best_cost < cost_to_zero[i] / num_cost_to_zero[i]) {
2614  best_cost = cost_to_zero[i] / num_cost_to_zero[i];
2615  selected_index = i;
2616  }
2617  }
2618 
2619  if (selected_index >= 0) {
2620  const Literal decision = integer_encoder->GetOrCreateAssociatedLiteral(
2621  IntegerLiteral::GreaterOrEqual(variables[selected_index],
2622  IntegerValue(1)));
2623  return decision.Index();
2624  }
2625 
2626  return kNoLiteralIndex;
2627  };
2628 }
2629 
2630 void LinearProgrammingConstraint::UpdateAverageReducedCosts() {
2631  const int num_vars = integer_variables_.size();
2632  if (sum_cost_down_.size() < num_vars) {
2633  sum_cost_down_.resize(num_vars, 0.0);
2634  num_cost_down_.resize(num_vars, 0);
2635  sum_cost_up_.resize(num_vars, 0.0);
2636  num_cost_up_.resize(num_vars, 0);
2637  rc_scores_.resize(num_vars, 0.0);
2638  }
2639 
2640  // Decay averages.
2641  num_calls_since_reduced_cost_averages_reset_++;
2642  if (num_calls_since_reduced_cost_averages_reset_ == 10000) {
2643  for (int i = 0; i < num_vars; i++) {
2644  sum_cost_up_[i] /= 2;
2645  num_cost_up_[i] /= 2;
2646  sum_cost_down_[i] /= 2;
2647  num_cost_down_[i] /= 2;
2648  }
2649  num_calls_since_reduced_cost_averages_reset_ = 0;
2650  }
2651 
2652  // Accumulate reduced costs of all unassigned variables.
2653  for (int i = 0; i < num_vars; i++) {
2654  const IntegerVariable var = integer_variables_[i];
2655 
2656  // Skip ignored and fixed variables.
2657  if (integer_trail_->IsCurrentlyIgnored(var)) continue;
2658  if (integer_trail_->IsFixed(var)) continue;
2659 
2660  // Skip reduced costs that are zero or close.
2661  const double rc = lp_reduced_cost_[i];
2662  if (std::abs(rc) < kCpEpsilon) continue;
2663 
2664  if (rc < 0.0) {
2665  sum_cost_down_[i] -= rc;
2666  num_cost_down_[i]++;
2667  } else {
2668  sum_cost_up_[i] += rc;
2669  num_cost_up_[i]++;
2670  }
2671  }
2672 
2673  // Tricky, we artificially reset the rc_rev_int_repository_ to level zero
2674  // so that the rev_rc_start_ is zero.
2675  rc_rev_int_repository_.SetLevel(0);
2676  rc_rev_int_repository_.SetLevel(trail_->CurrentDecisionLevel());
2677  rev_rc_start_ = 0;
2678 
2679  // Cache the new score (higher is better) using the average reduced costs
2680  // as a signal.
2681  positions_by_decreasing_rc_score_.clear();
2682  for (int i = 0; i < num_vars; i++) {
2683  // If only one direction exist, we takes its value divided by 2, so that
2684  // such variable should have a smaller cost than the min of the two side
2685  // except if one direction have a really high reduced costs.
2686  const double a_up =
2687  num_cost_up_[i] > 0 ? sum_cost_up_[i] / num_cost_up_[i] : 0.0;
2688  const double a_down =
2689  num_cost_down_[i] > 0 ? sum_cost_down_[i] / num_cost_down_[i] : 0.0;
2690  if (num_cost_down_[i] > 0 && num_cost_up_[i] > 0) {
2691  rc_scores_[i] = std::min(a_up, a_down);
2692  } else {
2693  rc_scores_[i] = 0.5 * (a_down + a_up);
2694  }
2695 
2696  // We ignore scores of zero (i.e. no data) and will follow the default
2697  // search heuristic if all variables are like this.
2698  if (rc_scores_[i] > 0.0) {
2699  positions_by_decreasing_rc_score_.push_back({-rc_scores_[i], i});
2700  }
2701  }
2702  std::sort(positions_by_decreasing_rc_score_.begin(),
2703  positions_by_decreasing_rc_score_.end());
2704 }
2705 
2706 std::function<LiteralIndex()>
2708  return [this]() { return this->LPReducedCostAverageDecision(); };
2709 }
2710 
2711 LiteralIndex LinearProgrammingConstraint::LPReducedCostAverageDecision() {
2712  // Select noninstantiated variable with highest positive average reduced cost.
2713  int selected_index = -1;
2714  const int size = positions_by_decreasing_rc_score_.size();
2715  rc_rev_int_repository_.SaveState(&rev_rc_start_);
2716  for (int i = rev_rc_start_; i < size; ++i) {
2717  const int index = positions_by_decreasing_rc_score_[i].second;
2718  const IntegerVariable var = integer_variables_[index];
2719  if (integer_trail_->IsCurrentlyIgnored(var)) continue;
2720  if (integer_trail_->IsFixed(var)) continue;
2721  selected_index = index;
2722  rev_rc_start_ = i;
2723  break;
2724  }
2725 
2726  if (selected_index == -1) return kNoLiteralIndex;
2727  const IntegerVariable var = integer_variables_[selected_index];
2728 
2729  // If ceil(value) is current upper bound, try var == upper bound first.
2730  // Guarding with >= prevents numerical problems.
2731  // With 0/1 variables, this will tend to try setting to 1 first,
2732  // which produces more shallow trees.
2733  const IntegerValue ub = integer_trail_->UpperBound(var);
2734  const IntegerValue value_ceil(
2735  std::ceil(this->GetSolutionValue(var) - kCpEpsilon));
2736  if (value_ceil >= ub) {
2737  const Literal result = integer_encoder_->GetOrCreateAssociatedLiteral(
2739  CHECK(!trail_->Assignment().LiteralIsAssigned(result));
2740  return result.Index();
2741  }
2742 
2743  // If floor(value) is current lower bound, try var == lower bound first.
2744  // Guarding with <= prevents numerical problems.
2745  const IntegerValue lb = integer_trail_->LowerBound(var);
2746  const IntegerValue value_floor(
2747  std::floor(this->GetSolutionValue(var) + kCpEpsilon));
2748  if (value_floor <= lb) {
2749  const Literal result = integer_encoder_->GetOrCreateAssociatedLiteral(
2751  CHECK(!trail_->Assignment().LiteralIsAssigned(result))
2752  << " " << lb << " " << ub;
2753  return result.Index();
2754  }
2755 
2756  // Here lb < value_floor <= value_ceil < ub.
2757  // Try the most promising split between var <= floor or var >= ceil.
2758  const double a_up =
2759  num_cost_up_[selected_index] > 0
2760  ? sum_cost_up_[selected_index] / num_cost_up_[selected_index]
2761  : 0.0;
2762  const double a_down =
2763  num_cost_down_[selected_index] > 0
2764  ? sum_cost_down_[selected_index] / num_cost_down_[selected_index]
2765  : 0.0;
2766  if (a_down < a_up) {
2767  const Literal result = integer_encoder_->GetOrCreateAssociatedLiteral(
2768  IntegerLiteral::LowerOrEqual(var, value_floor));
2769  CHECK(!trail_->Assignment().LiteralIsAssigned(result));
2770  return result.Index();
2771  } else {
2772  const Literal result = integer_encoder_->GetOrCreateAssociatedLiteral(
2773  IntegerLiteral::GreaterOrEqual(var, value_ceil));
2774  CHECK(!trail_->Assignment().LiteralIsAssigned(result));
2775  return result.Index();
2776  }
2777 }
2778 
2779 } // namespace sat
2780 } // namespace operations_research
operations_research::sat::LinearConstraintManager::ChangeLp
bool ChangeLp(const gtl::ITIVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
Definition: linear_constraint_manager.cc:440
operations_research::sat::SearchHeuristicsVector
Definition: integer.h:247
operations_research::sat::ZeroHalfCutHelper::InterestingCandidates
std::vector< std::vector< std::pair< glop::RowIndex, IntegerValue > > > InterestingCandidates(ModelRandomGenerator *random)
Definition: zero_half_cuts.cc:227
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::LinearProgrammingConstraint::GetSolutionValue
double GetSolutionValue(IntegerVariable variable) const
Definition: linear_programming_constraint.cc:589
tail
int64 tail
Definition: routing_flow.cc:127
operations_research::glop::LinearProgram::num_constraints
RowIndex num_constraints() const
Definition: lp_data.h:208
operations_research::sat::IntegerEncoder::GetOrCreateAssociatedLiteral
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:217
operations_research::glop::ConstraintStatus::FREE
@ FREE
operations_research::glop::RevisedSimplex::GetProblemStatus
ProblemStatus GetProblemStatus() const
Definition: revised_simplex.cc:415
operations_research::sat::ImpliedBoundsProcessor::ProcessUpperBoundedConstraintWithSlackCreation
void ProcessUpperBoundedConstraintWithSlackCreation(bool substitute_only_inner_variables, IntegerVariable first_slack, const gtl::ITIVector< IntegerVariable, double > &lp_values, LinearConstraint *cut, std::vector< SlackInfo > *slack_infos)
Definition: cuts.cc:1581
operations_research::sat::GenericLiteralWatcher::Register
int Register(PropagatorInterface *propagator)
Definition: integer.cc:1897
operations_research::glop::LinearProgram::AddSlackVariablesWhereNecessary
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
Definition: lp_data.cc:666
min
int64 min
Definition: alldiff_cst.cc:138
integral_types.h
operations_research::glop::DenseRow
StrictITIVector< ColIndex, Fractional > DenseRow
Definition: lp_types.h:299
map_util.h
operations_research::sat::IntegerTrail
Definition: integer.h:523
operations_research::glop::VariableStatus::BASIC
@ BASIC
operations_research::Arc
std::pair< int64, int64 > Arc
Definition: search.cc:3355
operations_research::CapSub
int64 CapSub(int64 x, int64 y)
Definition: saturated_arithmetic.h:154
operations_research::sat::kNoIntegerVariable
const IntegerVariable kNoIntegerVariable(-1)
operations_research::sat::FloorRatio
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:90
operations_research::sat::VariableIsPositive
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:130
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::sat::LinearConstraint::vars
std::vector< IntegerVariable > vars
Definition: linear_constraint.h:42
operations_research::glop::ProblemStatus::ABNORMAL
@ ABNORMAL
bound
int64 bound
Definition: routing_search.cc:972
operations_research::sat::LinearProgrammingConstraint::ConstraintIndex
glop::RowIndex ConstraintIndex
Definition: linear_programming_constraint.h:130
operations_research::sat::CutGenerator
Definition: cuts.h:40
operations_research::glop::LpScalingHelper::Scale
void Scale(LinearProgram *lp)
Definition: lp_data_utils.cc:76
operations_research::CapProd
int64 CapProd(int64 x, int64 y)
Definition: saturated_arithmetic.h:231
operations_research::sat::LinearProgrammingConstraint::GetSolutionReducedCost
double GetSolutionReducedCost(IntegerVariable variable) const
Definition: linear_programming_constraint.cc:594
operations_research::sat::kNoLiteralIndex
const LiteralIndex kNoLiteralIndex(-1)
operations_research::sat::LinearProgrammingConstraint::LPReducedCostAverageBranching
std::function< LiteralIndex()> LPReducedCostAverageBranching()
Definition: linear_programming_constraint.cc:2707
operations_research::sat::CreateStronglyConnectedGraphCutGenerator
CutGenerator CreateStronglyConnectedGraphCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, Model *model)
Definition: linear_programming_constraint.cc:2462
operations_research::glop::ConstraintStatus::FIXED_VALUE
@ FIXED_VALUE
operations_research::sat::CoverCutHelper::mutable_cut
LinearConstraint * mutable_cut()
Definition: cuts.h:249
operations_research::sat::IntegerTrail::LevelZeroUpperBound
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1283
operations_research::sat::IntegerTrail::LevelZeroLowerBound
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1278
operations_research::glop::RevisedSimplex::LoadStateForNextSolve
void LoadStateForNextSolve(const BasisState &state)
Definition: revised_simplex.cc:124
operations_research::sat::SeparateSubtourInequalities
void SeparateSubtourInequalities(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const gtl::ITIVector< IntegerVariable, double > &lp_values, absl::Span< const int64 > demands, int64 capacity, LinearConstraintManager *manager, Model *model)
Definition: linear_programming_constraint.cc:2221
gtl::ITIVector
Definition: int_type_indexed_vector.h:76
operations_research::glop::kEpsilon
const double kEpsilon
Definition: lp_types.h:86
operations_research::glop::RevisedSimplex::GetParameters
const GlopParameters & GetParameters() const
Definition: revised_simplex.h:153
logging.h
gtl::ITIVector::push_back
void push_back(const value_type &x)
Definition: int_type_indexed_vector.h:157
operations_research::sat::SatSolver
Definition: sat_solver.h:58
operations_research::sat::IntegerEncoder::GetLiteralView
const IntegerVariable GetLiteralView(Literal lit) const
Definition: integer.h:411
operations_research::sat::ModelRandomGenerator
Definition: sat/util.h:33
operations_research::glop::LinearProgram::SetConstraintBounds
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:307
operations_research::sat::GenericLiteralWatcher::RegisterReversibleInt
void RegisterReversibleInt(int id, int *rev)
Definition: integer.cc:1941
value
int64 value
Definition: demon_profiler.cc:43
operations_research::sat::CoverCutHelper::TrySimpleKnapsack
bool TrySimpleKnapsack(const LinearConstraint base_ct, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
Definition: cuts.cc:1153
operations_research::sat::ScatteredIntegerVector::ConvertToLinearConstraint
void ConvertToLinearConstraint(const std::vector< IntegerVariable > &integer_variables, IntegerValue upper_bound, LinearConstraint *result)
Definition: linear_programming_constraint.cc:109
operations_research::sat::LinearProgrammingConstraint::~LinearProgrammingConstraint
~LinearProgrammingConstraint() override
Definition: linear_programming_constraint.cc:184
saturated_arithmetic.h
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
operations_research::MathUtil::GCD64
static int64 GCD64(int64 x, int64 y)
Definition: mathutil.h:107
operations_research::sat::NegationOf
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:42
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::sat::LinearProgrammingConstraint::RegisterWith
void RegisterWith(Model *model)
Definition: linear_programming_constraint.cc:480
operations_research::sat::IntegerLiteral::GreaterOrEqual
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1208
operations_research::sat::LinearProgrammingConstraint::IncrementalPropagate
bool IncrementalPropagate(const std::vector< int > &watch_indices) override
Definition: linear_programming_constraint.cc:552
operations_research::glop::LinearProgram::NotifyThatColumnsAreClean
void NotifyThatColumnsAreClean()
Definition: lp_data.h:539
operations_research::sat::PositiveVariable
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:134
operations_research::glop::LpScalingHelper::UnscaleDualValue
Fractional UnscaleDualValue(RowIndex row, Fractional value) const
Definition: lp_data_utils.cc:108
int64
int64_t int64
Definition: integral_types.h:34
operations_research::sat::IncrementalAverage::CurrentAverage
double CurrentAverage() const
Definition: sat/util.h:113
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
index
int index
Definition: pack.cc:508
operations_research::glop::RevisedSimplex::GetObjectiveValue
Fractional GetObjectiveValue() const
Definition: revised_simplex.cc:419
operations_research::glop::Fractional
double Fractional
Definition: lp_types.h:77
operations_research::glop::ConstraintStatus::AT_UPPER_BOUND
@ AT_UPPER_BOUND
operations_research::sat::GenericLiteralWatcher
Definition: integer.h:1067
operations_research::glop::LinearProgram::CreateNewVariable
ColIndex CreateNewVariable()
Definition: lp_data.cc:160
operations_research::sat::ScatteredIntegerVector::GetTerms
std::vector< std::pair< glop::ColIndex, IntegerValue > > GetTerms()
Definition: linear_programming_constraint.cc:136
operations_research::glop::LinearProgram::CreateNewConstraint
RowIndex CreateNewConstraint()
Definition: lp_data.cc:189
operations_research::sat::ScatteredIntegerVector::ClearAndResize
void ClearAndResize(int size)
Definition: linear_programming_constraint.cc:53
gtl::FindOrDie
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:176
operations_research::glop::RevisedSimplex::Solve
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
Definition: revised_simplex.cc:134
strongly_connected_components.h
operations_research::glop::LinearProgram::Clear
void Clear()
Definition: lp_data.cc:132
operations_research::sat::LinearConstraintManager
Definition: linear_constraint_manager.h:40
demand
int64 demand
Definition: resource.cc:123
operations_research::glop::RowToColIndex
ColIndex RowToColIndex(RowIndex row)
Definition: lp_types.h:48
DEBUG_MODE
const bool DEBUG_MODE
Definition: macros.h:24
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::sat::IntegerTrail::UpperBoundAsLiteral
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1263
operations_research::RevRepository::SetLevel
void SetLevel(int level) final
Definition: rev.h:134
operations_research::sat::LinearConstraint
Definition: linear_constraint.h:39
operations_research::glop::RevisedSimplex::GetDualRayRowCombination
const DenseRow & GetDualRayRowCombination() const
Definition: revised_simplex.cc:479
operations_research::sat::IntTypeAbs
IntType IntTypeAbs(IntType t)
Definition: integer.h:77
operations_research::glop::BasisState
Definition: revised_simplex.h:132
operations_research::glop::LinearProgram::GetSparseColumn
const SparseColumn & GetSparseColumn(ColIndex col) const
Definition: lp_data.cc:407
operations_research::sat::GenericLiteralWatcher::SetPropagatorPriority
void SetPropagatorPriority(int id, int priority)
Definition: integer.cc:1920
operations_research::glop::LinearProgram::SetVariableBounds
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:247
operations_research::sat::IntegerTrail::UpperBound
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1232
operations_research::sat::IntegerTrail::Enqueue
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:988
operations_research::CapAdd
int64 CapAdd(int64 x, int64 y)
Definition: saturated_arithmetic.h:124
operations_research::glop::RevisedSimplex::GetConstraintStatus
ConstraintStatus GetConstraintStatus(RowIndex row) const
Definition: revised_simplex.cc:457
operations_research::glop::RevisedSimplex::GetState
const BasisState & GetState() const
Definition: revised_simplex.cc:449
operations_research::sat::LinearConstraintManager::LpConstraints
const std::vector< ConstraintIndex > & LpConstraints() const
Definition: linear_constraint_manager.h:117
operations_research::sat::LinearProgrammingConstraint::SetLevel
void SetLevel(int level) override
Definition: linear_programming_constraint.cc:522
operations_research::sat::kMaxIntegerValue
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
operations_research::sat::LinearConstraintManager::AllConstraints
const gtl::ITIVector< ConstraintIndex, ConstraintInfo > & AllConstraints() const
Definition: linear_constraint_manager.h:110
operations_research::sat::Trail::Assignment
const VariablesAssignment & Assignment() const
Definition: sat_base.h:380
mathutil.h
operations_research::glop::LpScalingHelper::UnscaleReducedCost
Fractional UnscaleReducedCost(ColIndex col, Fractional value) const
Definition: lp_data_utils.cc:102
operations_research::sat::LinearConstraintManager::Add
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
Definition: linear_constraint_manager.cc:124
operations_research::sat::LinearConstraint::lb
IntegerValue lb
Definition: linear_constraint.h:40
operations_research::sat::ImpliedBoundsProcessor::IbCutPool
TopNCuts & IbCutPool()
Definition: cuts.h:123
operations_research::sat::IncrementalAverage::AddData
void AddData(double new_record)
Definition: sat/util.cc:68
int_type_indexed_vector.h
operations_research::glop::ConstraintStatus::BASIC
@ BASIC
operations_research::sat::Literal::Index
LiteralIndex Index() const
Definition: sat_base.h:84
operations_research::sat::IntegerTrail::RegisterReversibleClass
void RegisterReversibleClass(ReversibleInterface *rev)
Definition: integer.h:806
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
operations_research::glop::RevisedSimplex::GetNumberOfIterations
int64 GetNumberOfIterations() const
Definition: revised_simplex.cc:423
ct
const Constraint * ct
Definition: demon_profiler.cc:42
operations_research::sat::LinearConstraintManager::SetObjectiveCoefficient
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
Definition: linear_constraint_manager.cc:301
operations_research::glop::LinearProgram::SetCoefficient
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:315
operations_research::sat::IntegerTrail::LowerBoundAsLiteral
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1258
operations_research::sat::AddProductTo
bool AddProductTo(IntegerValue a, IntegerValue b, IntegerValue *result)
Definition: integer.h:110
operations_research::sat::IntegerLiteral
Definition: integer.h:153
operations_research::sat::LinearProgrammingConstraintCollection
Definition: linear_programming_constraint.h:522
operations_research::glop::RevisedSimplex::GetUnitRowLeftInverse
const ScatteredRow & GetUnitRowLeftInverse(RowIndex row)
Definition: revised_simplex.h:223
operations_research::sat::ScatteredIntegerVector::AddLinearExpressionMultiple
bool AddLinearExpressionMultiple(IntegerValue multiplier, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms)
Definition: linear_programming_constraint.cc:81
operations_research::sat::LinearProgrammingConstraintLpSolution
Definition: linear_programming_constraint.h:51
operations_research::sat::LinearConstraintManager::AddCut
bool AddCut(LinearConstraint ct, std::string type_name, const gtl::ITIVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
Definition: linear_constraint_manager.cc:206
operations_research::sat::GenericLiteralWatcher::WatchIntegerVariable
void WatchIntegerVariable(IntegerVariable i, int id, int watch_index=-1)
Definition: integer.h:1316
operations_research::sat::ImpliedBounds
Definition: implied_bounds.h:77
operations_research::glop::LinearProgram::objective_scaling_factor
Fractional objective_scaling_factor() const
Definition: lp_data.h:261
operations_research::glop::RevisedSimplex::GetDualValue
Fractional GetDualValue(RowIndex row) const
Definition: revised_simplex.cc:441
operations_research::glop::RevisedSimplex::GetVariableStatus
VariableStatus GetVariableStatus(ColIndex col) const
Definition: revised_simplex.cc:445
operations_research::sat::ZeroHalfCutHelper::ProcessVariables
void ProcessVariables(const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds)
Definition: zero_half_cuts.cc:28
operations_research::sat::IntegerLiteral::LowerOrEqual
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1214
implied_bounds.h
operations_research::sat::ImpliedBoundsProcessor::SeparateSomeImpliedBoundCuts
void SeparateSomeImpliedBoundCuts(const gtl::ITIVector< IntegerVariable, double > &lp_values)
Definition: cuts.cc:1572
operations_research::glop::RevisedSimplex::SetParameters
void SetParameters(const GlopParameters &parameters)
Definition: revised_simplex.cc:2917
operations_research::sat::LinearProgrammingConstraint::HeuristicLPMostInfeasibleBinary
std::function< LiteralIndex()> HeuristicLPMostInfeasibleBinary(Model *model)
Definition: linear_programming_constraint.cc:2498
operations_research::sat::LinearProgrammingConstraint::HeuristicLPPseudoCostBinary
std::function< LiteralIndex()> HeuristicLPPseudoCostBinary(Model *model)
Definition: linear_programming_constraint.cc:2549
zero_half_cuts.h
operations_research::glop::RevisedSimplex::NotifyThatMatrixIsUnchangedForNextSolve
void NotifyThatMatrixIsUnchangedForNextSolve()
Definition: revised_simplex.cc:130
operations_research::sat::IntegerTrail::Index
int Index() const
Definition: integer.h:810
gtl::ITIVector::size
size_type size() const
Definition: int_type_indexed_vector.h:146
model
GRBmodel * model
Definition: gurobi_interface.cc:195
operations_research::sat::Trail::CurrentDecisionLevel
int CurrentDecisionLevel() const
Definition: sat_base.h:355
operations_research::sat::Literal
Definition: sat_base.h:64
operations_research::sat::IntegerTrail::RemoveLevelZeroBounds
void RemoveLevelZeroBounds(std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:918
operations_research::sat::LinearProgrammingConstraint::AddCutGenerator
void AddCutGenerator(CutGenerator generator)
Definition: linear_programming_constraint.cc:545
operations_research::glop::LpScalingHelper::UnscaleVariableValue
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Definition: lp_data_utils.cc:96
operations_research::glop::LpScalingHelper::VariableScalingFactor
Fractional VariableScalingFactor(ColIndex col) const
Definition: lp_data_utils.cc:90
operations_research::glop::LinearProgram::GetDimensionString
std::string GetDimensionString() const
Definition: lp_data.cc:423
operations_research::sat::ToDouble
double ToDouble(IntegerValue value)
Definition: integer.h:69
operations_research::sat::ImpliedBoundsProcessor::AddLpVariable
void AddLpVariable(IntegerVariable var)
Definition: cuts.h:106
gtl::ITIVector::clear
void clear()
Definition: int_type_indexed_vector.h:169
operations_research::glop::ProblemStatus::OPTIMAL
@ OPTIMAL
col
ColIndex col
Definition: markowitz.cc:176
operations_research::sat::IntegerTrail::ReportConflict
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.h:783
operations_research::glop::SparseVector::num_entries
EntryIndex num_entries() const
Definition: sparse_vector.h:270
operations_research::sat::kMinIntegerValue
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
stl_util.h
row
RowIndex row
Definition: markowitz.cc:175
operations_research::sat::ComputeActivity
double ComputeActivity(const LinearConstraint &constraint, const gtl::ITIVector< IntegerVariable, double > &values)
Definition: linear_constraint.cc:121
operations_research::glop::DenseColumn
StrictITIVector< RowIndex, Fractional > DenseColumn
Definition: lp_types.h:328
operations_research::glop::LinearProgram::SetObjectiveCoefficient
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:324
operations_research::sat::IntegerTrail::RelaxLinearReason
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:784
operations_research::glop::ConstraintStatus::AT_LOWER_BOUND
@ AT_LOWER_BOUND
operations_research::glop::RevisedSimplex::ClearStateForNextSolve
void ClearStateForNextSolve()
Definition: revised_simplex.cc:119
operations_research::sat::LinearProgrammingConstraint::SetObjectiveCoefficient
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
Definition: linear_programming_constraint.cc:227
operations_research::sat::CutGenerator::generate_cuts
std::function< void(const gtl::ITIVector< IntegerVariable, double > &lp_values, LinearConstraintManager *manager)> generate_cuts
Definition: cuts.h:44
operations_research::sat::ImpliedBoundsProcessor::ClearCache
void ClearCache() const
Definition: cuts.h:110
operations_research::sat::LinearProgrammingConstraint::LinearProgrammingConstraint
LinearProgrammingConstraint(Model *model)
Definition: linear_programming_constraint.cc:156
operations_research::sat::IntegerRoundingCutHelper::ComputeCut
void ComputeCut(RoundingOptions options, const std::vector< double > &lp_values, const std::vector< IntegerValue > &lower_bounds, const std::vector< IntegerValue > &upper_bounds, ImpliedBoundsProcessor *ib_processor, LinearConstraint *cut)
Definition: cuts.cc:705
operations_research::sat::LinearProgrammingConstraint::AddLinearConstraint
void AddLinearConstraint(const LinearConstraint &ct)
Definition: linear_programming_constraint.cc:189
operations_research::sat::ImpliedBoundsProcessor::DebugSlack
bool DebugSlack(IntegerVariable first_slack, const LinearConstraint &initial_cut, const LinearConstraint &cut, const std::vector< SlackInfo > &info)
Definition: cuts.cc:1722
operations_research::sat::CutGenerator::vars
std::vector< IntegerVariable > vars
Definition: cuts.h:41
operations_research::sat::IntegerTrail::IsFixed
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1236
b
int64 b
Definition: constraint_solver/table.cc:43
operations_research::sat::GenericLiteralWatcher::AlwaysCallAtLevelZero
void AlwaysCallAtLevelZero(int id)
Definition: integer.cc:1932
operations_research::glop::RevisedSimplex::GetProblemNumRows
RowIndex GetProblemNumRows() const
Definition: revised_simplex.cc:425
operations_research::sat::LinearProgrammingDispatcher
Definition: linear_programming_constraint.h:515
gtl::ITIVector::back
reference back()
Definition: int_type_indexed_vector.h:173
gtl::ITIVector::resize
void resize(size_type new_size)
Definition: int_type_indexed_vector.h:149
capacity
int64 capacity
Definition: routing_flow.cc:129
operations_research::sat::ScatteredIntegerVector::Add
bool Add(glop::ColIndex col, IntegerValue value)
Definition: linear_programming_constraint.cc:70
next
Block * next
Definition: constraint_solver.cc:667
operations_research::sat::IntegerTrail::LowerBound
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1228
operations_research::sat::CoverCutHelper::Info
const std::string Info()
Definition: cuts.h:253
operations_research::sat::CreateCVRPCutGenerator
CutGenerator CreateCVRPCutGenerator(int num_nodes, const std::vector< int > &tails, const std::vector< int > &heads, const std::vector< Literal > &literals, const std::vector< int64 > &demands, int64 capacity, Model *model)
Definition: linear_programming_constraint.cc:2478
operations_research::sat::ComputeInfinityNorm
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
Definition: linear_constraint.cc:140
operations_research::glop::RevisedSimplex::GetVariableValue
Fractional GetVariableValue(ColIndex col) const
Definition: revised_simplex.cc:429
gtl::ITIVector::assign
void assign(size_type n, const value_type &val)
Definition: int_type_indexed_vector.h:130
operations_research::sat::IntegerTrail::IsCurrentlyIgnored
bool IsCurrentlyIgnored(IntegerVariable i) const
Definition: integer.h:608
operations_research::glop::RevisedSimplex::GetDualRay
const DenseColumn & GetDualRay() const
Definition: revised_simplex.cc:474
operations_research::sat::LinearConstraint::coeffs
std::vector< IntegerValue > coeffs
Definition: linear_constraint.h:43
operations_research::sat::IntegerRoundingCutHelper::NumLiftedBooleans
int NumLiftedBooleans() const
Definition: cuts.h:219
operations_research::sat::ZeroHalfCutHelper::AddOneConstraint
void AddOneConstraint(glop::RowIndex, const std::vector< std::pair< glop::ColIndex, IntegerValue >> &terms, IntegerValue lb, IntegerValue ub)
Definition: zero_half_cuts.cc:58
operations_research::glop::RevisedSimplex::GetBasis
ColIndex GetBasis(RowIndex row) const
Definition: revised_simplex.cc:484
lp_types.h
head
int64 head
Definition: routing_flow.cc:128
operations_research::sat::DivideByGCD
void DivideByGCD(LinearConstraint *constraint)
Definition: linear_constraint.cc:187
operations_research::sat::LinearProgrammingConstraint::Propagate
bool Propagate() override
Definition: linear_programming_constraint.cc:1303
operations_research::glop::RevisedSimplex::GetReducedCosts
const DenseRow & GetReducedCosts() const
Definition: revised_simplex.cc:437
operations_research::sat::TopNCuts::TransferToManager
void TransferToManager(const gtl::ITIVector< IntegerVariable, double > &lp_solution, LinearConstraintManager *manager)
Definition: linear_constraint_manager.cc:721
preprocessor.h
operations_research::sat::LinearConstraint::ub
IntegerValue ub
Definition: linear_constraint.h:41
operations_research::glop::RevisedSimplex::GetReducedCost
Fractional GetReducedCost(ColIndex col) const
Definition: revised_simplex.cc:433
operations_research::RevRepository::SaveState
void SaveState(T *object)
Definition: rev.h:61
operations_research::sat::LinearConstraintManager::AddAllConstraintsToLp
void AddAllConstraintsToLp()
Definition: linear_constraint_manager.cc:682
operations_research::sat::LinearConstraintManager::DebugCheckConstraint
bool DebugCheckConstraint(const LinearConstraint &cut)
Definition: linear_constraint_manager.cc:690
commandlineflags.h
parameters
SatParameters parameters
Definition: cp_model_fz_solver.cc:107
operations_research::sat::IntegerEncoder
Definition: integer.h:267
operations_research::sat::VariablesAssignment::LiteralIsAssigned
bool LiteralIsAssigned(Literal literal) const
Definition: sat_base.h:153
integer.h
name
const std::string name
Definition: default_search.cc:807
operations_research::sat::GenericLiteralWatcher::WatchUpperBound
void WatchUpperBound(IntegerVariable var, int id, int watch_index=-1)
Definition: integer.h:1310
status.h
operations_research::sat::Trail
Definition: sat_base.h:233
parameters.pb.h
linear_programming_constraint.h
operations_research::glop::RevisedSimplex::GetProblemNumCols
ColIndex GetProblemNumCols() const
Definition: revised_simplex.cc:427
kint64max
static const int64 kint64max
Definition: integral_types.h:62