OR-Tools  8.1
clause.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 
14 #include "ortools/sat/clause.h"
15 
16 #include <algorithm>
17 #include <functional>
18 #include <memory>
19 #include <string>
20 #include <vector>
21 
22 #include "ortools/base/logging.h"
23 #include "ortools/base/stl_util.h"
24 #include "ortools/base/timer.h"
26 
27 namespace operations_research {
28 namespace sat {
29 
30 namespace {
31 
32 // Returns true if the given watcher list contains the given clause.
33 template <typename Watcher>
34 bool WatcherListContains(const std::vector<Watcher>& list,
35  const SatClause& candidate) {
36  for (const Watcher& watcher : list) {
37  if (watcher.clause == &candidate) return true;
38  }
39  return false;
40 }
41 
42 // A simple wrapper to simplify the erase(std::remove_if()) pattern.
43 template <typename Container, typename Predicate>
44 void RemoveIf(Container c, Predicate p) {
45  c->erase(std::remove_if(c->begin(), c->end(), p), c->end());
46 }
47 
48 } // namespace
49 
50 // ----- LiteralWatchers -----
51 
53  : SatPropagator("LiteralWatchers"),
54  implication_graph_(model->GetOrCreate<BinaryImplicationGraph>()),
55  trail_(model->GetOrCreate<Trail>()),
56  num_inspected_clauses_(0),
57  num_inspected_clause_literals_(0),
58  num_watched_clauses_(0),
59  stats_("LiteralWatchers") {
60  trail_->RegisterPropagator(this);
61 }
62 
64  gtl::STLDeleteElements(&clauses_);
65  IF_STATS_ENABLED(LOG(INFO) << stats_.StatString());
66 }
67 
68 void LiteralWatchers::Resize(int num_variables) {
69  DCHECK(is_clean_);
70  watchers_on_false_.resize(num_variables << 1);
71  reasons_.resize(num_variables);
72  needs_cleaning_.Resize(LiteralIndex(num_variables << 1));
73 }
74 
75 // Note that this is the only place where we add Watcher so the DCHECK
76 // guarantees that there are no duplicates.
77 void LiteralWatchers::AttachOnFalse(Literal literal, Literal blocking_literal,
78  SatClause* clause) {
79  SCOPED_TIME_STAT(&stats_);
80  DCHECK(is_clean_);
81  DCHECK(!WatcherListContains(watchers_on_false_[literal.Index()], *clause));
82  watchers_on_false_[literal.Index()].push_back(
83  Watcher(clause, blocking_literal));
84 }
85 
86 bool LiteralWatchers::PropagateOnFalse(Literal false_literal, Trail* trail) {
87  SCOPED_TIME_STAT(&stats_);
88  DCHECK(is_clean_);
89  std::vector<Watcher>& watchers = watchers_on_false_[false_literal.Index()];
90  const VariablesAssignment& assignment = trail->Assignment();
91 
92  // Note(user): It sounds better to inspect the list in order, this is because
93  // small clauses like binary or ternary clauses will often propagate and thus
94  // stay at the beginning of the list.
95  auto new_it = watchers.begin();
96  const auto end = watchers.end();
97  while (new_it != end && assignment.LiteralIsTrue(new_it->blocking_literal)) {
98  ++new_it;
99  }
100  for (auto it = new_it; it != end; ++it) {
101  // Don't even look at the clause memory if the blocking literal is true.
102  if (assignment.LiteralIsTrue(it->blocking_literal)) {
103  *new_it++ = *it;
104  continue;
105  }
106  ++num_inspected_clauses_;
107 
108  // If the other watched literal is true, just change the blocking literal.
109  // Note that we use the fact that the first two literals of the clause are
110  // the ones currently watched.
111  Literal* literals = it->clause->literals();
112  const Literal other_watched_literal(
113  LiteralIndex(literals[0].Index().value() ^ literals[1].Index().value() ^
114  false_literal.Index().value()));
115  if (assignment.LiteralIsTrue(other_watched_literal)) {
116  *new_it = *it;
117  new_it->blocking_literal = other_watched_literal;
118  ++new_it;
119  ++num_inspected_clause_literals_;
120  continue;
121  }
122 
123  // Look for another literal to watch. We go through the list in a cyclic
124  // fashion from start. The first two literals can be ignored as they are the
125  // watched ones.
126  {
127  const int start = it->start_index;
128  const int size = it->clause->size();
129  DCHECK_GE(start, 2);
130 
131  int i = start;
132  while (i < size && assignment.LiteralIsFalse(literals[i])) ++i;
133  num_inspected_clause_literals_ += i - start + 2;
134  if (i >= size) {
135  i = 2;
136  while (i < start && assignment.LiteralIsFalse(literals[i])) ++i;
137  num_inspected_clause_literals_ += i - 2;
138  if (i >= start) i = size;
139  }
140  if (i < size) {
141  // literal[i] is unassigned or true, it's now the new literal to watch.
142  // Note that by convention, we always keep the two watched literals at
143  // the beginning of the clause.
144  literals[0] = other_watched_literal;
145  literals[1] = literals[i];
146  literals[i] = false_literal;
147  watchers_on_false_[literals[1].Index()].emplace_back(
148  it->clause, other_watched_literal, i + 1);
149  continue;
150  }
151  }
152 
153  // At this point other_watched_literal is either false or unassigned, all
154  // other literals are false.
155  if (assignment.LiteralIsFalse(other_watched_literal)) {
156  // Conflict: All literals of it->clause are false.
157  //
158  // Note(user): we could avoid a copy here, but the conflict analysis
159  // complexity will be a lot higher than this anyway.
160  trail->MutableConflict()->assign(it->clause->begin(), it->clause->end());
161  trail->SetFailingSatClause(it->clause);
162  num_inspected_clause_literals_ += it - watchers.begin() + 1;
163  watchers.erase(new_it, it);
164  return false;
165  } else {
166  // Propagation: other_watched_literal is unassigned, set it to true and
167  // put it at position 0. Note that the position 0 is important because
168  // we will need later to recover the literal that was propagated from the
169  // clause using this convention.
170  literals[0] = other_watched_literal;
171  literals[1] = false_literal;
172  reasons_[trail->Index()] = it->clause;
173  trail->Enqueue(other_watched_literal, propagator_id_);
174  *new_it++ = *it;
175  }
176  }
177  num_inspected_clause_literals_ += watchers.size(); // The blocking ones.
178  watchers.erase(new_it, end);
179  return true;
180 }
181 
183  const int old_index = trail->Index();
184  while (trail->Index() == old_index && propagation_trail_index_ < old_index) {
185  const Literal literal = (*trail)[propagation_trail_index_++];
186  if (!PropagateOnFalse(literal.Negated(), trail)) return false;
187  }
188  return true;
189 }
190 
191 absl::Span<const Literal> LiteralWatchers::Reason(const Trail& trail,
192  int trail_index) const {
193  return reasons_[trail_index]->PropagationReason();
194 }
195 
196 SatClause* LiteralWatchers::ReasonClause(int trail_index) const {
197  return reasons_[trail_index];
198 }
199 
200 bool LiteralWatchers::AddClause(absl::Span<const Literal> literals) {
201  return AddClause(literals, trail_);
202 }
203 
204 bool LiteralWatchers::AddClause(absl::Span<const Literal> literals,
205  Trail* trail) {
206  SatClause* clause = SatClause::Create(literals);
207  clauses_.push_back(clause);
208  return AttachAndPropagate(clause, trail);
209 }
210 
212  const std::vector<Literal>& literals, Trail* trail) {
213  SatClause* clause = SatClause::Create(literals);
214  clauses_.push_back(clause);
215  CHECK(AttachAndPropagate(clause, trail));
216  return clause;
217 }
218 
219 // Sets up the 2-watchers data structure. It selects two non-false literals
220 // and attaches the clause to the event: one of the watched literals become
221 // false. It returns false if the clause only contains literals assigned to
222 // false. If only one literals is not false, it propagates it to true if it
223 // is not already assigned.
224 bool LiteralWatchers::AttachAndPropagate(SatClause* clause, Trail* trail) {
225  SCOPED_TIME_STAT(&stats_);
226 
227  const int size = clause->size();
228  Literal* literals = clause->literals();
229 
230  // Select the first two literals that are not assigned to false and put them
231  // on position 0 and 1.
232  int num_literal_not_false = 0;
233  for (int i = 0; i < size; ++i) {
234  if (!trail->Assignment().LiteralIsFalse(literals[i])) {
235  std::swap(literals[i], literals[num_literal_not_false]);
236  ++num_literal_not_false;
237  if (num_literal_not_false == 2) {
238  break;
239  }
240  }
241  }
242 
243  // Returns false if all the literals were false.
244  // This should only happen on an UNSAT problem, and there is no need to attach
245  // the clause in this case.
246  if (num_literal_not_false == 0) return false;
247 
248  if (num_literal_not_false == 1) {
249  // To maintain the validity of the 2-watcher algorithm, we need to watch
250  // the false literal with the highest decision level.
251  int max_level = trail->Info(literals[1].Variable()).level;
252  for (int i = 2; i < size; ++i) {
253  const int level = trail->Info(literals[i].Variable()).level;
254  if (level > max_level) {
255  max_level = level;
256  std::swap(literals[1], literals[i]);
257  }
258  }
259 
260  // Propagates literals[0] if it is unassigned.
261  if (!trail->Assignment().LiteralIsTrue(literals[0])) {
262  reasons_[trail->Index()] = clause;
263  trail->Enqueue(literals[0], propagator_id_);
264  }
265  }
266 
267  ++num_watched_clauses_;
268  AttachOnFalse(literals[0], literals[1], clause);
269  AttachOnFalse(literals[1], literals[0], clause);
270  return true;
271 }
272 
273 void LiteralWatchers::Attach(SatClause* clause, Trail* trail) {
274  Literal* literals = clause->literals();
275  CHECK(!trail->Assignment().LiteralIsAssigned(literals[0]));
276  CHECK(!trail->Assignment().LiteralIsAssigned(literals[1]));
277 
278  ++num_watched_clauses_;
279  AttachOnFalse(literals[0], literals[1], clause);
280  AttachOnFalse(literals[1], literals[0], clause);
281 }
282 
283 void LiteralWatchers::InternalDetach(SatClause* clause) {
284  --num_watched_clauses_;
285  const size_t size = clause->size();
286  if (drat_proof_handler_ != nullptr && size > 2) {
287  drat_proof_handler_->DeleteClause({clause->begin(), size});
288  }
289  clauses_info_.erase(clause);
290  clause->Clear();
291 }
292 
294  InternalDetach(clause);
295  is_clean_ = false;
296  needs_cleaning_.Set(clause->FirstLiteral().Index());
297  needs_cleaning_.Set(clause->SecondLiteral().Index());
298 }
299 
301  InternalDetach(clause);
302  for (const Literal l : {clause->FirstLiteral(), clause->SecondLiteral()}) {
303  needs_cleaning_.Clear(l.Index());
304  RemoveIf(&(watchers_on_false_[l.Index()]), [](const Watcher& watcher) {
305  return !watcher.clause->IsAttached();
306  });
307  }
308 }
309 
311  if (!all_clauses_are_attached_) return;
312  all_clauses_are_attached_ = false;
313 
314  // This is easy, and this allows to reset memory if some watcher lists where
315  // really long at some point.
316  is_clean_ = true;
317  num_watched_clauses_ = 0;
318  watchers_on_false_.clear();
319 }
320 
322  if (all_clauses_are_attached_) return;
323  all_clauses_are_attached_ = true;
324 
325  needs_cleaning_.ClearAll(); // This doesn't resize it.
326  watchers_on_false_.resize(needs_cleaning_.size().value());
327 
329  for (SatClause* clause : clauses_) {
330  ++num_watched_clauses_;
331  CHECK_GE(clause->size(), 2);
332  AttachOnFalse(clause->FirstLiteral(), clause->SecondLiteral(), clause);
333  AttachOnFalse(clause->SecondLiteral(), clause->FirstLiteral(), clause);
334  }
335 }
336 
337 // This one do not need the clause to be detached.
339  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
340  if (drat_proof_handler_ != nullptr) {
341  drat_proof_handler_->AddClause({true_literal});
342  }
343  // TODO(user): remove the test when the DRAT issue with fixed literal is
344  // resolved.
345  if (!trail_->Assignment().LiteralIsTrue(true_literal)) {
346  trail_->EnqueueWithUnitReason(true_literal);
347 
348  // Even when all clauses are detached, we can propagate the implication
349  // graph and we do that right away.
350  return implication_graph_->Propagate(trail_);
351  }
352  return true;
353 }
354 
355 // TODO(user): We could do something slower if the clauses are attached like
356 // we do for InprocessingRewriteClause().
358  CHECK(!all_clauses_are_attached_);
359  if (drat_proof_handler_ != nullptr) {
360  drat_proof_handler_->DeleteClause(clause->AsSpan());
361  }
362  clauses_info_.erase(clause);
363  clause->Clear();
364 }
365 
367  SatClause* clause, absl::Span<const Literal> new_clause) {
368  if (new_clause.empty()) return false; // UNSAT.
369 
370  if (DEBUG_MODE) {
371  for (const Literal l : new_clause) {
372  CHECK(!trail_->Assignment().LiteralIsAssigned(l));
373  }
374  }
375 
376  if (new_clause.size() == 1) {
377  if (!InprocessingFixLiteral(new_clause[0])) return false;
378  InprocessingRemoveClause(clause);
379  return true;
380  }
381 
382  if (new_clause.size() == 2) {
383  implication_graph_->AddBinaryClause(new_clause[0], new_clause[1]);
384  InprocessingRemoveClause(clause);
385  return true;
386  }
387 
388  if (drat_proof_handler_ != nullptr) {
389  // We must write the new clause before we delete the old one.
390  drat_proof_handler_->AddClause(new_clause);
391  drat_proof_handler_->DeleteClause(clause->AsSpan());
392  }
393 
394  if (all_clauses_are_attached_) {
395  // We can still rewrite the clause, but it is inefficient. We first
396  // detach it in a non-lazy way.
397  --num_watched_clauses_;
398  clause->Clear();
399  for (const Literal l : {clause->FirstLiteral(), clause->SecondLiteral()}) {
400  needs_cleaning_.Clear(l.Index());
401  RemoveIf(&(watchers_on_false_[l.Index()]), [](const Watcher& watcher) {
402  return !watcher.clause->IsAttached();
403  });
404  }
405  }
406 
407  clause->Rewrite(new_clause);
408 
409  // And we re-attach it.
410  if (all_clauses_are_attached_) Attach(clause, trail_);
411  return true;
412 }
413 
415  absl::Span<const Literal> new_clause) {
416  CHECK(!new_clause.empty());
417  CHECK(!all_clauses_are_attached_);
418  if (DEBUG_MODE) {
419  for (const Literal l : new_clause) {
420  CHECK(!trail_->Assignment().LiteralIsAssigned(l));
421  }
422  }
423 
424  if (new_clause.size() == 1) {
425  // TODO(user): We should return false...
426  if (!InprocessingFixLiteral(new_clause[0])) return nullptr;
427  return nullptr;
428  }
429 
430  if (new_clause.size() == 2) {
431  implication_graph_->AddBinaryClause(new_clause[0], new_clause[1]);
432  return nullptr;
433  }
434 
435  SatClause* clause = SatClause::Create(new_clause);
436  clauses_.push_back(clause);
437  return clause;
438 }
439 
441  SCOPED_TIME_STAT(&stats_);
442  for (LiteralIndex index : needs_cleaning_.PositionsSetAtLeastOnce()) {
443  DCHECK(needs_cleaning_[index]);
444  RemoveIf(&(watchers_on_false_[index]), [](const Watcher& watcher) {
445  return !watcher.clause->IsAttached();
446  });
447  needs_cleaning_.Clear(index);
448  }
449  needs_cleaning_.NotifyAllClear();
450  is_clean_ = true;
451 }
452 
454  DCHECK(is_clean_);
455 
456  // Update to_minimize_index_.
457  if (to_minimize_index_ >= clauses_.size()) {
458  to_minimize_index_ = clauses_.size();
459  }
460  to_minimize_index_ =
461  std::stable_partition(clauses_.begin(),
462  clauses_.begin() + to_minimize_index_,
463  [](SatClause* a) { return a->IsAttached(); }) -
464  clauses_.begin();
465 
466  // Do the proper deletion.
467  std::vector<SatClause*>::iterator iter =
468  std::stable_partition(clauses_.begin(), clauses_.end(),
469  [](SatClause* a) { return a->IsAttached(); });
470  gtl::STLDeleteContainerPointers(iter, clauses_.end());
471  clauses_.erase(iter, clauses_.end());
472 }
473 
474 // ----- BinaryImplicationGraph -----
475 
476 void BinaryImplicationGraph::Resize(int num_variables) {
477  SCOPED_TIME_STAT(&stats_);
478  implications_.resize(num_variables << 1);
479  is_redundant_.resize(implications_.size(), false);
480  is_removed_.resize(implications_.size(), false);
481  estimated_sizes_.resize(implications_.size(), 0);
482  in_direct_implications_.resize(implications_.size(), false);
483  reasons_.resize(num_variables);
484 }
485 
486 // TODO(user): Not all of the solver knows about representative literal, do
487 // use them here and in AddBinaryClauseDuringSearch() to maintains invariant?
488 // Explore this when we start cleaning our clauses using equivalence during
489 // search. We can easily do it for every conflict we learn instead of here.
491  SCOPED_TIME_STAT(&stats_);
492  if (drat_proof_handler_ != nullptr) {
493  // TODO(user): Like this we will duplicate all binary clause from the
494  // problem. However this leads to a simpler API (since we don't need to
495  // special case the loading of the original clauses) and we mainly use drat
496  // proof for testing anyway.
497  drat_proof_handler_->AddClause({a, b});
498  }
499  estimated_sizes_[a.NegatedIndex()]++;
500  estimated_sizes_[b.NegatedIndex()]++;
501  implications_[a.NegatedIndex()].push_back(b);
502  implications_[b.NegatedIndex()].push_back(a);
503  is_dag_ = false;
504  num_implications_ += 2;
505 }
506 
508  SCOPED_TIME_STAT(&stats_);
509  if (num_implications_ == 0) propagation_trail_index_ = trail_->Index();
510  AddBinaryClause(a, b);
511 
512  const auto& assignment = trail_->Assignment();
513  if (assignment.LiteralIsFalse(a)) {
514  if (assignment.LiteralIsAssigned(b)) {
515  if (assignment.LiteralIsFalse(b)) return false;
516  } else {
517  reasons_[trail_->Index()] = a;
518  trail_->Enqueue(b, propagator_id_);
519  }
520  } else if (assignment.LiteralIsFalse(b)) {
521  if (!assignment.LiteralIsAssigned(a)) {
522  reasons_[trail_->Index()] = b;
523  trail_->Enqueue(a, propagator_id_);
524  }
525  }
526  is_dag_ = false;
527  return true;
528 }
529 
531  absl::Span<const Literal> at_most_one) {
532  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
533  if (at_most_one.size() <= 1) return true;
534 
535  // Temporarily copy the at_most_one constraint at the end of
536  // at_most_one_buffer_. It will be cleaned up and added by
537  // CleanUpAndAddAtMostOnes().
538  const int base_index = at_most_one_buffer_.size();
539  at_most_one_buffer_.insert(at_most_one_buffer_.end(), at_most_one.begin(),
540  at_most_one.end());
541  at_most_one_buffer_.push_back(Literal(kNoLiteralIndex));
542 
543  is_dag_ = false;
544  return CleanUpAndAddAtMostOnes(base_index);
545 }
546 
547 // TODO(user): remove duplication with
548 // LiteralWatchers::InprocessingFixLiteral();
549 bool BinaryImplicationGraph::FixLiteral(Literal true_literal) {
550  if (trail_->Assignment().LiteralIsTrue(true_literal)) return true;
551  if (trail_->Assignment().LiteralIsFalse(true_literal)) return false;
552 
553  if (drat_proof_handler_ != nullptr) {
554  drat_proof_handler_->AddClause({true_literal});
555  }
556 
557  trail_->EnqueueWithUnitReason(true_literal);
558  return Propagate(trail_);
559 }
560 
561 // This works by doing a linear scan on the at_most_one_buffer_ and
562 // cleaning/copying the at most ones on the fly to the beginning of the same
563 // buffer.
564 bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(const int base_index) {
565  const VariablesAssignment& assignment = trail_->Assignment();
566  int local_end = base_index;
567  const int buffer_size = at_most_one_buffer_.size();
568  for (int i = base_index; i < buffer_size; ++i) {
569  if (at_most_one_buffer_[i].Index() == kNoLiteralIndex) continue;
570 
571  // Process a new at most one.
572  // It will be copied into buffer[local_start, local_end].
573  const int local_start = local_end;
574  bool set_all_left_to_false = false;
575  for (;; ++i) {
576  const Literal l = at_most_one_buffer_[i];
577  if (l.Index() == kNoLiteralIndex) break;
578  if (assignment.LiteralIsFalse(l)) continue;
579  if (is_removed_[l.Index()]) continue;
580  if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
581  set_all_left_to_false = true;
582  continue;
583  }
584  at_most_one_buffer_[local_end++] = RepresentativeOf(l);
585  }
586 
587  // Deal with all false.
588  if (set_all_left_to_false) {
589  for (int j = local_start; j < local_end; ++j) {
590  const Literal l = at_most_one_buffer_[j];
591  if (assignment.LiteralIsFalse(l)) continue;
592  if (assignment.LiteralIsTrue(l)) return false;
593  if (!FixLiteral(l.Negated())) return false;
594  }
595  local_end = local_start;
596  continue;
597  }
598 
599  // Deal with duplicates.
600  // Any duplicate in an "at most one" must be false.
601  {
602  int new_local_end = local_start;
603  std::sort(&at_most_one_buffer_[local_start],
604  &at_most_one_buffer_[local_end]);
605  for (int j = local_start; j < local_end; ++j) {
606  const Literal l = at_most_one_buffer_[j];
607  if (new_local_end > local_start &&
608  l == at_most_one_buffer_[new_local_end - 1]) {
609  if (assignment.LiteralIsTrue(l)) return false;
610  if (!assignment.LiteralIsFalse(l)) {
611  if (!FixLiteral(l.Negated())) return false;
612  }
613  --new_local_end;
614  continue;
615  }
616  at_most_one_buffer_[new_local_end++] = l;
617  }
618  local_end = new_local_end;
619  }
620 
621  // Create a Span<> to simplify the code below.
622  const absl::Span<const Literal> at_most_one(
623  &at_most_one_buffer_[local_start], local_end - local_start);
624 
625  // We expand small sizes into implications.
626  // TODO(user): Investigate what the best threshold is.
627  if (at_most_one.size() < 10) {
628  // Note that his automatically skip size 0 and 1.
629  for (const Literal a : at_most_one) {
630  for (const Literal b : at_most_one) {
631  if (a == b) continue;
632  implications_[a.Index()].push_back(b.Negated());
633  }
634  }
635  num_implications_ += at_most_one.size() * (at_most_one.size() - 1);
636 
637  // This will erase the at_most_one from the buffer.
638  local_end = local_start;
639  continue;
640  }
641 
642  // Index the new at most one.
643  for (const Literal l : at_most_one) {
644  if (l.Index() >= at_most_ones_.size()) {
645  at_most_ones_.resize(l.Index().value() + 1);
646  }
647  CHECK(!is_redundant_[l.Index()]);
648  at_most_ones_[l.Index()].push_back(local_start);
649  }
650 
651  // Add sentinel.
652  at_most_one_buffer_[local_end++] = Literal(kNoLiteralIndex);
653  }
654 
655  at_most_one_buffer_.resize(local_end);
656  return true;
657 }
658 
659 bool BinaryImplicationGraph::PropagateOnTrue(Literal true_literal,
660  Trail* trail) {
661  SCOPED_TIME_STAT(&stats_);
662 
663  const VariablesAssignment& assignment = trail->Assignment();
664  DCHECK(assignment.LiteralIsTrue(true_literal));
665 
666  // Note(user): This update is not exactly correct because in case of conflict
667  // we don't inspect that much clauses. But doing ++num_inspections_ inside the
668  // loop does slow down the code by a few percent.
669  num_inspections_ += implications_[true_literal.Index()].size();
670 
671  for (Literal literal : implications_[true_literal.Index()]) {
672  if (assignment.LiteralIsTrue(literal)) {
673  // Note(user): I tried to update the reason here if the literal was
674  // enqueued after the true_literal on the trail. This property is
675  // important for ComputeFirstUIPConflict() to work since it needs the
676  // trail order to be a topological order for the deduction graph.
677  // But the performance was not too good...
678  continue;
679  }
680 
681  ++num_propagations_;
682  if (assignment.LiteralIsFalse(literal)) {
683  // Conflict.
684  *(trail->MutableConflict()) = {true_literal.Negated(), literal};
685  return false;
686  } else {
687  // Propagation.
688  reasons_[trail->Index()] = true_literal.Negated();
689  trail->Enqueue(literal, propagator_id_);
690  }
691  }
692 
693  // Propagate the at_most_one constraints.
694  if (true_literal.Index() < at_most_ones_.size()) {
695  for (const int start : at_most_ones_[true_literal.Index()]) {
696  bool seen = false;
697  for (int i = start;; ++i) {
698  const Literal literal = at_most_one_buffer_[i];
699  if (literal.Index() == kNoLiteralIndex) break;
700 
701  ++num_inspections_;
702  if (literal == true_literal) {
703  if (DEBUG_MODE) {
704  CHECK(!seen);
705  seen = true;
706  }
707  continue;
708  }
709  if (assignment.LiteralIsFalse(literal)) continue;
710 
711  ++num_propagations_;
712  if (assignment.LiteralIsTrue(literal)) {
713  // Conflict.
714  *(trail->MutableConflict()) = {true_literal.Negated(),
715  literal.Negated()};
716  return false;
717  } else {
718  // Propagation.
719  reasons_[trail->Index()] = true_literal.Negated();
720  trail->Enqueue(literal.Negated(), propagator_id_);
721  }
722  }
723  }
724  }
725 
726  return true;
727 }
728 
730  if (IsEmpty()) {
731  propagation_trail_index_ = trail->Index();
732  return true;
733  }
734  while (propagation_trail_index_ < trail->Index()) {
735  const Literal literal = (*trail)[propagation_trail_index_++];
736  if (!PropagateOnTrue(literal, trail)) return false;
737  }
738  return true;
739 }
740 
741 absl::Span<const Literal> BinaryImplicationGraph::Reason(
742  const Trail& trail, int trail_index) const {
743  return {&reasons_[trail_index], 1};
744 }
745 
746 // Here, we remove all the literal whose negation are implied by the negation of
747 // the 1-UIP literal (which always appear first in the given conflict). Note
748 // that this algorithm is "optimal" in the sense that it leads to a minimized
749 // conflict with a backjump level as low as possible. However, not all possible
750 // literals are removed.
751 //
752 // TODO(user): Also consider at most one?
754  std::vector<Literal>* conflict) {
755  SCOPED_TIME_STAT(&stats_);
756  dfs_stack_.clear();
757 
758  // Compute the reachability from the literal "not(conflict->front())" using
759  // an iterative dfs.
760  const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
761  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
762  is_marked_.Set(root_literal_index);
763 
764  // TODO(user): This sounds like a good idea, but somehow it seems better not
765  // to do that even though it is almost for free. Investigate more.
766  //
767  // The idea here is that since we already compute the reachability from the
768  // root literal, we can use this computation to remove any implication
769  // root_literal => b if there is already root_literal => a and b is reachable
770  // from a.
771  const bool also_prune_direct_implication_list = false;
772 
773  // We treat the direct implications differently so we can also remove the
774  // redundant implications from this list at the same time.
775  auto& direct_implications = implications_[root_literal_index];
776  for (const Literal l : direct_implications) {
777  if (is_marked_[l.Index()]) continue;
778  dfs_stack_.push_back(l);
779  while (!dfs_stack_.empty()) {
780  const LiteralIndex index = dfs_stack_.back().Index();
781  dfs_stack_.pop_back();
782  if (!is_marked_[index]) {
783  is_marked_.Set(index);
784  for (Literal implied : implications_[index]) {
785  if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
786  }
787  }
788  }
789 
790  // The "trick" is to unmark 'l'. This way, if we explore it twice, it means
791  // that this l is reachable from some other 'l' from the direct implication
792  // list. Remarks:
793  // - We don't loose too much complexity when this happen since a literal
794  // can be unmarked only once, so in the worst case we loop twice over its
795  // children. Moreover, this literal will be pruned for later calls.
796  // - This is correct, i.e. we can't prune too many literals because of a
797  // strongly connected component. Proof by contradiction: If we take the
798  // first (in direct_implications) literal from a removed SCC, it must
799  // have marked all the others. But because they are marked, they will not
800  // be explored again and so can't mark the first literal.
801  if (also_prune_direct_implication_list) {
802  is_marked_.Clear(l.Index());
803  }
804  }
805 
806  // Now we can prune the direct implications list and make sure are the
807  // literals there are marked.
808  if (also_prune_direct_implication_list) {
809  int new_size = 0;
810  for (const Literal l : direct_implications) {
811  if (!is_marked_[l.Index()]) {
812  is_marked_.Set(l.Index());
813  direct_implications[new_size] = l;
814  ++new_size;
815  }
816  }
817  if (new_size < direct_implications.size()) {
818  num_redundant_implications_ += direct_implications.size() - new_size;
819  direct_implications.resize(new_size);
820  }
821  }
822 
823  RemoveRedundantLiterals(conflict);
824 }
825 
826 // Same as MinimizeConflictWithReachability() but also mark (in the given
827 // SparseBitset) the reachable literal already assigned to false. These literals
828 // will be implied if the 1-UIP literal is assigned to false, and the classic
829 // minimization algorithm can take advantage of that.
831  const Trail& trail, std::vector<Literal>* conflict,
833  SCOPED_TIME_STAT(&stats_);
834  CHECK(!conflict->empty());
835  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
836  MarkDescendants(conflict->front().Negated());
837  for (const LiteralIndex i : is_marked_.PositionsSetAtLeastOnce()) {
838  if (trail.Assignment().LiteralIsFalse(Literal(i))) {
839  marked->Set(Literal(i).Variable());
840  }
841  }
842  RemoveRedundantLiterals(conflict);
843 }
844 
845 // Same as MinimizeConflictFirst() but take advantage of this reachability
846 // computation to remove redundant implication in the implication list of the
847 // first UIP conflict.
849  const Trail& trail, std::vector<Literal>* conflict,
851  SCOPED_TIME_STAT(&stats_);
852  const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
853  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
854  is_marked_.Set(root_literal_index);
855 
856  int new_size = 0;
857  auto& direct_implications = implications_[root_literal_index];
858 
859  // The randomization allow to find more redundant implication since to find
860  // a => b and remove b, a must be before b in direct_implications. Note that
861  // a std::reverse() could work too. But randomization seems to work better.
862  // Probably because it has other impact on the search tree.
863  std::shuffle(direct_implications.begin(), direct_implications.end(), *random);
864  dfs_stack_.clear();
865  for (const Literal l : direct_implications) {
866  if (is_marked_[l.Index()]) {
867  // The literal is already marked! so it must be implied by one of the
868  // previous literal in the direct_implications list. We can safely remove
869  // it.
870  continue;
871  }
872  direct_implications[new_size++] = l;
873  dfs_stack_.push_back(l);
874  while (!dfs_stack_.empty()) {
875  const LiteralIndex index = dfs_stack_.back().Index();
876  dfs_stack_.pop_back();
877  if (!is_marked_[index]) {
878  is_marked_.Set(index);
879  for (Literal implied : implications_[index]) {
880  if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
881  }
882  }
883  }
884  }
885  if (new_size < direct_implications.size()) {
886  num_redundant_implications_ += direct_implications.size() - new_size;
887  direct_implications.resize(new_size);
888  }
889  RemoveRedundantLiterals(conflict);
890 }
891 
892 void BinaryImplicationGraph::RemoveRedundantLiterals(
893  std::vector<Literal>* conflict) {
894  SCOPED_TIME_STAT(&stats_);
895  int new_index = 1;
896  for (int i = 1; i < conflict->size(); ++i) {
897  if (!is_marked_[(*conflict)[i].NegatedIndex()]) {
898  (*conflict)[new_index] = (*conflict)[i];
899  ++new_index;
900  }
901  }
902  if (new_index < conflict->size()) {
903  ++num_minimization_;
904  num_literals_removed_ += conflict->size() - new_index;
905  conflict->resize(new_index);
906  }
907 }
908 
909 // TODO(user): Also consider at most one?
911  const Trail& trail, std::vector<Literal>* conflict) {
912  SCOPED_TIME_STAT(&stats_);
913  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
914  is_simplified_.ClearAndResize(LiteralIndex(implications_.size()));
915  for (Literal lit : *conflict) {
916  is_marked_.Set(lit.Index());
917  }
918 
919  // Identify and remove the redundant literals from the given conflict.
920  // 1/ If a -> b then a can be removed from the conflict clause.
921  // This is because not b -> not a.
922  // 2/ a -> b can only happen if level(a) <= level(b).
923  // 3/ Because of 2/, cycles can appear only at the same level.
924  // The vector is_simplified_ is used to avoid removing all elements of a
925  // cycle. Note that this is not optimal in the sense that we may not remove
926  // a literal that can be removed.
927  //
928  // Note that there is no need to explore the unique literal of the highest
929  // decision level since it can't be removed. Because this is a conflict, such
930  // literal is always at position 0, so we start directly at 1.
931  int index = 1;
932  for (int i = 1; i < conflict->size(); ++i) {
933  const Literal lit = (*conflict)[i];
934  const int lit_level = trail.Info(lit.Variable()).level;
935  bool keep_literal = true;
936  for (Literal implied : implications_[lit.Index()]) {
937  if (is_marked_[implied.Index()]) {
938  DCHECK_LE(lit_level, trail.Info(implied.Variable()).level);
939  if (lit_level == trail.Info(implied.Variable()).level &&
940  is_simplified_[implied.Index()]) {
941  continue;
942  }
943  keep_literal = false;
944  break;
945  }
946  }
947  if (keep_literal) {
948  (*conflict)[index] = lit;
949  ++index;
950  } else {
951  is_simplified_.Set(lit.Index());
952  }
953  }
954  if (index < conflict->size()) {
955  ++num_minimization_;
956  num_literals_removed_ += conflict->size() - index;
957  conflict->erase(conflict->begin() + index, conflict->end());
958  }
959 }
960 
962  SCOPED_TIME_STAT(&stats_);
963  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
964 
965  // Nothing to do if nothing changed since last call.
966  const int new_num_fixed = trail_->Index();
967  if (num_processed_fixed_variables_ == new_num_fixed) return;
968 
969  const VariablesAssignment& assignment = trail_->Assignment();
970  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
971  for (; num_processed_fixed_variables_ < new_num_fixed;
972  ++num_processed_fixed_variables_) {
973  const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
974  if (DEBUG_MODE) {
975  // The code assumes that everything is already propagated.
976  // Otherwise we will remove implications that didn't propagate yet!
977  for (const Literal lit : implications_[true_literal.Index()]) {
978  CHECK(trail_->Assignment().LiteralIsTrue(lit));
979  }
980  }
981 
982  // If b is true and a -> b then because not b -> not a, all the
983  // implications list that contains b will be marked by this process.
984  // And the ones that contains not(b) should correspond to a false literal!
985  //
986  // TODO(user): This might not be true if we remove implication by
987  // transitive reduction and the process was aborted due to the computation
988  // limit. I think it will be good to maintain that invariant though,
989  // otherwise fixed literals might never be removed from these lists...
990  for (const Literal lit : implications_[true_literal.NegatedIndex()]) {
991  is_marked_.Set(lit.NegatedIndex());
992  }
993  gtl::STLClearObject(&(implications_[true_literal.Index()]));
994  gtl::STLClearObject(&(implications_[true_literal.NegatedIndex()]));
995 
996  if (true_literal.Index() < at_most_ones_.size()) {
997  gtl::STLClearObject(&(at_most_ones_[true_literal.Index()]));
998  }
999  if (true_literal.NegatedIndex() < at_most_ones_.size()) {
1000  gtl::STLClearObject(&(at_most_ones_[true_literal.NegatedIndex()]));
1001  }
1002  }
1003  for (const LiteralIndex i : is_marked_.PositionsSetAtLeastOnce()) {
1004  RemoveIf(&implications_[i], [&assignment](const Literal& lit) {
1005  return assignment.LiteralIsTrue(lit);
1006  });
1007  }
1008 
1009  // TODO(user): This might be a bit slow. Do not call all the time if needed,
1010  // this shouldn't change the correctness of the code.
1011  at_most_ones_.clear();
1012  CleanUpAndAddAtMostOnes(/*base_index=*/0);
1013 }
1014 
1015 class SccGraph {
1016  public:
1017  using Implication =
1020  using SccFinder =
1022  std::vector<std::vector<int32>>>;
1023 
1024  explicit SccGraph(SccFinder* finder, Implication* graph,
1025  AtMostOne* at_most_ones,
1026  std::vector<Literal>* at_most_one_buffer)
1027  : finder_(*finder),
1028  implications_(*graph),
1029  at_most_ones_(*at_most_ones),
1030  at_most_one_buffer_(*at_most_one_buffer) {}
1031 
1032  const std::vector<int32>& operator[](int32 node) const {
1033  tmp_.clear();
1034  for (const Literal l : implications_[LiteralIndex(node)]) {
1035  tmp_.push_back(l.Index().value());
1036  if (finder_.NodeIsInCurrentDfsPath(l.NegatedIndex().value())) {
1037  to_fix_.push_back(l);
1038  }
1039  }
1040  if (node < at_most_ones_.size()) {
1041  for (const int start : at_most_ones_[LiteralIndex(node)]) {
1042  if (start >= at_most_one_already_explored_.size()) {
1043  at_most_one_already_explored_.resize(start + 1, false);
1044  previous_node_to_explore_at_most_one_.resize(start + 1);
1045  }
1046 
1047  // In the presence of at_most_ones_ contraints, expanding them
1048  // implicitely to implications in the SCC computation can result in a
1049  // quadratic complexity rather than a linear one in term of the input
1050  // data structure size. So this test here is critical on problem with
1051  // large at_most ones like the "ivu06-big.mps.gz" where without it, the
1052  // full FindStronglyConnectedComponents() take more than on hour instead
1053  // of less than a second!
1054  if (at_most_one_already_explored_[start]) {
1055  // We never expand a node twice.
1056  const int first_node = previous_node_to_explore_at_most_one_[start];
1057  CHECK_NE(node, first_node);
1058 
1059  if (finder_.NodeIsInCurrentDfsPath(first_node)) {
1060  // If the first node is not settled, then we do explore the
1061  // at_most_one constraint again. In "Mixed-Integer-Programming:
1062  // Analyzing 12 years of progress", Tobias Achterberg and Roland
1063  // Wunderling explains that an at most one need to be looped over at
1064  // most twice. I am not sure exactly how that works, so for now we
1065  // are not fully linear, but on actual instances, we only rarely
1066  // run into this case.
1067  //
1068  // Note that we change the previous node to explore at most one
1069  // since the current node will be settled before the old ones.
1070  //
1071  // TODO(user): avoid looping more than twice on the same at most one
1072  // constraints? Note that the second time we loop we have x => y =>
1073  // not(x), so we can already detect that x must be false which we
1074  // detect below.
1075  previous_node_to_explore_at_most_one_[start] = node;
1076  } else {
1077  // The first node is already settled and so are all its child. Only
1078  // not(first_node) might still need exploring.
1079  tmp_.push_back(
1080  Literal(LiteralIndex(first_node)).NegatedIndex().value());
1081  continue;
1082  }
1083  } else {
1084  at_most_one_already_explored_[start] = true;
1085  previous_node_to_explore_at_most_one_[start] = node;
1086  }
1087 
1088  for (int i = start;; ++i) {
1089  const Literal l = at_most_one_buffer_[i];
1090  if (l.Index() == kNoLiteralIndex) break;
1091  if (l.Index() == node) continue;
1092  tmp_.push_back(l.NegatedIndex().value());
1093  if (finder_.NodeIsInCurrentDfsPath(l.Index().value())) {
1094  to_fix_.push_back(l.Negated());
1095  }
1096  }
1097  }
1098  }
1099  work_done_ += tmp_.size();
1100  return tmp_;
1101  }
1102 
1103  // All these literals where detected to be true during the SCC computation.
1104  mutable std::vector<Literal> to_fix_;
1105 
1106  // For the deterministic time.
1107  mutable int64 work_done_ = 0;
1108 
1109  private:
1110  const SccFinder& finder_;
1111  const Implication& implications_;
1112  const AtMostOne& at_most_ones_;
1113  const std::vector<Literal>& at_most_one_buffer_;
1114 
1115  mutable std::vector<int32> tmp_;
1116 
1117  // Used to get a non-quadratic complexity in the presence of at most ones.
1118  mutable std::vector<bool> at_most_one_already_explored_;
1119  mutable std::vector<int> previous_node_to_explore_at_most_one_;
1120 };
1121 
1123  // This was already called, and no new constraint where added. Note that new
1124  // fixed variable cannote create new equivalence, only new binary clauses do.
1125  if (is_dag_) return true;
1127  wall_timer.Start();
1128  log_info |= VLOG_IS_ON(1);
1129 
1130  // Lets remove all fixed variables first.
1131  if (!Propagate(trail_)) return false;
1133  const VariablesAssignment& assignment = trail_->Assignment();
1134 
1135  // TODO(user): We could just do it directly though.
1136  int num_fixed_during_scc = 0;
1137  const int32 size(implications_.size());
1138  std::vector<std::vector<int32>> scc;
1139  double dtime = 0.0;
1140  {
1141  SccGraph::SccFinder finder;
1142  SccGraph graph(&finder, &implications_, &at_most_ones_,
1143  &at_most_one_buffer_);
1144  finder.FindStronglyConnectedComponents(size, graph, &scc);
1145  dtime += 4e-8 * graph.work_done_;
1146 
1147  for (const Literal l : graph.to_fix_) {
1148  if (assignment.LiteralIsFalse(l)) return false;
1149  if (assignment.LiteralIsTrue(l)) continue;
1150  ++num_fixed_during_scc;
1151  if (!FixLiteral(l)) return false;
1152  }
1153  }
1154 
1155  // The old values will still be valid.
1156  representative_of_.resize(size, kNoLiteralIndex);
1157  is_redundant_.resize(size, false);
1158 
1159  int num_equivalences = 0;
1160  reverse_topological_order_.clear();
1161  for (std::vector<int32>& component : scc) {
1162  // If one is fixed then all must be fixed. Note that the reason why the
1163  // propagation didn't already do that and we don't always get fixed
1164  // component of size 1 is because of the potential newly fixed literals
1165  // above.
1166  //
1167  // In any case, all fixed literals are marked as redundant.
1168  {
1169  bool all_fixed = false;
1170  bool all_true = false;
1171  for (const int32 i : component) {
1172  const Literal l = Literal(LiteralIndex(i));
1173  if (trail_->Assignment().LiteralIsAssigned(l)) {
1174  all_fixed = true;
1175  all_true = trail_->Assignment().LiteralIsTrue(l);
1176  break;
1177  }
1178  }
1179  if (all_fixed) {
1180  for (const int32 i : component) {
1181  const Literal l = Literal(LiteralIndex(i));
1182  if (!is_redundant_[l.Index()]) {
1183  ++num_redundant_literals_;
1184  is_redundant_[l.Index()] = true;
1185  }
1186  const Literal to_fix = all_true ? l : l.Negated();
1187  if (assignment.LiteralIsFalse(to_fix)) return false;
1188  if (assignment.LiteralIsTrue(to_fix)) continue;
1189  ++num_fixed_during_scc;
1190  if (!FixLiteral(l)) return false;
1191  }
1192 
1193  // Next component.
1194  continue;
1195  }
1196  }
1197 
1198  // We ignore variable that appear in no constraints.
1199  if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1200  continue;
1201  }
1202 
1203  // We always take the smallest literal index (which also corresponds to the
1204  // smallest BooleanVariable index) as a representative. This make sure that
1205  // the representative of a literal l and the one of not(l) will be the
1206  // negation of each other. There is also reason to think that it is
1207  // heuristically better to use a BooleanVariable that was created first.
1208  std::sort(component.begin(), component.end());
1209  const LiteralIndex representative(component[0]);
1210  reverse_topological_order_.push_back(representative);
1211 
1212  if (component.size() == 1) {
1213  // Note that because we process list in reverse topological order, this
1214  // is only needed if there is any equivalence before this point.
1215  if (num_equivalences > 0) {
1216  auto& representative_list = implications_[representative];
1217  for (Literal& ref : representative_list) {
1218  const LiteralIndex rep = representative_of_[ref.Index()];
1219  if (rep == representative) continue;
1220  if (rep == kNoLiteralIndex) continue;
1221  ref = Literal(rep);
1222  }
1223  gtl::STLSortAndRemoveDuplicates(&representative_list);
1224  }
1225  continue;
1226  }
1227 
1228  // Sets the representative.
1229  for (int i = 1; i < component.size(); ++i) {
1230  const Literal literal = Literal(LiteralIndex(component[i]));
1231  if (!is_redundant_[literal.Index()]) {
1232  ++num_redundant_literals_;
1233  is_redundant_[literal.Index()] = true;
1234  }
1235  representative_of_[literal.Index()] = representative;
1236 
1237  // Detect if x <=> not(x) which means unsat. Note that we relly on the
1238  // fact that when sorted, they will both be consecutive in the list.
1239  if (Literal(LiteralIndex(component[i - 1])).Negated() == literal) {
1240  LOG_IF(INFO, log_info) << "Trivially UNSAT in DetectEquivalences()";
1241  return false;
1242  }
1243  }
1244 
1245  // Merge all the lists in implications_[representative].
1246  // Note that we do not want representative in its own list.
1247  auto& representative_list = implications_[representative];
1248  int new_size = 0;
1249  for (const Literal l : representative_list) {
1250  const Literal rep = RepresentativeOf(l);
1251  if (rep.Index() == representative) continue;
1252  representative_list[new_size++] = rep;
1253  }
1254  representative_list.resize(new_size);
1255  for (int i = 1; i < component.size(); ++i) {
1256  const Literal literal = Literal(LiteralIndex(component[i]));
1257  auto& ref = implications_[literal.Index()];
1258  for (const Literal l : ref) {
1259  const Literal rep = RepresentativeOf(l);
1260  if (rep.Index() != representative) representative_list.push_back(rep);
1261  }
1262 
1263  // Add representative <=> literal.
1264  //
1265  // Remark: this relation do not need to be added to a DRAT proof since
1266  // the redundant variables should never be used again for a pure SAT
1267  // problem.
1268  representative_list.push_back(literal);
1269  ref.clear();
1270  ref.push_back(Literal(representative));
1271  }
1272  gtl::STLSortAndRemoveDuplicates(&representative_list);
1273  num_equivalences += component.size() - 1;
1274  }
1275 
1276  is_dag_ = true;
1277  if (num_equivalences != 0) {
1278  // Remap all at most ones. Remove fixed variables, process duplicates. Note
1279  // that this might result in more implications when we expand small at most
1280  // one.
1281  at_most_ones_.clear();
1282  CleanUpAndAddAtMostOnes(/*base_index=*/0);
1283 
1284  num_implications_ = 0;
1285  for (LiteralIndex i(0); i < size; ++i) {
1286  num_implications_ += implications_[i].size();
1287  }
1288  dtime += 2e-8 * num_implications_;
1289  }
1290 
1291  time_limit_->AdvanceDeterministicTime(dtime);
1292  LOG_IF(INFO, log_info) << "SCC. " << num_equivalences
1293  << " redundant equivalent literals. "
1294  << num_fixed_during_scc << " fixed. "
1295  << num_implications_ << " implications left. "
1296  << implications_.size() << " literals."
1297  << " size of at_most_one buffer = "
1298  << at_most_one_buffer_.size() << "."
1299  << " dtime: " << dtime
1300  << " wtime: " << wall_timer.Get();
1301  return true;
1302 }
1303 
1304 // Note that as a side effect this also do a full "failed literal probing"
1305 // using the binary implication graph only.
1306 //
1307 // TODO(user): Track which literal have new implications, and only process
1308 // the antecedants of these.
1310  CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1311  if (!DetectEquivalences()) return false;
1312 
1313  // TODO(user): the situation with fixed variable is not really "clean".
1314  // Simplify the code so we are sure we don't run into issue or have to deal
1315  // with any of that here.
1316  if (!Propagate(trail_)) return false;
1318 
1319  log_info |= VLOG_IS_ON(1);
1321  wall_timer.Start();
1322 
1323  int64 num_fixed = 0;
1324  int64 num_new_redundant_implications = 0;
1325  bool aborted = false;
1326  work_done_in_mark_descendants_ = 0;
1327  int marked_index = 0;
1328 
1329  // For each node we do a graph traversal and only keep the literals
1330  // at maximum distance 1. This only works because we have a DAG when ignoring
1331  // the "redundant" literal marked by DetectEquivalences(). Note that we also
1332  // need no duplicates in the implications list for correctness which is also
1333  // guaranteed by DetectEquivalences().
1334  //
1335  // TODO(user): We should be able to reuse some propagation like it is done for
1336  // tree-look. Once a node is processed, we just need to process a node that
1337  // implies it. Test if we can make this faster. Alternatively, only clear
1338  // a part of is_marked_ (after the first child of root in reverse topo order).
1339  //
1340  // TODO(user): Can we exploit the fact that the implication graph is a
1341  // skew-symmetric graph (isomorphic to its transposed) so that we do less
1342  // work? Also it would be nice to keep the property that even if we abort
1343  // during the algorithm, if a => b, then not(b) => not(a) is also present in
1344  // the other direct implication list.
1345  const LiteralIndex size(implications_.size());
1346  LiteralIndex previous = kNoLiteralIndex;
1347  for (const LiteralIndex root : reverse_topological_order_) {
1348  // In most situation reverse_topological_order_ contains no redundant, fixed
1349  // or removed variables. But the reverse_topological_order_ is only
1350  // recomputed when new binary are added to the graph, not when new variable
1351  // are fixed.
1352  if (is_redundant_[root]) continue;
1353  if (trail_->Assignment().LiteralIsAssigned(Literal(root))) continue;
1354 
1355  auto& direct_implications = implications_[root];
1356  if (direct_implications.empty()) continue;
1357 
1358  // This is a "poor" version of the tree look stuff, but it does show good
1359  // improvement. If we just processed one of the child of root, we don't
1360  // need to re-explore it.
1361  //
1362  // TODO(user): Another optim we can do is that we never need to expand
1363  // any node with a reverse topo order smaller or equal to the min of the
1364  // ones in this list.
1365  bool clear_previous_reachability = true;
1366  for (const Literal direct_child : direct_implications) {
1367  if (direct_child.Index() == previous) {
1368  clear_previous_reachability = false;
1369  is_marked_.Clear(previous);
1370  break;
1371  }
1372  }
1373  if (clear_previous_reachability) {
1374  is_marked_.ClearAndResize(size);
1375  marked_index = 0;
1376  }
1377  previous = root;
1378 
1379  for (const Literal direct_child : direct_implications) {
1380  if (is_redundant_[direct_child.Index()]) continue;
1381  if (is_marked_[direct_child.Index()]) continue;
1382 
1383  // This is a corner case where because of equivalent literal, root
1384  // appear in implications_[root], we will remove it below.
1385  if (direct_child.Index() == root) continue;
1386 
1387  // When this happens, then root must be false, we handle this just after
1388  // the loop.
1389  if (direct_child.NegatedIndex() == root) {
1390  is_marked_.Set(direct_child.Index());
1391  break;
1392  }
1393 
1394  MarkDescendants(direct_child);
1395 
1396  // We have a DAG, so direct_child could only be marked first.
1397  is_marked_.Clear(direct_child.Index());
1398  }
1399  CHECK(!is_marked_[root])
1400  << "DetectEquivalences() should have removed cycles!";
1401  is_marked_.Set(root);
1402 
1403  // Failed literal probing. If both x and not(x) are marked then root must be
1404  // false. Note that because we process "roots" in reverse topological order,
1405  // we will fix the LCA of x and not(x) first.
1406  const auto& marked_positions = is_marked_.PositionsSetAtLeastOnce();
1407  for (; marked_index < marked_positions.size(); ++marked_index) {
1408  const LiteralIndex i = marked_positions[marked_index];
1409  if (is_marked_[Literal(i).NegatedIndex()]) {
1410  // We tested that at the beginning.
1411  CHECK(!trail_->Assignment().LiteralIsAssigned(Literal(root)));
1412 
1413  // We propagate right away the binary implications so that we do not
1414  // need to consider all antecedants of root in the transitive
1415  // reduction.
1416  ++num_fixed;
1417  if (!FixLiteral(Literal(root).Negated())) return false;
1418  break;
1419  }
1420  }
1421 
1422  // Note that direct_implications will be cleared by
1423  // RemoveFixedVariables() that will need to inspect it to completely
1424  // remove Literal(root) from all lists.
1425  if (trail_->Assignment().LiteralIsAssigned(Literal(root))) continue;
1426 
1427  // Only keep the non-marked literal (and the redundant one which are never
1428  // marked). We mark root to remove it in the corner case where it was
1429  // there.
1430  int new_size = 0;
1431  for (const Literal l : direct_implications) {
1432  if (!is_marked_[l.Index()]) {
1433  direct_implications[new_size++] = l;
1434  } else {
1435  CHECK(!is_redundant_[l.Index()]);
1436  }
1437  }
1438  const int diff = direct_implications.size() - new_size;
1439  direct_implications.resize(new_size);
1440  direct_implications.shrink_to_fit();
1441  num_new_redundant_implications += diff;
1442  num_implications_ -= diff;
1443 
1444  // Abort if the computation involved is too big.
1445  if (work_done_in_mark_descendants_ > 1e8) {
1446  aborted = true;
1447  break;
1448  }
1449  }
1450 
1451  is_marked_.ClearAndResize(size);
1452 
1453  const double dtime = 1e-8 * work_done_in_mark_descendants_;
1454  time_limit_->AdvanceDeterministicTime(dtime);
1455  num_redundant_implications_ += num_new_redundant_implications;
1456  LOG_IF(INFO, log_info) << "Transitive reduction removed "
1457  << num_new_redundant_implications << " literals. "
1458  << num_fixed << " fixed. " << num_implications_
1459  << " implications left. " << implications_.size()
1460  << " literals."
1461  << " dtime: " << dtime
1462  << " wtime: " << wall_timer.Get()
1463  << (aborted ? " Aborted." : "");
1464  return true;
1465 }
1466 
1467 namespace {
1468 
1469 bool IntersectionIsEmpty(const std::vector<int>& a, const std::vector<int>& b) {
1470  DCHECK(std::is_sorted(a.begin(), a.end()));
1471  DCHECK(std::is_sorted(b.begin(), b.end()));
1472  int i = 0;
1473  int j = 0;
1474  for (; i < a.size() && j < b.size();) {
1475  if (a[i] == b[j]) return false;
1476  if (a[i] < b[j]) {
1477  ++i;
1478  } else {
1479  ++j;
1480  }
1481  }
1482  return true;
1483 }
1484 
1485 // Used by TransformIntoMaxCliques().
1486 struct VectorHash {
1487  std::size_t operator()(const std::vector<Literal>& at_most_one) const {
1488  size_t hash = 0;
1489  for (Literal literal : at_most_one) {
1490  hash = util_hash::Hash(literal.Index().value(), hash);
1491  }
1492  return hash;
1493  }
1494 };
1495 
1496 } // namespace
1497 
1499  std::vector<std::vector<Literal>>* at_most_ones,
1500  int64 max_num_explored_nodes) {
1501  // The code below assumes a DAG.
1502  if (!DetectEquivalences()) return false;
1503  work_done_in_mark_descendants_ = 0;
1504 
1505  int num_extended = 0;
1506  int num_removed = 0;
1507  int num_added = 0;
1508 
1509  absl::flat_hash_set<std::vector<Literal>, VectorHash> max_cliques;
1510  gtl::ITIVector<LiteralIndex, std::vector<int>> max_cliques_containing(
1511  implications_.size());
1512 
1513  // We starts by processing larger constraints first.
1514  std::sort(at_most_ones->begin(), at_most_ones->end(),
1515  [](const std::vector<Literal> a, const std::vector<Literal> b) {
1516  return a.size() > b.size();
1517  });
1518  for (std::vector<Literal>& clique : *at_most_ones) {
1519  const int old_size = clique.size();
1520 
1521  // Remap the clique to only use representative.
1522  //
1523  // Note(user): Because we always use literal with the smallest variable
1524  // indices as representative, this make sure that if possible, we express
1525  // the clique in term of user provided variable (that are always created
1526  // first).
1527  for (Literal& ref : clique) {
1528  DCHECK_LT(ref.Index(), representative_of_.size());
1529  const LiteralIndex rep = representative_of_[ref.Index()];
1530  if (rep == kNoLiteralIndex) continue;
1531  ref = Literal(rep);
1532  }
1533 
1534  // Special case for clique of size 2, we don't expand them if they
1535  // are included in an already added clique.
1536  //
1537  // TODO(user): the second condition means the literal must be false!
1538  if (old_size == 2 && clique[0] != clique[1]) {
1539  if (!IntersectionIsEmpty(max_cliques_containing[clique[0].Index()],
1540  max_cliques_containing[clique[1].Index()])) {
1541  ++num_removed;
1542  clique.clear();
1543  continue;
1544  }
1545  }
1546 
1547  // We only expand the clique as long as we didn't spend too much time.
1548  if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1549  clique = ExpandAtMostOne(clique);
1550  }
1551  std::sort(clique.begin(), clique.end());
1552  if (!gtl::InsertIfNotPresent(&max_cliques, clique)) {
1553  ++num_removed;
1554  clique.clear();
1555  continue;
1556  }
1557 
1558  const int clique_index = max_cliques.size();
1559  for (const Literal l : clique) {
1560  max_cliques_containing[l.Index()].push_back(clique_index);
1561  }
1562  if (clique.size() > old_size) ++num_extended;
1563  ++num_added;
1564  }
1565 
1566  if (num_extended > 0 || num_removed > 0 || num_added > 0) {
1567  VLOG(1) << "Clique Extended: " << num_extended
1568  << " Removed: " << num_removed << " Added: " << num_added
1569  << (work_done_in_mark_descendants_ > max_num_explored_nodes
1570  ? " (Aborted)"
1571  : "");
1572  }
1573  return true;
1574 }
1575 
1576 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOneWithWeight(
1577  const absl::Span<const Literal> at_most_one,
1578  const gtl::ITIVector<LiteralIndex, bool>& can_be_included,
1579  const gtl::ITIVector<LiteralIndex, double>& expanded_lp_values) {
1580  std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1581  std::vector<LiteralIndex> intersection;
1582  double clique_weight = 0.0;
1583  const int64 old_work = work_done_in_mark_descendants_;
1584  for (const Literal l : clique) clique_weight += expanded_lp_values[l.Index()];
1585  for (int i = 0; i < clique.size(); ++i) {
1586  // Do not spend too much time here.
1587  if (work_done_in_mark_descendants_ - old_work > 1e8) break;
1588 
1589  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
1590  MarkDescendants(clique[i]);
1591  if (i == 0) {
1592  for (const LiteralIndex index : is_marked_.PositionsSetAtLeastOnce()) {
1593  if (can_be_included[index]) intersection.push_back(index);
1594  }
1595  for (const Literal l : clique) is_marked_.Clear(l.NegatedIndex());
1596  }
1597 
1598  int new_size = 0;
1599  double intersection_weight = 0.0;
1600  is_marked_.Clear(clique[i].Index());
1601  is_marked_.Clear(clique[i].NegatedIndex());
1602  for (const LiteralIndex index : intersection) {
1603  if (!is_marked_[index]) continue;
1604  intersection[new_size++] = index;
1605  intersection_weight += expanded_lp_values[index];
1606  }
1607  intersection.resize(new_size);
1608  if (intersection.empty()) break;
1609 
1610  // We can't generate a violated cut this way. This is because intersection
1611  // contains all the possible ways to extend the current clique.
1612  if (clique_weight + intersection_weight <= 1.0) {
1613  clique.clear();
1614  return clique;
1615  }
1616 
1617  // Expand? The negation of any literal in the intersection is a valid way
1618  // to extend the clique.
1619  if (i + 1 == clique.size()) {
1620  // Heuristic: use literal with largest lp value. We randomize slightly.
1621  int index = -1;
1622  double max_lp = 0.0;
1623  for (int j = 0; j < intersection.size(); ++j) {
1624  const double lp = 1.0 - expanded_lp_values[intersection[j]] +
1625  absl::Uniform<double>(*random_, 0.0, 1e-4);
1626  if (index == -1 || lp > max_lp) {
1627  index = j;
1628  max_lp = lp;
1629  }
1630  }
1631  if (index != -1) {
1632  clique.push_back(Literal(intersection[index]).Negated());
1633  std::swap(intersection.back(), intersection[index]);
1634  intersection.pop_back();
1635  clique_weight += expanded_lp_values[clique.back().Index()];
1636  }
1637  }
1638  }
1639  return clique;
1640 }
1641 
1642 const std::vector<std::vector<Literal>>&
1644  const std::vector<Literal>& literals,
1645  const std::vector<double>& lp_values) {
1646  // We only want to generate a cut with literals from the LP, not extra ones.
1647  const int num_literals = implications_.size();
1648  gtl::ITIVector<LiteralIndex, bool> can_be_included(num_literals, false);
1649  gtl::ITIVector<LiteralIndex, double> expanded_lp_values(num_literals, 0.0);
1650  const int size = literals.size();
1651  for (int i = 0; i < size; ++i) {
1652  const Literal l = literals[i];
1653  can_be_included[l.Index()] = true;
1654  can_be_included[l.NegatedIndex()] = true;
1655 
1656  const double value = lp_values[i];
1657  expanded_lp_values[l.Index()] = value;
1658  expanded_lp_values[l.NegatedIndex()] = 1.0 - value;
1659  }
1660 
1661  // We want highest sum first.
1662  struct Candidate {
1663  Literal a;
1664  Literal b;
1665  double sum;
1666  bool operator<(const Candidate& other) const { return sum > other.sum; }
1667  };
1668  std::vector<Candidate> candidates;
1669 
1670  // First heuristic. Currently we only consider violated at most one of size 2,
1671  // and extend them. Right now, the code is a bit slow to try too many at every
1672  // LP node so it is why we are defensive like this. Note also that because we
1673  // currently still statically add the initial implications, this will only add
1674  // cut based on newly learned binary clause. Or the one that were not added
1675  // to the relaxation in the first place.
1676  for (int i = 0; i < size; ++i) {
1677  Literal current_literal = literals[i];
1678  double current_value = lp_values[i];
1679  if (trail_->Assignment().LiteralIsAssigned(current_literal)) continue;
1680  if (is_redundant_[current_literal.Index()]) continue;
1681 
1682  if (current_value < 0.5) {
1683  current_literal = current_literal.Negated();
1684  current_value = 1.0 - current_value;
1685  }
1686 
1687  // We consider only one candidate for each current_literal.
1688  LiteralIndex best = kNoLiteralIndex;
1689  double best_value = 0.0;
1690  for (const Literal l : implications_[current_literal.Index()]) {
1691  if (!can_be_included[l.Index()]) continue;
1692  const double activity =
1693  current_value + expanded_lp_values[l.NegatedIndex()];
1694  if (activity <= 1.01) continue;
1695  const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
1696  if (best == kNoLiteralIndex || v > best_value) {
1697  best_value = v;
1698  best = l.NegatedIndex();
1699  }
1700  }
1701  if (best != kNoLiteralIndex) {
1702  const double activity = current_value + expanded_lp_values[best];
1703  candidates.push_back({current_literal, Literal(best), activity});
1704  }
1705  }
1706 
1707  // Do not genate too many cut at once.
1708  const int kMaxNumberOfCutPerCall = 50;
1709  std::sort(candidates.begin(), candidates.end());
1710  if (candidates.size() > kMaxNumberOfCutPerCall) {
1711  candidates.resize(kMaxNumberOfCutPerCall);
1712  }
1713 
1714  // Expand to a maximal at most one each candidates before returning them.
1715  // Note that we only expand using literal from the LP.
1716  tmp_cuts_.clear();
1717  std::vector<Literal> at_most_one;
1718  for (const Candidate& candidate : candidates) {
1719  at_most_one = ExpandAtMostOneWithWeight(
1720  {candidate.a, candidate.b}, can_be_included, expanded_lp_values);
1721  if (!at_most_one.empty()) tmp_cuts_.push_back(at_most_one);
1722  }
1723  return tmp_cuts_;
1724 }
1725 
1726 // We use dfs_stack_ but we actually do a BFS.
1727 void BinaryImplicationGraph::MarkDescendants(Literal root) {
1728  dfs_stack_ = {root};
1729  is_marked_.Set(root.Index());
1730  if (is_redundant_[root.Index()]) return;
1731  for (int j = 0; j < dfs_stack_.size(); ++j) {
1732  const Literal current = dfs_stack_[j];
1733  for (const Literal l : implications_[current.Index()]) {
1734  if (!is_marked_[l.Index()] && !is_redundant_[l.Index()]) {
1735  dfs_stack_.push_back(l);
1736  is_marked_.Set(l.Index());
1737  }
1738  }
1739 
1740  if (current.Index() >= at_most_ones_.size()) continue;
1741  for (const int start : at_most_ones_[current.Index()]) {
1742  for (int i = start;; ++i) {
1743  const Literal l = at_most_one_buffer_[i];
1744  if (l.Index() == kNoLiteralIndex) break;
1745  if (l == current) continue;
1746  if (!is_marked_[l.NegatedIndex()] && !is_redundant_[l.NegatedIndex()]) {
1747  dfs_stack_.push_back(l.Negated());
1748  is_marked_.Set(l.NegatedIndex());
1749  }
1750  }
1751  }
1752  }
1753  work_done_in_mark_descendants_ += dfs_stack_.size();
1754 }
1755 
1756 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
1757  const absl::Span<const Literal> at_most_one) {
1758  std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1759 
1760  // Optim.
1761  for (int i = 0; i < clique.size(); ++i) {
1762  if (implications_[clique[i].Index()].empty() ||
1763  is_redundant_[clique[i].Index()]) {
1764  return clique;
1765  }
1766  }
1767 
1768  std::vector<LiteralIndex> intersection;
1769  for (int i = 0; i < clique.size(); ++i) {
1770  is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
1771  MarkDescendants(clique[i]);
1772  if (i == 0) {
1773  intersection = is_marked_.PositionsSetAtLeastOnce();
1774  for (const Literal l : clique) is_marked_.Clear(l.NegatedIndex());
1775  }
1776 
1777  int new_size = 0;
1778  is_marked_.Clear(clique[i].NegatedIndex()); // TODO(user): explain.
1779  for (const LiteralIndex index : intersection) {
1780  if (is_marked_[index]) intersection[new_size++] = index;
1781  }
1782  intersection.resize(new_size);
1783  if (intersection.empty()) break;
1784 
1785  // Expand?
1786  if (i + 1 == clique.size()) {
1787  clique.push_back(Literal(intersection.back()).Negated());
1788  intersection.pop_back();
1789  }
1790  }
1791  return clique;
1792 }
1793 
1794 // TODO(user): lazy cleanup the lists on is_removed_?
1795 // TODO(user): Mark fixed variable as is_removed_ for faster iteration?
1797  Literal literal) {
1798  CHECK(!is_removed_[literal.Index()]);
1799 
1800  // Clear old state.
1801  for (const Literal l : direct_implications_) {
1802  in_direct_implications_[l.Index()] = false;
1803  }
1804  direct_implications_.clear();
1805 
1806  // Fill new state.
1807  const VariablesAssignment& assignment = trail_->Assignment();
1808  CHECK(!assignment.LiteralIsAssigned(literal));
1809  for (const Literal l : implications_[literal.Index()]) {
1810  if (l == literal) continue;
1811  if (assignment.LiteralIsAssigned(l)) continue;
1812  if (!is_removed_[l.Index()] && !in_direct_implications_[l.Index()]) {
1813  in_direct_implications_[l.Index()] = true;
1814  direct_implications_.push_back(l);
1815  }
1816  }
1817  if (literal.Index() < at_most_ones_.size()) {
1818  if (is_redundant_[literal.Index()]) {
1819  CHECK(at_most_ones_[literal.Index()].empty());
1820  }
1821  for (const int start : at_most_ones_[literal.Index()]) {
1822  for (int i = start;; ++i) {
1823  const Literal l = at_most_one_buffer_[i];
1824  if (l.Index() == kNoLiteralIndex) break;
1825  if (l == literal) continue;
1826  if (assignment.LiteralIsAssigned(l)) continue;
1827  if (!is_removed_[l.Index()] &&
1828  !in_direct_implications_[l.NegatedIndex()]) {
1829  in_direct_implications_[l.NegatedIndex()] = true;
1830  direct_implications_.push_back(l.Negated());
1831  }
1832  }
1833  }
1834  }
1835  estimated_sizes_[literal.Index()] = direct_implications_.size();
1836  return direct_implications_;
1837 }
1838 
1840  bool* is_unsat) {
1841  const int saved_index = propagation_trail_index_;
1842  CHECK_EQ(propagation_trail_index_, trail_->Index()); // Propagation done.
1843 
1844  const VariablesAssignment& assignment = trail_->Assignment();
1845  if (assignment.VariableIsAssigned(var)) return false;
1846 
1847  const Literal literal(var, true);
1848  direct_implications_of_negated_literal_ =
1849  DirectImplications(literal.Negated());
1850  DirectImplications(literal); // Fill in_direct_implications_.
1851  for (const Literal l : direct_implications_of_negated_literal_) {
1852  if (in_direct_implications_[l.Index()]) {
1853  // not(l) => literal => l.
1854  if (!FixLiteral(l)) {
1855  *is_unsat = true;
1856  return false;
1857  }
1858  }
1859  }
1860 
1861  return propagation_trail_index_ > saved_index;
1862 }
1863 
1865  BooleanVariable var) {
1866  const Literal literal(var, true);
1867  int64 result = 0;
1868  direct_implications_of_negated_literal_ =
1869  DirectImplications(literal.Negated());
1870  const int64 s1 = DirectImplications(literal).size();
1871  for (const Literal l : direct_implications_of_negated_literal_) {
1872  result += s1;
1873 
1874  // We should have dealt with that in FindFailedLiteralAroundVar().
1875  CHECK(!in_direct_implications_[l.Index()]);
1876 
1877  // l => literal => l: equivalent variable!
1878  if (in_direct_implications_[l.NegatedIndex()]) result--;
1879  }
1880  return result;
1881 }
1882 
1883 // For all possible a => var => b, add a => b.
1885  BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses) {
1886  const Literal literal(var, true);
1887  direct_implications_of_negated_literal_ =
1888  DirectImplications(literal.Negated());
1889  for (const Literal b : DirectImplications(literal)) {
1890  estimated_sizes_[b.NegatedIndex()]--;
1891  for (const Literal a_negated : direct_implications_of_negated_literal_) {
1892  if (a_negated.Negated() == b) continue;
1893  AddImplication(a_negated.Negated(), b);
1894  }
1895  }
1896  for (const Literal a_negated : direct_implications_of_negated_literal_) {
1897  estimated_sizes_[a_negated.NegatedIndex()]--;
1898  }
1899 
1900  // Notify the deletion to the proof checker and the postsolve.
1901  // Note that we want var first in these clauses for the postsolve.
1902  for (const Literal b : direct_implications_) {
1903  if (drat_proof_handler_ != nullptr) {
1904  drat_proof_handler_->DeleteClause({Literal(var, false), b});
1905  }
1906  postsolve_clauses->push_back({Literal(var, false), b});
1907  }
1908  for (const Literal a_negated : direct_implications_of_negated_literal_) {
1909  if (drat_proof_handler_ != nullptr) {
1910  drat_proof_handler_->DeleteClause({Literal(var, true), a_negated});
1911  }
1912  postsolve_clauses->push_back({Literal(var, true), a_negated});
1913  }
1914 
1915  // We need to remove any occurrence of var in our implication lists, this will
1916  // be delayed to the CleanupAllRemovedVariables() call.
1917  for (LiteralIndex index : {literal.Index(), literal.NegatedIndex()}) {
1918  is_removed_[index] = true;
1919  if (!is_redundant_[index]) {
1920  ++num_redundant_literals_;
1921  is_redundant_[index] = true;
1922  }
1923  implications_[index].clear();
1924  }
1925 }
1926 
1928  for (auto& implication : implications_) {
1929  int new_size = 0;
1930  for (const Literal l : implication) {
1931  if (!is_removed_[l.Index()]) implication[new_size++] = l;
1932  }
1933  implication.resize(new_size);
1934  }
1935 
1936  // Clean-up at most ones.
1937  at_most_ones_.clear();
1938  CleanUpAndAddAtMostOnes(/*base_index=*/0);
1939 }
1940 
1941 // ----- SatClause -----
1942 
1943 // static
1944 SatClause* SatClause::Create(absl::Span<const Literal> literals) {
1945  CHECK_GE(literals.size(), 2);
1946  SatClause* clause = reinterpret_cast<SatClause*>(
1947  ::operator new(sizeof(SatClause) + literals.size() * sizeof(Literal)));
1948  clause->size_ = literals.size();
1949  for (int i = 0; i < literals.size(); ++i) {
1950  clause->literals_[i] = literals[i];
1951  }
1952  return clause;
1953 }
1954 
1955 // Note that for an attached clause, removing fixed literal is okay because if
1956 // any of the watched literal is assigned, then the clause is necessarily true.
1958  const VariablesAssignment& assignment) {
1959  DCHECK(IsAttached());
1960  if (assignment.VariableIsAssigned(literals_[0].Variable()) ||
1961  assignment.VariableIsAssigned(literals_[1].Variable())) {
1962  DCHECK(IsSatisfied(assignment));
1963  return true;
1964  }
1965  int j = 2;
1966  while (j < size_ && !assignment.VariableIsAssigned(literals_[j].Variable())) {
1967  ++j;
1968  }
1969  for (int i = j; i < size_; ++i) {
1970  if (assignment.VariableIsAssigned(literals_[i].Variable())) {
1971  if (assignment.LiteralIsTrue(literals_[i])) return true;
1972  } else {
1973  std::swap(literals_[j], literals_[i]);
1974  ++j;
1975  }
1976  }
1977  size_ = j;
1978  return false;
1979 }
1980 
1981 bool SatClause::IsSatisfied(const VariablesAssignment& assignment) const {
1982  for (const Literal literal : *this) {
1983  if (assignment.LiteralIsTrue(literal)) return true;
1984  }
1985  return false;
1986 }
1987 
1988 std::string SatClause::DebugString() const {
1989  std::string result;
1990  for (const Literal literal : *this) {
1991  if (!result.empty()) result.append(" ");
1992  result.append(literal.DebugString());
1993  }
1994  return result;
1995 }
1996 
1997 } // namespace sat
1998 } // namespace operations_research
var
IntVar * var
Definition: expr_array.cc:1858
operations_research::sat::Literal::Variable
BooleanVariable Variable() const
Definition: sat_base.h:80
INFO
const int INFO
Definition: log_severity.h:31
operations_research::sat::BinaryImplicationGraph
Definition: clause.h:456
DCHECK_LT
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:888
operations_research::sat::Trail::EnqueueWithUnitReason
void EnqueueWithUnitReason(Literal true_literal)
Definition: sat_base.h:265
VLOG
#define VLOG(verboselevel)
Definition: base/logging.h:978
operations_research::SparseBitset::size
IntegerType size() const
Definition: bitset.h:771
operations_research::sat::SatClause::AsSpan
absl::Span< const Literal > AsSpan() const
Definition: clause.h:90
operations_research::sat::BinaryImplicationGraph::MinimizeConflictFirstWithTransitiveReduction
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked, random_engine_t *random)
Definition: clause.cc:848
operations_research::sat::BinaryImplicationGraph::Resize
void Resize(int num_variables)
Definition: clause.cc:476
operations_research::sat::LiteralWatchers::InprocessingFixLiteral
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
Definition: clause.cc:338
operations_research::sat::SatClause::DebugString
std::string DebugString() const
Definition: clause.cc:1988
IF_STATS_ENABLED
#define IF_STATS_ENABLED(instructions)
Definition: stats.h:435
operations_research::SparseBitset::ClearAndResize
void ClearAndResize(IntegerType size)
Definition: bitset.h:780
operations_research::sat::BinaryImplicationGraph::Reason
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
Definition: clause.cc:741
StronglyConnectedComponentsFinder
Definition: strongly_connected_components.h:103
LOG
#define LOG(severity)
Definition: base/logging.h:420
operations_research::sat::kNoLiteralIndex
const LiteralIndex kNoLiteralIndex(-1)
StronglyConnectedComponentsFinder::NodeIsInCurrentDfsPath
bool NodeIsInCurrentDfsPath(NodeIndex node) const
Definition: strongly_connected_components.h:176
WallTimer::Get
double Get() const
Definition: timer.h:45
operations_research::sat::SatClause::RemoveFixedLiteralsAndTestIfTrue
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
Definition: clause.cc:1957
operations_research::sat::BinaryImplicationGraph::FindFailedLiteralAroundVar
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
Definition: clause.cc:1839
operations_research::sat::LiteralWatchers::Reason
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
Definition: clause.cc:191
CHECK_GE
#define CHECK_GE(val1, val2)
Definition: base/logging.h:701
gtl::ITIVector
Definition: int_type_indexed_vector.h:76
operations_research::sat::Trail::Info
const AssignmentInfo & Info(BooleanVariable var) const
Definition: sat_base.h:381
operations_research::sat::SccGraph::to_fix_
std::vector< Literal > to_fix_
Definition: clause.cc:1104
logging.h
operations_research::sat::BinaryImplicationGraph::DetectEquivalences
bool DetectEquivalences(bool log_info=false)
Definition: clause.cc:1122
gtl::STLClearObject
void STLClearObject(T *obj)
Definition: stl_util.h:123
gtl::ITIVector::push_back
void push_back(const value_type &x)
Definition: int_type_indexed_vector.h:157
operations_research::sat::LiteralWatchers::InprocessingRemoveClause
void InprocessingRemoveClause(SatClause *clause)
Definition: clause.cc:357
hash
int64 hash
Definition: matrix_utils.cc:60
value
int64 value
Definition: demon_profiler.cc:43
operations_research::sat::BinaryImplicationGraph::Propagate
bool Propagate(Trail *trail) final
Definition: clause.cc:729
operations_research::sat::LiteralWatchers::Propagate
bool Propagate(Trail *trail) final
Definition: clause.cc:182
operations_research::sat::BinaryImplicationGraph::GenerateAtMostOnesWithLargeWeight
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(const std::vector< Literal > &literals, const std::vector< double > &lp_values)
Definition: clause.cc:1643
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::VariablesAssignment::LiteralIsTrue
bool LiteralIsTrue(Literal literal) const
Definition: sat_base.h:150
operations_research::sat::BinaryImplicationGraph::ComputeTransitiveReduction
bool ComputeTransitiveReduction(bool log_info=false)
Definition: clause.cc:1309
operations_research::sat::SatPropagator::propagation_trail_index_
int propagation_trail_index_
Definition: sat_base.h:506
operations_research::sat::BinaryImplicationGraph::RemoveBooleanVariable
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal >> *postsolve_clauses)
Definition: clause.cc:1884
int64
int64_t int64
Definition: integral_types.h:34
operations_research::sat::Literal::Negated
Literal Negated() const
Definition: sat_base.h:91
operations_research::sat::Literal::NegatedIndex
LiteralIndex NegatedIndex() const
Definition: sat_base.h:85
operations_research::sat::LiteralWatchers::AttachAllClauses
void AttachAllClauses()
Definition: clause.cc:321
index
int index
Definition: pack.cc:508
operations_research::sat::SatPropagator::propagator_id_
int propagator_id_
Definition: sat_base.h:505
operations_research::sat::LiteralWatchers::Watcher::clause
SatClause * clause
Definition: clause.h:307
int32
int int32
Definition: integral_types.h:33
operations_research::sat::LiteralWatchers::DeleteRemovedClauses
void DeleteRemovedClauses()
Definition: clause.cc:453
operations_research::sat::BinaryImplicationGraph::DirectImplications
const std::vector< Literal > & DirectImplications(Literal literal)
Definition: clause.cc:1796
gtl::ITIVector::empty
bool empty() const
Definition: int_type_indexed_vector.h:155
clause.h
operations_research::sat::Trail::Index
int Index() const
Definition: sat_base.h:378
operations_research::sat::BinaryImplicationGraph::NumImplicationOnVariableRemoval
int64 NumImplicationOnVariableRemoval(BooleanVariable var)
Definition: clause.cc:1864
operations_research::sat::LiteralWatchers::ReasonClause
SatClause * ReasonClause(int trail_index) const
Definition: clause.cc:196
operations_research::sat::LiteralWatchers::AddRemovableClause
SatClause * AddRemovableClause(const std::vector< Literal > &literals, Trail *trail)
Definition: clause.cc:211
operations_research::sat::Trail::RegisterPropagator
void RegisterPropagator(SatPropagator *propagator)
Definition: sat_base.h:550
operations_research::SparseBitset::Set
void Set(IntegerType index)
Definition: bitset.h:805
strongly_connected_components.h
operations_research::SparseBitset::NotifyAllClear
void NotifyAllClear()
Definition: bitset.h:825
SCOPED_TIME_STAT
#define SCOPED_TIME_STAT(stats)
Definition: stats.h:436
operations_research::sat::BinaryImplicationGraph::MinimizeConflictExperimental
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
Definition: clause.cc:910
DEBUG_MODE
const bool DEBUG_MODE
Definition: macros.h:24
StronglyConnectedComponentsFinder::FindStronglyConnectedComponents
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
Definition: strongly_connected_components.h:105
operations_research::sat::LiteralWatchers::Watcher
Definition: clause.h:287
a
int64 a
Definition: constraint_solver/table.cc:42
WallTimer::Start
void Start()
Definition: timer.h:31
operations_research::SparseBitset< BooleanVariable >
wall_timer
WallTimer * wall_timer
Definition: cp_model_solver.cc:2082
operations_research::sat::BinaryImplicationGraph::AddBinaryClauseDuringSearch
bool AddBinaryClauseDuringSearch(Literal a, Literal b)
Definition: clause.cc:507
operations_research::sat::SccGraph
Definition: clause.cc:1015
timer.h
gtl::STLSortAndRemoveDuplicates
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
Definition: stl_util.h:58
operations_research::sat::BinaryImplicationGraph::TransformIntoMaxCliques
bool TransformIntoMaxCliques(std::vector< std::vector< Literal >> *at_most_ones, int64 max_num_explored_nodes=1e8)
Definition: clause.cc:1498
operations_research::sat::BinaryImplicationGraph::MinimizeConflictFirst
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
Definition: clause.cc:830
operations_research::sat::Trail::Assignment
const VariablesAssignment & Assignment() const
Definition: sat_base.h:380
CHECK_EQ
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
representative
ColIndex representative
Definition: preprocessor.cc:424
operations_research::SparseBitset::PositionsSetAtLeastOnce
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
Definition: bitset.h:815
operations_research::sat::LiteralWatchers::InprocessingAddClause
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
Definition: clause.cc:414
operations_research::sat::BinaryImplicationGraph::AddAtMostOne
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
Definition: clause.cc:530
operations_research::sat::Literal::Index
LiteralIndex Index() const
Definition: sat_base.h:84
operations_research::sat::LiteralWatchers::AddClause
bool AddClause(absl::Span< const Literal > literals, Trail *trail)
Definition: clause.cc:204
operations_research::sat::Model
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
operations_research::sat::BinaryImplicationGraph::RemoveFixedVariables
void RemoveFixedVariables()
Definition: clause.cc:961
WallTimer
Definition: timer.h:23
operations_research::sat::SatClause::SecondLiteral
Literal SecondLiteral() const
Definition: clause.h:75
operations_research::sat::LiteralWatchers::DetachAllClauses
void DetachAllClauses()
Definition: clause.cc:310
operations_research::sat::LiteralWatchers::LazyDetach
void LazyDetach(SatClause *clause)
Definition: clause.cc:293
LOG_IF
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
operations_research::sat::LiteralWatchers::Attach
void Attach(SatClause *clause, Trail *trail)
Definition: clause.cc:273
DCHECK
#define DCHECK(condition)
Definition: base/logging.h:884
operations_research::sat::LiteralWatchers::LiteralWatchers
LiteralWatchers(Model *model)
Definition: clause.cc:52
operations_research::sat::SccGraph::operator[]
const std::vector< int32 > & operator[](int32 node) const
Definition: clause.cc:1032
operations_research::sat::BinaryImplicationGraph::MinimizeConflictWithReachability
void MinimizeConflictWithReachability(std::vector< Literal > *c)
Definition: clause.cc:753
operations_research::sat::SatClause::Create
static SatClause * Create(absl::Span< const Literal > literals)
Definition: clause.cc:1944
operations_research::sat::BinaryImplicationGraph::CleanupAllRemovedVariables
void CleanupAllRemovedVariables()
Definition: clause.cc:1927
operations_research::SparseBitset::ClearAll
void ClearAll()
Definition: bitset.h:776
gtl::ITIVector::size
size_type size() const
Definition: int_type_indexed_vector.h:146
operations_research::sat::DratProofHandler::DeleteClause
void DeleteClause(absl::Span< const Literal > clause)
Definition: drat_proof_handler.cc:81
DCHECK_GE
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:889
model
GRBmodel * model
Definition: gurobi_interface.cc:269
operations_research::TimeLimit::AdvanceDeterministicTime
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
Definition: time_limit.h:226
operations_research::sat::Trail::CurrentDecisionLevel
int CurrentDecisionLevel() const
Definition: sat_base.h:355
operations_research::sat::Literal
Definition: sat_base.h:64
operations_research::sat::SatClause::IsAttached
bool IsAttached() const
Definition: clause.h:108
operations_research::sat::BinaryImplicationGraph::AddBinaryClause
void AddBinaryClause(Literal a, Literal b)
Definition: clause.cc:490
operations_research::SparseBitset::Resize
void Resize(IntegerType size)
Definition: bitset.h:791
operations_research::sat::LiteralWatchers::InprocessingRewriteClause
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
Definition: clause.cc:366
operations_research::sat::AssignmentInfo::level
uint32 level
Definition: sat_base.h:199
gtl::ITIVector::clear
void clear()
Definition: int_type_indexed_vector.h:169
operations_research::SparseBitset::Clear
void Clear(IntegerType index)
Definition: bitset.h:811
gtl::InsertIfNotPresent
bool InsertIfNotPresent(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:103
stl_util.h
operations_research::Trail
Definition: constraint_solver.cc:723
gtl::STLDeleteContainerPointers
void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end)
Definition: stl_util.h:314
operations_research::sat::SatClause::FirstLiteral
Literal FirstLiteral() const
Definition: clause.h:74
operations_research::sat::BinaryImplicationGraph::IsEmpty
bool IsEmpty()
Definition: clause.h:483
operations_research::sat::BinaryImplicationGraph::RepresentativeOf
Literal RepresentativeOf(Literal l) const
Definition: clause.h:561
operations_research::sat::Trail::Enqueue
void Enqueue(Literal true_literal, int propagator_id)
Definition: sat_base.h:250
b
int64 b
Definition: constraint_solver/table.cc:43
operations_research::sat::SatClause::begin
const Literal *const begin() const
Definition: clause.h:69
operations_research::sat::LiteralWatchers::Detach
void Detach(SatClause *clause)
Definition: clause.cc:300
VLOG_IS_ON
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:41
operations_research::sat::DratProofHandler::AddClause
void AddClause(absl::Span< const Literal > clause)
Definition: drat_proof_handler.cc:71
gtl::ITIVector::resize
void resize(size_type new_size)
Definition: int_type_indexed_vector.h:149
operations_research::sat::VariablesAssignment::VariableIsAssigned
bool VariableIsAssigned(BooleanVariable var) const
Definition: sat_base.h:158
operations_research::sat::SatClause::size
int size() const
Definition: clause.h:65
gtl::STLDeleteElements
void STLDeleteElements(T *container)
Definition: stl_util.h:372
operations_research::glop::Index
int32 Index
Definition: lp_types.h:37
CHECK_NE
#define CHECK_NE(val1, val2)
Definition: base/logging.h:698
operations_research::sat::SccGraph::SccGraph
SccGraph(SccFinder *finder, Implication *graph, AtMostOne *at_most_ones, std::vector< Literal > *at_most_one_buffer)
Definition: clause.cc:1024
operations_research::StatsGroup::StatString
std::string StatString() const
Definition: stats.cc:71
operations_research::sat::SatClause
Definition: clause.h:51
operations_research::sat::SatClause::IsSatisfied
bool IsSatisfied(const VariablesAssignment &assignment) const
Definition: clause.cc:1981
literal
Literal literal
Definition: optimization.cc:84
operations_research::sat::VariablesAssignment
Definition: sat_base.h:122
DCHECK_LE
#define DCHECK_LE(val1, val2)
Definition: base/logging.h:887
operations_research::sat::BinaryImplicationGraph::AddImplication
void AddImplication(Literal a, Literal b)
Definition: clause.h:488
CHECK
#define CHECK(condition)
Definition: base/logging.h:495
operations_research::sat::SatPropagator
Definition: sat_base.h:444
operations_research::sat::LiteralWatchers::Resize
void Resize(int num_variables)
Definition: clause.cc:68
operations_research::sat::LiteralWatchers::CleanUpWatchers
void CleanUpWatchers()
Definition: clause.cc:440
util_hash::Hash
uint64 Hash(uint64 num, uint64 c)
Definition: hash.h:150
operations_research::sat::VariablesAssignment::LiteralIsAssigned
bool LiteralIsAssigned(Literal literal) const
Definition: sat_base.h:153
operations_research::sat::VariablesAssignment::LiteralIsFalse
bool LiteralIsFalse(Literal literal) const
Definition: sat_base.h:147
operations_research::sat::Trail
Definition: sat_base.h:233
operations_research::random_engine_t
std::mt19937 random_engine_t
Definition: random_engine.h:23
operations_research::sat::LiteralWatchers::~LiteralWatchers
~LiteralWatchers() override
Definition: clause.cc:63
operations_research::sat::SccGraph::work_done_
int64 work_done_
Definition: clause.cc:1107