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