OR-Tools  8.0
precedences.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 <memory>
18 
19 #include "ortools/base/cleanup.h"
20 #include "ortools/base/logging.h"
21 #include "ortools/base/stl_util.h"
22 #include "ortools/sat/clause.h"
24 
25 namespace operations_research {
26 namespace sat {
27 
28 namespace {
29 
30 void AppendLowerBoundReasonIfValid(IntegerVariable var,
31  const IntegerTrail& i_trail,
32  std::vector<IntegerLiteral>* reason) {
33  if (var != kNoIntegerVariable) {
34  reason->push_back(i_trail.LowerBoundAsLiteral(var));
35  }
36 }
37 
38 } // namespace
39 
41 
43  while (propagation_trail_index_ < trail_->Index()) {
44  const Literal literal = (*trail_)[propagation_trail_index_++];
45  if (literal.Index() >= literal_to_new_impacted_arcs_.size()) continue;
46 
47  // IMPORTANT: Because of the way Untrail() work, we need to add all the
48  // potential arcs before we can abort. It is why we iterate twice here.
49  for (const ArcIndex arc_index :
50  literal_to_new_impacted_arcs_[literal.Index()]) {
51  if (--arc_counts_[arc_index] == 0) {
52  const ArcInfo& arc = arcs_[arc_index];
53  impacted_arcs_[arc.tail_var].push_back(arc_index);
54  }
55  }
56 
57  // Iterate again to check for a propagation and indirectly update
58  // modified_vars_.
59  for (const ArcIndex arc_index :
60  literal_to_new_impacted_arcs_[literal.Index()]) {
61  if (arc_counts_[arc_index] > 0) continue;
62  const ArcInfo& arc = arcs_[arc_index];
63  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
64  const IntegerValue new_head_lb =
65  integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
66  if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
67  if (!EnqueueAndCheck(arc, new_head_lb, trail_)) return false;
68  }
69  }
70  }
71 
72  // Do the actual propagation of the IntegerVariable bounds.
73  InitializeBFQueueWithModifiedNodes();
74  if (!BellmanFordTarjan(trail_)) return false;
75 
76  // We can only test that no propagation is left if we didn't enqueue new
77  // literal in the presence of optional variables.
78  if (propagation_trail_index_ == trail_->Index()) {
79  DCHECK(NoPropagationLeft(*trail_));
80  }
81 
82  // Propagate the presence literals of the arcs that can't be added.
83  PropagateOptionalArcs(trail_);
84 
85  // Clean-up modified_vars_ to do as little as possible on the next call.
86  modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
87  return true;
88 }
89 
91  for (const ArcIndex arc_index : impacted_arcs_[var]) {
92  const ArcInfo& arc = arcs_[arc_index];
93  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
94  const IntegerValue new_head_lb =
95  integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
96  if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
97  if (!EnqueueAndCheck(arc, new_head_lb, trail_)) return false;
98  }
99  }
100  return true;
101 }
102 
103 void PrecedencesPropagator::Untrail(const Trail& trail, int trail_index) {
104  if (propagation_trail_index_ > trail_index) {
105  // This means that we already propagated all there is to propagate
106  // at the level trail_index, so we can safely clear modified_vars_ in case
107  // it wasn't already done.
108  modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
109  }
110  while (propagation_trail_index_ > trail_index) {
111  const Literal literal = trail[--propagation_trail_index_];
112  if (literal.Index() >= literal_to_new_impacted_arcs_.size()) continue;
113  for (const ArcIndex arc_index :
114  literal_to_new_impacted_arcs_[literal.Index()]) {
115  if (arc_counts_[arc_index]++ == 0) {
116  const ArcInfo& arc = arcs_[arc_index];
117  impacted_arcs_[arc.tail_var].pop_back();
118  }
119  }
120  }
121 }
122 
123 // Instead of simply sorting the IntegerPrecedences returned by .var,
124 // experiments showed that it is faster to regroup all the same .var "by hand"
125 // by first computing how many times they appear and then apply the sorting
126 // permutation.
128  const std::vector<IntegerVariable>& vars,
129  std::vector<IntegerPrecedences>* output) {
130  tmp_sorted_vars_.clear();
131  tmp_precedences_.clear();
132  for (int index = 0; index < vars.size(); ++index) {
133  const IntegerVariable var = vars[index];
134  CHECK_NE(kNoIntegerVariable, var);
135  if (var >= impacted_arcs_.size()) continue;
136  for (const ArcIndex arc_index : impacted_arcs_[var]) {
137  const ArcInfo& arc = arcs_[arc_index];
138  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
139 
140  IntegerValue offset = arc.offset;
141  if (arc.offset_var != kNoIntegerVariable) {
142  offset += integer_trail_->LowerBound(arc.offset_var);
143  }
144 
145  // TODO(user): it seems better to ignore negative min offset as we will
146  // often have relation of the form interval_start >= interval_end -
147  // offset, and such relation are usually not useful. Revisit this in case
148  // we see problems where we can propagate more without this test.
149  if (offset < 0) continue;
150 
151  if (var_to_degree_[arc.head_var] == 0) {
152  tmp_sorted_vars_.push_back(
153  {arc.head_var, integer_trail_->LowerBound(arc.head_var)});
154  } else {
155  // This "seen" mechanism is needed because we may have multi-arc and we
156  // don't want any duplicates in the "is_before" relation. Note that it
157  // works because var_to_last_index_ is reset by the var_to_degree_ == 0
158  // case.
159  if (var_to_last_index_[arc.head_var] == index) continue;
160  }
161  var_to_last_index_[arc.head_var] = index;
162  var_to_degree_[arc.head_var]++;
163  tmp_precedences_.push_back(
164  {index, arc.head_var, arc_index.value(), offset});
165  }
166  }
167 
168  // This order is a topological order for the precedences relation order
169  // provided that all the offset between the involved IntegerVariable are
170  // positive.
171  //
172  // TODO(user): use an order that is always topological? This is not clear
173  // since it may be slower to compute and not worth it because the order below
174  // is more natural and may work better.
175  std::sort(tmp_sorted_vars_.begin(), tmp_sorted_vars_.end());
176 
177  // Permute tmp_precedences_ into the output to put it in the correct order.
178  // For that we transform var_to_degree_ to point to the first position of
179  // each lbvar in the output vector.
180  int start = 0;
181  for (const SortedVar pair : tmp_sorted_vars_) {
182  const int degree = var_to_degree_[pair.var];
183  if (degree > 1) {
184  var_to_degree_[pair.var] = start;
185  start += degree;
186  } else {
187  // Optimization: we remove degree one relations.
188  var_to_degree_[pair.var] = -1;
189  }
190  }
191  output->resize(start);
192  for (const IntegerPrecedences& precedence : tmp_precedences_) {
193  if (var_to_degree_[precedence.var] < 0) continue;
194  (*output)[var_to_degree_[precedence.var]++] = precedence;
195  }
196 
197  // Cleanup var_to_degree_, note that we don't need to clean
198  // var_to_last_index_.
199  for (const SortedVar pair : tmp_sorted_vars_) {
200  var_to_degree_[pair.var] = 0;
201  }
202 }
203 
205  int arc_index, IntegerValue min_offset,
206  std::vector<Literal>* literal_reason,
207  std::vector<IntegerLiteral>* integer_reason) const {
208  const ArcInfo& arc = arcs_[ArcIndex(arc_index)];
209  for (const Literal l : arc.presence_literals) {
210  literal_reason->push_back(l.Negated());
211  }
212  if (arc.offset_var != kNoIntegerVariable) {
213  // Reason for ArcOffset(arc) to be >= min_offset.
214  integer_reason->push_back(IntegerLiteral::GreaterOrEqual(
215  arc.offset_var, min_offset - arc.offset));
216  }
217 }
218 
219 void PrecedencesPropagator::AdjustSizeFor(IntegerVariable i) {
220  const int index = std::max(i.value(), NegationOf(i).value());
221  if (index >= impacted_arcs_.size()) {
222  // TODO(user): only watch lower bound of the relevant variable instead
223  // of watching everything in [0, max_index_of_variable_used_in_this_class).
224  for (IntegerVariable var(impacted_arcs_.size()); var <= index; ++var) {
225  watcher_->WatchLowerBound(var, watcher_id_);
226  }
227  impacted_arcs_.resize(index + 1);
228  impacted_potential_arcs_.resize(index + 1);
229  var_to_degree_.resize(index + 1);
230  var_to_last_index_.resize(index + 1);
231  }
232 }
233 
234 void PrecedencesPropagator::AddArc(
235  IntegerVariable tail, IntegerVariable head, IntegerValue offset,
236  IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
237  DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
238  AdjustSizeFor(tail);
239  AdjustSizeFor(head);
240  if (offset_var != kNoIntegerVariable) AdjustSizeFor(offset_var);
241 
242  // This arc is present iff all the literals here are true.
243  absl::InlinedVector<Literal, 6> enforcement_literals;
244  {
245  for (const Literal l : presence_literals) {
246  enforcement_literals.push_back(l);
247  }
248  if (integer_trail_->IsOptional(tail)) {
249  enforcement_literals.push_back(
250  integer_trail_->IsIgnoredLiteral(tail).Negated());
251  }
252  if (integer_trail_->IsOptional(head)) {
253  enforcement_literals.push_back(
254  integer_trail_->IsIgnoredLiteral(head).Negated());
255  }
256  if (offset_var != kNoIntegerVariable &&
257  integer_trail_->IsOptional(offset_var)) {
258  enforcement_literals.push_back(
259  integer_trail_->IsIgnoredLiteral(offset_var).Negated());
260  }
261  gtl::STLSortAndRemoveDuplicates(&enforcement_literals);
262  int new_size = 0;
263  for (const Literal l : enforcement_literals) {
264  if (trail_->Assignment().LiteralIsTrue(Literal(l))) {
265  continue; // At true, ignore this literal.
266  } else if (trail_->Assignment().LiteralIsFalse(Literal(l))) {
267  return; // At false, ignore completely this arc.
268  }
269  enforcement_literals[new_size++] = l;
270  }
271  enforcement_literals.resize(new_size);
272  }
273 
274  if (head == tail) {
275  // A self-arc is either plain SAT or plain UNSAT or it forces something on
276  // the given offset_var or presence_literal_index. In any case it could be
277  // presolved in something more efficent.
278  VLOG(1) << "Self arc! This could be presolved. "
279  << "var:" << tail << " offset:" << offset
280  << " offset_var:" << offset_var
281  << " conditioned_by:" << presence_literals;
282  }
283 
284  // Remove the offset_var if it is fixed.
285  // TODO(user): We should also handle the case where tail or head is fixed.
286  if (offset_var != kNoIntegerVariable) {
287  const IntegerValue lb = integer_trail_->LowerBound(offset_var);
288  if (lb == integer_trail_->UpperBound(offset_var)) {
289  offset += lb;
290  offset_var = kNoIntegerVariable;
291  }
292  }
293 
294  // Deal first with impacted_potential_arcs_/potential_arcs_.
295  if (!enforcement_literals.empty()) {
296  const OptionalArcIndex arc_index(potential_arcs_.size());
297  potential_arcs_.push_back(
298  {tail, head, offset, offset_var, enforcement_literals});
299  impacted_potential_arcs_[tail].push_back(arc_index);
300  impacted_potential_arcs_[NegationOf(head)].push_back(arc_index);
301  if (offset_var != kNoIntegerVariable) {
302  impacted_potential_arcs_[offset_var].push_back(arc_index);
303  }
304  }
305 
306  // Now deal with impacted_arcs_/arcs_.
307  struct InternalArc {
308  IntegerVariable tail_var;
309  IntegerVariable head_var;
310  IntegerVariable offset_var;
311  };
312  std::vector<InternalArc> to_add;
313  if (offset_var == kNoIntegerVariable) {
314  // a + offset <= b and -b + offset <= -a
315  to_add.push_back({tail, head, kNoIntegerVariable});
316  to_add.push_back({NegationOf(head), NegationOf(tail), kNoIntegerVariable});
317  } else {
318  // tail (a) and offset_var (b) are symmetric, so we add:
319  // - a + b + offset <= c
320  to_add.push_back({tail, head, offset_var});
321  to_add.push_back({offset_var, head, tail});
322  // - a - c + offset <= -b
323  to_add.push_back({tail, NegationOf(offset_var), NegationOf(head)});
324  to_add.push_back({NegationOf(head), NegationOf(offset_var), tail});
325  // - b - c + offset <= -a
326  to_add.push_back({offset_var, NegationOf(tail), NegationOf(head)});
327  to_add.push_back({NegationOf(head), NegationOf(tail), offset_var});
328  }
329  for (const InternalArc a : to_add) {
330  // Since we add a new arc, we will need to consider its tail during the next
331  // propagation. Note that the size of modified_vars_ will be automatically
332  // updated when new integer variables are created since we register it with
333  // IntegerTrail in this class contructor.
334  //
335  // TODO(user): Adding arcs and then calling Untrail() before Propagate()
336  // will cause this mecanism to break. Find a more robust implementation.
337  //
338  // TODO(user): In some rare corner case, rescanning the whole list of arc
339  // leaving tail_var can make AddVar() have a quadratic complexity where it
340  // shouldn't. A better solution would be to see if this new arc currently
341  // propagate something, and if it does, just update the lower bound of
342  // a.head_var and let the normal "is modified" mecanism handle any eventual
343  // follow up propagations.
344  modified_vars_.Set(a.tail_var);
345 
346  // If a.head_var is optional, we can potentially remove some literal from
347  // enforcement_literals.
348  const ArcIndex arc_index(arcs_.size());
349  arcs_.push_back(
350  {a.tail_var, a.head_var, offset, a.offset_var, enforcement_literals});
351  auto& presence_literals = arcs_.back().presence_literals;
352  if (integer_trail_->IsOptional(a.head_var)) {
353  // TODO(user): More generally, we can remove any literal that is implied
354  // by to_remove.
355  const Literal to_remove =
356  integer_trail_->IsIgnoredLiteral(a.head_var).Negated();
357  const auto it = std::find(presence_literals.begin(),
358  presence_literals.end(), to_remove);
359  if (it != presence_literals.end()) presence_literals.erase(it);
360  }
361 
362  if (presence_literals.empty()) {
363  impacted_arcs_[a.tail_var].push_back(arc_index);
364  } else {
365  for (const Literal l : presence_literals) {
366  if (l.Index() >= literal_to_new_impacted_arcs_.size()) {
367  literal_to_new_impacted_arcs_.resize(l.Index().value() + 1);
368  }
369  literal_to_new_impacted_arcs_[l.Index()].push_back(arc_index);
370  }
371  }
372  arc_counts_.push_back(presence_literals.size());
373  }
374 }
375 
376 // TODO(user): On jobshop problems with a lot of tasks per machine (500), this
377 // takes up a big chunck of the running time even before we find a solution.
378 // This is because, for each lower bound changed, we inspect 500 arcs even
379 // though they will never be propagated because the other bound is still at the
380 // horizon. Find an even sparser algorithm?
381 void PrecedencesPropagator::PropagateOptionalArcs(Trail* trail) {
382  for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
383  if (var >= impacted_potential_arcs_.size()) break;
384 
385  // Note that we can currently check the same ArcInfo up to 3 times, one for
386  // each of the arc variables: tail, NegationOf(head) and offset_var.
387  for (const OptionalArcIndex arc_index : impacted_potential_arcs_[var]) {
388  const ArcInfo& arc = potential_arcs_[arc_index];
389  int num_not_true = 0;
390  Literal to_propagate;
391  for (const Literal l : arc.presence_literals) {
392  if (!trail->Assignment().LiteralIsTrue(l)) {
393  ++num_not_true;
394  to_propagate = l;
395  }
396  }
397  if (num_not_true != 1) continue;
398  if (trail->Assignment().LiteralIsFalse(to_propagate)) continue;
399 
400  // Test if this arc can be present or not.
401  // Important arc.tail_var can be different from var here.
402  const IntegerValue tail_lb = integer_trail_->LowerBound(arc.tail_var);
403  const IntegerValue head_ub = integer_trail_->UpperBound(arc.head_var);
404  if (tail_lb + ArcOffset(arc) > head_ub) {
405  integer_reason_.clear();
406  integer_reason_.push_back(
407  integer_trail_->LowerBoundAsLiteral(arc.tail_var));
408  integer_reason_.push_back(
409  integer_trail_->UpperBoundAsLiteral(arc.head_var));
410  AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
411  &integer_reason_);
412  literal_reason_.clear();
413  for (const Literal l : arc.presence_literals) {
414  if (l != to_propagate) literal_reason_.push_back(l.Negated());
415  }
416  integer_trail_->EnqueueLiteral(to_propagate.Negated(), literal_reason_,
417  integer_reason_);
418  }
419  }
420  }
421 }
422 
423 IntegerValue PrecedencesPropagator::ArcOffset(const ArcInfo& arc) const {
424  return arc.offset + (arc.offset_var == kNoIntegerVariable
425  ? IntegerValue(0)
426  : integer_trail_->LowerBound(arc.offset_var));
427 }
428 
429 bool PrecedencesPropagator::EnqueueAndCheck(const ArcInfo& arc,
430  IntegerValue new_head_lb,
431  Trail* trail) {
432  DCHECK_GT(new_head_lb, integer_trail_->LowerBound(arc.head_var));
433 
434  // Compute the reason for new_head_lb.
435  //
436  // TODO(user): do like for clause and keep the negation of
437  // arc.presence_literals? I think we could change the integer.h API to accept
438  // true literal like for IntegerVariable, it is really confusing currently.
439  literal_reason_.clear();
440  for (const Literal l : arc.presence_literals) {
441  literal_reason_.push_back(l.Negated());
442  }
443 
444  integer_reason_.clear();
445  integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(arc.tail_var));
446  AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
447  &integer_reason_);
448 
449  // The code works without this block since Enqueue() below can already take
450  // care of conflicts. However, it is better to deal with the conflict
451  // ourselves because we can be smarter about the reason this way.
452  //
453  // The reason for a "precedence" conflict is always a linear reason
454  // involving the tail lower_bound, the head upper bound and eventually the
455  // size lower bound. Because of that, we can use the RelaxLinearReason()
456  // code.
457  if (new_head_lb > integer_trail_->UpperBound(arc.head_var)) {
458  const IntegerValue slack =
459  new_head_lb - integer_trail_->UpperBound(arc.head_var) - 1;
460  integer_reason_.push_back(
461  integer_trail_->UpperBoundAsLiteral(arc.head_var));
462  std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
463  integer_trail_->RelaxLinearReason(slack, coeffs, &integer_reason_);
464 
465  if (!integer_trail_->IsOptional(arc.head_var)) {
466  return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
467  } else {
468  CHECK(!integer_trail_->IsCurrentlyIgnored(arc.head_var));
469  const Literal l = integer_trail_->IsIgnoredLiteral(arc.head_var);
470  if (trail->Assignment().LiteralIsFalse(l)) {
471  literal_reason_.push_back(l);
472  return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
473  } else {
474  integer_trail_->EnqueueLiteral(l, literal_reason_, integer_reason_);
475  return true;
476  }
477  }
478  }
479 
480  return integer_trail_->Enqueue(
481  IntegerLiteral::GreaterOrEqual(arc.head_var, new_head_lb),
482  literal_reason_, integer_reason_);
483 }
484 
485 bool PrecedencesPropagator::NoPropagationLeft(const Trail& trail) const {
486  const int num_nodes = impacted_arcs_.size();
487  for (IntegerVariable var(0); var < num_nodes; ++var) {
488  for (const ArcIndex arc_index : impacted_arcs_[var]) {
489  const ArcInfo& arc = arcs_[arc_index];
490  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
491  if (integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc) >
492  integer_trail_->LowerBound(arc.head_var)) {
493  return false;
494  }
495  }
496  }
497  return true;
498 }
499 
500 void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
501  // Sparse clear of the queue. TODO(user): only use the sparse version if
502  // queue.size() is small or use SparseBitset.
503  const int num_nodes = impacted_arcs_.size();
504  bf_in_queue_.resize(num_nodes, false);
505  for (const int node : bf_queue_) bf_in_queue_[node] = false;
506  bf_queue_.clear();
507  DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
508  [](bool v) { return v; }));
509  for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
510  if (var >= num_nodes) continue;
511  bf_queue_.push_back(var.value());
512  bf_in_queue_[var.value()] = true;
513  }
514 }
515 
516 void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
517  // To be sparse, we use the fact that each node with a parent must be in
518  // modified_vars_.
519  const int num_nodes = impacted_arcs_.size();
520  for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
521  if (var >= num_nodes) continue;
522  const ArcIndex parent_arc_index = bf_parent_arc_of_[var.value()];
523  if (parent_arc_index != -1) {
524  arcs_[parent_arc_index].is_marked = false;
525  bf_parent_arc_of_[var.value()] = -1;
526  bf_can_be_skipped_[var.value()] = false;
527  }
528  }
529  DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
530  [](ArcIndex v) { return v != -1; }));
531  DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
532  [](bool v) { return v; }));
533 }
534 
535 bool PrecedencesPropagator::DisassembleSubtree(
536  int source, int target, std::vector<bool>* can_be_skipped) {
537  // Note that we explore a tree, so we can do it in any order, and the one
538  // below seems to be the fastest.
539  tmp_vector_.clear();
540  tmp_vector_.push_back(source);
541  while (!tmp_vector_.empty()) {
542  const int tail = tmp_vector_.back();
543  tmp_vector_.pop_back();
544  for (const ArcIndex arc_index : impacted_arcs_[IntegerVariable(tail)]) {
545  const ArcInfo& arc = arcs_[arc_index];
546  if (arc.is_marked) {
547  arc.is_marked = false; // mutable.
548  if (arc.head_var.value() == target) return true;
549  DCHECK(!(*can_be_skipped)[arc.head_var.value()]);
550  (*can_be_skipped)[arc.head_var.value()] = true;
551  tmp_vector_.push_back(arc.head_var.value());
552  }
553  }
554  }
555  return false;
556 }
557 
558 void PrecedencesPropagator::AnalyzePositiveCycle(
559  ArcIndex first_arc, Trail* trail, std::vector<Literal>* must_be_all_true,
560  std::vector<Literal>* literal_reason,
561  std::vector<IntegerLiteral>* integer_reason) {
562  must_be_all_true->clear();
563  literal_reason->clear();
564  integer_reason->clear();
565 
566  // Follow bf_parent_arc_of_[] to find the cycle containing first_arc.
567  const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
568  ArcIndex arc_index = first_arc;
569  std::vector<ArcIndex> arc_on_cycle;
570 
571  // Just to be safe and avoid an infinite loop we use the fact that the maximum
572  // cycle size on a graph with n nodes is of size n. If we have more in the
573  // code below, it means first_arc is not part of a cycle according to
574  // bf_parent_arc_of_[], which should never happen.
575  const int num_nodes = impacted_arcs_.size();
576  while (arc_on_cycle.size() <= num_nodes) {
577  arc_on_cycle.push_back(arc_index);
578  const ArcInfo& arc = arcs_[arc_index];
579  if (arc.tail_var == first_arc_head) break;
580  arc_index = bf_parent_arc_of_[arc.tail_var.value()];
581  CHECK_NE(arc_index, ArcIndex(-1));
582  }
583  CHECK_NE(arc_on_cycle.size(), num_nodes + 1) << "Infinite loop.";
584 
585  // Compute the reason for this cycle.
586  IntegerValue sum(0);
587  for (const ArcIndex arc_index : arc_on_cycle) {
588  const ArcInfo& arc = arcs_[arc_index];
589  sum += ArcOffset(arc);
590  AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
591  integer_reason);
592  for (const Literal l : arc.presence_literals) {
593  literal_reason->push_back(l.Negated());
594  }
595 
596  // If the cycle happens to contain optional variable not yet ignored, then
597  // it is not a conflict anymore, but we can infer that these variable must
598  // all be ignored. This is because since we propagated them even if they
599  // where not present for sure, their presence literal must form a cycle
600  // together (i.e. they are all absent or present at the same time).
601  if (integer_trail_->IsOptional(arc.head_var)) {
602  must_be_all_true->push_back(
603  integer_trail_->IsIgnoredLiteral(arc.head_var));
604  }
605  }
606 
607  // TODO(user): what if the sum overflow? this is just a check so I guess
608  // we don't really care, but fix the issue.
609  CHECK_GT(sum, 0);
610 }
611 
612 // Note that in our settings it is important to use an algorithm that tries to
613 // minimize the number of integer_trail_->Enqueue() as much as possible.
614 //
615 // TODO(user): The current algorithm is quite efficient, but there is probably
616 // still room for improvments.
617 bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) {
618  const int num_nodes = impacted_arcs_.size();
619 
620  // These vector are reset by CleanUpMarkedArcsAndParents() so resize is ok.
621  bf_can_be_skipped_.resize(num_nodes, false);
622  bf_parent_arc_of_.resize(num_nodes, ArcIndex(-1));
623  const auto cleanup =
624  ::absl::MakeCleanup([this]() { CleanUpMarkedArcsAndParents(); });
625 
626  // The queue initialization is done by InitializeBFQueueWithModifiedNodes().
627  while (!bf_queue_.empty()) {
628  const int node = bf_queue_.front();
629  bf_queue_.pop_front();
630  bf_in_queue_[node] = false;
631 
632  // TODO(user): we don't need bf_can_be_skipped_ since we can detect this
633  // if this node has a parent arc which is not marked. Investigate if it is
634  // faster without the vector<bool>.
635  //
636  // TODO(user): An alternative algorithm is to remove all these nodes from
637  // the queue instead of simply marking them. This should also lead to a
638  // better "relaxation" order of the arcs. It is however a bit more work to
639  // remove them since we need to track their position.
640  if (bf_can_be_skipped_[node]) {
641  DCHECK_NE(bf_parent_arc_of_[node], -1);
642  DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
643  continue;
644  }
645 
646  const IntegerValue tail_lb =
647  integer_trail_->LowerBound(IntegerVariable(node));
648  for (const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
649  const ArcInfo& arc = arcs_[arc_index];
650  DCHECK_EQ(arc.tail_var, node);
651  const IntegerValue candidate = tail_lb + ArcOffset(arc);
652  if (candidate > integer_trail_->LowerBound(arc.head_var)) {
653  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
654  if (!EnqueueAndCheck(arc, candidate, trail)) return false;
655 
656  // This is the Tarjan contribution to Bellman-Ford. This code detect
657  // positive cycle, and because it disassemble the subtree while doing
658  // so, the cost is amortized during the algorithm execution. Another
659  // advantages is that it will mark the node explored here as skippable
660  // which will avoid to propagate them too early (knowing that they will
661  // need to be propagated again later).
662  if (DisassembleSubtree(arc.head_var.value(), arc.tail_var.value(),
663  &bf_can_be_skipped_)) {
664  std::vector<Literal> must_be_all_true;
665  AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
666  &literal_reason_, &integer_reason_);
667  if (must_be_all_true.empty()) {
668  return integer_trail_->ReportConflict(literal_reason_,
669  integer_reason_);
670  } else {
671  gtl::STLSortAndRemoveDuplicates(&must_be_all_true);
672  for (const Literal l : must_be_all_true) {
673  if (trail_->Assignment().LiteralIsFalse(l)) {
674  literal_reason_.push_back(l);
675  return integer_trail_->ReportConflict(literal_reason_,
676  integer_reason_);
677  }
678  }
679  for (const Literal l : must_be_all_true) {
680  if (trail_->Assignment().LiteralIsTrue(l)) continue;
681  integer_trail_->EnqueueLiteral(l, literal_reason_,
682  integer_reason_);
683  }
684 
685  // We just marked some optional variable as ignored, no need
686  // to update bf_parent_arc_of_[].
687  continue;
688  }
689  }
690 
691  // We need to enforce the invariant that only the arc_index in
692  // bf_parent_arc_of_[] are marked (but not necessarily all of them
693  // since we unmark some in DisassembleSubtree()).
694  if (bf_parent_arc_of_[arc.head_var.value()] != -1) {
695  arcs_[bf_parent_arc_of_[arc.head_var.value()]].is_marked = false;
696  }
697 
698  // Tricky: We just enqueued the fact that the lower-bound of head is
699  // candidate. However, because the domain of head may be discrete, it is
700  // possible that the lower-bound of head is now higher than candidate!
701  // If this is the case, we don't update bf_parent_arc_of_[] so that we
702  // don't wrongly detect a positive weight cycle because of this "extra
703  // push".
704  if (integer_trail_->LowerBound(arc.head_var) == candidate) {
705  bf_parent_arc_of_[arc.head_var.value()] = arc_index;
706  arcs_[arc_index].is_marked = true;
707  } else {
708  // We still unmark any previous dependency, since we have pushed the
709  // value of arc.head_var further.
710  bf_parent_arc_of_[arc.head_var.value()] = -1;
711  }
712 
713  bf_can_be_skipped_[arc.head_var.value()] = false;
714  if (!bf_in_queue_[arc.head_var.value()]) {
715  bf_queue_.push_back(arc.head_var.value());
716  bf_in_queue_[arc.head_var.value()] = true;
717  }
718  }
719  }
720  }
721  return true;
722 }
723 
724 int PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraintsFromClause(
725  const absl::Span<const Literal> clause, Model* model) {
726  CHECK_EQ(model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
727  if (clause.size() < 2) return 0;
728 
729  // Collect all arcs impacted by this clause.
730  std::vector<ArcInfo> infos;
731  for (const Literal l : clause) {
732  if (l.Index() >= literal_to_new_impacted_arcs_.size()) continue;
733  for (const ArcIndex arc_index : literal_to_new_impacted_arcs_[l.Index()]) {
734  const ArcInfo& arc = arcs_[arc_index];
735  if (arc.presence_literals.size() != 1) continue;
736 
737  // TODO(user): Support variable offset.
738  if (arc.offset_var != kNoIntegerVariable) continue;
739  infos.push_back(arc);
740  }
741  }
742  if (infos.size() <= 1) return 0;
743 
744  // Stable sort by head_var so that for a same head_var, the entry are sorted
745  // by Literal as they appear in clause.
746  std::stable_sort(infos.begin(), infos.end(),
747  [](const ArcInfo& a, const ArcInfo& b) {
748  return a.head_var < b.head_var;
749  });
750 
751  // We process ArcInfo with the same head_var toghether.
752  int num_added_constraints = 0;
753  auto* solver = model->GetOrCreate<SatSolver>();
754  for (int i = 0; i < infos.size();) {
755  const int start = i;
756  const IntegerVariable head_var = infos[start].head_var;
757  for (i++; i < infos.size() && infos[i].head_var == head_var; ++i) {
758  }
759  const absl::Span<ArcInfo> arcs(&infos[start], i - start);
760 
761  // Skip single arcs since it will already be fully propagated.
762  if (arcs.size() < 2) continue;
763 
764  // Heuristic. Look for full or almost full clauses. We could add
765  // GreaterThanAtLeastOneOf() with more enforcement literals. TODO(user):
766  // experiments.
767  if (arcs.size() + 1 < clause.size()) continue;
768 
769  std::vector<IntegerVariable> vars;
770  std::vector<IntegerValue> offsets;
771  std::vector<Literal> selectors;
772  std::vector<Literal> enforcements;
773 
774  int j = 0;
775  for (const Literal l : clause) {
776  bool added = false;
777  for (; j < arcs.size() && l == arcs[j].presence_literals.front(); ++j) {
778  added = true;
779  vars.push_back(arcs[j].tail_var);
780  offsets.push_back(arcs[j].offset);
781 
782  // Note that duplicate selector are supported.
783  //
784  // TODO(user): If we support variable offset, we should regroup the arcs
785  // into one (tail + offset <= head) though, instead of having too
786  // identical entries.
787  selectors.push_back(l);
788  }
789  if (!added) {
790  enforcements.push_back(l.Negated());
791  }
792  }
793 
794  // No point adding a constraint if there is not at least two different
795  // literals in selectors.
796  if (enforcements.size() + 1 == clause.size()) continue;
797 
798  ++num_added_constraints;
799  model->Add(GreaterThanAtLeastOneOf(head_var, vars, offsets, selectors,
800  enforcements));
801  if (!solver->FinishPropagation()) return num_added_constraints;
802  }
803  return num_added_constraints;
804 }
805 
806 int PrecedencesPropagator::
807  AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(Model* model) {
808  auto* time_limit = model->GetOrCreate<TimeLimit>();
809  auto* solver = model->GetOrCreate<SatSolver>();
810 
811  // Fill the set of incoming conditional arcs for each variables.
813  for (ArcIndex arc_index(0); arc_index < arcs_.size(); ++arc_index) {
814  const ArcInfo& arc = arcs_[arc_index];
815 
816  // Only keep arc that have a fixed offset and a single presence_literals.
817  if (arc.offset_var != kNoIntegerVariable) continue;
818  if (arc.tail_var == arc.head_var) continue;
819  if (arc.presence_literals.size() != 1) continue;
820 
821  if (arc.head_var >= incoming_arcs_.size()) {
822  incoming_arcs_.resize(arc.head_var.value() + 1);
823  }
824  incoming_arcs_[arc.head_var].push_back(arc_index);
825  }
826 
827  int num_added_constraints = 0;
828  for (IntegerVariable target(0); target < incoming_arcs_.size(); ++target) {
829  if (incoming_arcs_[target].size() <= 1) continue;
830  if (time_limit->LimitReached()) return num_added_constraints;
831 
832  // Detect set of incoming arcs for which at least one must be present.
833  // TODO(user): Find more than one disjoint set of incoming arcs.
834  // TODO(user): call MinimizeCoreWithPropagation() on the clause.
835  solver->Backtrack(0);
836  if (solver->IsModelUnsat()) return num_added_constraints;
837  std::vector<Literal> clause;
838  for (const ArcIndex arc_index : incoming_arcs_[target]) {
839  const Literal literal = arcs_[arc_index].presence_literals.front();
840  if (solver->Assignment().LiteralIsFalse(literal)) continue;
841 
842  const int old_level = solver->CurrentDecisionLevel();
843  solver->EnqueueDecisionAndBacktrackOnConflict(literal.Negated());
844  if (solver->IsModelUnsat()) return num_added_constraints;
845  const int new_level = solver->CurrentDecisionLevel();
846  if (new_level <= old_level) {
847  clause = solver->GetLastIncompatibleDecisions();
848  break;
849  }
850  }
851  solver->Backtrack(0);
852 
853  if (clause.size() > 1) {
854  // Extract the set of arc for which at least one must be present.
855  const std::set<Literal> clause_set(clause.begin(), clause.end());
856  std::vector<ArcIndex> arcs_in_clause;
857  for (const ArcIndex arc_index : incoming_arcs_[target]) {
858  const Literal literal(arcs_[arc_index].presence_literals.front());
859  if (gtl::ContainsKey(clause_set, literal.Negated())) {
860  arcs_in_clause.push_back(arc_index);
861  }
862  }
863 
864  VLOG(2) << arcs_in_clause.size() << "/" << incoming_arcs_[target].size();
865 
866  ++num_added_constraints;
867  std::vector<IntegerVariable> vars;
868  std::vector<IntegerValue> offsets;
869  std::vector<Literal> selectors;
870  for (const ArcIndex a : arcs_in_clause) {
871  vars.push_back(arcs_[a].tail_var);
872  offsets.push_back(arcs_[a].offset);
873  selectors.push_back(Literal(arcs_[a].presence_literals.front()));
874  }
875  model->Add(GreaterThanAtLeastOneOf(target, vars, offsets, selectors));
876  if (!solver->FinishPropagation()) return num_added_constraints;
877  }
878  }
879 
880  return num_added_constraints;
881 }
882 
884  VLOG(1) << "Detecting GreaterThanAtLeastOneOf() constraints...";
885  auto* time_limit = model->GetOrCreate<TimeLimit>();
886  auto* solver = model->GetOrCreate<SatSolver>();
887  auto* clauses = model->GetOrCreate<LiteralWatchers>();
888  int num_added_constraints = 0;
889 
890  // We have two possible approaches. For now, we prefer the first one except if
891  // there is too many clauses in the problem.
892  //
893  // TODO(user): Do more extensive experiment. Remove the second approach as
894  // it is more time consuming? or identify when it make sense. Note that the
895  // first approach also allows to use "incomplete" at least one between arcs.
896  if (clauses->AllClausesInCreationOrder().size() < 1e6) {
897  // TODO(user): This does not take into account clause of size 2 since they
898  // are stored in the BinaryImplicationGraph instead. Some ideas specific
899  // to size 2:
900  // - There can be a lot of such clauses, but it might be nice to consider
901  // them. we need to experiments.
902  // - The automatic clause detection might be a better approach and it
903  // could be combined with probing.
904  for (const SatClause* clause : clauses->AllClausesInCreationOrder()) {
905  if (time_limit->LimitReached()) return num_added_constraints;
906  if (solver->IsModelUnsat()) return num_added_constraints;
907  num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
908  clause->AsSpan(), model);
909  }
910  } else {
911  num_added_constraints +=
912  AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(model);
913  }
914 
915  VLOG(1) << "Added " << num_added_constraints
916  << " GreaterThanAtLeastOneOf() constraints.";
917  return num_added_constraints;
918 }
919 
920 } // namespace sat
921 } // namespace operations_research
operations_research::sat::Trail
Definition: sat_base.h:233
operations_research::sat::LiteralWatchers
Definition: clause.h:159
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::VariablesAssignment::LiteralIsTrue
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:150
tail
int64 tail
Definition: routing_flow.cc:127
operations_research::sat::LowerBound
std::function< int64(const Model &)> LowerBound(IntegerVariable v)
Definition: integer.h:1376
operations_research::SparseBitset::ClearAndResize
void ClearAndResize(IntegerType size)
Definition: bitset.h:780
operations_research::sat::IntegerLiteral::GreaterOrEqual
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1197
operations_research::sat::kNoIntegerVariable
const IntegerVariable kNoIntegerVariable(-1)
max
int64 max
Definition: alldiff_cst.cc:139
operations_research::sat::IntegerTrail::IsIgnoredLiteral
Literal IsIgnoredLiteral(IntegerVariable i) const
Definition: integer.h:617
operations_research::sat::IntegerTrail::IsOptional
bool IsOptional(IntegerVariable i) const
Definition: integer.h:609
operations_research::sat::PrecedencesPropagator::IntegerPrecedences
Definition: precedences.h:111
operations_research::SparseBitset::Set
void Set(IntegerType index)
Definition: bitset.h:805
operations_research::sat::IntegerTrail::UpperBound
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1221
gtl::ITIVector::front
reference front()
Definition: int_type_indexed_vector.h:171
logging.h
value
int64 value
Definition: demon_profiler.cc:43
gtl::ITIVector::resize
void resize(size_type new_size)
Definition: int_type_indexed_vector.h:149
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::Trail::Index
int Index() const
Definition: sat_base.h:378
cleanup.h
operations_research::sat::NegationOf
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:42
operations_research::sat::PrecedencesPropagator::Untrail
void Untrail(const Trail &trail, int trail_index) final
Definition: precedences.cc:103
operations_research::sat::IntegerTrail::Enqueue
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:965
operations_research::sat::IntegerTrail::IsCurrentlyIgnored
bool IsCurrentlyIgnored(IntegerVariable i) const
Definition: integer.h:612
operations_research::sat::PrecedencesPropagator::PropagateOutgoingArcs
bool PropagateOutgoingArcs(IntegerVariable var)
Definition: precedences.cc:90
operations_research::sat::PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraints
int AddGreaterThanAtLeastOneOfConstraints(Model *model)
Definition: precedences.cc:883
gtl::ITIVector::size
size_type size() const
Definition: int_type_indexed_vector.h:146
absl::MakeCleanup
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Definition: cleanup.h:120
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
index
int index
Definition: pack.cc:508
operations_research::sat::SatSolver
Definition: sat_solver.h:58
clause.h
operations_research::sat::Trail::CurrentDecisionLevel
int CurrentDecisionLevel() const
Definition: sat_base.h:355
operations_research::sat::Literal
Definition: sat_base.h:64
a
int64 a
Definition: constraint_solver/table.cc:42
operations_research::sat::Literal::Negated
Literal Negated() const
Definition: sat_base.h:91
operations_research::sat::SatClause
Definition: clause.h:50
time_limit
SharedTimeLimit * time_limit
Definition: cp_model_solver.cc:2025
precedences.h
operations_research::TimeLimit
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:105
operations_research::sat::SatPropagator::propagation_trail_index_
int propagation_trail_index_
Definition: sat_base.h:506
gtl::STLSortAndRemoveDuplicates
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
gtl::ITIVector::pop_back
void pop_back()
Definition: int_type_indexed_vector.h:167
operations_research::sat::IntegerTrail::RelaxLinearReason
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:773
operations_research::sat::IntegerTrail::UpperBoundAsLiteral
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1252
operations_research::sat::Trail::Assignment
const VariablesAssignment & Assignment() const
Definition: sat_base.h:380
operations_research::sat::IntegerTrail::NumIntegerVariables
IntegerVariable NumIntegerVariables() const
Definition: integer.h:558
operations_research::SparseBitset::PositionsSetAtLeastOnce
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:815
operations_research::sat::IntegerTrail::ReportConflict
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.h:784
cp_constraints.h
operations_research::sat::IntegerTrail::LowerBoundAsLiteral
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1247
operations_research::ArcIndex
int32 ArcIndex
Definition: ebert_graph.h:201
operations_research::sat::PrecedencesPropagator::AddPrecedenceReason
void AddPrecedenceReason(int arc_index, IntegerValue min_offset, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
Definition: precedences.cc:204
model
GRBmodel * model
Definition: gurobi_interface.cc:195
operations_research::sat::IntegerTrail::LowerBound
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1217
stl_util.h
operations_research::sat::VariablesAssignment::LiteralIsFalse
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:147
operations_research::sat::IntegerTrail::EnqueueLiteral
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1034
b
int64 b
Definition: constraint_solver/table.cc:43
operations_research::sat::PrecedencesPropagator::ComputePrecedences
void ComputePrecedences(const std::vector< IntegerVariable > &vars, std::vector< IntegerPrecedences > *output)
Definition: precedences.cc:127
gtl::ITIVector
Definition: int_type_indexed_vector.h:76
operations_research::glop::Index
int32 Index
Definition: lp_types.h:37
gtl::ITIVector::push_back
void push_back(const value_type &x)
Definition: int_type_indexed_vector.h:157
head
int64 head
Definition: routing_flow.cc:128
literal
Literal literal
Definition: optimization.cc:84
operations_research::sat::GenericLiteralWatcher::WatchLowerBound
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
Definition: integer.h:1285
gtl::ITIVector::back
reference back()
Definition: int_type_indexed_vector.h:173
operations_research::sat::GreaterThanAtLeastOneOf
std::function< void(Model *)> GreaterThanAtLeastOneOf(IntegerVariable target_var, const absl::Span< const IntegerVariable > vars, const absl::Span< const IntegerValue > offsets, const absl::Span< const Literal > selectors)
Definition: cp_constraints.h:123
operations_research::sat::PrecedencesPropagator::Propagate
bool Propagate() final
Definition: precedences.cc:42
gtl::ContainsKey
bool ContainsKey(const Collection &collection, const Key &key)
Definition: map_util.h:170