OR-Tools  8.1
var_domination.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 "ortools/base/stl_util.h"
17 
18 namespace operations_research {
19 namespace sat {
20 
21 void VarDomination::Reset(int num_variables) {
22  phase_ = 0;
23  num_vars_with_negation_ = 2 * num_variables;
24  partition_ = absl::make_unique<DynamicPartition>(num_vars_with_negation_);
25 
26  can_freely_decrease_.assign(num_vars_with_negation_, true);
27 
28  shared_buffer_.clear();
29  initial_candidates_.assign(num_vars_with_negation_, IntegerVariableSpan());
30 
31  buffer_.clear();
32  dominating_vars_.assign(num_vars_with_negation_, IntegerVariableSpan());
33 
34  ct_index_for_signature_ = 0;
35  block_down_signatures_.assign(num_vars_with_negation_, 0);
36 }
37 
38 void VarDomination::RefinePartition(std::vector<int>* vars) {
39  if (vars->empty()) return;
40  partition_->Refine(*vars);
41  for (int& var : *vars) {
42  const IntegerVariable wrapped(var);
43  can_freely_decrease_[wrapped] = false;
44  can_freely_decrease_[NegationOf(wrapped)] = false;
45  var = NegationOf(wrapped).value();
46  }
47  partition_->Refine(*vars);
48 }
49 
50 void VarDomination::CanOnlyDominateEachOther(absl::Span<const int> refs) {
51  if (phase_ != 0) return;
52  tmp_vars_.clear();
53  for (const int ref : refs) {
54  tmp_vars_.push_back(RefToIntegerVariable(ref).value());
55  }
56  RefinePartition(&tmp_vars_);
57  tmp_vars_.clear();
58 }
59 
60 void VarDomination::ActivityShouldNotChange(absl::Span<const int> refs,
61  absl::Span<const int64> coeffs) {
62  if (phase_ != 0) return;
63  FillTempRanks(/*reverse_references=*/false, /*enforcements=*/{}, refs,
64  coeffs);
65  tmp_vars_.clear();
66  for (int i = 0; i < tmp_ranks_.size(); ++i) {
67  if (i > 0 && tmp_ranks_[i].rank != tmp_ranks_[i - 1].rank) {
68  RefinePartition(&tmp_vars_);
69  tmp_vars_.clear();
70  }
71  tmp_vars_.push_back(tmp_ranks_[i].var.value());
72  }
73  RefinePartition(&tmp_vars_);
74  tmp_vars_.clear();
75 }
76 
77 // This correspond to a lower bounded constraint.
78 void VarDomination::ProcessTempRanks() {
79  if (phase_ == 0) {
80  // We actually "split" tmp_ranks_ according to the current partition and
81  // process each resulting list independently for a faster algo.
82  ++ct_index_for_signature_;
83  for (IntegerVariableWithRank& entry : tmp_ranks_) {
84  can_freely_decrease_[entry.var] = false;
85  block_down_signatures_[entry.var] |= uint64{1}
86  << (ct_index_for_signature_ % 64);
87  entry.part = partition_->PartOf(entry.var.value());
88  }
89  std::stable_sort(
90  tmp_ranks_.begin(), tmp_ranks_.end(),
91  [](const IntegerVariableWithRank& a, const IntegerVariableWithRank& b) {
92  return a.part < b.part;
93  });
94  int start = 0;
95  for (int i = 1; i < tmp_ranks_.size(); ++i) {
96  if (tmp_ranks_[i].part != tmp_ranks_[start].part) {
97  Initialize({&tmp_ranks_[start], static_cast<size_t>(i - start)});
98  start = i;
99  }
100  }
101  if (start < tmp_ranks_.size()) {
102  Initialize({&tmp_ranks_[start], tmp_ranks_.size() - start});
103  }
104  } else if (phase_ == 1) {
105  FilterUsingTempRanks();
106  } else {
107  // This is only used for debugging, and we shouldn't reach here in prod.
108  CheckUsingTempRanks();
109  }
110 }
111 
113  absl::Span<const int> enforcements, absl::Span<const int> refs,
114  absl::Span<const int64> coeffs) {
115  FillTempRanks(/*reverse_references=*/false, enforcements, refs, coeffs);
116  ProcessTempRanks();
117 }
118 
120  absl::Span<const int> enforcements, absl::Span<const int> refs,
121  absl::Span<const int64> coeffs) {
122  FillTempRanks(/*reverse_references=*/true, enforcements, refs, coeffs);
123  ProcessTempRanks();
124 }
125 
126 void VarDomination::MakeRankEqualToStartOfPart(
127  absl::Span<IntegerVariableWithRank> span) {
128  const int size = span.size();
129  int start = 0;
130  int previous_value = 0;
131  for (int i = 0; i < size; ++i) {
132  const int64 value = span[i].rank;
133  if (value != previous_value) {
134  previous_value = value;
135  start = i;
136  }
137  span[i].rank = start;
138  }
139 }
140 
141 void VarDomination::Initialize(absl::Span<IntegerVariableWithRank> span) {
142  // The rank can be wrong and need to be recomputed because of how we splitted
143  // tmp_ranks_ into spans.
144  MakeRankEqualToStartOfPart(span);
145 
146  const int future_start = shared_buffer_.size();
147  int first_start = -1;
148 
149  // This is mainly to avoid corner case and hopefully, it should be big enough
150  // to not matter too much.
151  const int kSizeThreshold = 1000;
152  const int size = span.size();
153  for (int i = std::max(0, size - kSizeThreshold); i < size; ++i) {
154  const IntegerVariableWithRank entry = span[i];
155  const int num_candidates = size - entry.rank;
156  if (num_candidates >= kSizeThreshold) continue;
157 
158  // Compute size to beat.
159  int size_threshold = kSizeThreshold;
160 
161  // Take into account the current partition size.
162  const int var_part = partition_->PartOf(entry.var.value());
163  const int part_size = partition_->SizeOfPart(var_part);
164  size_threshold = std::min(size_threshold, part_size);
165 
166  // Take into account our current best candidate if there is one.
167  const int current_num_candidates = initial_candidates_[entry.var].size;
168  if (current_num_candidates != 0) {
169  size_threshold = std::min(size_threshold, current_num_candidates);
170  }
171 
172  if (num_candidates < size_threshold) {
173  if (first_start == -1) first_start = entry.rank;
174  initial_candidates_[entry.var] = {
175  future_start - first_start + static_cast<int>(entry.rank),
176  num_candidates};
177  }
178  }
179 
180  // Only store what is necessary.
181  if (first_start == -1) return;
182  for (int i = first_start; i < size; ++i) {
183  shared_buffer_.push_back(span[i].var);
184  }
185 }
186 
187 // TODO(user): Use more heuristics to not miss as much dominance relation when
188 // we crop initial lists.
190  CHECK_EQ(phase_, 0);
191  phase_ = 1;
192 
193  // Some initial lists ar too long and will be cropped to this size.
194  // We will handle them slightly differently.
195  //
196  // TODO(user): Tune the initial size, 50 might be a bit large, since our
197  // complexity is borned by this number times the number of entries in the
198  // constraints. Still we should in most situation be a lot lower than that.
199  const int kMaxInitialSize = 50;
200  std::vector<IntegerVariable> cropped_lists;
201  absl::StrongVector<IntegerVariable, bool> is_cropped(num_vars_with_negation_,
202  false);
203 
204  // Fill the initial domination candidates.
205  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
206  if (can_freely_decrease_[var]) continue;
207  const int part = partition_->PartOf(var.value());
208  const int part_size = partition_->SizeOfPart(part);
209 
210  const int start = buffer_.size();
211  int new_size = 0;
212 
213  const uint64 var_sig = block_down_signatures_[var];
214  const uint64 not_var_sig = block_down_signatures_[NegationOf(var)];
215  const int stored_size = initial_candidates_[var].size;
216  if (stored_size == 0 || part_size < stored_size) {
217  // We start with the partition part.
218  // Note that all constraint will be filtered again in the second pass.
219  int num_tested = 0;
220  for (const int value : partition_->ElementsInPart(part)) {
221  const IntegerVariable c = IntegerVariable(value);
222 
223  // This is to limit the complexity to 1k * num_vars. We fill the list
224  // with dummy node so that the heuristic below will fill it with
225  // potential transpose candidates.
226  if (++num_tested > 1000) {
227  is_cropped[var] = true;
228  cropped_lists.push_back(var);
229  int extra = new_size;
230  while (extra < kMaxInitialSize) {
231  ++extra;
232  buffer_.push_back(kNoIntegerVariable);
233  }
234  break;
235  }
236  if (PositiveVariable(c) == PositiveVariable(var)) continue;
237  if (can_freely_decrease_[NegationOf(c)]) continue;
238  if (var_sig & ~block_down_signatures_[c]) continue; // !included.
239  if (block_down_signatures_[NegationOf(c)] & ~not_var_sig) continue;
240  ++new_size;
241  buffer_.push_back(c);
242 
243  // We do not want too many candidates per variables.
244  // TODO(user): randomize?
245  if (new_size > kMaxInitialSize) {
246  is_cropped[var] = true;
247  cropped_lists.push_back(var);
248  break;
249  }
250  }
251  } else {
252  // Copy the one that are in the same partition_ part.
253  //
254  // TODO(user): This can be too long maybe? even if we have list of at
255  // most 1000 at this point, see InitializeUsingTempRanks().
256  for (const IntegerVariable c : InitialDominatingCandidates(var)) {
257  if (PositiveVariable(c) == PositiveVariable(var)) continue;
258  if (can_freely_decrease_[NegationOf(c)]) continue;
259  if (partition_->PartOf(c.value()) != part) continue;
260  if (var_sig & ~block_down_signatures_[c]) continue; // !included.
261  if (block_down_signatures_[NegationOf(c)] & ~not_var_sig) continue;
262  ++new_size;
263  buffer_.push_back(c);
264 
265  // We do not want too many candidates per variables.
266  // TODO(user): randomize?
267  if (new_size > kMaxInitialSize) {
268  is_cropped[var] = true;
269  cropped_lists.push_back(var);
270  break;
271  }
272  }
273  }
274 
275  dominating_vars_[var] = {start, new_size};
276  }
277 
278  // Heuristic: To try not to remove domination relations corresponding to short
279  // lists during transposition (see EndSecondPhase()), we fill half of the
280  // cropped list with the transpose of the short list relations. This helps
281  // finding more relation in the presence of cropped lists.
282  for (const IntegerVariable var : cropped_lists) {
283  if (kMaxInitialSize / 2 < dominating_vars_[var].size) {
284  dominating_vars_[var].size = kMaxInitialSize / 2; // Restrict.
285  }
286  }
287  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
288  for (const IntegerVariable dom : DominatingVariables(var)) {
289  if (!is_cropped[NegationOf(dom)]) continue;
290  IntegerVariableSpan& s = dominating_vars_[NegationOf(dom)];
291  if (s.size >= kMaxInitialSize) continue;
292  buffer_[s.start + s.size++] = NegationOf(var);
293  }
294  }
295 
296  // Remove any duplicates.
297  //
298  // TODO(user): Maybe we should do that with all lists in case the
299  // input function are called with duplicates too.
300  for (const IntegerVariable var : cropped_lists) {
301  if (!is_cropped[var]) continue;
302  IntegerVariableSpan& s = dominating_vars_[var];
303  std::sort(&buffer_[s.start], &buffer_[s.start + s.size]);
304  const auto p = std::unique(&buffer_[s.start], &buffer_[s.start + s.size]);
305  s.size = p - &buffer_[s.start];
306  }
307 
308  // We no longer need the first phase memory.
309  VLOG(1) << "Num initial list that where cropped: " << cropped_lists.size();
310  VLOG(1) << "Shared buffer size: " << shared_buffer_.size();
311  VLOG(1) << "Buffer size: " << buffer_.size();
312  gtl::STLClearObject(&initial_candidates_);
313  gtl::STLClearObject(&shared_buffer_);
314 }
315 
317  CHECK_EQ(phase_, 1);
318  phase_ = 2;
319 
320  // Perform intersection with transpose.
321  shared_buffer_.clear();
322  initial_candidates_.assign(num_vars_with_negation_, IntegerVariableSpan());
323 
324  // Pass 1: count.
325  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
326  for (const IntegerVariable dom : DominatingVariables(var)) {
327  ++initial_candidates_[NegationOf(dom)].size;
328  }
329  }
330 
331  // Pass 2: compute starts.
332  int start = 0;
333  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
334  initial_candidates_[var].start = start;
335  start += initial_candidates_[var].size;
336  initial_candidates_[var].size = 0;
337  }
338  shared_buffer_.resize(start);
339 
340  // Pass 3: transpose.
341  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
342  for (const IntegerVariable dom : DominatingVariables(var)) {
343  IntegerVariableSpan& span = initial_candidates_[NegationOf(dom)];
344  shared_buffer_[span.start + span.size++] = NegationOf(var);
345  }
346  }
347 
348  // Pass 4: intersect.
349  int num_removed = 0;
350  tmp_var_to_rank_.resize(num_vars_with_negation_, -1);
351  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
352  for (const IntegerVariable dom : InitialDominatingCandidates(var)) {
353  tmp_var_to_rank_[dom] = 1;
354  }
355 
356  int new_size = 0;
357  IntegerVariableSpan& span = dominating_vars_[var];
358  for (const IntegerVariable dom : DominatingVariables(var)) {
359  if (tmp_var_to_rank_[dom] != 1) {
360  ++num_removed;
361  continue;
362  }
363  buffer_[span.start + new_size++] = dom;
364  }
365  span.size = new_size;
366 
367  for (const IntegerVariable dom : InitialDominatingCandidates(var)) {
368  tmp_var_to_rank_[dom] = -1;
369  }
370  }
371 
372  VLOG(1) << "Transpose removed " << num_removed;
373  gtl::STLClearObject(&initial_candidates_);
374  gtl::STLClearObject(&shared_buffer_);
375 }
376 
377 void VarDomination::FillTempRanks(bool reverse_references,
378  absl::Span<const int> enforcements,
379  absl::Span<const int> refs,
380  absl::Span<const int64> coeffs) {
381  tmp_ranks_.clear();
382  if (coeffs.empty()) {
383  // Simple case: all coefficients are assumed to be the same.
384  for (const int ref : refs) {
385  const IntegerVariable var =
386  RefToIntegerVariable(reverse_references ? NegatedRef(ref) : ref);
387  tmp_ranks_.push_back({var, 0, 0});
388  }
389  } else {
390  // Complex case: different coefficients.
391  for (int i = 0; i < refs.size(); ++i) {
392  if (coeffs[i] == 0) continue;
393  const IntegerVariable var = RefToIntegerVariable(
394  reverse_references ? NegatedRef(refs[i]) : refs[i]);
395  if (coeffs[i] > 0) {
396  tmp_ranks_.push_back({var, 0, coeffs[i]});
397  } else {
398  tmp_ranks_.push_back({NegationOf(var), 0, -coeffs[i]});
399  }
400  }
401  std::sort(tmp_ranks_.begin(), tmp_ranks_.end());
402  MakeRankEqualToStartOfPart({&tmp_ranks_[0], tmp_ranks_.size()});
403  }
404 
405  // Add the enforcement last with a new rank. We add their negation since
406  // we want the activity to not decrease, and we want to allow any
407  // enforcement-- to dominate a variable in the constraint.
408  const int enforcement_rank = tmp_ranks_.size();
409  for (const int ref : enforcements) {
410  tmp_ranks_.push_back(
411  {RefToIntegerVariable(NegatedRef(ref)), 0, enforcement_rank});
412  }
413 }
414 
415 // We take the intersection of the current dominating candidate with the
416 // restriction imposed by the current content of tmp_ranks_.
417 void VarDomination::FilterUsingTempRanks() {
418  // Expand ranks in temp vector.
419  tmp_var_to_rank_.resize(num_vars_with_negation_, -1);
420  for (const IntegerVariableWithRank entry : tmp_ranks_) {
421  tmp_var_to_rank_[entry.var] = entry.rank;
422  }
423 
424  // The activity of the variable in tmp_rank must not decrease.
425  for (const IntegerVariableWithRank entry : tmp_ranks_) {
426  // The only variables that can be paired with a var-- in the constriants are
427  // the var++ in the constraints with the same rank or higher.
428  //
429  // Note that we only filter the var-- domination lists here, we do not
430  // remove the var-- appearing in all the lists corresponding to wrong var++.
431  // This is left to the tranpose operation in EndSecondPhase().
432  {
433  IntegerVariableSpan& span = dominating_vars_[entry.var];
434  if (span.size == 0) continue;
435  int new_size = 0;
436  for (const IntegerVariable candidate : DominatingVariables(entry.var)) {
437  if (tmp_var_to_rank_[candidate] < entry.rank) continue;
438  buffer_[span.start + new_size++] = candidate;
439  }
440  span.size = new_size;
441  }
442  }
443 
444  // Reset temporary vector to all -1.
445  for (const IntegerVariableWithRank entry : tmp_ranks_) {
446  tmp_var_to_rank_[entry.var] = -1;
447  }
448 }
449 
450 // Slow: This is for debugging only.
451 void VarDomination::CheckUsingTempRanks() {
452  tmp_var_to_rank_.resize(num_vars_with_negation_, -1);
453  for (const IntegerVariableWithRank entry : tmp_ranks_) {
454  tmp_var_to_rank_[entry.var] = entry.rank;
455  }
456 
457  // The activity of the variable in tmp_rank must not decrease.
458  for (IntegerVariable var(0); var < num_vars_with_negation_; ++var) {
459  const int var_rank = tmp_var_to_rank_[var];
460  const int negated_var_rank = tmp_var_to_rank_[NegationOf(var)];
461  for (const IntegerVariable dom : DominatingVariables(var)) {
462  CHECK(!can_freely_decrease_[NegationOf(dom)]);
463 
464  // Doing X--, Y++ is compatible if the rank[X] <= rank[Y]. But we also
465  // need to check if doing Not(Y)-- is compatible with Not(X)++.
466  CHECK_LE(var_rank, tmp_var_to_rank_[dom]);
467  CHECK_LE(tmp_var_to_rank_[NegationOf(dom)], negated_var_rank);
468  }
469  }
470 
471  for (const IntegerVariableWithRank entry : tmp_ranks_) {
472  tmp_var_to_rank_[entry.var] = -1;
473  }
474 }
475 
476 bool VarDomination::CanFreelyDecrease(int ref) const {
478 }
479 
480 bool VarDomination::CanFreelyDecrease(IntegerVariable var) const {
481  return can_freely_decrease_[var];
482 }
483 
484 absl::Span<const IntegerVariable> VarDomination::InitialDominatingCandidates(
485  IntegerVariable var) const {
486  const IntegerVariableSpan span = initial_candidates_[var];
487  if (span.size == 0) return absl::Span<const IntegerVariable>();
488  return absl::Span<const IntegerVariable>(&shared_buffer_[span.start],
489  span.size);
490 }
491 
492 absl::Span<const IntegerVariable> VarDomination::DominatingVariables(
493  int ref) const {
495 }
496 
497 absl::Span<const IntegerVariable> VarDomination::DominatingVariables(
498  IntegerVariable var) const {
499  const IntegerVariableSpan span = dominating_vars_[var];
500  if (span.size == 0) return absl::Span<const IntegerVariable>();
501  return absl::Span<const IntegerVariable>(&buffer_[span.start], span.size);
502 }
503 
504 std::string VarDomination::DominationDebugString(IntegerVariable var) const {
505  const int ref = IntegerVariableToRef(var);
506  std::string result =
507  absl::StrCat(PositiveRef(ref), RefIsPositive(ref) ? "--" : "++", " : ");
508  for (const IntegerVariable dom : DominatingVariables(var)) {
509  const int dom_ref = IntegerVariableToRef(dom);
510  absl::StrAppend(&result, PositiveRef(dom_ref),
511  RefIsPositive(dom_ref) ? "++" : "--", " ");
512  }
513  return result;
514 }
515 
516 void DualBoundStrengthening::CannotDecrease(absl::Span<const int> refs) {
517  for (const int ref : refs) {
518  const IntegerVariable var = RefToIntegerVariable(ref);
519  can_freely_decrease_until_[var] = kMaxIntegerValue;
520  }
521 }
522 
523 void DualBoundStrengthening::CannotIncrease(absl::Span<const int> refs) {
524  for (const int ref : refs) {
525  const IntegerVariable var = RefToIntegerVariable(ref);
526  can_freely_decrease_until_[NegationOf(var)] = kMaxIntegerValue;
527  }
528 }
529 
530 void DualBoundStrengthening::CannotMove(absl::Span<const int> refs) {
531  for (const int ref : refs) {
532  const IntegerVariable var = RefToIntegerVariable(ref);
533  can_freely_decrease_until_[var] = kMaxIntegerValue;
534  can_freely_decrease_until_[NegationOf(var)] = kMaxIntegerValue;
535  }
536 }
537 
538 template <typename LinearProto>
540  bool is_objective, const PresolveContext& context,
541  const LinearProto& linear, int64 min_activity, int64 max_activity) {
542  const int64 lb_limit = linear.domain(linear.domain_size() - 2);
543  const int64 ub_limit = linear.domain(1);
544  const int num_terms = linear.vars_size();
545  for (int i = 0; i < num_terms; ++i) {
546  int ref = linear.vars(i);
547  int64 coeff = linear.coeffs(i);
548  if (coeff < 0) {
549  ref = NegatedRef(ref);
550  coeff = -coeff;
551  }
552 
553  const int64 min_term = coeff * context.MinOf(ref);
554  const int64 max_term = coeff * context.MaxOf(ref);
555  const int64 term_diff = max_term - min_term;
556  const IntegerVariable var = RefToIntegerVariable(ref);
557 
558  // lb side.
559  if (min_activity < lb_limit) {
560  if (min_activity + term_diff < lb_limit) {
561  can_freely_decrease_until_[var] = kMaxIntegerValue;
562  } else {
563  const IntegerValue slack(lb_limit - min_activity);
564  const IntegerValue var_diff =
565  CeilRatio(IntegerValue(slack), IntegerValue(coeff));
566  can_freely_decrease_until_[var] =
567  std::max(can_freely_decrease_until_[var],
568  IntegerValue(context.MinOf(ref)) + var_diff);
569  }
570  }
571 
572  if (is_objective) {
573  // We never want to increase the objective value.
574  can_freely_decrease_until_[NegationOf(var)] = kMaxIntegerValue;
575  continue;
576  }
577 
578  // ub side.
579  if (max_activity > ub_limit) {
580  if (max_activity - term_diff > ub_limit) {
581  can_freely_decrease_until_[NegationOf(var)] = kMaxIntegerValue;
582  } else {
583  const IntegerValue slack(max_activity - ub_limit);
584  const IntegerValue var_diff =
585  CeilRatio(IntegerValue(slack), IntegerValue(coeff));
586  can_freely_decrease_until_[NegationOf(var)] =
587  std::max(can_freely_decrease_until_[NegationOf(var)],
588  -IntegerValue(context.MaxOf(ref)) + var_diff);
589  }
590  }
591  }
592 }
593 
595  const CpModelProto& cp_model = *context->working_model;
596  const int num_vars = cp_model.variables_size();
597  for (int var = 0; var < num_vars; ++var) {
598  if (context->IsFixed(var)) continue;
599 
600  // Fix to lb?
601  const int64 lb = context->MinOf(var);
602  const int64 ub_limit = std::max(lb, CanFreelyDecreaseUntil(var));
603  if (ub_limit == lb) {
604  context->UpdateRuleStats("dual: fix variable");
605  CHECK(context->IntersectDomainWith(var, Domain(lb)));
606  continue;
607  }
608 
609  // Fix to ub?
610  const int64 ub = context->MaxOf(var);
611  const int64 lb_limit =
613  if (lb_limit == ub) {
614  context->UpdateRuleStats("dual: fix variable");
615  CHECK(context->IntersectDomainWith(var, Domain(ub)));
616  continue;
617  }
618 
619  // Here we can fix to any value in [ub_limit, lb_limit] that is compatible
620  // with the current domain. We prefer zero or the lowest possible magnitude.
621  if (lb_limit > ub_limit) {
622  const Domain domain =
623  context->DomainOf(var).IntersectionWith(Domain(ub_limit, lb_limit));
624  if (!domain.IsEmpty()) {
625  int64 value = domain.Contains(0) ? 0 : domain.Min();
626  if (value != 0) {
627  for (const int64 bound : domain.FlattenedIntervals()) {
628  if (std::abs(bound) < std::abs(value)) value = bound;
629  }
630  }
631  context->UpdateRuleStats("dual: fix variable with multiple choices");
632  CHECK(context->IntersectDomainWith(var, Domain(value)));
633  continue;
634  }
635  }
636 
637  // Here we can reduce the domain, but we must be careful when the domain
638  // has holes.
639  if (lb_limit > lb || ub_limit < ub) {
640  const int64 new_ub =
641  ub_limit < ub ? context->DomainOf(var)
642  .IntersectionWith(Domain(ub_limit, kint64max))
643  .Min()
644  : ub;
645  const int64 new_lb =
646  lb_limit > lb ? context->DomainOf(var)
647  .IntersectionWith(Domain(kint64min, lb_limit))
648  .Max()
649  : lb;
650  context->UpdateRuleStats("dual: reduced domain");
651  CHECK(context->IntersectDomainWith(var, Domain(new_lb, new_ub)));
652  }
653  }
654  return true;
655 }
656 
657 namespace {
658 
659 // TODO(user): Maybe we should avoid recomputing that here.
660 template <typename LinearExprProto>
661 void FillMinMaxActivity(const PresolveContext& context,
662  const LinearExprProto& proto, int64* min_activity,
663  int64* max_activity) {
664  *min_activity = 0;
665  *max_activity = 0;
666  const int num_vars = proto.vars().size();
667  for (int i = 0; i < num_vars; ++i) {
668  const int64 a = proto.coeffs(i) * context.MinOf(proto.vars(i));
669  const int64 b = proto.coeffs(i) * context.MaxOf(proto.vars(i));
670  *min_activity += std::min(a, b);
671  *max_activity += std::max(a, b);
672  }
673 }
674 
675 } // namespace
676 
678  const PresolveContext& context, VarDomination* var_domination,
679  DualBoundStrengthening* dual_bound_strengthening) {
680  const CpModelProto& cp_model = *context.working_model;
681  const int num_vars = cp_model.variables().size();
682  var_domination->Reset(num_vars);
683  dual_bound_strengthening->Reset(num_vars);
684 
685  int64 min_activity = kint64min;
686  int64 max_activity = kint64max;
687 
688  for (int var = 0; var < num_vars; ++var) {
689  // Deal with the affine relations that are not part of the proto.
690  // Those only need to be processed in the first pass.
691  //
692  // TODO(user): This is not ideal since if only the representative is still
693  // used, we shouldn't restrict any dominance relation involving it.
694  const AffineRelation::Relation r = context.GetAffineRelation(var);
695  if (r.representative != var) {
696  dual_bound_strengthening->CannotMove({var, r.representative});
697  if (r.coeff == 1) {
698  var_domination->CanOnlyDominateEachOther(
700  } else if (r.coeff == -1) {
701  var_domination->CanOnlyDominateEachOther({var, r.representative});
702  } else {
703  var_domination->CanOnlyDominateEachOther({var});
704  var_domination->CanOnlyDominateEachOther({r.representative});
705  }
706  }
707 
708  // Also ignore variables that have been substitued already or are unused.
709  if (context.IsFixed(var) || context.VariableWasRemoved(var) ||
710  context.VariableIsNotUsedAnymore(var)) {
711  dual_bound_strengthening->CannotMove({var});
712  var_domination->CanOnlyDominateEachOther({var});
713  }
714  }
715 
716  // TODO(user): Benchmark and experiment with 3 phases algo:
717  // - Only ActivityShouldNotChange()/CanOnlyDominateEachOther().
718  // - The other cases once.
719  // - EndFirstPhase() and then the other cases a second time.
720  std::vector<int> tmp;
721  const int num_constraints = cp_model.constraints_size();
722  for (int phase = 0; phase < 2; phase++) {
723  for (int c = 0; c < num_constraints; ++c) {
724  const ConstraintProto& ct = cp_model.constraints(c);
725  if (phase == 0) {
726  dual_bound_strengthening->CannotIncrease(ct.enforcement_literal());
727  }
728  switch (ct.constraint_case()) {
729  case ConstraintProto::kBoolOr:
730  if (phase == 0) {
731  dual_bound_strengthening->CannotDecrease(ct.bool_or().literals());
732  }
733  var_domination->ActivityShouldNotDecrease(ct.enforcement_literal(),
734  ct.bool_or().literals(),
735  /*coeffs=*/{});
736  break;
737  case ConstraintProto::kBoolAnd:
738  if (phase == 0) {
739  dual_bound_strengthening->CannotDecrease(ct.bool_and().literals());
740  }
741 
742  // We process it like n clauses.
743  //
744  // TODO(user): the way we process that is a bit restrictive. By
745  // working on the implication graph we could detect more dominance
746  // relations. Since if a => b we say that a++ can only be paired with
747  // b--, but it could actually be paired with any variables that when
748  // dereased implies b = 0. This is a bit mitigated by the fact that
749  // we regroup when we can such implications into big at most ones.
750  tmp.clear();
751  for (const int ref : ct.enforcement_literal()) {
752  tmp.push_back(NegatedRef(ref));
753  }
754  for (const int ref : ct.bool_and().literals()) {
755  tmp.push_back(ref);
756  var_domination->ActivityShouldNotDecrease(/*enforcements=*/{}, tmp,
757  /*coeffs=*/{});
758  tmp.pop_back();
759  }
760  break;
761  case ConstraintProto::kAtMostOne:
762  if (phase == 0) {
763  dual_bound_strengthening->CannotIncrease(
764  ct.at_most_one().literals());
765  }
766  var_domination->ActivityShouldNotIncrease(ct.enforcement_literal(),
767  ct.at_most_one().literals(),
768  /*coeffs=*/{});
769  break;
770  case ConstraintProto::kLinear: {
771  FillMinMaxActivity(context, ct.linear(), &min_activity,
772  &max_activity);
773  if (phase == 0) {
774  dual_bound_strengthening->ProcessLinearConstraint(
775  false, context, ct.linear(), min_activity, max_activity);
776  }
777  const bool domain_is_simple = ct.linear().domain().size() == 2;
778  const bool free_to_increase =
779  domain_is_simple && ct.linear().domain(1) >= max_activity;
780  const bool free_to_decrease =
781  domain_is_simple && ct.linear().domain(0) <= min_activity;
782  if (free_to_decrease && free_to_increase) break;
783  if (free_to_increase) {
784  var_domination->ActivityShouldNotDecrease(ct.enforcement_literal(),
785  ct.linear().vars(),
786  ct.linear().coeffs());
787  } else if (free_to_decrease) {
788  var_domination->ActivityShouldNotIncrease(ct.enforcement_literal(),
789  ct.linear().vars(),
790  ct.linear().coeffs());
791  } else {
792  // TODO(user): Handle enforcement better here.
793  if (!ct.enforcement_literal().empty()) {
794  var_domination->ActivityShouldNotIncrease(
795  /*enforcements=*/{}, ct.enforcement_literal(), /*coeffs=*/{});
796  }
797  var_domination->ActivityShouldNotChange(ct.linear().vars(),
798  ct.linear().coeffs());
799  }
800  break;
801  }
802  default:
803  // We cannot infer anything if we don't know the constraint.
804  // TODO(user): Handle enforcement better here.
805  if (phase == 0) {
806  dual_bound_strengthening->CannotMove(context.ConstraintToVars(c));
807  }
808  for (const int var : context.ConstraintToVars(c)) {
809  var_domination->CanOnlyDominateEachOther({var});
810  }
811  break;
812  }
813  }
814 
815  // The objective is handled like a <= constraints, or an == constraint if
816  // there is a non-trivial domain.
817  if (cp_model.has_objective()) {
818  // WARNING: The proto objective might not be up to date, so we need to
819  // write it first.
820  if (phase == 0) context.WriteObjectiveToProto();
821  FillMinMaxActivity(context, cp_model.objective(), &min_activity,
822  &max_activity);
823  dual_bound_strengthening->ProcessLinearConstraint(
824  true, context, cp_model.objective(), min_activity, max_activity);
825  const auto& domain = cp_model.objective().domain();
826  if (domain.empty() || (domain.size() == 2 && domain[0] <= min_activity)) {
827  var_domination->ActivityShouldNotIncrease(
828  /*enforcements=*/{}, cp_model.objective().vars(),
829  cp_model.objective().coeffs());
830  } else {
831  var_domination->ActivityShouldNotChange(cp_model.objective().vars(),
832  cp_model.objective().coeffs());
833  }
834  }
835 
836  if (phase == 0) var_domination->EndFirstPhase();
837  if (phase == 1) var_domination->EndSecondPhase();
838  }
839 
840  // Some statistics.
841  int64 num_unconstrained_refs = 0;
842  int64 num_dominated_refs = 0;
843  int64 num_dominance_relations = 0;
844  for (int var = 0; var < num_vars; ++var) {
845  if (context.IsFixed(var)) continue;
846 
847  for (const int ref : {var, NegatedRef(var)}) {
848  if (var_domination->CanFreelyDecrease(ref)) {
849  num_unconstrained_refs++;
850  } else if (!var_domination->DominatingVariables(ref).empty()) {
851  num_dominated_refs++;
852  num_dominance_relations +=
853  var_domination->DominatingVariables(ref).size();
854  }
855  }
856  }
857  if (num_unconstrained_refs == 0 && num_dominated_refs == 0) return;
858  VLOG(1) << "Dominance:"
859  << " num_unconstrained_refs=" << num_unconstrained_refs
860  << " num_dominated_refs=" << num_dominated_refs
861  << " num_dominance_relations=" << num_dominance_relations;
862 }
863 
864 bool ExploitDominanceRelations(const VarDomination& var_domination,
866  const CpModelProto& cp_model = *context->working_model;
867  const int num_vars = cp_model.variables_size();
868 
869  // Abort early if there is nothing to do.
870  bool work_to_do = false;
871  for (int var = 0; var < num_vars; ++var) {
872  if (context->IsFixed(var)) continue;
873  if (!var_domination.DominatingVariables(var).empty() ||
874  !var_domination.DominatingVariables(NegatedRef(var)).empty()) {
875  work_to_do = true;
876  break;
877  }
878  }
879  if (!work_to_do) return true;
880 
881  absl::StrongVector<IntegerVariable, int64> var_lb_to_ub_diff(num_vars * 2, 0);
882  absl::StrongVector<IntegerVariable, bool> in_constraints(num_vars * 2, false);
883 
884  const int num_constraints = cp_model.constraints_size();
885  for (int c = 0; c < num_constraints; ++c) {
886  const ConstraintProto& ct = cp_model.constraints(c);
887 
888  if (ct.constraint_case() == ConstraintProto::kBoolAnd) {
889  if (ct.enforcement_literal().size() != 1) continue;
890  const int a = ct.enforcement_literal(0);
891  if (context->IsFixed(a)) continue;
892  for (const int b : ct.bool_and().literals()) {
893  if (context->IsFixed(b)) continue;
894 
895  // If (a--, b--) is valid, we can always set a to false.
896  for (const IntegerVariable ivar :
897  var_domination.DominatingVariables(a)) {
898  const int ref = VarDomination::IntegerVariableToRef(ivar);
899  if (ref == NegatedRef(b)) {
900  context->UpdateRuleStats("domination: in implication");
901  if (!context->SetLiteralToFalse(a)) return false;
902  break;
903  }
904  }
905  if (context->IsFixed(a)) break;
906 
907  // If (b++, a++) is valid, then we can always set b to true.
908  for (const IntegerVariable ivar :
909  var_domination.DominatingVariables(NegatedRef(b))) {
910  const int ref = VarDomination::IntegerVariableToRef(ivar);
911  if (ref == a) {
912  context->UpdateRuleStats("domination: in implication");
913  if (!context->SetLiteralToTrue(b)) return false;
914  break;
915  }
916  }
917  }
918  continue;
919  }
920 
921  if (!ct.enforcement_literal().empty()) continue;
922 
923  if (ct.constraint_case() == ConstraintProto::kAtMostOne) {
924  for (const int ref : ct.at_most_one().literals()) {
925  in_constraints[VarDomination::RefToIntegerVariable(ref)] = true;
926  }
927  for (const int ref : ct.at_most_one().literals()) {
928  if (context->IsFixed(ref)) continue;
929 
930  const auto dominating_ivars = var_domination.DominatingVariables(ref);
931  if (dominating_ivars.empty()) continue;
932  for (const IntegerVariable ivar : dominating_ivars) {
933  if (!in_constraints[ivar]) continue;
934  if (context->IsFixed(VarDomination::IntegerVariableToRef(ivar))) {
935  continue;
936  }
937 
938  // We can set the dominated variable to false.
939  context->UpdateRuleStats("domination: in at most one");
940  if (!context->SetLiteralToFalse(ref)) return false;
941  break;
942  }
943  }
944  for (const int ref : ct.at_most_one().literals()) {
945  in_constraints[VarDomination::RefToIntegerVariable(ref)] = false;
946  }
947  }
948 
949  if (ct.constraint_case() != ConstraintProto::kLinear) continue;
950 
951  int num_dominated = 0;
952  for (const int var : context->ConstraintToVars(c)) {
953  if (!var_domination.DominatingVariables(var).empty()) ++num_dominated;
954  if (!var_domination.DominatingVariables(NegatedRef(var)).empty()) {
955  ++num_dominated;
956  }
957  }
958  if (num_dominated == 0) continue;
959 
960  // Precompute.
961  int64 min_activity = 0;
962  int64 max_activity = 0;
963  const int num_terms = ct.linear().vars_size();
964  for (int i = 0; i < num_terms; ++i) {
965  int ref = ct.linear().vars(i);
966  int64 coeff = ct.linear().coeffs(i);
967  if (coeff < 0) {
968  ref = NegatedRef(ref);
969  coeff = -coeff;
970  }
971  const int64 min_term = coeff * context->MinOf(ref);
972  const int64 max_term = coeff * context->MaxOf(ref);
973  min_activity += min_term;
974  max_activity += max_term;
975  const IntegerVariable ivar = VarDomination::RefToIntegerVariable(ref);
976  var_lb_to_ub_diff[ivar] = max_term - min_term;
977  var_lb_to_ub_diff[NegationOf(ivar)] = min_term - max_term;
978  }
979  const int64 rhs_lb = ct.linear().domain(0);
980  const int64 rhs_ub = ct.linear().domain(ct.linear().domain_size() - 1);
981  if (max_activity < rhs_lb || min_activity > rhs_ub) {
982  return context->NotifyThatModelIsUnsat("linear equation unsat.");
983  }
984 
985  // Look for dominated var.
986  for (int i = 0; i < num_terms; ++i) {
987  const int ref = ct.linear().vars(i);
988  const int64 coeff = ct.linear().coeffs(i);
989  const int64 coeff_magnitude = std::abs(coeff);
990  if (context->IsFixed(ref)) continue;
991 
992  for (const int current_ref : {ref, NegatedRef(ref)}) {
993  const absl::Span<const IntegerVariable> dominated_by =
994  var_domination.DominatingVariables(current_ref);
995  if (dominated_by.empty()) continue;
996 
997  const bool ub_side = (coeff > 0) == (current_ref == ref);
998  if (ub_side) {
999  if (max_activity <= rhs_ub) continue;
1000  } else {
1001  if (min_activity >= rhs_lb) continue;
1002  }
1003  const int64 slack =
1004  ub_side ? rhs_ub - min_activity : max_activity - rhs_lb;
1005 
1006  // Compute the delta in activity if all dominating var moves to their
1007  // other bound.
1008  int64 delta = 0;
1009  for (const IntegerVariable ivar : dominated_by) {
1010  if (ub_side) {
1011  delta += std::max(int64{0}, var_lb_to_ub_diff[ivar]);
1012  } else {
1013  delta += std::max(int64{0}, -var_lb_to_ub_diff[ivar]);
1014  }
1015  }
1016 
1017  const int64 lb = context->MinOf(current_ref);
1018  if (delta + coeff_magnitude > slack) {
1019  context->UpdateRuleStats("domination: fixed to lb.");
1020  if (!context->IntersectDomainWith(current_ref, Domain(lb))) {
1021  return false;
1022  }
1023 
1024  // We need to update the precomputed quantities.
1025  const IntegerVariable current_var =
1027  if (ub_side) {
1028  CHECK_GE(var_lb_to_ub_diff[current_var], 0);
1029  max_activity -= var_lb_to_ub_diff[current_var];
1030  } else {
1031  CHECK_LE(var_lb_to_ub_diff[current_var], 0);
1032  min_activity -= var_lb_to_ub_diff[current_var];
1033  }
1034  var_lb_to_ub_diff[current_var] = 0;
1035  var_lb_to_ub_diff[NegationOf(current_var)] = 0;
1036 
1037  continue;
1038  }
1039 
1040  const IntegerValue diff = FloorRatio(IntegerValue(slack - delta),
1041  IntegerValue(coeff_magnitude));
1042  const int64 new_ub = lb + diff.value();
1043  if (new_ub < context->MaxOf(current_ref)) {
1044  context->UpdateRuleStats("domination: reduced ub.");
1045  if (!context->IntersectDomainWith(current_ref, Domain(lb, new_ub))) {
1046  return false;
1047  }
1048 
1049  // We need to update the precomputed quantities.
1050  const IntegerVariable current_var =
1052  if (ub_side) {
1053  CHECK_GE(var_lb_to_ub_diff[current_var], 0);
1054  max_activity -= var_lb_to_ub_diff[current_var];
1055  } else {
1056  CHECK_LE(var_lb_to_ub_diff[current_var], 0);
1057  min_activity -= var_lb_to_ub_diff[current_var];
1058  }
1059  const int64 new_diff = std::abs(coeff_magnitude * (new_ub - lb));
1060  if (ub_side) {
1061  var_lb_to_ub_diff[current_var] = new_diff;
1062  var_lb_to_ub_diff[NegationOf(current_var)] = -new_diff;
1063  max_activity += new_diff;
1064  } else {
1065  var_lb_to_ub_diff[current_var] = -new_diff;
1066  var_lb_to_ub_diff[NegationOf(current_var)] = +new_diff;
1067  min_activity -= new_diff;
1068  }
1069  }
1070  }
1071  }
1072 
1073  // Restore.
1074  for (const int ref : ct.linear().vars()) {
1075  const IntegerVariable ivar = VarDomination::RefToIntegerVariable(ref);
1076  var_lb_to_ub_diff[ivar] = 0;
1077  var_lb_to_ub_diff[NegationOf(ivar)] = 0;
1078  }
1079  }
1080 
1081  // For any dominance relation still left (i.e. between non-fixed vars), if
1082  // the variable are Boolean and X is dominated by Y, we can add
1083  // (X == 1) => (Y = 1). But, as soon as we do that, we break some symmetry
1084  // and cannot add any incompatible relations.
1085  //
1086  // EX: It is possible that X dominate Y and Y dominate X if they are both
1087  // appearing in exactly the same constraint with the same coefficient.
1088  //
1089  // TODO(user): generalize to non Booleans?
1090  // TODO(user): We always keep adding the same relations. Do that only once!
1091  int num_added = 0;
1092  absl::StrongVector<IntegerVariable, bool> increase_is_forbidden(2 * num_vars,
1093  false);
1094  for (int positive_ref = 0; positive_ref < num_vars; ++positive_ref) {
1095  if (context->IsFixed(positive_ref)) continue;
1096  if (!context->CanBeUsedAsLiteral(positive_ref)) continue;
1097  for (const int ref : {positive_ref, NegatedRef(positive_ref)}) {
1098  const IntegerVariable var = VarDomination::RefToIntegerVariable(ref);
1099  if (increase_is_forbidden[NegationOf(var)]) continue;
1100  for (const IntegerVariable dom :
1101  var_domination.DominatingVariables(ref)) {
1102  if (increase_is_forbidden[dom]) continue;
1103  const int dom_ref = VarDomination::IntegerVariableToRef(dom);
1104  if (context->IsFixed(dom_ref)) continue;
1105  if (!context->CanBeUsedAsLiteral(dom_ref)) continue;
1106  ++num_added;
1107  context->AddImplication(ref, dom_ref);
1108 
1109  // dom-- or var++ are now forbidden.
1110  increase_is_forbidden[var] = true;
1111  increase_is_forbidden[NegationOf(dom)] = true;
1112  }
1113  }
1114  }
1115  if (num_added > 0) {
1116  VLOG(1) << "Added " << num_added << " domination implications.";
1117  context->UpdateNewConstraintsVariableUsage();
1118  context->UpdateRuleStats("domination: added implications", num_added);
1119  }
1120 
1121  return true;
1122 }
1123 
1124 } // namespace sat
1125 } // namespace operations_research
operations_research::AffineRelation::Relation::coeff
int64 coeff
Definition: affine_relation.h:78
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::DualBoundStrengthening::CannotIncrease
void CannotIncrease(absl::Span< const int > refs)
Definition: var_domination.cc:523
operations_research::sat::DualBoundStrengthening::Strengthen
bool Strengthen(PresolveContext *context)
Definition: var_domination.cc:594
min
int64 min
Definition: alldiff_cst.cc:138
operations_research::sat::DualBoundStrengthening::CannotMove
void CannotMove(absl::Span< const int > refs)
Definition: var_domination.cc:530
VLOG
#define VLOG(verboselevel)
Definition: base/logging.h:978
operations_research::sat::kNoIntegerVariable
const IntegerVariable kNoIntegerVariable(-1)
operations_research::sat::FloorRatio
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:90
max
int64 max
Definition: alldiff_cst.cc:139
bound
int64 bound
Definition: routing_search.cc:971
operations_research::sat::VarDomination::IntegerVariableToRef
static int IntegerVariableToRef(IntegerVariable var)
Definition: var_domination.h:56
operations_research::Domain::Contains
bool Contains(int64 value) const
Returns true iff value is in Domain.
Definition: sorted_interval_list.cc:221
absl::StrongVector::size
size_type size() const
Definition: strong_vector.h:147
operations_research::sat::CeilRatio
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
Definition: integer.h:81
operations_research::Domain::FlattenedIntervals
std::vector< int64 > FlattenedIntervals() const
This method returns the flattened list of interval bounds of the domain.
Definition: sorted_interval_list.cc:517
CHECK_GE
#define CHECK_GE(val1, val2)
Definition: base/logging.h:701
gtl::STLClearObject
void STLClearObject(T *obj)
Definition: stl_util.h:123
operations_research::sat::VarDomination::RefToIntegerVariable
static IntegerVariable RefToIntegerVariable(int ref)
Definition: var_domination.h:52
operations_research::sat::VarDomination
Definition: var_domination.h:45
operations_research::Domain::IsEmpty
bool IsEmpty() const
Returns true if this is the empty set.
Definition: sorted_interval_list.cc:190
operations_research::sat::VarDomination::ActivityShouldNotIncrease
void ActivityShouldNotIncrease(absl::Span< const int > enforcements, absl::Span< const int > refs, absl::Span< const int64 > coeffs)
Definition: var_domination.cc:119
value
int64 value
Definition: demon_profiler.cc:43
coeff_magnitude
Fractional coeff_magnitude
Definition: revised_simplex.cc:1803
operations_research::sat::VarDomination::Reset
void Reset(int num_variables)
Definition: var_domination.cc:21
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:27
kint64min
static const int64 kint64min
Definition: integral_types.h:60
operations_research::sat::DualBoundStrengthening::CannotDecrease
void CannotDecrease(absl::Span< const int > refs)
Definition: var_domination.cc:516
operations_research::Domain
We call domain any subset of Int64 = [kint64min, kint64max].
Definition: sorted_interval_list.h:81
operations_research::sat::PositiveVariable
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:134
int64
int64_t int64
Definition: integral_types.h:34
context
GurobiMPCallbackContext * context
Definition: gurobi_interface.cc:509
absl::StrongVector::assign
void assign(size_type n, const value_type &val)
Definition: strong_vector.h:131
operations_research::sat::PositiveRef
int PositiveRef(int ref)
Definition: cp_model_utils.h:33
operations_research::sat::VarDomination::DominationDebugString
std::string DominationDebugString(IntegerVariable var) const
Definition: var_domination.cc:504
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::sat::ExploitDominanceRelations
bool ExploitDominanceRelations(const VarDomination &var_domination, PresolveContext *context)
Definition: var_domination.cc:864
var_domination.h
operations_research::sat::VarDomination::CanOnlyDominateEachOther
void CanOnlyDominateEachOther(absl::Span< const int > refs)
Definition: var_domination.cc:50
operations_research::AffineRelation::Relation
Definition: affine_relation.h:76
operations_research::sat::kMaxIntegerValue
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
CHECK_EQ
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
ct
const Constraint * ct
Definition: demon_profiler.cc:42
operations_research::sat::VarDomination::EndSecondPhase
void EndSecondPhase()
Definition: var_domination.cc:316
operations_research::sat::VarDomination::ActivityShouldNotChange
void ActivityShouldNotChange(absl::Span< const int > refs, absl::Span< const int64 > coeffs)
Definition: var_domination.cc:60
uint64
uint64_t uint64
Definition: integral_types.h:39
operations_research::sat::NegatedRef
int NegatedRef(int ref)
Definition: cp_model_utils.h:32
CHECK_LE
#define CHECK_LE(val1, val2)
Definition: base/logging.h:699
operations_research::Domain::Min
int64 Min() const
Returns the min value of the domain.
Definition: sorted_interval_list.cc:206
operations_research::sat::PresolveContext
Definition: presolve_context.h:72
operations_research::sat::VarDomination::ActivityShouldNotDecrease
void ActivityShouldNotDecrease(absl::Span< const int > enforcements, absl::Span< const int > refs, absl::Span< const int64 > coeffs)
Definition: var_domination.cc:112
operations_research::sat::DualBoundStrengthening
Definition: var_domination.h:209
operations_research::sat::RefIsPositive
bool RefIsPositive(int ref)
Definition: cp_model_utils.h:34
absl::StrongVector< IntegerVariable, bool >
operations_research::AffineRelation::Relation::representative
int representative
Definition: affine_relation.h:77
stl_util.h
operations_research::sat::DualBoundStrengthening::Reset
void Reset(int num_variables)
Definition: var_domination.h:213
operations_research::sat::DualBoundStrengthening::ProcessLinearConstraint
void ProcessLinearConstraint(bool is_objective, const PresolveContext &context, const LinearProto &linear, int64 min_activity, int64 max_activity)
Definition: var_domination.cc:539
phase
DecisionBuilder *const phase
Definition: default_search.cc:806
delta
int64 delta
Definition: resource.cc:1684
b
int64 b
Definition: constraint_solver/table.cc:43
absl::StrongVector::resize
void resize(size_type new_size)
Definition: strong_vector.h:150
proto
CpModelProto proto
Definition: cp_model_fz_solver.cc:107
operations_research::sat::DetectDominanceRelations
void DetectDominanceRelations(const PresolveContext &context, VarDomination *var_domination, DualBoundStrengthening *dual_bound_strengthening)
Definition: var_domination.cc:677
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
operations_research::sat::VarDomination::DominatingVariables
absl::Span< const IntegerVariable > DominatingVariables(int ref) const
Definition: var_domination.cc:492
kint64max
static const int64 kint64max
Definition: integral_types.h:62
operations_research::sat::VarDomination::CanFreelyDecrease
bool CanFreelyDecrease(int ref) const
Definition: var_domination.cc:476
operations_research::sat::DualBoundStrengthening::CanFreelyDecreaseUntil
int64 CanFreelyDecreaseUntil(int ref) const
Definition: var_domination.h:235
operations_research::sat::VarDomination::EndFirstPhase
void EndFirstPhase()
Definition: var_domination.cc:189