OR-Tools  8.0
linear_constraint_manager.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <limits>
19 #include <utility>
20 
21 #include "absl/container/flat_hash_set.h"
22 #include "ortools/sat/integer.h"
24 
25 namespace operations_research {
26 namespace sat {
27 
28 namespace {
29 
30 const LinearConstraintManager::ConstraintIndex kInvalidConstraintIndex(-1);
31 
32 size_t ComputeHashOfTerms(const LinearConstraint& ct) {
33  DCHECK(std::is_sorted(ct.vars.begin(), ct.vars.end()));
34  size_t hash = 0;
35  const int num_terms = ct.vars.size();
36  for (int i = 0; i < num_terms; ++i) {
37  hash = util_hash::Hash(ct.vars[i].value(), hash);
38  hash = util_hash::Hash(ct.coeffs[i].value(), hash);
39  }
40  return hash;
41 }
42 
43 } // namespace
44 
46  if (num_merged_constraints_ > 0) {
47  VLOG(2) << "num_merged_constraints: " << num_merged_constraints_;
48  }
49  if (num_shortened_constraints_ > 0) {
50  VLOG(2) << "num_shortened_constraints: " << num_shortened_constraints_;
51  }
52  if (num_splitted_constraints_ > 0) {
53  VLOG(2) << "num_splitted_constraints: " << num_splitted_constraints_;
54  }
55  if (num_coeff_strenghtening_ > 0) {
56  VLOG(2) << "num_coeff_strenghtening: " << num_coeff_strenghtening_;
57  }
58  if (sat_parameters_.log_search_progress() && num_cuts_ > 0) {
59  LOG(INFO) << "Total cuts added: " << num_cuts_;
60  for (const auto& entry : type_to_num_cuts_) {
61  LOG(INFO) << "Added " << entry.second << " cuts of type '" << entry.first
62  << "'.";
63  }
64  }
65 }
66 
67 void LinearConstraintManager::RescaleActiveCounts(const double scaling_factor) {
68  for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
69  constraint_infos_[i].active_count *= scaling_factor;
70  }
71  constraint_active_count_increase_ *= scaling_factor;
72  VLOG(2) << "Rescaled active counts by " << scaling_factor;
73 }
74 
75 bool LinearConstraintManager::MaybeRemoveSomeInactiveConstraints(
76  glop::BasisState* solution_state) {
77  if (solution_state->IsEmpty()) return false; // Mainly to simplify tests.
78  const glop::RowIndex num_rows(lp_constraints_.size());
79  const glop::ColIndex num_cols =
80  solution_state->statuses.size() - RowToColIndex(num_rows);
81 
82  int new_size = 0;
83  for (int i = 0; i < num_rows; ++i) {
84  const ConstraintIndex constraint_index = lp_constraints_[i];
85 
86  // Constraints that are not tight in the current solution have a basic
87  // status. We remove the ones that have been inactive in the last recent
88  // solves.
89  //
90  // TODO(user): More advanced heuristics might perform better, I didn't do
91  // a lot of tuning experiments yet.
92  const glop::VariableStatus row_status =
93  solution_state->statuses[num_cols + glop::ColIndex(i)];
94  if (row_status == glop::VariableStatus::BASIC) {
95  constraint_infos_[constraint_index].inactive_count++;
96  if (constraint_infos_[constraint_index].inactive_count >
97  sat_parameters_.max_consecutive_inactive_count()) {
98  constraint_infos_[constraint_index].is_in_lp = false;
99  continue; // Remove it.
100  }
101  } else {
102  // Only count consecutive inactivities.
103  constraint_infos_[constraint_index].inactive_count = 0;
104  }
105 
106  lp_constraints_[new_size] = constraint_index;
107  solution_state->statuses[num_cols + glop::ColIndex(new_size)] = row_status;
108  new_size++;
109  }
110  const int num_removed_constraints = lp_constraints_.size() - new_size;
111  lp_constraints_.resize(new_size);
112  solution_state->statuses.resize(num_cols + glop::ColIndex(new_size));
113  if (num_removed_constraints > 0) {
114  VLOG(2) << "Removed " << num_removed_constraints << " constraints";
115  }
116  return num_removed_constraints > 0;
117 }
118 
119 // Because sometimes we split a == constraint in two (>= and <=), it makes sense
120 // to detect duplicate constraints and merge bounds. This is also relevant if
121 // we regenerate identical cuts for some reason.
122 LinearConstraintManager::ConstraintIndex LinearConstraintManager::Add(
123  LinearConstraint ct, bool* added) {
124  CHECK(!ct.vars.empty());
125  DCHECK(NoDuplicateVariable(ct));
126  SimplifyConstraint(&ct);
127  DivideByGCD(&ct);
129  DCHECK(DebugCheckConstraint(ct));
130 
131  // If an identical constraint exists, only updates its bound.
132  const size_t key = ComputeHashOfTerms(ct);
133  if (gtl::ContainsKey(equiv_constraints_, key)) {
134  const ConstraintIndex ct_index = equiv_constraints_[key];
135  if (constraint_infos_[ct_index].constraint.vars == ct.vars &&
136  constraint_infos_[ct_index].constraint.coeffs == ct.coeffs) {
137  if (added != nullptr) *added = false;
138  if (ct.lb > constraint_infos_[ct_index].constraint.lb) {
139  if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ = true;
140  constraint_infos_[ct_index].constraint.lb = ct.lb;
141  if (added != nullptr) *added = true;
142  }
143  if (ct.ub < constraint_infos_[ct_index].constraint.ub) {
144  if (constraint_infos_[ct_index].is_in_lp) current_lp_is_changed_ = true;
145  constraint_infos_[ct_index].constraint.ub = ct.ub;
146  if (added != nullptr) *added = true;
147  }
148  ++num_merged_constraints_;
149  return ct_index;
150  }
151  }
152 
153  if (added != nullptr) *added = true;
154  const ConstraintIndex ct_index(constraint_infos_.size());
155  ConstraintInfo ct_info;
156  ct_info.constraint = std::move(ct);
157  ct_info.l2_norm = ComputeL2Norm(ct_info.constraint);
158  ct_info.hash = key;
159  equiv_constraints_[key] = ct_index;
160  ct_info.active_count = constraint_active_count_increase_;
161  constraint_infos_.push_back(std::move(ct_info));
162  return ct_index;
163 }
164 
165 void LinearConstraintManager::ComputeObjectiveParallelism(
166  const ConstraintIndex ct_index) {
167  CHECK(objective_is_defined_);
168  // lazy computation of objective norm.
169  if (!objective_norm_computed_) {
170  double sum = 0.0;
171  for (const double coeff : dense_objective_coeffs_) {
172  sum += coeff * coeff;
173  }
174  objective_l2_norm_ = std::sqrt(sum);
175  objective_norm_computed_ = true;
176  }
177  CHECK_GT(objective_l2_norm_, 0.0);
178 
179  constraint_infos_[ct_index].objective_parallelism_computed = true;
180  if (constraint_infos_[ct_index].l2_norm == 0.0) {
181  constraint_infos_[ct_index].objective_parallelism = 0.0;
182  return;
183  }
184 
185  const LinearConstraint& lc = constraint_infos_[ct_index].constraint;
186  double unscaled_objective_parallelism = 0.0;
187  for (int i = 0; i < lc.vars.size(); ++i) {
188  const IntegerVariable var = lc.vars[i];
189  DCHECK(VariableIsPositive(var));
190  if (var < dense_objective_coeffs_.size()) {
191  unscaled_objective_parallelism +=
192  ToDouble(lc.coeffs[i]) * dense_objective_coeffs_[var];
193  }
194  }
195  const double objective_parallelism =
196  unscaled_objective_parallelism /
197  (constraint_infos_[ct_index].l2_norm * objective_l2_norm_);
198  constraint_infos_[ct_index].objective_parallelism =
199  std::abs(objective_parallelism);
200 }
201 
202 // Same as Add(), but logs some information about the newly added constraint.
203 // Cuts are also handled slightly differently than normal constraints.
205  LinearConstraint ct, std::string type_name,
206  const gtl::ITIVector<IntegerVariable, double>& lp_solution,
207  std::string extra_info) {
208  if (ct.vars.empty()) return false;
209 
210  const double activity = ComputeActivity(ct, lp_solution);
211  const double violation =
212  std::max(activity - ToDouble(ct.ub), ToDouble(ct.lb) - activity);
213  const double l2_norm = ComputeL2Norm(ct);
214 
215  // Only add cut with sufficient efficacy.
216  if (violation / l2_norm < 1e-5) return false;
217 
218  bool added = false;
219  const ConstraintIndex ct_index = Add(std::move(ct), &added);
220 
221  // We only mark the constraint as a cut if it is not an update of an already
222  // existing one.
223  if (!added) return false;
224 
225  // TODO(user): Use better heuristic here for detecting good cuts and mark
226  // them undeletable.
227  constraint_infos_[ct_index].is_deletable = true;
228 
229  VLOG(1) << "Cut '" << type_name << "'"
230  << " size=" << constraint_infos_[ct_index].constraint.vars.size()
231  << " max_magnitude="
232  << ComputeInfinityNorm(constraint_infos_[ct_index].constraint)
233  << " norm=" << l2_norm << " violation=" << violation
234  << " eff=" << violation / l2_norm << " " << extra_info;
235 
236  num_cuts_++;
237  num_deletable_constraints_++;
238  type_to_num_cuts_[type_name]++;
239  return true;
240 }
241 
242 void LinearConstraintManager::PermanentlyRemoveSomeConstraints() {
243  std::vector<double> deletable_constraint_counts;
244  for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
245  if (constraint_infos_[i].is_deletable && !constraint_infos_[i].is_in_lp) {
246  deletable_constraint_counts.push_back(constraint_infos_[i].active_count);
247  }
248  }
249  if (deletable_constraint_counts.empty()) return;
250  std::sort(deletable_constraint_counts.begin(),
251  deletable_constraint_counts.end());
252 
253  // We will delete the oldest (in the order they where added) cleanup target
254  // constraints with a count lower or equal to this.
255  double active_count_threshold = std::numeric_limits<double>::infinity();
256  if (sat_parameters_.cut_cleanup_target() <
257  deletable_constraint_counts.size()) {
258  active_count_threshold =
259  deletable_constraint_counts[sat_parameters_.cut_cleanup_target()];
260  }
261 
262  ConstraintIndex new_size(0);
263  equiv_constraints_.clear();
265  constraint_infos_.size());
266  int num_deleted_constraints = 0;
267  for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
268  if (constraint_infos_[i].is_deletable && !constraint_infos_[i].is_in_lp &&
269  constraint_infos_[i].active_count <= active_count_threshold &&
270  num_deleted_constraints < sat_parameters_.cut_cleanup_target()) {
271  ++num_deleted_constraints;
272  continue;
273  }
274 
275  if (i != new_size) {
276  constraint_infos_[new_size] = std::move(constraint_infos_[i]);
277  }
278  index_mapping[i] = new_size;
279 
280  // Make sure we recompute the hash_map of identical constraints.
281  equiv_constraints_[constraint_infos_[new_size].hash] = new_size;
282  new_size++;
283  }
284  constraint_infos_.resize(new_size.value());
285 
286  // Also update lp_constraints_
287  for (int i = 0; i < lp_constraints_.size(); ++i) {
288  lp_constraints_[i] = index_mapping[lp_constraints_[i]];
289  }
290 
291  if (num_deleted_constraints > 0) {
292  VLOG(1) << "Constraint manager cleanup: #deleted:"
293  << num_deleted_constraints;
294  }
295  num_deletable_constraints_ -= num_deleted_constraints;
296 }
297 
299  IntegerValue coeff) {
300  if (coeff == IntegerValue(0)) return;
301  objective_is_defined_ = true;
302  if (!VariableIsPositive(var)) {
303  var = NegationOf(var);
304  coeff = -coeff;
305  }
306  if (var.value() >= dense_objective_coeffs_.size()) {
307  dense_objective_coeffs_.resize(var.value() + 1, 0.0);
308  }
309  dense_objective_coeffs_[var] = ToDouble(coeff);
310 }
311 
312 bool LinearConstraintManager::SimplifyConstraint(LinearConstraint* ct) {
313  bool term_changed = false;
314 
315  IntegerValue min_sum(0);
316  IntegerValue max_sum(0);
317  IntegerValue max_magnitude(0);
318  int new_size = 0;
319  const int num_terms = ct->vars.size();
320  for (int i = 0; i < num_terms; ++i) {
321  const IntegerVariable var = ct->vars[i];
322  const IntegerValue coeff = ct->coeffs[i];
323  const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
324  const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
325 
326  // For now we do not change ct, but just compute its new_size if we where
327  // to remove a fixed term.
328  if (lb == ub) continue;
329  ++new_size;
330 
331  max_magnitude = std::max(max_magnitude, IntTypeAbs(coeff));
332  if (coeff > 0.0) {
333  min_sum += coeff * lb;
334  max_sum += coeff * ub;
335  } else {
336  min_sum += coeff * ub;
337  max_sum += coeff * lb;
338  }
339  }
340 
341  // Shorten the constraint if needed.
342  if (new_size < num_terms) {
343  term_changed = true;
344  ++num_shortened_constraints_;
345  new_size = 0;
346  for (int i = 0; i < num_terms; ++i) {
347  const IntegerVariable var = ct->vars[i];
348  const IntegerValue coeff = ct->coeffs[i];
349  const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
350  const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
351  if (lb == ub) {
352  const IntegerValue rhs_adjust = lb * coeff;
353  if (ct->lb > kMinIntegerValue) ct->lb -= rhs_adjust;
354  if (ct->ub < kMaxIntegerValue) ct->ub -= rhs_adjust;
355  continue;
356  }
357  ct->vars[new_size] = var;
358  ct->coeffs[new_size] = coeff;
359  ++new_size;
360  }
361  ct->vars.resize(new_size);
362  ct->coeffs.resize(new_size);
363  }
364 
365  // Relax the bound if needed, note that this doesn't require a change to
366  // the equiv map.
367  if (min_sum >= ct->lb) ct->lb = kMinIntegerValue;
368  if (max_sum <= ct->ub) ct->ub = kMaxIntegerValue;
369 
370  // Clear constraints that are always true.
371  // We rely on the deletion code to remove them eventually.
372  if (ct->lb == kMinIntegerValue && ct->ub == kMaxIntegerValue) {
373  ct->vars.clear();
374  ct->coeffs.clear();
375  return true;
376  }
377 
378  // TODO(user): Split constraint in two if it is boxed and there is possible
379  // reduction?
380  //
381  // TODO(user): Make sure there cannot be any overflow. They shouldn't, but
382  // I am not sure all the generated cuts are safe regarding min/max sum
383  // computation. We should check this.
384  if (ct->ub != kMaxIntegerValue && max_magnitude > max_sum - ct->ub) {
385  if (ct->lb != kMinIntegerValue) {
386  ++num_splitted_constraints_;
387  } else {
388  term_changed = true;
389  ++num_coeff_strenghtening_;
390  const int num_terms = ct->vars.size();
391  const IntegerValue target = max_sum - ct->ub;
392  for (int i = 0; i < num_terms; ++i) {
393  const IntegerValue coeff = ct->coeffs[i];
394  if (coeff > target) {
395  const IntegerVariable var = ct->vars[i];
396  const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
397  ct->coeffs[i] = target;
398  ct->ub -= (coeff - target) * ub;
399  } else if (coeff < -target) {
400  const IntegerVariable var = ct->vars[i];
401  const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
402  ct->coeffs[i] = -target;
403  ct->ub += (-target - coeff) * lb;
404  }
405  }
406  }
407  }
408 
409  if (ct->lb != kMinIntegerValue && max_magnitude > ct->lb - min_sum) {
410  if (ct->ub != kMaxIntegerValue) {
411  ++num_splitted_constraints_;
412  } else {
413  term_changed = true;
414  ++num_coeff_strenghtening_;
415  const int num_terms = ct->vars.size();
416  const IntegerValue target = ct->lb - min_sum;
417  for (int i = 0; i < num_terms; ++i) {
418  const IntegerValue coeff = ct->coeffs[i];
419  if (coeff > target) {
420  const IntegerVariable var = ct->vars[i];
421  const IntegerValue lb = integer_trail_.LevelZeroLowerBound(var);
422  ct->coeffs[i] = target;
423  ct->lb -= (coeff - target) * lb;
424  } else if (coeff < -target) {
425  const IntegerVariable var = ct->vars[i];
426  const IntegerValue ub = integer_trail_.LevelZeroUpperBound(var);
427  ct->coeffs[i] = -target;
428  ct->lb += (-target - coeff) * ub;
429  }
430  }
431  }
432  }
433 
434  return term_changed;
435 }
436 
438  const gtl::ITIVector<IntegerVariable, double>& lp_solution,
439  glop::BasisState* solution_state) {
440  VLOG(3) << "Enter ChangeLP, scan " << constraint_infos_.size()
441  << " constraints";
442  std::vector<ConstraintIndex> new_constraints;
443  std::vector<double> new_constraints_efficacies;
444  std::vector<double> new_constraints_orthogonalities;
445 
446  const bool simplify_constraints =
447  integer_trail_.num_level_zero_enqueues() > last_simplification_timestamp_;
448  last_simplification_timestamp_ = integer_trail_.num_level_zero_enqueues();
449 
450  // We keep any constraints that is already present, and otherwise, we add the
451  // ones that are currently not satisfied by at least "tolerance".
452  bool rescale_active_count = false;
453  const double tolerance = 1e-6;
454  for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
455  // Inprocessing of the constraint.
456  if (simplify_constraints &&
457  SimplifyConstraint(&constraint_infos_[i].constraint)) {
458  // Note that the canonicalization shouldn't be needed since the order
459  // of the variable is not changed by the simplification, and we only
460  // reduce the coefficients at both end of the spectrum.
461  DivideByGCD(&constraint_infos_[i].constraint);
462  DCHECK(DebugCheckConstraint(constraint_infos_[i].constraint));
463 
464  constraint_infos_[i].objective_parallelism_computed = false;
465  constraint_infos_[i].l2_norm =
466  ComputeL2Norm(constraint_infos_[i].constraint);
467 
468  if (constraint_infos_[i].is_in_lp) current_lp_is_changed_ = true;
469  equiv_constraints_.erase(constraint_infos_[i].hash);
470  constraint_infos_[i].hash =
471  ComputeHashOfTerms(constraint_infos_[i].constraint);
472 
473  // TODO(user): Because we simplified this constraint, it is possible that
474  // it is now a duplicate of another one. Merge them.
475  equiv_constraints_[constraint_infos_[i].hash] = i;
476  }
477 
478  if (constraint_infos_[i].is_in_lp) continue;
479 
480  const double activity =
481  ComputeActivity(constraint_infos_[i].constraint, lp_solution);
482  const double lb_violation =
483  ToDouble(constraint_infos_[i].constraint.lb) - activity;
484  const double ub_violation =
485  activity - ToDouble(constraint_infos_[i].constraint.ub);
486  const double violation = std::max(lb_violation, ub_violation);
487  if (violation >= tolerance) {
488  constraint_infos_[i].inactive_count = 0;
489  new_constraints.push_back(i);
490  new_constraints_efficacies.push_back(violation /
491  constraint_infos_[i].l2_norm);
492  new_constraints_orthogonalities.push_back(1.0);
493 
494  if (objective_is_defined_ &&
495  !constraint_infos_[i].objective_parallelism_computed) {
496  ComputeObjectiveParallelism(i);
497  } else if (!objective_is_defined_) {
498  constraint_infos_[i].objective_parallelism = 0.0;
499  }
500 
501  constraint_infos_[i].current_score =
502  new_constraints_efficacies.back() +
503  constraint_infos_[i].objective_parallelism;
504 
505  if (constraint_infos_[i].is_deletable) {
506  constraint_infos_[i].active_count += constraint_active_count_increase_;
507  if (constraint_infos_[i].active_count >
508  sat_parameters_.cut_max_active_count_value()) {
509  rescale_active_count = true;
510  }
511  }
512  }
513  }
514 
515  // Bump activities of active constraints in LP.
516  if (solution_state != nullptr) {
517  const glop::RowIndex num_rows(lp_constraints_.size());
518  const glop::ColIndex num_cols =
519  solution_state->statuses.size() - RowToColIndex(num_rows);
520 
521  for (int i = 0; i < num_rows; ++i) {
522  const ConstraintIndex constraint_index = lp_constraints_[i];
523  const glop::VariableStatus row_status =
524  solution_state->statuses[num_cols + glop::ColIndex(i)];
525  if (row_status != glop::VariableStatus::BASIC &&
526  constraint_infos_[constraint_index].is_deletable) {
527  constraint_infos_[constraint_index].active_count +=
528  constraint_active_count_increase_;
529  if (constraint_infos_[constraint_index].active_count >
530  sat_parameters_.cut_max_active_count_value()) {
531  rescale_active_count = true;
532  }
533  }
534  }
535  }
536 
537  if (rescale_active_count) {
538  CHECK_GT(sat_parameters_.cut_max_active_count_value(), 0.0);
539  RescaleActiveCounts(1.0 / sat_parameters_.cut_max_active_count_value());
540  }
541 
542  // Update the increment counter.
543  constraint_active_count_increase_ *=
544  1.0 / sat_parameters_.cut_active_count_decay();
545 
546  // Remove constraints from the current LP that have been inactive for a while.
547  // We do that after we computed new_constraints so we do not need to iterate
548  // over the just deleted constraints.
549  if (MaybeRemoveSomeInactiveConstraints(solution_state)) {
550  current_lp_is_changed_ = true;
551  }
552 
553  // Note that the algo below is in O(limit * new_constraint). In order to
554  // limit spending too much time on this, we first sort all the constraints
555  // with an imprecise score (no orthogonality), then limit the size of the
556  // vector of constraints to precisely score, then we do the actual scoring.
557  //
558  // On problem crossword_opt_grid-19.05_dict-80_sat with linearization_level=2,
559  // new_constraint.size() > 1.5M.
560  //
561  // TODO(user): This blowup factor could be adaptative w.r.t. the constraint
562  // limit.
563  const int kBlowupFactor = 4;
564  int constraint_limit = std::min(sat_parameters_.new_constraints_batch_size(),
565  static_cast<int>(new_constraints.size()));
566  if (lp_constraints_.empty()) {
567  constraint_limit = std::min(1000, static_cast<int>(new_constraints.size()));
568  }
569  VLOG(3) << " - size = " << new_constraints.size()
570  << ", limit = " << constraint_limit;
571 
572  std::stable_sort(new_constraints.begin(), new_constraints.end(),
573  [&](ConstraintIndex a, ConstraintIndex b) {
574  return constraint_infos_[a].current_score >
575  constraint_infos_[b].current_score;
576  });
577  if (new_constraints.size() > kBlowupFactor * constraint_limit) {
578  VLOG(3) << "Resize candidate constraints from " << new_constraints.size()
579  << " down to " << kBlowupFactor * constraint_limit;
580  new_constraints.resize(kBlowupFactor * constraint_limit);
581  }
582 
583  int num_added = 0;
584  int num_skipped_checks = 0;
585  const int kCheckFrequency = 100;
586  ConstraintIndex last_added_candidate = kInvalidConstraintIndex;
587  for (int i = 0; i < constraint_limit; ++i) {
588  // Iterate through all new constraints and select the one with the best
589  // score.
590  double best_score = 0.0;
591  ConstraintIndex best_candidate = kInvalidConstraintIndex;
592  for (int j = 0; j < new_constraints.size(); ++j) {
593  // Checks the time limit, and returns if the lp has changed.
594  if (++num_skipped_checks >= kCheckFrequency) {
595  if (time_limit_->LimitReached()) return current_lp_is_changed_;
596  num_skipped_checks = 0;
597  }
598 
599  const ConstraintIndex new_constraint = new_constraints[j];
600  if (constraint_infos_[new_constraint].is_in_lp) continue;
601 
602  if (last_added_candidate != kInvalidConstraintIndex) {
603  const double current_orthogonality =
604  1.0 - (std::abs(ScalarProduct(
605  constraint_infos_[last_added_candidate].constraint,
606  constraint_infos_[new_constraint].constraint)) /
607  (constraint_infos_[last_added_candidate].l2_norm *
608  constraint_infos_[new_constraint].l2_norm));
609  new_constraints_orthogonalities[j] =
610  std::min(new_constraints_orthogonalities[j], current_orthogonality);
611  }
612 
613  // NOTE(user): It is safe to not add this constraint as the constraint
614  // that is almost parallel to this constraint is present in the LP or is
615  // inactive for a long time and is removed from the LP. In either case,
616  // this constraint is not adding significant value and is only making the
617  // LP larger.
618  if (new_constraints_orthogonalities[j] <
619  sat_parameters_.min_orthogonality_for_lp_constraints()) {
620  continue;
621  }
622 
623  // TODO(user): Experiment with different weights or different
624  // functions for computing score.
625  const double score = new_constraints_orthogonalities[j] +
626  constraint_infos_[new_constraint].current_score;
627  CHECK_GE(score, 0.0);
628  if (score > best_score || best_candidate == kInvalidConstraintIndex) {
629  best_score = score;
630  best_candidate = new_constraint;
631  }
632  }
633 
634  if (best_candidate != kInvalidConstraintIndex) {
635  // Add the best constraint in the LP.
636  constraint_infos_[best_candidate].is_in_lp = true;
637  // Note that it is important for LP incremental solving that the old
638  // constraints stays at the same position in this list (and thus in the
639  // returned GetLp()).
640  ++num_added;
641  current_lp_is_changed_ = true;
642  lp_constraints_.push_back(best_candidate);
643  last_added_candidate = best_candidate;
644  }
645  }
646 
647  if (num_added > 0) {
648  // We update the solution sate to match the new LP size.
649  VLOG(2) << "Added " << num_added << " constraints.";
650  solution_state->statuses.resize(solution_state->statuses.size() + num_added,
652  }
653 
654  // TODO(user): Instead of comparing num_deletable_constraints with cut
655  // limit, compare number of deletable constraints not in lp against the limit.
656  if (num_deletable_constraints_ > sat_parameters_.max_num_cuts()) {
657  PermanentlyRemoveSomeConstraints();
658  }
659 
660  // The LP changed only if we added new constraints or if some constraints
661  // already inside changed (simplification or tighter bounds).
662  if (current_lp_is_changed_) {
663  current_lp_is_changed_ = false;
664  return true;
665  }
666  return false;
667 }
668 
670  for (ConstraintIndex i(0); i < constraint_infos_.size(); ++i) {
671  if (constraint_infos_[i].is_in_lp) continue;
672  constraint_infos_[i].is_in_lp = true;
673  lp_constraints_.push_back(i);
674  }
675 }
676 
678  const LinearConstraint& cut) {
679  if (model_->Get<DebugSolution>() == nullptr) return true;
680  const auto& debug_solution = *(model_->Get<DebugSolution>());
681  if (debug_solution.empty()) return true;
682 
683  IntegerValue activity(0);
684  for (int i = 0; i < cut.vars.size(); ++i) {
685  const IntegerVariable var = cut.vars[i];
686  const IntegerValue coeff = cut.coeffs[i];
687  activity += coeff * debug_solution[var];
688  }
689  if (activity > cut.ub || activity < cut.lb) {
690  LOG(INFO) << "activity " << activity << " not in [" << cut.lb << ","
691  << cut.ub << "]";
692  return false;
693  }
694  return true;
695 }
696 
697 } // namespace sat
698 } // namespace operations_research
var
IntVar * var
Definition: expr_array.cc:1858
min
int64 min
Definition: alldiff_cst.cc:138
operations_research::sat::LinearConstraint::ub
IntegerValue ub
Definition: linear_constraint.h:41
operations_research::glop::VariableStatus::BASIC
@ BASIC
operations_research::sat::IntegerTrail::num_level_zero_enqueues
int64 num_level_zero_enqueues() const
Definition: integer.h:772
operations_research::sat::ScalarProduct
double ScalarProduct(const LinearConstraint &constraint1, const LinearConstraint &constraint2)
Definition: linear_constraint.cc:143
operations_research::sat::VariableIsPositive
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:141
max
int64 max
Definition: alldiff_cst.cc:139
linear_constraint.h
operations_research::sat::IntegerTrail::LevelZeroLowerBound
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1267
hash
int64 hash
Definition: matrix_utils.cc:60
operations_research::sat::NoDuplicateVariable
bool NoDuplicateVariable(const LinearConstraint &ct)
Definition: linear_constraint.cc:258
operations_research::sat::IntegerTrail::LevelZeroUpperBound
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1272
operations_research::sat::LinearConstraintManager::AddCut
bool AddCut(LinearConstraint ct, std::string type_name, const gtl::ITIVector< IntegerVariable, double > &lp_solution, std::string extra_info="")
Definition: linear_constraint_manager.cc:204
gtl::ITIVector::resize
void resize(size_type new_size)
Definition: int_type_indexed_vector.h:149
operations_research::sat::LinearConstraint::lb
IntegerValue lb
Definition: linear_constraint.h:40
operations_research
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
Definition: dense_doubly_linked_list.h:21
operations_research::sat::NegationOf
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:42
operations_research::sat::LinearConstraintManager::ConstraintInfo::hash
size_t hash
Definition: linear_constraint_manager.h:49
operations_research::sat::LinearConstraintManager::ChangeLp
bool ChangeLp(const gtl::ITIVector< IntegerVariable, double > &lp_solution, glop::BasisState *solution_state)
Definition: linear_constraint_manager.cc:437
operations_research::glop::StrictITIVector::resize
void resize(IntType size)
Definition: lp_types.h:269
gtl::ITIVector::size
size_type size() const
Definition: int_type_indexed_vector.h:146
operations_research::sat::Model::Get
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
Definition: sat/model.h:87
operations_research::glop::BasisState
Definition: revised_simplex.h:132
operations_research::sat::LinearConstraintManager::Add
ConstraintIndex Add(LinearConstraint ct, bool *added=nullptr)
Definition: linear_constraint_manager.cc:122
operations_research::glop::RowToColIndex
ColIndex RowToColIndex(RowIndex row)
Definition: lp_types.h:48
operations_research::sat::LinearConstraint
Definition: linear_constraint.h:39
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::glop::BasisState::statuses
VariableStatusRow statuses
Definition: revised_simplex.h:140
operations_research::sat::LinearConstraintManager::ConstraintInfo::constraint
LinearConstraint constraint
Definition: linear_constraint_manager.h:43
operations_research::sat::IntTypeAbs
IntType IntTypeAbs(IntType t)
Definition: integer.h:77
operations_research::sat::kMaxIntegerValue
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
operations_research::glop::StrictITIVector::size
IntType size() const
Definition: lp_types.h:276
operations_research::TimeLimit::LimitReached
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:532
ct
const Constraint * ct
Definition: demon_profiler.cc:42
operations_research::sat::ComputeL2Norm
double ComputeL2Norm(const LinearConstraint &constraint)
Definition: linear_constraint.cc:127
operations_research::sat::LinearConstraintManager::AddAllConstraintsToLp
void AddAllConstraintsToLp()
Definition: linear_constraint_manager.cc:669
operations_research::sat::ToDouble
double ToDouble(IntegerValue value)
Definition: integer.h:69
operations_research::sat::LinearConstraintManager::ConstraintInfo::l2_norm
double l2_norm
Definition: linear_constraint_manager.h:44
operations_research::sat::kMinIntegerValue
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue)
linear_constraint_manager.h
operations_research::sat::ComputeActivity
double ComputeActivity(const LinearConstraint &constraint, const gtl::ITIVector< IntegerVariable, double > &values)
Definition: linear_constraint.cc:116
operations_research::sat::LinearConstraint::vars
std::vector< IntegerVariable > vars
Definition: linear_constraint.h:42
b
int64 b
Definition: constraint_solver/table.cc:43
operations_research::sat::LinearConstraintManager::ConstraintInfo
Definition: linear_constraint_manager.h:42
gtl::ITIVector< IntegerVariable, double >
operations_research::sat::LinearConstraintManager::ConstraintInfo::active_count
double active_count
Definition: linear_constraint_manager.h:55
operations_research::sat::ComputeInfinityNorm
IntegerValue ComputeInfinityNorm(const LinearConstraint &constraint)
Definition: linear_constraint.cc:135
operations_research::glop::VariableStatus
VariableStatus
Definition: lp_types.h:196
operations_research::sat::DebugSolution
Definition: integer.h:249
operations_research::sat::DivideByGCD
void DivideByGCD(LinearConstraint *constraint)
Definition: linear_constraint.cc:182
operations_research::sat::LinearConstraintManager::~LinearConstraintManager
~LinearConstraintManager()
Definition: linear_constraint_manager.cc:45
operations_research::sat::CanonicalizeConstraint
void CanonicalizeConstraint(LinearConstraint *ct)
Definition: linear_constraint.cc:237
util_hash::Hash
uint64 Hash(uint64 num, uint64 c)
Definition: hash.h:150
integer.h
operations_research::sat::LinearConstraint::coeffs
std::vector< IntegerValue > coeffs
Definition: linear_constraint.h:43
operations_research::sat::LinearConstraintManager::DebugCheckConstraint
bool DebugCheckConstraint(const LinearConstraint &cut)
Definition: linear_constraint_manager.cc:677
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170
operations_research::sat::LinearConstraintManager::SetObjectiveCoefficient
void SetObjectiveCoefficient(IntegerVariable var, IntegerValue coeff)
Definition: linear_constraint_manager.cc:298