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_;
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);
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()]) {
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);
1023 std::vector<std::vector<int32>>>;
1027 std::vector<Literal>* at_most_one_buffer)
1029 implications_(*graph),
1030 at_most_ones_(*at_most_ones),
1031 at_most_one_buffer_(*at_most_one_buffer) {}
1035 for (
const Literal l : implications_[LiteralIndex(node)]) {
1036 tmp_.push_back(l.Index().value());
1041 if (node < at_most_ones_.
size()) {
1042 for (
const int start : at_most_ones_[LiteralIndex(node)]) {
1043 if (start >= at_most_one_already_explored_.size()) {
1044 at_most_one_already_explored_.resize(start + 1,
false);
1045 previous_node_to_explore_at_most_one_.resize(start + 1);
1055 if (at_most_one_already_explored_[start]) {
1057 const int first_node = previous_node_to_explore_at_most_one_[start];
1076 previous_node_to_explore_at_most_one_[start] = node;
1081 Literal(LiteralIndex(first_node)).NegatedIndex().
value());
1085 at_most_one_already_explored_[start] =
true;
1086 previous_node_to_explore_at_most_one_[start] = node;
1089 for (
int i = start;; ++i) {
1090 const Literal l = at_most_one_buffer_[i];
1092 if (l.
Index() == node)
continue;
1114 const std::vector<Literal>& at_most_one_buffer_;
1116 mutable std::vector<int32> tmp_;
1119 mutable std::vector<bool> at_most_one_already_explored_;
1120 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1126 if (is_dag_)
return true;
1137 int num_fixed_during_scc = 0;
1138 const int32 size(implications_.size());
1139 std::vector<std::vector<int32>> scc;
1143 SccGraph graph(&finder, &implications_, &at_most_ones_,
1144 &at_most_one_buffer_);
1151 ++num_fixed_during_scc;
1152 if (!FixLiteral(l))
return false;
1158 is_redundant_.
resize(size,
false);
1160 int num_equivalences = 0;
1161 reverse_topological_order_.clear();
1162 for (std::vector<int32>& component : scc) {
1170 bool all_fixed =
false;
1171 bool all_true =
false;
1172 for (
const int32 i : component) {
1181 for (
const int32 i : component) {
1183 if (!is_redundant_[l.
Index()]) {
1184 ++num_redundant_literals_;
1185 is_redundant_[l.
Index()] =
true;
1190 ++num_fixed_during_scc;
1191 if (!FixLiteral(l))
return false;
1200 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1209 std::sort(component.begin(), component.end());
1213 if (component.size() == 1) {
1216 if (num_equivalences > 0) {
1218 for (
Literal& ref : representative_list) {
1219 const LiteralIndex rep = representative_of_[ref.Index()];
1230 for (
int i = 1; i < component.size(); ++i) {
1232 if (!is_redundant_[
literal.Index()]) {
1233 ++num_redundant_literals_;
1234 is_redundant_[
literal.Index()] =
true;
1240 if (
Literal(LiteralIndex(component[i - 1])).Negated() ==
literal) {
1241 LOG_IF(
INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
1250 for (
const Literal l : representative_list) {
1253 representative_list[new_size++] = rep;
1255 representative_list.resize(new_size);
1256 for (
int i = 1; i < component.size(); ++i) {
1258 auto& ref = implications_[
literal.Index()];
1269 representative_list.push_back(
literal);
1274 num_equivalences += component.size() - 1;
1278 if (num_equivalences != 0) {
1282 at_most_ones_.
clear();
1283 CleanUpAndAddAtMostOnes(0);
1285 num_implications_ = 0;
1286 for (LiteralIndex i(0); i < size; ++i) {
1287 num_implications_ += implications_[i].size();
1289 dtime += 2e-8 * num_implications_;
1293 LOG_IF(
INFO, log_info) <<
"SCC. " << num_equivalences
1294 <<
" redundant equivalent literals. "
1295 << num_fixed_during_scc <<
" fixed. "
1296 << num_implications_ <<
" implications left. "
1297 << implications_.size() <<
" literals."
1298 <<
" size of at_most_one buffer = "
1299 << at_most_one_buffer_.size() <<
"."
1300 <<
" dtime: " << dtime
1324 int64 num_fixed = 0;
1325 int64 num_new_redundant_implications = 0;
1326 bool aborted =
false;
1327 work_done_in_mark_descendants_ = 0;
1328 int marked_index = 0;
1346 const LiteralIndex size(implications_.size());
1348 for (
const LiteralIndex root : reverse_topological_order_) {
1353 if (is_redundant_[root])
continue;
1356 auto& direct_implications = implications_[root];
1357 if (direct_implications.empty())
continue;
1366 bool clear_previous_reachability =
true;
1367 for (
const Literal direct_child : direct_implications) {
1368 if (direct_child.Index() == previous) {
1369 clear_previous_reachability =
false;
1370 is_marked_.
Clear(previous);
1374 if (clear_previous_reachability) {
1380 for (
const Literal direct_child : direct_implications) {
1381 if (is_redundant_[direct_child.Index()])
continue;
1382 if (is_marked_[direct_child.Index()])
continue;
1386 if (direct_child.Index() == root)
continue;
1390 if (direct_child.NegatedIndex() == root) {
1391 is_marked_.
Set(direct_child.Index());
1395 MarkDescendants(direct_child);
1398 is_marked_.
Clear(direct_child.Index());
1400 CHECK(!is_marked_[root])
1401 <<
"DetectEquivalences() should have removed cycles!";
1402 is_marked_.
Set(root);
1408 for (; marked_index < marked_positions.size(); ++marked_index) {
1409 const LiteralIndex i = marked_positions[marked_index];
1410 if (is_marked_[
Literal(i).NegatedIndex()]) {
1418 if (!FixLiteral(
Literal(root).Negated()))
return false;
1432 for (
const Literal l : direct_implications) {
1433 if (!is_marked_[l.Index()]) {
1434 direct_implications[new_size++] = l;
1436 CHECK(!is_redundant_[l.Index()]);
1439 const int diff = direct_implications.size() - new_size;
1440 direct_implications.resize(new_size);
1441 direct_implications.shrink_to_fit();
1442 num_new_redundant_implications += diff;
1443 num_implications_ -= diff;
1446 if (work_done_in_mark_descendants_ > 1e8) {
1454 const double dtime = 1e-8 * work_done_in_mark_descendants_;
1456 num_redundant_implications_ += num_new_redundant_implications;
1457 LOG_IF(
INFO, log_info) <<
"Transitive reduction removed "
1458 << num_new_redundant_implications <<
" literals. "
1459 << num_fixed <<
" fixed. " << num_implications_
1460 <<
" implications left. " << implications_.size()
1462 <<
" dtime: " << dtime
1464 << (aborted ?
" Aborted." :
"");
1470 bool IntersectionIsEmpty(
const std::vector<int>&
a,
const std::vector<int>&
b) {
1471 DCHECK(std::is_sorted(
a.begin(),
a.end()));
1472 DCHECK(std::is_sorted(
b.begin(),
b.end()));
1475 for (; i <
a.size() && j <
b.size();) {
1476 if (
a[i] ==
b[j])
return false;
1488 std::size_t operator()(
const std::vector<Literal>& at_most_one)
const {
1490 for (Literal
literal : at_most_one) {
1500 std::vector<std::vector<Literal>>* at_most_ones,
1501 int64 max_num_explored_nodes) {
1504 work_done_in_mark_descendants_ = 0;
1506 int num_extended = 0;
1507 int num_removed = 0;
1510 absl::flat_hash_set<std::vector<Literal>, VectorHash> max_cliques;
1512 implications_.size());
1515 std::sort(at_most_ones->begin(), at_most_ones->end(),
1516 [](
const std::vector<Literal>
a,
const std::vector<Literal>
b) {
1517 return a.size() > b.size();
1519 for (std::vector<Literal>& clique : *at_most_ones) {
1520 const int old_size = clique.size();
1530 const LiteralIndex rep = representative_of_[ref.Index()];
1539 if (old_size == 2 && clique[0] != clique[1]) {
1540 if (!IntersectionIsEmpty(max_cliques_containing[clique[0].
Index()],
1541 max_cliques_containing[clique[1].
Index()])) {
1549 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1550 clique = ExpandAtMostOne(clique);
1552 std::sort(clique.begin(), clique.end());
1559 const int clique_index = max_cliques.size();
1560 for (
const Literal l : clique) {
1561 max_cliques_containing[l.Index()].
push_back(clique_index);
1563 if (clique.size() > old_size) ++num_extended;
1567 if (num_extended > 0 || num_removed > 0 || num_added > 0) {
1568 VLOG(1) <<
"Clique Extended: " << num_extended
1569 <<
" Removed: " << num_removed <<
" Added: " << num_added
1570 << (work_done_in_mark_descendants_ > max_num_explored_nodes
1577 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOneWithWeight(
1578 const absl::Span<const Literal> at_most_one,
1581 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1582 std::vector<LiteralIndex> intersection;
1583 double clique_weight = 0.0;
1584 const int64 old_work = work_done_in_mark_descendants_;
1585 for (
const Literal l : clique) clique_weight += expanded_lp_values[l.Index()];
1586 for (
int i = 0; i < clique.size(); ++i) {
1588 if (work_done_in_mark_descendants_ - old_work > 1e8)
break;
1591 MarkDescendants(clique[i]);
1594 if (can_be_included[
index]) intersection.push_back(
index);
1596 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1600 double intersection_weight = 0.0;
1602 is_marked_.
Clear(clique[i].NegatedIndex());
1603 for (
const LiteralIndex
index : intersection) {
1604 if (!is_marked_[
index])
continue;
1605 intersection[new_size++] =
index;
1606 intersection_weight += expanded_lp_values[
index];
1608 intersection.
resize(new_size);
1609 if (intersection.empty())
break;
1613 if (clique_weight + intersection_weight <= 1.0) {
1620 if (i + 1 == clique.size()) {
1623 double max_lp = 0.0;
1624 for (
int j = 0; j < intersection.size(); ++j) {
1625 const double lp = 1.0 - expanded_lp_values[intersection[j]] +
1626 absl::Uniform<double>(*random_, 0.0, 1e-4);
1627 if (
index == -1 || lp > max_lp) {
1633 clique.push_back(Literal(intersection[
index]).Negated());
1634 std::swap(intersection.back(), intersection[
index]);
1635 intersection.pop_back();
1636 clique_weight += expanded_lp_values[clique.back().Index()];
1643 const std::vector<std::vector<Literal>>&
1645 const std::vector<Literal>& literals,
1646 const std::vector<double>& lp_values) {
1648 const int num_literals = implications_.size();
1652 const int size = literals.size();
1653 for (
int i = 0; i < size; ++i) {
1654 const Literal l = literals[i];
1655 can_be_included[l.
Index()] =
true;
1658 const double value = lp_values[i];
1668 bool operator<(
const Candidate& other)
const {
return sum > other.sum; }
1670 std::vector<Candidate> candidates;
1678 for (
int i = 0; i < size; ++i) {
1679 Literal current_literal = literals[i];
1680 double current_value = lp_values[i];
1682 if (is_redundant_[current_literal.
Index()])
continue;
1684 if (current_value < 0.5) {
1685 current_literal = current_literal.
Negated();
1686 current_value = 1.0 - current_value;
1691 double best_value = 0.0;
1692 for (
const Literal l : implications_[current_literal.
Index()]) {
1693 if (!can_be_included[l.Index()])
continue;
1694 const double activity =
1695 current_value + expanded_lp_values[l.NegatedIndex()];
1696 if (activity <= 1.01)
continue;
1697 const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
1700 best = l.NegatedIndex();
1704 const double activity = current_value + expanded_lp_values[best];
1705 candidates.push_back({current_literal,
Literal(best), activity});
1710 const int kMaxNumberOfCutPerCall = 50;
1711 std::sort(candidates.begin(), candidates.end());
1712 if (candidates.size() > kMaxNumberOfCutPerCall) {
1713 candidates.resize(kMaxNumberOfCutPerCall);
1719 std::vector<Literal> at_most_one;
1720 for (
const Candidate& candidate : candidates) {
1721 at_most_one = ExpandAtMostOneWithWeight(
1722 {candidate.a, candidate.b}, can_be_included, expanded_lp_values);
1723 if (!at_most_one.empty()) tmp_cuts_.push_back(at_most_one);
1729 void BinaryImplicationGraph::MarkDescendants(
Literal root) {
1730 dfs_stack_ = {root};
1732 if (is_redundant_[root.
Index()])
return;
1733 for (
int j = 0; j < dfs_stack_.size(); ++j) {
1734 const Literal current = dfs_stack_[j];
1735 for (
const Literal l : implications_[current.Index()]) {
1736 if (!is_marked_[l.Index()] && !is_redundant_[l.Index()]) {
1737 dfs_stack_.push_back(l);
1738 is_marked_.
Set(l.Index());
1742 if (current.Index() >= at_most_ones_.
size())
continue;
1743 for (
const int start : at_most_ones_[current.Index()]) {
1744 for (
int i = start;; ++i) {
1745 const Literal l = at_most_one_buffer_[i];
1747 if (l == current)
continue;
1748 if (!is_marked_[l.NegatedIndex()] && !is_redundant_[l.NegatedIndex()]) {
1749 dfs_stack_.push_back(l.Negated());
1750 is_marked_.
Set(l.NegatedIndex());
1755 work_done_in_mark_descendants_ += dfs_stack_.size();
1758 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
1759 const absl::Span<const Literal> at_most_one) {
1760 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1763 for (
int i = 0; i < clique.size(); ++i) {
1764 if (implications_[clique[i].
Index()].empty() ||
1765 is_redundant_[clique[i].
Index()]) {
1770 std::vector<LiteralIndex> intersection;
1771 for (
int i = 0; i < clique.size(); ++i) {
1773 MarkDescendants(clique[i]);
1776 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1780 is_marked_.
Clear(clique[i].NegatedIndex());
1781 for (
const LiteralIndex
index : intersection) {
1782 if (is_marked_[
index]) intersection[new_size++] =
index;
1784 intersection.resize(new_size);
1785 if (intersection.empty())
break;
1788 if (i + 1 == clique.size()) {
1789 clique.push_back(Literal(intersection.back()).Negated());
1790 intersection.pop_back();
1803 for (
const Literal l : direct_implications_) {
1804 in_direct_implications_[l.Index()] =
false;
1806 direct_implications_.
clear();
1814 if (!is_removed_[l.Index()] && !in_direct_implications_[l.Index()]) {
1815 in_direct_implications_[l.Index()] =
true;
1820 if (is_redundant_[
literal.Index()]) {
1823 for (
const int start : at_most_ones_[
literal.Index()]) {
1824 for (
int i = start;; ++i) {
1825 const Literal l = at_most_one_buffer_[i];
1829 if (!is_removed_[l.
Index()] &&
1837 estimated_sizes_[
literal.Index()] = direct_implications_.
size();
1838 return direct_implications_;
1850 direct_implications_of_negated_literal_ =
1853 for (
const Literal l : direct_implications_of_negated_literal_) {
1854 if (in_direct_implications_[l.Index()]) {
1856 if (!FixLiteral(l)) {
1867 BooleanVariable
var) {
1870 direct_implications_of_negated_literal_ =
1873 for (
const Literal l : direct_implications_of_negated_literal_) {
1877 CHECK(!in_direct_implications_[l.Index()]);
1880 if (in_direct_implications_[l.NegatedIndex()]) result--;
1887 BooleanVariable
var, std::deque<std::vector<Literal>>* postsolve_clauses) {
1889 direct_implications_of_negated_literal_ =
1892 estimated_sizes_[
b.NegatedIndex()]--;
1893 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1894 if (a_negated.Negated() ==
b)
continue;
1898 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1899 estimated_sizes_[a_negated.NegatedIndex()]--;
1904 for (
const Literal b : direct_implications_) {
1905 if (drat_proof_handler_ !=
nullptr) {
1908 postsolve_clauses->push_back({
Literal(
var,
false),
b});
1910 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1911 if (drat_proof_handler_ !=
nullptr) {
1914 postsolve_clauses->push_back({
Literal(
var,
true), a_negated});
1920 is_removed_[
index] =
true;
1921 if (!is_redundant_[
index]) {
1922 ++num_redundant_literals_;
1923 is_redundant_[
index] =
true;
1925 implications_[
index].clear();
1930 for (
auto& implication : implications_) {
1932 for (
const Literal l : implication) {
1933 if (!is_removed_[l.Index()]) implication[new_size++] = l;
1935 implication.resize(new_size);
1939 at_most_ones_.
clear();
1940 CleanUpAndAddAtMostOnes(0);
1950 clause->size_ = literals.size();
1951 for (
int i = 0; i < literals.size(); ++i) {
1952 clause->literals_[i] = literals[i];
1971 for (
int i = j; i < size_; ++i) {
1975 std::swap(literals_[j], literals_[i]);
1993 if (!result.empty()) result.append(
" ");
1994 result.append(
literal.DebugString());