OR-Tools  9.6
precedences.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <deque>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/container/btree_set.h"
23 #include "absl/container/inlined_vector.h"
24 #include "absl/types/span.h"
25 #include "ortools/base/cleanup.h"
26 #include "ortools/base/logging.h"
27 #include "ortools/base/stl_util.h"
30 #include "ortools/sat/clause.h"
32 #include "ortools/sat/integer.h"
33 #include "ortools/sat/model.h"
34 #include "ortools/sat/sat_base.h"
35 #include "ortools/sat/sat_solver.h"
36 #include "ortools/util/bitset.h"
39 
40 namespace operations_research {
41 namespace sat {
42 
43 namespace {
44 
45 void AppendLowerBoundReasonIfValid(IntegerVariable var,
46  const IntegerTrail& i_trail,
47  std::vector<IntegerLiteral>* reason) {
48  if (var != kNoIntegerVariable) {
49  reason->push_back(i_trail.LowerBoundAsLiteral(var));
50  }
51 }
52 
53 } // namespace
54 
56  if (!VLOG_IS_ON(1)) return;
57  if (shared_stats_ == nullptr) return;
58  std::vector<std::pair<std::string, int64_t>> stats;
59  stats.push_back({"precedences/num_cycles", num_cycles_});
60  stats.push_back({"precedences/num_pushes", num_pushes_});
61  stats.push_back(
62  {"precedences/num_enforcement_pushes", num_enforcement_pushes_});
63  shared_stats_->AddStats(stats);
64 }
65 
67 
69  while (propagation_trail_index_ < trail_->Index()) {
70  const Literal literal = (*trail_)[propagation_trail_index_++];
71  if (literal.Index() >= literal_to_new_impacted_arcs_.size()) continue;
72 
73  // IMPORTANT: Because of the way Untrail() work, we need to add all the
74  // potential arcs before we can abort. It is why we iterate twice here.
75  for (const ArcIndex arc_index :
76  literal_to_new_impacted_arcs_[literal.Index()]) {
77  if (--arc_counts_[arc_index] == 0) {
78  const ArcInfo& arc = arcs_[arc_index];
79  impacted_arcs_[arc.tail_var].push_back(arc_index);
80  }
81  }
82 
83  // Iterate again to check for a propagation and indirectly update
84  // modified_vars_.
85  for (const ArcIndex arc_index :
86  literal_to_new_impacted_arcs_[literal.Index()]) {
87  if (arc_counts_[arc_index] > 0) continue;
88  const ArcInfo& arc = arcs_[arc_index];
89  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
90  const IntegerValue new_head_lb =
91  integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
92  if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
93  if (!EnqueueAndCheck(arc, new_head_lb, trail_)) return false;
94  }
95  }
96  }
97 
98  // Do the actual propagation of the IntegerVariable bounds.
99  InitializeBFQueueWithModifiedNodes();
100  if (!BellmanFordTarjan(trail_)) return false;
101 
102  // We can only test that no propagation is left if we didn't enqueue new
103  // literal in the presence of optional variables.
104  //
105  // TODO(user): Because of our code to deal with InPropagationLoop(), this is
106  // not always true. Find a cleaner way to DCHECK() while not failing in this
107  // corner case.
108  if (/*DISABLES CODE*/ (false) &&
109  propagation_trail_index_ == trail_->Index()) {
110  DCHECK(NoPropagationLeft(*trail_));
111  }
112 
113  // Propagate the presence literals of the arcs that can't be added.
114  PropagateOptionalArcs(trail_);
115 
116  // Clean-up modified_vars_ to do as little as possible on the next call.
117  modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
118  return true;
119 }
120 
122  CHECK_NE(var, kNoIntegerVariable);
123  if (var >= impacted_arcs_.size()) return true;
124  for (const ArcIndex arc_index : impacted_arcs_[var]) {
125  const ArcInfo& arc = arcs_[arc_index];
126  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
127  const IntegerValue new_head_lb =
128  integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc);
129  if (new_head_lb > integer_trail_->LowerBound(arc.head_var)) {
130  if (!EnqueueAndCheck(arc, new_head_lb, trail_)) return false;
131  }
132  }
133  return true;
134 }
135 
136 void PrecedencesPropagator::Untrail(const Trail& trail, int trail_index) {
137  if (propagation_trail_index_ > trail_index) {
138  // This means that we already propagated all there is to propagate
139  // at the level trail_index, so we can safely clear modified_vars_ in case
140  // it wasn't already done.
141  modified_vars_.ClearAndResize(integer_trail_->NumIntegerVariables());
142  }
143  while (propagation_trail_index_ > trail_index) {
144  const Literal literal = trail[--propagation_trail_index_];
145  if (literal.Index() >= literal_to_new_impacted_arcs_.size()) continue;
146  for (const ArcIndex arc_index :
147  literal_to_new_impacted_arcs_[literal.Index()]) {
148  if (arc_counts_[arc_index]++ == 0) {
149  const ArcInfo& arc = arcs_[arc_index];
150  impacted_arcs_[arc.tail_var].pop_back();
151  }
152  }
153  }
154 }
155 
156 // Instead of simply sorting the IntegerPrecedences returned by .var,
157 // experiments showed that it is faster to regroup all the same .var "by hand"
158 // by first computing how many times they appear and then apply the sorting
159 // permutation.
161  const std::vector<IntegerVariable>& vars,
162  std::vector<IntegerPrecedences>* output) {
163  tmp_sorted_vars_.clear();
164  tmp_precedences_.clear();
165  for (int index = 0; index < vars.size(); ++index) {
166  const IntegerVariable var = vars[index];
167  CHECK_NE(kNoIntegerVariable, var);
168  if (var >= impacted_arcs_.size()) continue;
169  for (const ArcIndex arc_index : impacted_arcs_[var]) {
170  const ArcInfo& arc = arcs_[arc_index];
171  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
172 
173  IntegerValue offset = arc.offset;
174  if (arc.offset_var != kNoIntegerVariable) {
175  offset += integer_trail_->LowerBound(arc.offset_var);
176  }
177 
178  // TODO(user): it seems better to ignore negative min offset as we will
179  // often have relation of the form interval_start >= interval_end -
180  // offset, and such relation are usually not useful. Revisit this in case
181  // we see problems where we can propagate more without this test.
182  if (offset < 0) continue;
183 
184  if (var_to_degree_[arc.head_var] == 0) {
185  tmp_sorted_vars_.push_back(
186  {arc.head_var, integer_trail_->LowerBound(arc.head_var)});
187  } else {
188  // This "seen" mechanism is needed because we may have multi-arc and we
189  // don't want any duplicates in the "is_before" relation. Note that it
190  // works because var_to_last_index_ is reset by the var_to_degree_ == 0
191  // case.
192  if (var_to_last_index_[arc.head_var] == index) continue;
193  }
194  var_to_last_index_[arc.head_var] = index;
195  var_to_degree_[arc.head_var]++;
196  tmp_precedences_.push_back(
197  {index, arc.head_var, arc_index.value(), offset});
198  }
199  }
200 
201  // This order is a topological order for the precedences relation order
202  // provided that all the offset between the involved IntegerVariable are
203  // positive.
204  //
205  // TODO(user): use an order that is always topological? This is not clear
206  // since it may be slower to compute and not worth it because the order below
207  // is more natural and may work better.
208  std::sort(tmp_sorted_vars_.begin(), tmp_sorted_vars_.end());
209 
210  // Permute tmp_precedences_ into the output to put it in the correct order.
211  // For that we transform var_to_degree_ to point to the first position of
212  // each lbvar in the output vector.
213  int start = 0;
214  for (const SortedVar pair : tmp_sorted_vars_) {
215  const int degree = var_to_degree_[pair.var];
216  if (degree > 1) {
217  var_to_degree_[pair.var] = start;
218  start += degree;
219  } else {
220  // Optimization: we remove degree one relations.
221  var_to_degree_[pair.var] = -1;
222  }
223  }
224  output->resize(start);
225  for (const IntegerPrecedences& precedence : tmp_precedences_) {
226  if (var_to_degree_[precedence.var] < 0) continue;
227  (*output)[var_to_degree_[precedence.var]++] = precedence;
228  }
229 
230  // Cleanup var_to_degree_, note that we don't need to clean
231  // var_to_last_index_.
232  for (const SortedVar pair : tmp_sorted_vars_) {
233  var_to_degree_[pair.var] = 0;
234  }
235 }
236 
238  bool call_compute_precedences, const std::vector<IntegerVariable>& vars,
239  std::vector<FullIntegerPrecedence>* output) {
240  output->clear();
241  DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
242 
243  if (call_compute_precedences) {
244  std::vector<PrecedencesPropagator::IntegerPrecedences> before;
245  ComputePrecedences(vars, &before);
246 
247  // Convert format.
248  const int size = before.size();
249  for (int i = 0; i < size;) {
251  data.var = before[i].var;
252  const IntegerVariable var = before[i].var;
253  DCHECK_NE(var, kNoIntegerVariable);
254  for (; i < size && before[i].var == var; ++i) {
255  data.indices.push_back(before[i].index);
256  data.offsets.push_back(before[i].offset);
257  }
258  output->push_back(std::move(data));
259  }
260  return;
261  }
262 
263  // Get a topological order of the DAG formed by all the arcs that are present.
264  //
265  // TODO(user): This can fail if we don't have a DAG. We could just skip Bad
266  // edges instead, and have a sub-DAG as an heuristic. Or analyze the arc
267  // weight and make sure cycle are not an issue. We can also start with arcs
268  // with strictly positive weight.
269  //
270  // TODO(user): Only explore the sub-graph reachable from "vars".
271  const int num_nodes = integer_trail_->NumIntegerVariables().value();
272  DenseIntStableTopologicalSorter sorter(num_nodes);
273  for (const auto& arcs : impacted_arcs_) {
274  for (const ArcIndex arc_index : arcs) {
275  const ArcInfo& arc = arcs_[arc_index];
276  if (arc.tail_var == arc.head_var) continue;
277  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
278  sorter.AddEdge(arc.tail_var.value(), arc.head_var.value());
279  }
280  }
281  int next;
282  bool graph_has_cycle = false;
283  std::vector<IntegerVariable> topological_order;
284  while (sorter.GetNext(&next, &graph_has_cycle, nullptr)) {
285  topological_order.push_back(IntegerVariable(next));
286  if (graph_has_cycle) return;
287  }
288 
289  // Compute all precedences.
290  // We loop over the node in topological order, and we maintain for all
291  // variable we encounter, the list of "to_consider" variables that are before.
292  //
293  // TODO(user): use vector of fixed size.
294  absl::flat_hash_set<IntegerVariable> is_interesting;
295  absl::flat_hash_set<IntegerVariable> to_consider(vars.begin(), vars.end());
296  absl::flat_hash_map<IntegerVariable,
297  absl::flat_hash_map<IntegerVariable, IntegerValue>>
298  vars_before_with_offset;
299  absl::flat_hash_map<IntegerVariable, IntegerValue> tail_map;
300  for (const IntegerVariable tail_var : topological_order) {
301  if (!to_consider.contains(tail_var) &&
302  !vars_before_with_offset.contains(tail_var)) {
303  continue;
304  }
305 
306  // Update the relation for the variable that are after.
307  if (tail_var >= impacted_arcs_.size()) continue;
308 
309  // We copy the data for tail_var here, because the pointer is not stable.
310  // TODO(user): optimize when needed.
311  tail_map.clear();
312  {
313  const auto it = vars_before_with_offset.find(tail_var);
314  if (it != vars_before_with_offset.end()) {
315  tail_map = it->second;
316  }
317  }
318 
319  for (const ArcIndex arc_index : impacted_arcs_[tail_var]) {
320  const ArcInfo& arc = arcs_[arc_index];
321  if (arc.tail_var == arc.head_var) continue;
322  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
323  CHECK_EQ(arc.tail_var, tail_var);
324 
325  // No need to create an empty entry in this case.
326  if (tail_map.empty() && !to_consider.contains(tail_var)) continue;
327 
328  IntegerValue arc_offset = arc.offset;
329  if (arc.offset_var != kNoIntegerVariable) {
330  arc_offset += integer_trail_->LowerBound(arc.offset_var);
331  }
332 
333  auto& to_update = vars_before_with_offset[arc.head_var];
334  for (const auto& [var_before, offset] : tail_map) {
335  if (!to_update.contains(var_before)) {
336  to_update[var_before] = arc_offset + offset;
337  } else {
338  to_update[var_before] =
339  std::max(arc_offset + offset, to_update[var_before]);
340  }
341  }
342  if (to_consider.contains(tail_var)) {
343  if (!to_update.contains(tail_var)) {
344  to_update[tail_var] = arc_offset;
345  } else {
346  to_update[tail_var] = std::max(arc_offset, to_update[tail_var]);
347  }
348  }
349 
350  // Small filtering heuristic: if we have (before) < tail, and tail < head,
351  // we really do not need to list (before, tail) < head. We only need that
352  // if the list of variable before head contains some variable that are not
353  // already before tail.
354  if (to_update.size() > tail_map.size() + 1) {
355  is_interesting.insert(arc.head_var);
356  } else {
357  is_interesting.erase(arc.head_var);
358  }
359  }
360 
361  // Extract the output for tail_var. Because of the topological ordering, the
362  // data for tail_var is already final now.
363  //
364  // TODO(user): Release the memory right away.
365  if (!is_interesting.contains(tail_var)) continue;
366  if (tail_map.size() == 1) continue;
367 
369  data.var = tail_var;
370  IntegerValue min_offset = kMaxIntegerValue;
371  for (int i = 0; i < vars.size(); ++i) {
372  const auto offset_it = tail_map.find(vars[i]);
373  if (offset_it == tail_map.end()) continue;
374  data.indices.push_back(i);
375  data.offsets.push_back(offset_it->second);
376  min_offset = std::min(data.offsets.back(), min_offset);
377  }
378  output->push_back(std::move(data));
379  }
380 }
381 
383  int arc_index, IntegerValue min_offset,
384  std::vector<Literal>* literal_reason,
385  std::vector<IntegerLiteral>* integer_reason) const {
386  const ArcInfo& arc = arcs_[ArcIndex(arc_index)];
387  for (const Literal l : arc.presence_literals) {
388  literal_reason->push_back(l.Negated());
389  }
390  if (arc.offset_var != kNoIntegerVariable) {
391  // Reason for ArcOffset(arc) to be >= min_offset.
392  integer_reason->push_back(IntegerLiteral::GreaterOrEqual(
393  arc.offset_var, min_offset - arc.offset));
394  }
395 }
396 
397 void PrecedencesPropagator::AdjustSizeFor(IntegerVariable i) {
398  const int index = std::max(i.value(), NegationOf(i).value());
399  if (index >= impacted_arcs_.size()) {
400  // TODO(user): only watch lower bound of the relevant variable instead
401  // of watching everything in [0, max_index_of_variable_used_in_this_class).
402  for (IntegerVariable var(impacted_arcs_.size()); var <= index; ++var) {
403  watcher_->WatchLowerBound(var, watcher_id_);
404  }
405  impacted_arcs_.resize(index + 1);
406  impacted_potential_arcs_.resize(index + 1);
407  var_to_degree_.resize(index + 1);
408  var_to_last_index_.resize(index + 1);
409  }
410 }
411 
412 void PrecedencesPropagator::AddArc(
413  IntegerVariable tail, IntegerVariable head, IntegerValue offset,
414  IntegerVariable offset_var, absl::Span<const Literal> presence_literals) {
415  DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
416  AdjustSizeFor(tail);
417  AdjustSizeFor(head);
418  if (offset_var != kNoIntegerVariable) AdjustSizeFor(offset_var);
419 
420  // This arc is present iff all the literals here are true.
421  absl::InlinedVector<Literal, 6> enforcement_literals;
422  {
423  for (const Literal l : presence_literals) {
424  enforcement_literals.push_back(l);
425  }
426  if (integer_trail_->IsOptional(tail)) {
427  enforcement_literals.push_back(
428  integer_trail_->IsIgnoredLiteral(tail).Negated());
429  }
430  if (integer_trail_->IsOptional(head)) {
431  enforcement_literals.push_back(
432  integer_trail_->IsIgnoredLiteral(head).Negated());
433  }
434  if (offset_var != kNoIntegerVariable &&
435  integer_trail_->IsOptional(offset_var)) {
436  enforcement_literals.push_back(
437  integer_trail_->IsIgnoredLiteral(offset_var).Negated());
438  }
439  gtl::STLSortAndRemoveDuplicates(&enforcement_literals);
440  int new_size = 0;
441  for (const Literal l : enforcement_literals) {
442  if (trail_->Assignment().LiteralIsTrue(Literal(l))) {
443  continue; // At true, ignore this literal.
444  } else if (trail_->Assignment().LiteralIsFalse(Literal(l))) {
445  return; // At false, ignore completely this arc.
446  }
447  enforcement_literals[new_size++] = l;
448  }
449  enforcement_literals.resize(new_size);
450  }
451 
452  if (head == tail) {
453  // A self-arc is either plain SAT or plain UNSAT or it forces something on
454  // the given offset_var or presence_literal_index. In any case it could be
455  // presolved in something more efficient.
456  VLOG(1) << "Self arc! This could be presolved. "
457  << "var:" << tail << " offset:" << offset
458  << " offset_var:" << offset_var
459  << " conditioned_by:" << presence_literals;
460  }
461 
462  // Remove the offset_var if it is fixed.
463  // TODO(user): We should also handle the case where tail or head is fixed.
464  if (offset_var != kNoIntegerVariable) {
465  const IntegerValue lb = integer_trail_->LowerBound(offset_var);
466  if (lb == integer_trail_->UpperBound(offset_var)) {
467  offset += lb;
468  offset_var = kNoIntegerVariable;
469  }
470  }
471 
472  // Deal first with impacted_potential_arcs_/potential_arcs_.
473  if (!enforcement_literals.empty()) {
474  const OptionalArcIndex arc_index(potential_arcs_.size());
475  potential_arcs_.push_back(
476  {tail, head, offset, offset_var, enforcement_literals});
477  impacted_potential_arcs_[tail].push_back(arc_index);
478  impacted_potential_arcs_[NegationOf(head)].push_back(arc_index);
479  if (offset_var != kNoIntegerVariable) {
480  impacted_potential_arcs_[offset_var].push_back(arc_index);
481  }
482  }
483 
484  // Now deal with impacted_arcs_/arcs_.
485  struct InternalArc {
486  IntegerVariable tail_var;
487  IntegerVariable head_var;
488  IntegerVariable offset_var;
489  };
490  std::vector<InternalArc> to_add;
491  if (offset_var == kNoIntegerVariable) {
492  // a + offset <= b and -b + offset <= -a
493  to_add.push_back({tail, head, kNoIntegerVariable});
494  to_add.push_back({NegationOf(head), NegationOf(tail), kNoIntegerVariable});
495  } else {
496  // tail (a) and offset_var (b) are symmetric, so we add:
497  // - a + b + offset <= c
498  to_add.push_back({tail, head, offset_var});
499  to_add.push_back({offset_var, head, tail});
500  // - a - c + offset <= -b
501  to_add.push_back({tail, NegationOf(offset_var), NegationOf(head)});
502  to_add.push_back({NegationOf(head), NegationOf(offset_var), tail});
503  // - b - c + offset <= -a
504  to_add.push_back({offset_var, NegationOf(tail), NegationOf(head)});
505  to_add.push_back({NegationOf(head), NegationOf(tail), offset_var});
506  }
507  for (const InternalArc a : to_add) {
508  // Since we add a new arc, we will need to consider its tail during the next
509  // propagation. Note that the size of modified_vars_ will be automatically
510  // updated when new integer variables are created since we register it with
511  // IntegerTrail in this class constructor.
512  //
513  // TODO(user): Adding arcs and then calling Untrail() before Propagate()
514  // will cause this mecanism to break. Find a more robust implementation.
515  //
516  // TODO(user): In some rare corner case, rescanning the whole list of arc
517  // leaving tail_var can make AddVar() have a quadratic complexity where it
518  // shouldn't. A better solution would be to see if this new arc currently
519  // propagate something, and if it does, just update the lower bound of
520  // a.head_var and let the normal "is modified" mecanism handle any eventual
521  // follow up propagations.
522  modified_vars_.Set(a.tail_var);
523 
524  // If a.head_var is optional, we can potentially remove some literal from
525  // enforcement_literals.
526  const ArcIndex arc_index(arcs_.size());
527  arcs_.push_back(
528  {a.tail_var, a.head_var, offset, a.offset_var, enforcement_literals});
529  auto& presence_literals = arcs_.back().presence_literals;
530  if (integer_trail_->IsOptional(a.head_var)) {
531  // TODO(user): More generally, we can remove any literal that is implied
532  // by to_remove.
533  const Literal to_remove =
534  integer_trail_->IsIgnoredLiteral(a.head_var).Negated();
535  const auto it = std::find(presence_literals.begin(),
536  presence_literals.end(), to_remove);
537  if (it != presence_literals.end()) presence_literals.erase(it);
538  }
539 
540  if (presence_literals.empty()) {
541  impacted_arcs_[a.tail_var].push_back(arc_index);
542  } else {
543  for (const Literal l : presence_literals) {
544  if (l.Index() >= literal_to_new_impacted_arcs_.size()) {
545  literal_to_new_impacted_arcs_.resize(l.Index().value() + 1);
546  }
547  literal_to_new_impacted_arcs_[l.Index()].push_back(arc_index);
548  }
549  }
550  arc_counts_.push_back(presence_literals.size());
551  }
552 }
553 
554 // TODO(user): On jobshop problems with a lot of tasks per machine (500), this
555 // takes up a big chunck of the running time even before we find a solution.
556 // This is because, for each lower bound changed, we inspect 500 arcs even
557 // though they will never be propagated because the other bound is still at the
558 // horizon. Find an even sparser algorithm?
559 void PrecedencesPropagator::PropagateOptionalArcs(Trail* trail) {
560  for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
561  // The variables are not in increasing order, so we need to continue.
562  if (var >= impacted_potential_arcs_.size()) continue;
563 
564  // Note that we can currently check the same ArcInfo up to 3 times, one for
565  // each of the arc variables: tail, NegationOf(head) and offset_var.
566  for (const OptionalArcIndex arc_index : impacted_potential_arcs_[var]) {
567  const ArcInfo& arc = potential_arcs_[arc_index];
568  int num_not_true = 0;
569  Literal to_propagate;
570  for (const Literal l : arc.presence_literals) {
571  if (!trail->Assignment().LiteralIsTrue(l)) {
572  ++num_not_true;
573  to_propagate = l;
574  }
575  }
576  if (num_not_true != 1) continue;
577  if (trail->Assignment().LiteralIsFalse(to_propagate)) continue;
578 
579  // Test if this arc can be present or not.
580  // Important arc.tail_var can be different from var here.
581  const IntegerValue tail_lb = integer_trail_->LowerBound(arc.tail_var);
582  const IntegerValue head_ub = integer_trail_->UpperBound(arc.head_var);
583  if (tail_lb + ArcOffset(arc) > head_ub) {
584  integer_reason_.clear();
585  integer_reason_.push_back(
586  integer_trail_->LowerBoundAsLiteral(arc.tail_var));
587  integer_reason_.push_back(
588  integer_trail_->UpperBoundAsLiteral(arc.head_var));
589  AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
590  &integer_reason_);
591  literal_reason_.clear();
592  for (const Literal l : arc.presence_literals) {
593  if (l != to_propagate) literal_reason_.push_back(l.Negated());
594  }
595  ++num_enforcement_pushes_;
596  integer_trail_->EnqueueLiteral(to_propagate.Negated(), literal_reason_,
597  integer_reason_);
598  }
599  }
600  }
601 }
602 
603 IntegerValue PrecedencesPropagator::ArcOffset(const ArcInfo& arc) const {
604  return arc.offset + (arc.offset_var == kNoIntegerVariable
605  ? IntegerValue(0)
606  : integer_trail_->LowerBound(arc.offset_var));
607 }
608 
609 bool PrecedencesPropagator::EnqueueAndCheck(const ArcInfo& arc,
610  IntegerValue new_head_lb,
611  Trail* trail) {
612  ++num_pushes_;
613  DCHECK_GT(new_head_lb, integer_trail_->LowerBound(arc.head_var));
614 
615  // Compute the reason for new_head_lb.
616  //
617  // TODO(user): do like for clause and keep the negation of
618  // arc.presence_literals? I think we could change the integer.h API to accept
619  // true literal like for IntegerVariable, it is really confusing currently.
620  literal_reason_.clear();
621  for (const Literal l : arc.presence_literals) {
622  literal_reason_.push_back(l.Negated());
623  }
624 
625  integer_reason_.clear();
626  integer_reason_.push_back(integer_trail_->LowerBoundAsLiteral(arc.tail_var));
627  AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
628  &integer_reason_);
629 
630  // The code works without this block since Enqueue() below can already take
631  // care of conflicts. However, it is better to deal with the conflict
632  // ourselves because we can be smarter about the reason this way.
633  //
634  // The reason for a "precedence" conflict is always a linear reason
635  // involving the tail lower_bound, the head upper bound and eventually the
636  // size lower bound. Because of that, we can use the RelaxLinearReason()
637  // code.
638  if (new_head_lb > integer_trail_->UpperBound(arc.head_var)) {
639  const IntegerValue slack =
640  new_head_lb - integer_trail_->UpperBound(arc.head_var) - 1;
641  integer_reason_.push_back(
642  integer_trail_->UpperBoundAsLiteral(arc.head_var));
643  std::vector<IntegerValue> coeffs(integer_reason_.size(), IntegerValue(1));
644  integer_trail_->RelaxLinearReason(slack, coeffs, &integer_reason_);
645 
646  if (!integer_trail_->IsOptional(arc.head_var)) {
647  return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
648  } else {
649  CHECK(!integer_trail_->IsCurrentlyIgnored(arc.head_var));
650  const Literal l = integer_trail_->IsIgnoredLiteral(arc.head_var);
651  if (trail->Assignment().LiteralIsFalse(l)) {
652  literal_reason_.push_back(l);
653  return integer_trail_->ReportConflict(literal_reason_, integer_reason_);
654  } else {
655  integer_trail_->EnqueueLiteral(l, literal_reason_, integer_reason_);
656  return true;
657  }
658  }
659  }
660 
661  return integer_trail_->Enqueue(
662  IntegerLiteral::GreaterOrEqual(arc.head_var, new_head_lb),
663  literal_reason_, integer_reason_);
664 }
665 
666 bool PrecedencesPropagator::NoPropagationLeft(const Trail& trail) const {
667  const int num_nodes = impacted_arcs_.size();
668  for (IntegerVariable var(0); var < num_nodes; ++var) {
669  for (const ArcIndex arc_index : impacted_arcs_[var]) {
670  const ArcInfo& arc = arcs_[arc_index];
671  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
672  if (integer_trail_->LowerBound(arc.tail_var) + ArcOffset(arc) >
673  integer_trail_->LowerBound(arc.head_var)) {
674  return false;
675  }
676  }
677  }
678  return true;
679 }
680 
681 void PrecedencesPropagator::InitializeBFQueueWithModifiedNodes() {
682  // Sparse clear of the queue. TODO(user): only use the sparse version if
683  // queue.size() is small or use SparseBitset.
684  const int num_nodes = impacted_arcs_.size();
685  bf_in_queue_.resize(num_nodes, false);
686  for (const int node : bf_queue_) bf_in_queue_[node] = false;
687  bf_queue_.clear();
688  DCHECK(std::none_of(bf_in_queue_.begin(), bf_in_queue_.end(),
689  [](bool v) { return v; }));
690  for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
691  if (var >= num_nodes) continue;
692  bf_queue_.push_back(var.value());
693  bf_in_queue_[var.value()] = true;
694  }
695 }
696 
697 void PrecedencesPropagator::CleanUpMarkedArcsAndParents() {
698  // To be sparse, we use the fact that each node with a parent must be in
699  // modified_vars_.
700  const int num_nodes = impacted_arcs_.size();
701  for (const IntegerVariable var : modified_vars_.PositionsSetAtLeastOnce()) {
702  if (var >= num_nodes) continue;
703  const ArcIndex parent_arc_index = bf_parent_arc_of_[var.value()];
704  if (parent_arc_index != -1) {
705  arcs_[parent_arc_index].is_marked = false;
706  bf_parent_arc_of_[var.value()] = -1;
707  bf_can_be_skipped_[var.value()] = false;
708  }
709  }
710  DCHECK(std::none_of(bf_parent_arc_of_.begin(), bf_parent_arc_of_.end(),
711  [](ArcIndex v) { return v != -1; }));
712  DCHECK(std::none_of(bf_can_be_skipped_.begin(), bf_can_be_skipped_.end(),
713  [](bool v) { return v; }));
714 }
715 
716 bool PrecedencesPropagator::DisassembleSubtree(
717  int source, int target, std::vector<bool>* can_be_skipped) {
718  // Note that we explore a tree, so we can do it in any order, and the one
719  // below seems to be the fastest.
720  tmp_vector_.clear();
721  tmp_vector_.push_back(source);
722  while (!tmp_vector_.empty()) {
723  const int tail = tmp_vector_.back();
724  tmp_vector_.pop_back();
725  for (const ArcIndex arc_index : impacted_arcs_[IntegerVariable(tail)]) {
726  const ArcInfo& arc = arcs_[arc_index];
727  if (arc.is_marked) {
728  arc.is_marked = false; // mutable.
729  if (arc.head_var.value() == target) return true;
730  DCHECK(!(*can_be_skipped)[arc.head_var.value()]);
731  (*can_be_skipped)[arc.head_var.value()] = true;
732  tmp_vector_.push_back(arc.head_var.value());
733  }
734  }
735  }
736  return false;
737 }
738 
739 void PrecedencesPropagator::AnalyzePositiveCycle(
740  ArcIndex first_arc, Trail* trail, std::vector<Literal>* must_be_all_true,
741  std::vector<Literal>* literal_reason,
742  std::vector<IntegerLiteral>* integer_reason) {
743  must_be_all_true->clear();
744  literal_reason->clear();
745  integer_reason->clear();
746 
747  // Follow bf_parent_arc_of_[] to find the cycle containing first_arc.
748  const IntegerVariable first_arc_head = arcs_[first_arc].head_var;
749  ArcIndex arc_index = first_arc;
750  std::vector<ArcIndex> arc_on_cycle;
751 
752  // Just to be safe and avoid an infinite loop we use the fact that the maximum
753  // cycle size on a graph with n nodes is of size n. If we have more in the
754  // code below, it means first_arc is not part of a cycle according to
755  // bf_parent_arc_of_[], which should never happen.
756  const int num_nodes = impacted_arcs_.size();
757  while (arc_on_cycle.size() <= num_nodes) {
758  arc_on_cycle.push_back(arc_index);
759  const ArcInfo& arc = arcs_[arc_index];
760  if (arc.tail_var == first_arc_head) break;
761  arc_index = bf_parent_arc_of_[arc.tail_var.value()];
762  CHECK_NE(arc_index, ArcIndex(-1));
763  }
764  CHECK_NE(arc_on_cycle.size(), num_nodes + 1) << "Infinite loop.";
765 
766  // Compute the reason for this cycle.
767  IntegerValue sum(0);
768  for (const ArcIndex arc_index : arc_on_cycle) {
769  const ArcInfo& arc = arcs_[arc_index];
770  sum += ArcOffset(arc);
771  AppendLowerBoundReasonIfValid(arc.offset_var, *integer_trail_,
772  integer_reason);
773  for (const Literal l : arc.presence_literals) {
774  literal_reason->push_back(l.Negated());
775  }
776 
777  // If the cycle happens to contain optional variable not yet ignored, then
778  // it is not a conflict anymore, but we can infer that these variable must
779  // all be ignored. This is because since we propagated them even if they
780  // where not present for sure, their presence literal must form a cycle
781  // together (i.e. they are all absent or present at the same time).
782  if (integer_trail_->IsOptional(arc.head_var)) {
783  must_be_all_true->push_back(
784  integer_trail_->IsIgnoredLiteral(arc.head_var));
785  }
786  }
787 
788  // TODO(user): what if the sum overflow? this is just a check so I guess
789  // we don't really care, but fix the issue.
790  CHECK_GT(sum, 0);
791 }
792 
793 // Note that in our settings it is important to use an algorithm that tries to
794 // minimize the number of integer_trail_->Enqueue() as much as possible.
795 //
796 // TODO(user): The current algorithm is quite efficient, but there is probably
797 // still room for improvements.
798 bool PrecedencesPropagator::BellmanFordTarjan(Trail* trail) {
799  const int num_nodes = impacted_arcs_.size();
800 
801  // These vector are reset by CleanUpMarkedArcsAndParents() so resize is ok.
802  bf_can_be_skipped_.resize(num_nodes, false);
803  bf_parent_arc_of_.resize(num_nodes, ArcIndex(-1));
804  const auto cleanup =
805  ::absl::MakeCleanup([this]() { CleanUpMarkedArcsAndParents(); });
806 
807  // The queue initialization is done by InitializeBFQueueWithModifiedNodes().
808  while (!bf_queue_.empty()) {
809  const int node = bf_queue_.front();
810  bf_queue_.pop_front();
811  bf_in_queue_[node] = false;
812 
813  // TODO(user): we don't need bf_can_be_skipped_ since we can detect this
814  // if this node has a parent arc which is not marked. Investigate if it is
815  // faster without the vector<bool>.
816  //
817  // TODO(user): An alternative algorithm is to remove all these nodes from
818  // the queue instead of simply marking them. This should also lead to a
819  // better "relaxation" order of the arcs. It is however a bit more work to
820  // remove them since we need to track their position.
821  if (bf_can_be_skipped_[node]) {
822  DCHECK_NE(bf_parent_arc_of_[node], -1);
823  DCHECK(!arcs_[bf_parent_arc_of_[node]].is_marked);
824  continue;
825  }
826 
827  const IntegerValue tail_lb =
828  integer_trail_->LowerBound(IntegerVariable(node));
829  for (const ArcIndex arc_index : impacted_arcs_[IntegerVariable(node)]) {
830  const ArcInfo& arc = arcs_[arc_index];
831  DCHECK_EQ(arc.tail_var, node);
832  const IntegerValue candidate = tail_lb + ArcOffset(arc);
833  if (candidate > integer_trail_->LowerBound(arc.head_var)) {
834  if (integer_trail_->IsCurrentlyIgnored(arc.head_var)) continue;
835  if (!EnqueueAndCheck(arc, candidate, trail)) return false;
836 
837  // This is the Tarjan contribution to Bellman-Ford. This code detect
838  // positive cycle, and because it disassemble the subtree while doing
839  // so, the cost is amortized during the algorithm execution. Another
840  // advantages is that it will mark the node explored here as skippable
841  // which will avoid to propagate them too early (knowing that they will
842  // need to be propagated again later).
843  if (DisassembleSubtree(arc.head_var.value(), arc.tail_var.value(),
844  &bf_can_be_skipped_)) {
845  std::vector<Literal> must_be_all_true;
846  AnalyzePositiveCycle(arc_index, trail, &must_be_all_true,
847  &literal_reason_, &integer_reason_);
848  if (must_be_all_true.empty()) {
849  ++num_cycles_;
850  return integer_trail_->ReportConflict(literal_reason_,
851  integer_reason_);
852  } else {
853  gtl::STLSortAndRemoveDuplicates(&must_be_all_true);
854  for (const Literal l : must_be_all_true) {
855  if (trail_->Assignment().LiteralIsFalse(l)) {
856  literal_reason_.push_back(l);
857  return integer_trail_->ReportConflict(literal_reason_,
858  integer_reason_);
859  }
860  }
861  for (const Literal l : must_be_all_true) {
862  if (trail_->Assignment().LiteralIsTrue(l)) continue;
863  integer_trail_->EnqueueLiteral(l, literal_reason_,
864  integer_reason_);
865  }
866 
867  // We just marked some optional variable as ignored, no need
868  // to update bf_parent_arc_of_[].
869  continue;
870  }
871  }
872 
873  // We need to enforce the invariant that only the arc_index in
874  // bf_parent_arc_of_[] are marked (but not necessarily all of them
875  // since we unmark some in DisassembleSubtree()).
876  if (bf_parent_arc_of_[arc.head_var.value()] != -1) {
877  arcs_[bf_parent_arc_of_[arc.head_var.value()]].is_marked = false;
878  }
879 
880  // Tricky: We just enqueued the fact that the lower-bound of head is
881  // candidate. However, because the domain of head may be discrete, it is
882  // possible that the lower-bound of head is now higher than candidate!
883  // If this is the case, we don't update bf_parent_arc_of_[] so that we
884  // don't wrongly detect a positive weight cycle because of this "extra
885  // push".
886  const IntegerValue new_bound = integer_trail_->LowerBound(arc.head_var);
887  if (new_bound == candidate) {
888  bf_parent_arc_of_[arc.head_var.value()] = arc_index;
889  arcs_[arc_index].is_marked = true;
890  } else {
891  // We still unmark any previous dependency, since we have pushed the
892  // value of arc.head_var further.
893  bf_parent_arc_of_[arc.head_var.value()] = -1;
894  }
895 
896  // We do not re-enqueue if we are in a propagation loop and new_bound
897  // was not pushed to candidate or higher.
898  bf_can_be_skipped_[arc.head_var.value()] = false;
899  if (!bf_in_queue_[arc.head_var.value()] && new_bound >= candidate) {
900  bf_queue_.push_back(arc.head_var.value());
901  bf_in_queue_[arc.head_var.value()] = true;
902  }
903  }
904  }
905  }
906  return true;
907 }
908 
909 int PrecedencesPropagator::AddGreaterThanAtLeastOneOfConstraintsFromClause(
910  const absl::Span<const Literal> clause, Model* model) {
911  CHECK_EQ(model->GetOrCreate<Trail>()->CurrentDecisionLevel(), 0);
912  if (clause.size() < 2) return 0;
913 
914  // Collect all arcs impacted by this clause.
915  std::vector<ArcInfo> infos;
916  for (const Literal l : clause) {
917  if (l.Index() >= literal_to_new_impacted_arcs_.size()) continue;
918  for (const ArcIndex arc_index : literal_to_new_impacted_arcs_[l.Index()]) {
919  const ArcInfo& arc = arcs_[arc_index];
920  if (arc.presence_literals.size() != 1) continue;
921 
922  // TODO(user): Support variable offset.
923  if (arc.offset_var != kNoIntegerVariable) continue;
924  infos.push_back(arc);
925  }
926  }
927  if (infos.size() <= 1) return 0;
928 
929  // Stable sort by head_var so that for a same head_var, the entry are sorted
930  // by Literal as they appear in clause.
931  std::stable_sort(infos.begin(), infos.end(),
932  [](const ArcInfo& a, const ArcInfo& b) {
933  return a.head_var < b.head_var;
934  });
935 
936  // We process ArcInfo with the same head_var toghether.
937  int num_added_constraints = 0;
938  auto* solver = model->GetOrCreate<SatSolver>();
939  for (int i = 0; i < infos.size();) {
940  const int start = i;
941  const IntegerVariable head_var = infos[start].head_var;
942  for (i++; i < infos.size() && infos[i].head_var == head_var; ++i) {
943  }
944  const absl::Span<ArcInfo> arcs(&infos[start], i - start);
945 
946  // Skip single arcs since it will already be fully propagated.
947  if (arcs.size() < 2) continue;
948 
949  // Heuristic. Look for full or almost full clauses. We could add
950  // GreaterThanAtLeastOneOf() with more enforcement literals. TODO(user):
951  // experiments.
952  if (arcs.size() + 1 < clause.size()) continue;
953 
954  std::vector<IntegerVariable> vars;
955  std::vector<IntegerValue> offsets;
956  std::vector<Literal> selectors;
957  std::vector<Literal> enforcements;
958 
959  int j = 0;
960  for (const Literal l : clause) {
961  bool added = false;
962  for (; j < arcs.size() && l == arcs[j].presence_literals.front(); ++j) {
963  added = true;
964  vars.push_back(arcs[j].tail_var);
965  offsets.push_back(arcs[j].offset);
966 
967  // Note that duplicate selector are supported.
968  //
969  // TODO(user): If we support variable offset, we should regroup the arcs
970  // into one (tail + offset <= head) though, instead of having too
971  // identical entries.
972  selectors.push_back(l);
973  }
974  if (!added) {
975  enforcements.push_back(l.Negated());
976  }
977  }
978 
979  // No point adding a constraint if there is not at least two different
980  // literals in selectors.
981  if (enforcements.size() + 1 == clause.size()) continue;
982 
983  ++num_added_constraints;
984  model->Add(GreaterThanAtLeastOneOf(head_var, vars, offsets, selectors,
985  enforcements));
986  if (!solver->FinishPropagation()) return num_added_constraints;
987  }
988  return num_added_constraints;
989 }
990 
991 int PrecedencesPropagator::
992  AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(Model* model) {
993  auto* time_limit = model->GetOrCreate<TimeLimit>();
994  auto* solver = model->GetOrCreate<SatSolver>();
995 
996  // Fill the set of incoming conditional arcs for each variables.
998  for (ArcIndex arc_index(0); arc_index < arcs_.size(); ++arc_index) {
999  const ArcInfo& arc = arcs_[arc_index];
1000 
1001  // Only keep arc that have a fixed offset and a single presence_literals.
1002  if (arc.offset_var != kNoIntegerVariable) continue;
1003  if (arc.tail_var == arc.head_var) continue;
1004  if (arc.presence_literals.size() != 1) continue;
1005 
1006  if (arc.head_var >= incoming_arcs_.size()) {
1007  incoming_arcs_.resize(arc.head_var.value() + 1);
1008  }
1009  incoming_arcs_[arc.head_var].push_back(arc_index);
1010  }
1011 
1012  int num_added_constraints = 0;
1013  for (IntegerVariable target(0); target < incoming_arcs_.size(); ++target) {
1014  if (incoming_arcs_[target].size() <= 1) continue;
1015  if (time_limit->LimitReached()) return num_added_constraints;
1016 
1017  // Detect set of incoming arcs for which at least one must be present.
1018  // TODO(user): Find more than one disjoint set of incoming arcs.
1019  // TODO(user): call MinimizeCoreWithPropagation() on the clause.
1020  solver->Backtrack(0);
1021  if (solver->ModelIsUnsat()) return num_added_constraints;
1022  std::vector<Literal> clause;
1023  for (const ArcIndex arc_index : incoming_arcs_[target]) {
1024  const Literal literal = arcs_[arc_index].presence_literals.front();
1025  if (solver->Assignment().LiteralIsFalse(literal)) continue;
1026  const SatSolver::Status status =
1027  solver->EnqueueDecisionAndBacktrackOnConflict(literal.Negated());
1028  if (status == SatSolver::INFEASIBLE) return num_added_constraints;
1030  clause = solver->GetLastIncompatibleDecisions();
1031  break;
1032  }
1033  }
1034  solver->Backtrack(0);
1035 
1036  if (clause.size() > 1) {
1037  // Extract the set of arc for which at least one must be present.
1038  const absl::btree_set<Literal> clause_set(clause.begin(), clause.end());
1039  std::vector<ArcIndex> arcs_in_clause;
1040  for (const ArcIndex arc_index : incoming_arcs_[target]) {
1041  const Literal literal(arcs_[arc_index].presence_literals.front());
1042  if (clause_set.contains(literal.Negated())) {
1043  arcs_in_clause.push_back(arc_index);
1044  }
1045  }
1046 
1047  VLOG(2) << arcs_in_clause.size() << "/" << incoming_arcs_[target].size();
1048 
1049  ++num_added_constraints;
1050  std::vector<IntegerVariable> vars;
1051  std::vector<IntegerValue> offsets;
1052  std::vector<Literal> selectors;
1053  for (const ArcIndex a : arcs_in_clause) {
1054  vars.push_back(arcs_[a].tail_var);
1055  offsets.push_back(arcs_[a].offset);
1056  selectors.push_back(Literal(arcs_[a].presence_literals.front()));
1057  }
1058  model->Add(GreaterThanAtLeastOneOf(target, vars, offsets, selectors));
1059  if (!solver->FinishPropagation()) return num_added_constraints;
1060  }
1061  }
1062 
1063  return num_added_constraints;
1064 }
1065 
1067  VLOG(1) << "Detecting GreaterThanAtLeastOneOf() constraints...";
1068  auto* time_limit = model->GetOrCreate<TimeLimit>();
1069  auto* solver = model->GetOrCreate<SatSolver>();
1070  auto* clauses = model->GetOrCreate<LiteralWatchers>();
1071  int num_added_constraints = 0;
1072 
1073  // We have two possible approaches. For now, we prefer the first one except if
1074  // there is too many clauses in the problem.
1075  //
1076  // TODO(user): Do more extensive experiment. Remove the second approach as
1077  // it is more time consuming? or identify when it make sense. Note that the
1078  // first approach also allows to use "incomplete" at least one between arcs.
1079  if (clauses->AllClausesInCreationOrder().size() < 1e6) {
1080  // TODO(user): This does not take into account clause of size 2 since they
1081  // are stored in the BinaryImplicationGraph instead. Some ideas specific
1082  // to size 2:
1083  // - There can be a lot of such clauses, but it might be nice to consider
1084  // them. we need to experiments.
1085  // - The automatic clause detection might be a better approach and it
1086  // could be combined with probing.
1087  for (const SatClause* clause : clauses->AllClausesInCreationOrder()) {
1088  if (time_limit->LimitReached()) return num_added_constraints;
1089  if (solver->ModelIsUnsat()) return num_added_constraints;
1090  num_added_constraints += AddGreaterThanAtLeastOneOfConstraintsFromClause(
1091  clause->AsSpan(), model);
1092  }
1093 
1094  // It is common that there is only two alternatives to push a variable.
1095  // In this case, our presolve most likely made sure that the two are
1096  // controlled by a single Boolean. This allows to detect this and add the
1097  // appropriate greater than at least one of.
1098  const int num_booleans = solver->NumVariables();
1099  if (num_booleans < 1e6) {
1100  for (int i = 0; i < num_booleans; ++i) {
1101  if (time_limit->LimitReached()) return num_added_constraints;
1102  if (solver->ModelIsUnsat()) return num_added_constraints;
1103  num_added_constraints +=
1104  AddGreaterThanAtLeastOneOfConstraintsFromClause(
1105  {Literal(BooleanVariable(i), true),
1106  Literal(BooleanVariable(i), false)},
1107  model);
1108  }
1109  }
1110 
1111  } else {
1112  num_added_constraints +=
1113  AddGreaterThanAtLeastOneOfConstraintsWithClauseAutoDetection(model);
1114  }
1115 
1116  VLOG(1) << "Added " << num_added_constraints
1117  << " GreaterThanAtLeastOneOf() constraints.";
1118  return num_added_constraints;
1119 }
1120 
1121 } // namespace sat
1122 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void resize(size_type new_size)
size_type size() const
void push_back(const value_type &x)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:806
void Set(IntegerType index)
Definition: bitset.h:792
void ClearAndResize(IntegerType size)
Definition: bitset.h:767
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
void WatchLowerBound(IntegerVariable var, int id, int watch_index=-1)
Definition: integer.h:1681
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1228
bool IsCurrentlyIgnored(IntegerVariable i) const
Definition: integer.h:775
IntegerLiteral LowerBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1589
bool ReportConflict(absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.h:1004
void EnqueueLiteral(Literal literal, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1387
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1561
void RelaxLinearReason(IntegerValue slack, absl::Span< const IntegerValue > coeffs, std::vector< IntegerLiteral > *reason) const
Definition: integer.cc:984
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1557
IntegerLiteral UpperBoundAsLiteral(IntegerVariable i) const
Definition: integer.h:1594
Literal IsIgnoredLiteral(IntegerVariable i) const
Definition: integer.h:780
bool IsOptional(IntegerVariable i) const
Definition: integer.h:772
IntegerVariable NumIntegerVariables() const
Definition: integer.h:715
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
void AddPrecedenceReason(int arc_index, IntegerValue min_offset, std::vector< Literal > *literal_reason, std::vector< IntegerLiteral > *integer_reason) const
Definition: precedences.cc:382
void ComputeFullPrecedences(bool call_compute_precedences, const std::vector< IntegerVariable > &vars, std::vector< FullIntegerPrecedence > *output)
Definition: precedences.cc:237
void ComputePrecedences(const std::vector< IntegerVariable > &vars, std::vector< IntegerPrecedences > *output)
Definition: precedences.cc:160
void Untrail(const Trail &trail, int trail_index) final
Definition: precedences.cc:136
bool PropagateOutgoingArcs(IntegerVariable var)
Definition: precedences.cc:121
void AddStats(absl::Span< const std::pair< std::string, int64_t >> stats)
const VariablesAssignment & Assignment() const
Definition: sat_base.h:402
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:164
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:161
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
int64_t b
int64_t a
Block * next
SharedClausesManager * clauses
ModelSharedTimeLimit * time_limit
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Status status
Definition: g_gurobi.cc:41
GRBmodel * model
int arc
int index
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Definition: cleanup.h:125
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
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)
const IntegerVariable kNoIntegerVariable(-1)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:46
std::function< int64_t(const Model &)> LowerBound(IntegerVariable v)
Definition: integer.h:1775
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:88
int64_t tail
int64_t head
int64_t start
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1499
#define VLOG(verboselevel)
Definition: vlog.h:39
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:47