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;
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());
56 num_inspected_clauses_(0),
57 num_inspected_clause_literals_(0),
58 num_watched_clauses_(0),
59 stats_(
"LiteralWatchers") {
70 watchers_on_false_.resize(num_variables << 1);
71 reasons_.resize(num_variables);
72 needs_cleaning_.
Resize(LiteralIndex(num_variables << 1));
81 DCHECK(!WatcherListContains(watchers_on_false_[
literal.Index()], *clause));
82 watchers_on_false_[
literal.Index()].push_back(
83 Watcher(clause, blocking_literal));
86 bool LiteralWatchers::PropagateOnFalse(Literal false_literal,
Trail* trail) {
89 std::vector<Watcher>& watchers = watchers_on_false_[false_literal.Index()];
90 const VariablesAssignment& assignment = trail->Assignment();
95 auto new_it = watchers.begin();
96 const auto end = watchers.end();
97 while (new_it != end && assignment.LiteralIsTrue(new_it->blocking_literal)) {
100 for (
auto it = new_it; it != end; ++it) {
102 if (assignment.LiteralIsTrue(it->blocking_literal)) {
106 ++num_inspected_clauses_;
111 Literal* literals = it->clause->literals();
112 const Literal other_watched_literal(
114 false_literal.Index().value()));
115 if (assignment.LiteralIsTrue(other_watched_literal)) {
117 new_it->blocking_literal = other_watched_literal;
119 ++num_inspected_clause_literals_;
127 const int start = it->start_index;
128 const int size = it->clause->size();
132 while (i < size && assignment.LiteralIsFalse(literals[i])) ++i;
133 num_inspected_clause_literals_ += i - start + 2;
136 while (i < start && assignment.LiteralIsFalse(literals[i])) ++i;
137 num_inspected_clause_literals_ += i - 2;
138 if (i >= start) i = size;
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);
155 if (assignment.LiteralIsFalse(other_watched_literal)) {
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);
170 literals[0] = other_watched_literal;
171 literals[1] = false_literal;
172 reasons_[trail->Index()] = it->clause;
177 num_inspected_clause_literals_ += watchers.size();
178 watchers.erase(new_it, end);
183 const int old_index = trail->
Index();
186 if (!PropagateOnFalse(
literal.Negated(), trail))
return false;
192 int trail_index)
const {
193 return reasons_[trail_index]->PropagationReason();
197 return reasons_[trail_index];
207 clauses_.push_back(clause);
208 return AttachAndPropagate(clause, trail);
212 const std::vector<Literal>& literals,
Trail* trail) {
214 clauses_.push_back(clause);
215 CHECK(AttachAndPropagate(clause, trail));
224 bool LiteralWatchers::AttachAndPropagate(
SatClause* clause,
Trail* trail) {
227 const int size = clause->
size();
228 Literal* literals = clause->literals();
232 int num_literal_not_false = 0;
233 for (
int i = 0; i < size; ++i) {
235 std::swap(literals[i], literals[num_literal_not_false]);
236 ++num_literal_not_false;
237 if (num_literal_not_false == 2) {
246 if (num_literal_not_false == 0)
return false;
248 if (num_literal_not_false == 1) {
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) {
256 std::swap(literals[1], literals[i]);
262 reasons_[trail->
Index()] = clause;
267 ++num_watched_clauses_;
268 AttachOnFalse(literals[0], literals[1], clause);
269 AttachOnFalse(literals[1], literals[0], clause);
274 Literal* literals = clause->literals();
278 ++num_watched_clauses_;
279 AttachOnFalse(literals[0], literals[1], clause);
280 AttachOnFalse(literals[1], literals[0], clause);
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) {
289 clauses_info_.erase(clause);
294 InternalDetach(clause);
301 InternalDetach(clause);
303 needs_cleaning_.
Clear(l.Index());
304 RemoveIf(&(watchers_on_false_[l.Index()]), [](
const Watcher& watcher) {
305 return !watcher.clause->IsAttached();
311 if (!all_clauses_are_attached_)
return;
312 all_clauses_are_attached_ =
false;
317 num_watched_clauses_ = 0;
318 watchers_on_false_.clear();
322 if (all_clauses_are_attached_)
return;
323 all_clauses_are_attached_ =
true;
326 watchers_on_false_.resize(needs_cleaning_.
size().value());
330 ++num_watched_clauses_;
331 CHECK_GE(clause->
size(), 2);
340 if (drat_proof_handler_ !=
nullptr) {
341 drat_proof_handler_->
AddClause({true_literal});
350 return implication_graph_->
Propagate(trail_);
358 CHECK(!all_clauses_are_attached_);
359 if (drat_proof_handler_ !=
nullptr) {
362 clauses_info_.erase(clause);
367 SatClause* clause, absl::Span<const Literal> new_clause) {
368 if (new_clause.empty())
return false;
371 for (
const Literal l : new_clause) {
376 if (new_clause.size() == 1) {
382 if (new_clause.size() == 2) {
388 if (drat_proof_handler_ !=
nullptr) {
390 drat_proof_handler_->
AddClause(new_clause);
394 if (all_clauses_are_attached_) {
397 --num_watched_clauses_;
400 needs_cleaning_.
Clear(l.Index());
401 RemoveIf(&(watchers_on_false_[l.Index()]), [](
const Watcher& watcher) {
402 return !watcher.clause->IsAttached();
407 clause->Rewrite(new_clause);
410 if (all_clauses_are_attached_)
Attach(clause, trail_);
415 absl::Span<const Literal> new_clause) {
416 CHECK(!new_clause.empty());
417 CHECK(!all_clauses_are_attached_);
419 for (
const Literal l : new_clause) {
424 if (new_clause.size() == 1) {
430 if (new_clause.size() == 2) {
436 clauses_.push_back(clause);
443 DCHECK(needs_cleaning_[
index]);
444 RemoveIf(&(watchers_on_false_[
index]), [](
const Watcher& watcher) {
457 if (to_minimize_index_ >= clauses_.size()) {
458 to_minimize_index_ = clauses_.size();
461 std::stable_partition(clauses_.begin(),
462 clauses_.begin() + to_minimize_index_,
463 [](
SatClause*
a) { return a->IsAttached(); }) -
467 std::vector<SatClause*>::iterator iter =
468 std::stable_partition(clauses_.begin(), clauses_.end(),
469 [](
SatClause*
a) { return a->IsAttached(); });
471 clauses_.erase(iter, clauses_.end());
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);
492 if (drat_proof_handler_ !=
nullptr) {
499 estimated_sizes_[
a.NegatedIndex()]++;
500 estimated_sizes_[
b.NegatedIndex()]++;
501 implications_[
a.NegatedIndex()].push_back(
b);
502 implications_[
b.NegatedIndex()].push_back(
a);
504 num_implications_ += 2;
512 const auto& assignment = trail_->
Assignment();
513 if (assignment.LiteralIsFalse(
a)) {
514 if (assignment.LiteralIsAssigned(
b)) {
515 if (assignment.LiteralIsFalse(
b))
return false;
517 reasons_[trail_->
Index()] =
a;
520 }
else if (assignment.LiteralIsFalse(
b)) {
521 if (!assignment.LiteralIsAssigned(
a)) {
522 reasons_[trail_->
Index()] =
b;
531 absl::Span<const Literal> at_most_one) {
533 if (at_most_one.size() <= 1)
return true;
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(),
544 return CleanUpAndAddAtMostOnes(base_index);
549 bool BinaryImplicationGraph::FixLiteral(
Literal true_literal) {
553 if (drat_proof_handler_ !=
nullptr) {
554 drat_proof_handler_->
AddClause({true_literal});
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) {
573 const int local_start = local_end;
574 bool set_all_left_to_false =
false;
576 const Literal l = at_most_one_buffer_[i];
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;
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;
595 local_end = local_start;
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;
616 at_most_one_buffer_[new_local_end++] = l;
618 local_end = new_local_end;
622 const absl::Span<const Literal> at_most_one(
623 &at_most_one_buffer_[local_start], local_end - local_start);
627 if (at_most_one.size() < 10) {
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());
635 num_implications_ += at_most_one.size() * (at_most_one.size() - 1);
638 local_end = local_start;
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);
647 CHECK(!is_redundant_[l.Index()]);
648 at_most_ones_[l.Index()].
push_back(local_start);
655 at_most_one_buffer_.resize(local_end);
659 bool BinaryImplicationGraph::PropagateOnTrue(Literal true_literal,
663 const VariablesAssignment& assignment = trail->Assignment();
664 DCHECK(assignment.LiteralIsTrue(true_literal));
669 num_inspections_ += implications_[true_literal.Index()].size();
671 for (Literal
literal : implications_[true_literal.Index()]) {
672 if (assignment.LiteralIsTrue(
literal)) {
682 if (assignment.LiteralIsFalse(
literal)) {
684 *(trail->MutableConflict()) = {true_literal.Negated(),
literal};
688 reasons_[trail->Index()] = true_literal.Negated();
694 if (true_literal.Index() < at_most_ones_.
size()) {
695 for (
const int start : at_most_ones_[true_literal.Index()]) {
697 for (
int i = start;; ++i) {
698 const Literal
literal = at_most_one_buffer_[i];
709 if (assignment.LiteralIsFalse(
literal))
continue;
712 if (assignment.LiteralIsTrue(
literal)) {
714 *(trail->MutableConflict()) = {true_literal.Negated(),
719 reasons_[trail->Index()] = true_literal.Negated();
734 while (propagation_trail_index_ < trail->
Index()) {
736 if (!PropagateOnTrue(
literal, trail))
return false;
742 const Trail& trail,
int trail_index)
const {
743 return {&reasons_[trail_index], 1};
754 std::vector<Literal>* conflict) {
760 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
762 is_marked_.
Set(root_literal_index);
771 const bool also_prune_direct_implication_list =
false;
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]) {
785 if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
801 if (also_prune_direct_implication_list) {
802 is_marked_.
Clear(l.Index());
808 if (also_prune_direct_implication_list) {
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;
817 if (new_size < direct_implications.size()) {
818 num_redundant_implications_ += direct_implications.size() - new_size;
819 direct_implications.resize(new_size);
823 RemoveRedundantLiterals(conflict);
831 const Trail& trail, std::vector<Literal>* conflict,
834 CHECK(!conflict->empty());
836 MarkDescendants(conflict->front().Negated());
842 RemoveRedundantLiterals(conflict);
849 const Trail& trail, std::vector<Literal>* conflict,
852 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
854 is_marked_.
Set(root_literal_index);
857 auto& direct_implications = implications_[root_literal_index];
863 std::shuffle(direct_implications.begin(), direct_implications.end(), *random);
865 for (
const Literal l : direct_implications) {
866 if (is_marked_[l.Index()]) {
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]) {
880 if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
885 if (new_size < direct_implications.size()) {
886 num_redundant_implications_ += direct_implications.size() - new_size;
887 direct_implications.resize(new_size);
889 RemoveRedundantLiterals(conflict);
892 void BinaryImplicationGraph::RemoveRedundantLiterals(
893 std::vector<Literal>* conflict) {
896 for (
int i = 1; i < conflict->size(); ++i) {
897 if (!is_marked_[(*conflict)[i].NegatedIndex()]) {
898 (*conflict)[new_index] = (*conflict)[i];
902 if (new_index < conflict->size()) {
904 num_literals_removed_ += conflict->size() - new_index;
905 conflict->resize(new_index);
911 const Trail& trail, std::vector<Literal>* conflict) {
914 is_simplified_.
ClearAndResize(LiteralIndex(implications_.size()));
915 for (
Literal lit : *conflict) {
916 is_marked_.
Set(lit.Index());
932 for (
int i = 1; i < conflict->size(); ++i) {
933 const Literal lit = (*conflict)[i];
935 bool keep_literal =
true;
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()]) {
943 keep_literal =
false;
948 (*conflict)[
index] = lit;
954 if (index < conflict->size()) {
956 num_literals_removed_ += conflict->size() -
index;
957 conflict->erase(conflict->begin() +
index, conflict->end());
966 const int new_num_fixed = trail_->
Index();
967 if (num_processed_fixed_variables_ == new_num_fixed)
return;
971 for (; num_processed_fixed_variables_ < new_num_fixed;
972 ++num_processed_fixed_variables_) {
973 const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
977 for (
const Literal lit : implications_[true_literal.
Index()]) {
991 is_marked_.
Set(lit.NegatedIndex());
996 if (true_literal.
Index() < at_most_ones_.
size()) {
1004 RemoveIf(&implications_[i], [&assignment](
const Literal& lit) {
1011 at_most_ones_.
clear();
1012 CleanUpAndAddAtMostOnes(0);
1022 std::vector<std::vector<int32>>>;
1026 std::vector<Literal>* at_most_one_buffer)
1028 implications_(*graph),
1029 at_most_ones_(*at_most_ones),
1030 at_most_one_buffer_(*at_most_one_buffer) {}
1034 for (
const Literal l : implications_[LiteralIndex(node)]) {
1035 tmp_.push_back(l.Index().value());
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);
1054 if (at_most_one_already_explored_[start]) {
1056 const int first_node = previous_node_to_explore_at_most_one_[start];
1057 CHECK_NE(node, first_node);
1075 previous_node_to_explore_at_most_one_[start] = node;
1080 Literal(LiteralIndex(first_node)).NegatedIndex().
value());
1084 at_most_one_already_explored_[start] =
true;
1085 previous_node_to_explore_at_most_one_[start] = node;
1088 for (
int i = start;; ++i) {
1089 const Literal l = at_most_one_buffer_[i];
1091 if (l.
Index() == node)
continue;
1113 const std::vector<Literal>& at_most_one_buffer_;
1115 mutable std::vector<int32> tmp_;
1118 mutable std::vector<bool> at_most_one_already_explored_;
1119 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1125 if (is_dag_)
return true;
1128 log_info |= VLOG_IS_ON(1);
1136 int num_fixed_during_scc = 0;
1137 const int32 size(implications_.size());
1138 std::vector<std::vector<int32>> scc;
1142 SccGraph graph(&finder, &implications_, &at_most_ones_,
1143 &at_most_one_buffer_);
1150 ++num_fixed_during_scc;
1151 if (!FixLiteral(l))
return false;
1157 is_redundant_.
resize(size,
false);
1159 int num_equivalences = 0;
1160 reverse_topological_order_.clear();
1161 for (std::vector<int32>& component : scc) {
1169 bool all_fixed =
false;
1170 bool all_true =
false;
1171 for (
const int32 i : component) {
1180 for (
const int32 i : component) {
1182 if (!is_redundant_[l.
Index()]) {
1183 ++num_redundant_literals_;
1184 is_redundant_[l.
Index()] =
true;
1189 ++num_fixed_during_scc;
1190 if (!FixLiteral(l))
return false;
1199 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1208 std::sort(component.begin(), component.end());
1212 if (component.size() == 1) {
1215 if (num_equivalences > 0) {
1217 for (
Literal& ref : representative_list) {
1218 const LiteralIndex rep = representative_of_[ref.Index()];
1229 for (
int i = 1; i < component.size(); ++i) {
1231 if (!is_redundant_[
literal.Index()]) {
1232 ++num_redundant_literals_;
1233 is_redundant_[
literal.Index()] =
true;
1239 if (
Literal(LiteralIndex(component[i - 1])).Negated() ==
literal) {
1240 LOG_IF(INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
1249 for (
const Literal l : representative_list) {
1252 representative_list[new_size++] = rep;
1254 representative_list.resize(new_size);
1255 for (
int i = 1; i < component.size(); ++i) {
1257 auto& ref = implications_[
literal.Index()];
1268 representative_list.push_back(
literal);
1273 num_equivalences += component.size() - 1;
1277 if (num_equivalences != 0) {
1281 at_most_ones_.
clear();
1282 CleanUpAndAddAtMostOnes(0);
1284 num_implications_ = 0;
1285 for (LiteralIndex i(0); i < size; ++i) {
1286 num_implications_ += implications_[i].size();
1288 dtime += 2e-8 * num_implications_;
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
1319 log_info |= VLOG_IS_ON(1);
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;
1345 const LiteralIndex size(implications_.size());
1347 for (
const LiteralIndex root : reverse_topological_order_) {
1352 if (is_redundant_[root])
continue;
1355 auto& direct_implications = implications_[root];
1356 if (direct_implications.empty())
continue;
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);
1373 if (clear_previous_reachability) {
1379 for (
const Literal direct_child : direct_implications) {
1380 if (is_redundant_[direct_child.Index()])
continue;
1381 if (is_marked_[direct_child.Index()])
continue;
1385 if (direct_child.Index() == root)
continue;
1389 if (direct_child.NegatedIndex() == root) {
1390 is_marked_.
Set(direct_child.Index());
1394 MarkDescendants(direct_child);
1397 is_marked_.
Clear(direct_child.Index());
1399 CHECK(!is_marked_[root])
1400 <<
"DetectEquivalences() should have removed cycles!";
1401 is_marked_.
Set(root);
1407 for (; marked_index < marked_positions.size(); ++marked_index) {
1408 const LiteralIndex i = marked_positions[marked_index];
1409 if (is_marked_[
Literal(i).NegatedIndex()]) {
1417 if (!FixLiteral(
Literal(root).Negated()))
return false;
1431 for (
const Literal l : direct_implications) {
1432 if (!is_marked_[l.Index()]) {
1433 direct_implications[new_size++] = l;
1435 CHECK(!is_redundant_[l.Index()]);
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;
1445 if (work_done_in_mark_descendants_ > 1e8) {
1453 const double dtime = 1e-8 * work_done_in_mark_descendants_;
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()
1461 <<
" dtime: " << dtime
1463 << (aborted ?
" Aborted." :
"");
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()));
1474 for (; i <
a.size() && j <
b.size();) {
1475 if (
a[i] ==
b[j])
return false;
1487 std::size_t operator()(
const std::vector<Literal>& at_most_one)
const {
1489 for (Literal
literal : at_most_one) {
1499 std::vector<std::vector<Literal>>* at_most_ones,
1500 int64 max_num_explored_nodes) {
1503 work_done_in_mark_descendants_ = 0;
1505 int num_extended = 0;
1506 int num_removed = 0;
1509 absl::flat_hash_set<std::vector<Literal>, VectorHash> max_cliques;
1511 implications_.size());
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();
1518 for (std::vector<Literal>& clique : *at_most_ones) {
1519 const int old_size = clique.size();
1528 DCHECK_LT(ref.Index(), representative_of_.
size());
1529 const LiteralIndex rep = representative_of_[ref.Index()];
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()])) {
1548 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1549 clique = ExpandAtMostOne(clique);
1551 std::sort(clique.begin(), clique.end());
1558 const int clique_index = max_cliques.size();
1559 for (
const Literal l : clique) {
1560 max_cliques_containing[l.Index()].
push_back(clique_index);
1562 if (clique.size() > old_size) ++num_extended;
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
1577 void BinaryImplicationGraph::MarkDescendants(
Literal root) {
1578 dfs_stack_ = {root};
1580 if (is_redundant_[root.
Index()])
return;
1581 for (
int j = 0; j < dfs_stack_.size(); ++j) {
1582 const Literal current = dfs_stack_[j];
1583 for (
const Literal l : implications_[current.Index()]) {
1584 if (!is_marked_[l.Index()] && !is_redundant_[l.Index()]) {
1585 dfs_stack_.push_back(l);
1586 is_marked_.
Set(l.Index());
1590 if (current.Index() >= at_most_ones_.
size())
continue;
1591 for (
const int start : at_most_ones_[current.Index()]) {
1592 for (
int i = start;; ++i) {
1593 const Literal l = at_most_one_buffer_[i];
1595 if (l == current)
continue;
1596 if (!is_marked_[l.NegatedIndex()] && !is_redundant_[l.NegatedIndex()]) {
1597 dfs_stack_.push_back(l.Negated());
1598 is_marked_.
Set(l.NegatedIndex());
1603 work_done_in_mark_descendants_ += dfs_stack_.size();
1606 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
1607 const absl::Span<const Literal> at_most_one) {
1608 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1611 for (
int i = 0; i < clique.size(); ++i) {
1612 if (implications_[clique[i].
Index()].empty() ||
1613 is_redundant_[clique[i].
Index()]) {
1618 std::vector<LiteralIndex> intersection;
1619 for (
int i = 0; i < clique.size(); ++i) {
1621 MarkDescendants(clique[i]);
1624 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1628 is_marked_.
Clear(clique[i].NegatedIndex());
1629 for (
const LiteralIndex
index : intersection) {
1630 if (is_marked_[
index]) intersection[new_size++] =
index;
1632 intersection.resize(new_size);
1633 if (intersection.empty())
break;
1636 if (i + 1 == clique.size()) {
1637 clique.push_back(Literal(intersection.back()).Negated());
1638 intersection.pop_back();
1648 CHECK(!is_removed_[
literal.Index()]);
1651 for (
const Literal l : direct_implications_) {
1652 in_direct_implications_[l.Index()] =
false;
1654 direct_implications_.
clear();
1662 if (!is_removed_[l.Index()] && !in_direct_implications_[l.Index()]) {
1663 in_direct_implications_[l.Index()] =
true;
1668 if (is_redundant_[
literal.Index()]) {
1671 for (
const int start : at_most_ones_[
literal.Index()]) {
1672 for (
int i = start;; ++i) {
1673 const Literal l = at_most_one_buffer_[i];
1677 if (!is_removed_[l.
Index()] &&
1685 estimated_sizes_[
literal.Index()] = direct_implications_.
size();
1686 return direct_implications_;
1698 direct_implications_of_negated_literal_ =
1701 for (
const Literal l : direct_implications_of_negated_literal_) {
1702 if (in_direct_implications_[l.Index()]) {
1704 if (!FixLiteral(l)) {
1715 BooleanVariable
var) {
1718 direct_implications_of_negated_literal_ =
1721 for (
const Literal l : direct_implications_of_negated_literal_) {
1725 CHECK(!in_direct_implications_[l.Index()]);
1728 if (in_direct_implications_[l.NegatedIndex()]) result--;
1735 BooleanVariable
var, std::deque<std::vector<Literal>>* postsolve_clauses) {
1737 direct_implications_of_negated_literal_ =
1740 estimated_sizes_[
b.NegatedIndex()]--;
1741 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1742 if (a_negated.Negated() ==
b)
continue;
1746 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1747 estimated_sizes_[a_negated.NegatedIndex()]--;
1752 for (
const Literal b : direct_implications_) {
1753 if (drat_proof_handler_ !=
nullptr) {
1756 postsolve_clauses->push_back({
Literal(
var,
false),
b});
1758 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1759 if (drat_proof_handler_ !=
nullptr) {
1762 postsolve_clauses->push_back({
Literal(
var,
true), a_negated});
1768 is_removed_[
index] =
true;
1769 if (!is_redundant_[
index]) {
1770 ++num_redundant_literals_;
1771 is_redundant_[
index] =
true;
1773 implications_[
index].clear();
1778 for (
auto& implication : implications_) {
1780 for (
const Literal l : implication) {
1781 if (!is_removed_[l.Index()]) implication[new_size++] = l;
1783 implication.resize(new_size);
1787 at_most_ones_.
clear();
1788 CleanUpAndAddAtMostOnes(0);
1795 CHECK_GE(literals.size(), 2);
1798 clause->size_ = literals.size();
1799 for (
int i = 0; i < literals.size(); ++i) {
1800 clause->literals_[i] = literals[i];
1819 for (
int i = j; i < size_; ++i) {
1823 std::swap(literals_[j], literals_[i]);
1841 if (!result.empty()) result.append(
" ");
1842 result.append(
literal.DebugString());