35 template <
typename Watcher>
36 bool WatcherListContains(
const std::vector<Watcher>& list,
37 const SatClause& candidate) {
38 for (
const Watcher& watcher : list) {
39 if (watcher.clause == &candidate)
return true;
45 template <
typename Container,
typename Predicate>
46 void RemoveIf(Container c, Predicate p) {
47 c->erase(std::remove_if(c->begin(), c->end(), p), c->end());
58 num_inspected_clauses_(0),
59 num_inspected_clause_literals_(0),
60 num_watched_clauses_(0),
61 stats_(
"LiteralWatchers") {
72 watchers_on_false_.resize(num_variables << 1);
73 reasons_.resize(num_variables);
74 needs_cleaning_.
Resize(LiteralIndex(num_variables << 1));
83 DCHECK(!WatcherListContains(watchers_on_false_[
literal.Index()], *clause));
84 watchers_on_false_[
literal.Index()].push_back(
85 Watcher(clause, blocking_literal));
88 bool LiteralWatchers::PropagateOnFalse(Literal false_literal,
Trail* trail) {
91 std::vector<Watcher>& watchers = watchers_on_false_[false_literal.Index()];
92 const VariablesAssignment& assignment = trail->Assignment();
97 auto new_it = watchers.begin();
98 const auto end = watchers.end();
99 while (new_it != end && assignment.LiteralIsTrue(new_it->blocking_literal)) {
102 for (
auto it = new_it; it != end; ++it) {
104 if (assignment.LiteralIsTrue(it->blocking_literal)) {
108 ++num_inspected_clauses_;
113 Literal* literals = it->clause->literals();
114 const Literal other_watched_literal(
116 false_literal.Index().value()));
117 if (assignment.LiteralIsTrue(other_watched_literal)) {
119 new_it->blocking_literal = other_watched_literal;
121 ++num_inspected_clause_literals_;
129 const int start = it->start_index;
130 const int size = it->clause->size();
134 while (i < size && assignment.LiteralIsFalse(literals[i])) ++i;
135 num_inspected_clause_literals_ += i - start + 2;
138 while (i < start && assignment.LiteralIsFalse(literals[i])) ++i;
139 num_inspected_clause_literals_ += i - 2;
140 if (i >= start) i = size;
146 literals[0] = other_watched_literal;
147 literals[1] = literals[i];
148 literals[i] = false_literal;
149 watchers_on_false_[literals[1].Index()].emplace_back(
150 it->clause, other_watched_literal, i + 1);
157 if (assignment.LiteralIsFalse(other_watched_literal)) {
162 trail->MutableConflict()->assign(it->clause->begin(), it->clause->end());
163 trail->SetFailingSatClause(it->clause);
164 num_inspected_clause_literals_ += it - watchers.begin() + 1;
165 watchers.erase(new_it, it);
172 literals[0] = other_watched_literal;
173 literals[1] = false_literal;
174 reasons_[trail->Index()] = it->clause;
179 num_inspected_clause_literals_ += watchers.size();
180 watchers.erase(new_it, end);
185 const int old_index = trail->
Index();
188 if (!PropagateOnFalse(
literal.Negated(), trail))
return false;
194 int trail_index)
const {
195 return reasons_[trail_index]->PropagationReason();
199 return reasons_[trail_index];
209 clauses_.push_back(clause);
210 return AttachAndPropagate(clause, trail);
214 const std::vector<Literal>& literals,
Trail* trail) {
216 clauses_.push_back(clause);
217 CHECK(AttachAndPropagate(clause, trail));
226 bool LiteralWatchers::AttachAndPropagate(
SatClause* clause,
Trail* trail) {
229 const int size = clause->
size();
230 Literal* literals = clause->literals();
234 int num_literal_not_false = 0;
235 for (
int i = 0; i < size; ++i) {
237 std::swap(literals[i], literals[num_literal_not_false]);
238 ++num_literal_not_false;
239 if (num_literal_not_false == 2) {
248 if (num_literal_not_false == 0)
return false;
250 if (num_literal_not_false == 1) {
253 int max_level = trail->
Info(literals[1].Variable()).
level;
254 for (
int i = 2; i < size; ++i) {
255 const int level = trail->
Info(literals[i].Variable()).
level;
256 if (level > max_level) {
264 reasons_[trail->
Index()] = clause;
269 ++num_watched_clauses_;
270 AttachOnFalse(literals[0], literals[1], clause);
271 AttachOnFalse(literals[1], literals[0], clause);
276 Literal* literals = clause->literals();
280 ++num_watched_clauses_;
281 AttachOnFalse(literals[0], literals[1], clause);
282 AttachOnFalse(literals[1], literals[0], clause);
285 void LiteralWatchers::InternalDetach(
SatClause* clause) {
286 --num_watched_clauses_;
287 const size_t size = clause->
size();
288 if (drat_proof_handler_ !=
nullptr && size > 2) {
291 clauses_info_.erase(clause);
296 InternalDetach(clause);
303 InternalDetach(clause);
305 needs_cleaning_.
Clear(l.Index());
306 RemoveIf(&(watchers_on_false_[l.Index()]), [](
const Watcher& watcher) {
313 if (!all_clauses_are_attached_)
return;
314 all_clauses_are_attached_ =
false;
319 num_watched_clauses_ = 0;
320 watchers_on_false_.clear();
324 if (all_clauses_are_attached_)
return;
325 all_clauses_are_attached_ =
true;
328 watchers_on_false_.resize(needs_cleaning_.
size().value());
332 ++num_watched_clauses_;
342 if (drat_proof_handler_ !=
nullptr) {
343 drat_proof_handler_->
AddClause({true_literal});
352 return implication_graph_->
Propagate(trail_);
360 CHECK(!all_clauses_are_attached_);
361 if (drat_proof_handler_ !=
nullptr) {
364 clauses_info_.erase(clause);
369 SatClause* clause, absl::Span<const Literal> new_clause) {
370 if (new_clause.empty())
return false;
373 for (
const Literal l : new_clause) {
378 if (new_clause.size() == 1) {
384 if (new_clause.size() == 2) {
390 if (drat_proof_handler_ !=
nullptr) {
392 drat_proof_handler_->
AddClause(new_clause);
396 if (all_clauses_are_attached_) {
399 --num_watched_clauses_;
402 needs_cleaning_.
Clear(l.Index());
403 RemoveIf(&(watchers_on_false_[l.Index()]), [](
const Watcher& watcher) {
409 clause->Rewrite(new_clause);
412 if (all_clauses_are_attached_)
Attach(clause, trail_);
417 absl::Span<const Literal> new_clause) {
418 CHECK(!new_clause.empty());
419 CHECK(!all_clauses_are_attached_);
421 for (
const Literal l : new_clause) {
426 if (new_clause.size() == 1) {
432 if (new_clause.size() == 2) {
438 clauses_.push_back(clause);
446 RemoveIf(&(watchers_on_false_[
index]), [](
const Watcher& watcher) {
459 if (to_minimize_index_ >= clauses_.size()) {
460 to_minimize_index_ = clauses_.size();
463 std::stable_partition(clauses_.begin(),
464 clauses_.begin() + to_minimize_index_,
469 std::vector<SatClause*>::iterator iter =
470 std::stable_partition(clauses_.begin(), clauses_.end(),
473 clauses_.erase(iter, clauses_.end());
480 implications_.resize(num_variables << 1);
481 is_redundant_.
resize(implications_.size(),
false);
482 is_removed_.
resize(implications_.size(),
false);
483 estimated_sizes_.
resize(implications_.size(), 0);
484 in_direct_implications_.
resize(implications_.size(),
false);
485 reasons_.resize(num_variables);
494 if (drat_proof_handler_ !=
nullptr) {
501 estimated_sizes_[
a.NegatedIndex()]++;
502 estimated_sizes_[
b.NegatedIndex()]++;
503 implications_[
a.NegatedIndex()].push_back(
b);
504 implications_[
b.NegatedIndex()].push_back(
a);
506 num_implications_ += 2;
514 const auto& assignment = trail_->
Assignment();
515 if (assignment.LiteralIsFalse(
a)) {
516 if (assignment.LiteralIsAssigned(
b)) {
517 if (assignment.LiteralIsFalse(
b))
return false;
519 reasons_[trail_->
Index()] =
a;
522 }
else if (assignment.LiteralIsFalse(
b)) {
523 if (!assignment.LiteralIsAssigned(
a)) {
524 reasons_[trail_->
Index()] =
b;
533 absl::Span<const Literal> at_most_one) {
535 if (at_most_one.size() <= 1)
return true;
540 const int base_index = at_most_one_buffer_.size();
541 at_most_one_buffer_.insert(at_most_one_buffer_.end(), at_most_one.begin(),
546 return CleanUpAndAddAtMostOnes(base_index);
551 bool BinaryImplicationGraph::FixLiteral(
Literal true_literal) {
555 if (drat_proof_handler_ !=
nullptr) {
556 drat_proof_handler_->
AddClause({true_literal});
566 bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(
const int base_index) {
567 const VariablesAssignment& assignment = trail_->
Assignment();
568 int local_end = base_index;
569 const int buffer_size = at_most_one_buffer_.size();
570 for (
int i = base_index; i < buffer_size; ++i) {
575 const int local_start = local_end;
576 bool set_all_left_to_false =
false;
578 const Literal l = at_most_one_buffer_[i];
580 if (assignment.LiteralIsFalse(l))
continue;
581 if (is_removed_[l.Index()])
continue;
582 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
583 set_all_left_to_false =
true;
591 bool some_duplicates =
false;
592 if (!set_all_left_to_false) {
593 int new_local_end = local_start;
594 std::sort(&at_most_one_buffer_[local_start],
595 &at_most_one_buffer_[local_end]);
597 bool remove_previous =
false;
598 for (
int j = local_start; j < local_end; ++j) {
599 const Literal l = at_most_one_buffer_[j];
600 if (l.Index() == previous) {
601 if (assignment.LiteralIsTrue(l))
return false;
602 if (!assignment.LiteralIsFalse(l)) {
603 if (!FixLiteral(l.Negated()))
return false;
605 remove_previous =
true;
606 some_duplicates =
true;
612 if (remove_previous) {
614 remove_previous =
false;
616 previous = l.Index();
617 at_most_one_buffer_[new_local_end++] = l;
619 if (remove_previous) --new_local_end;
620 local_end = new_local_end;
625 if (some_duplicates) {
626 int new_local_end = local_start;
627 for (
int j = local_start; j < local_end; ++j) {
628 const Literal l = at_most_one_buffer_[j];
629 if (assignment.LiteralIsFalse(l))
continue;
630 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
631 set_all_left_to_false =
true;
634 at_most_one_buffer_[new_local_end++] = l;
636 local_end = new_local_end;
640 if (set_all_left_to_false) {
641 for (
int j = local_start; j < local_end; ++j) {
642 const Literal l = at_most_one_buffer_[j];
643 if (assignment.LiteralIsFalse(l))
continue;
644 if (assignment.LiteralIsTrue(l))
return false;
645 if (!FixLiteral(l.Negated()))
return false;
647 local_end = local_start;
652 const absl::Span<const Literal> at_most_one(
653 &at_most_one_buffer_[local_start], local_end - local_start);
657 if (at_most_one.size() < 10) {
659 for (
const Literal
a : at_most_one) {
660 for (
const Literal
b : at_most_one) {
661 if (
a ==
b)
continue;
662 implications_[
a.Index()].push_back(
b.Negated());
665 num_implications_ += at_most_one.size() * (at_most_one.size() - 1);
668 local_end = local_start;
673 for (
const Literal l : at_most_one) {
674 if (l.Index() >= at_most_ones_.
size()) {
675 at_most_ones_.
resize(l.Index().value() + 1);
677 CHECK(!is_redundant_[l.Index()]);
678 at_most_ones_[l.Index()].
push_back(local_start);
685 at_most_one_buffer_.resize(local_end);
689 bool BinaryImplicationGraph::PropagateOnTrue(Literal true_literal,
693 const VariablesAssignment& assignment = trail->Assignment();
694 DCHECK(assignment.LiteralIsTrue(true_literal));
699 num_inspections_ += implications_[true_literal.Index()].size();
701 for (Literal
literal : implications_[true_literal.Index()]) {
702 if (assignment.LiteralIsTrue(
literal)) {
712 if (assignment.LiteralIsFalse(
literal)) {
714 *(trail->MutableConflict()) = {true_literal.Negated(),
literal};
718 reasons_[trail->Index()] = true_literal.Negated();
724 if (true_literal.Index() < at_most_ones_.
size()) {
725 for (
const int start : at_most_ones_[true_literal.Index()]) {
727 for (
int i = start;; ++i) {
728 const Literal
literal = at_most_one_buffer_[i];
739 if (assignment.LiteralIsFalse(
literal))
continue;
742 if (assignment.LiteralIsTrue(
literal)) {
744 *(trail->MutableConflict()) = {true_literal.Negated(),
749 reasons_[trail->Index()] = true_literal.Negated();
764 while (propagation_trail_index_ < trail->
Index()) {
766 if (!PropagateOnTrue(
literal, trail))
return false;
772 const Trail& trail,
int trail_index)
const {
773 return {&reasons_[trail_index], 1};
784 std::vector<Literal>* conflict) {
790 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
792 is_marked_.
Set(root_literal_index);
801 const bool also_prune_direct_implication_list =
false;
805 auto& direct_implications = implications_[root_literal_index];
806 for (
const Literal l : direct_implications) {
807 if (is_marked_[l.Index()])
continue;
808 dfs_stack_.push_back(l);
809 while (!dfs_stack_.empty()) {
810 const LiteralIndex
index = dfs_stack_.back().Index();
811 dfs_stack_.pop_back();
812 if (!is_marked_[
index]) {
815 if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
831 if (also_prune_direct_implication_list) {
832 is_marked_.
Clear(l.Index());
838 if (also_prune_direct_implication_list) {
840 for (
const Literal l : direct_implications) {
841 if (!is_marked_[l.Index()]) {
842 is_marked_.
Set(l.Index());
843 direct_implications[new_size] = l;
847 if (new_size < direct_implications.size()) {
848 num_redundant_implications_ += direct_implications.size() - new_size;
849 direct_implications.resize(new_size);
853 RemoveRedundantLiterals(conflict);
861 const Trail& trail, std::vector<Literal>* conflict,
864 CHECK(!conflict->empty());
866 MarkDescendants(conflict->front().Negated());
872 RemoveRedundantLiterals(conflict);
879 const Trail& trail, std::vector<Literal>* conflict,
882 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
884 is_marked_.
Set(root_literal_index);
887 auto& direct_implications = implications_[root_literal_index];
893 std::shuffle(direct_implications.begin(), direct_implications.end(), random);
895 for (
const Literal l : direct_implications) {
896 if (is_marked_[l.Index()]) {
902 direct_implications[new_size++] = l;
903 dfs_stack_.push_back(l);
904 while (!dfs_stack_.empty()) {
905 const LiteralIndex
index = dfs_stack_.back().Index();
906 dfs_stack_.pop_back();
907 if (!is_marked_[
index]) {
910 if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
915 if (new_size < direct_implications.size()) {
916 num_redundant_implications_ += direct_implications.size() - new_size;
917 direct_implications.resize(new_size);
919 RemoveRedundantLiterals(conflict);
922 void BinaryImplicationGraph::RemoveRedundantLiterals(
923 std::vector<Literal>* conflict) {
926 for (
int i = 1; i < conflict->size(); ++i) {
927 if (!is_marked_[(*conflict)[i].NegatedIndex()]) {
928 (*conflict)[new_index] = (*conflict)[i];
932 if (new_index < conflict->size()) {
934 num_literals_removed_ += conflict->size() - new_index;
935 conflict->resize(new_index);
941 const Trail& trail, std::vector<Literal>* conflict) {
944 is_simplified_.
ClearAndResize(LiteralIndex(implications_.size()));
945 for (
Literal lit : *conflict) {
946 is_marked_.
Set(lit.Index());
962 for (
int i = 1; i < conflict->size(); ++i) {
963 const Literal lit = (*conflict)[i];
965 bool keep_literal =
true;
967 if (is_marked_[implied.Index()]) {
969 if (lit_level == trail.
Info(implied.Variable()).level &&
970 is_simplified_[implied.Index()]) {
973 keep_literal =
false;
978 (*conflict)[
index] = lit;
984 if (index < conflict->size()) {
986 num_literals_removed_ += conflict->size() -
index;
987 conflict->erase(conflict->begin() +
index, conflict->end());
997 const int new_num_fixed = trail_->
Index();
999 if (num_processed_fixed_variables_ == new_num_fixed)
return;
1003 for (; num_processed_fixed_variables_ < new_num_fixed;
1004 ++num_processed_fixed_variables_) {
1005 const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
1009 for (
const Literal lit : implications_[true_literal.
Index()]) {
1023 is_marked_.
Set(lit.NegatedIndex());
1028 if (true_literal.
Index() < at_most_ones_.
size()) {
1036 RemoveIf(&implications_[i], [&assignment](
const Literal& lit) {
1043 at_most_ones_.
clear();
1044 CleanUpAndAddAtMostOnes(0);
1055 std::vector<std::vector<int32_t>>>;
1059 std::vector<Literal>* at_most_one_buffer)
1061 implications_(*graph),
1062 at_most_ones_(*at_most_ones),
1063 at_most_one_buffer_(*at_most_one_buffer) {}
1067 for (
const Literal l : implications_[LiteralIndex(node)]) {
1068 tmp_.push_back(l.Index().value());
1073 if (node < at_most_ones_.
size()) {
1074 for (
const int start : at_most_ones_[LiteralIndex(node)]) {
1075 if (start >= at_most_one_already_explored_.size()) {
1076 at_most_one_already_explored_.resize(start + 1,
false);
1077 previous_node_to_explore_at_most_one_.resize(start + 1);
1087 if (at_most_one_already_explored_[start]) {
1089 const int first_node = previous_node_to_explore_at_most_one_[start];
1108 previous_node_to_explore_at_most_one_[start] = node;
1113 Literal(LiteralIndex(first_node)).NegatedIndex().
value());
1117 at_most_one_already_explored_[start] =
true;
1118 previous_node_to_explore_at_most_one_[start] = node;
1121 for (
int i = start;; ++i) {
1122 const Literal l = at_most_one_buffer_[i];
1124 if (l.
Index() == node)
continue;
1146 const std::vector<Literal>& at_most_one_buffer_;
1148 mutable std::vector<int32_t> tmp_;
1151 mutable std::vector<bool> at_most_one_already_explored_;
1152 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1158 if (is_dag_)
return true;
1169 int num_fixed_during_scc = 0;
1170 const int32_t size(implications_.size());
1171 std::vector<std::vector<int32_t>> scc;
1175 SccGraph graph(&finder, &implications_, &at_most_ones_,
1176 &at_most_one_buffer_);
1183 ++num_fixed_during_scc;
1184 if (!FixLiteral(l))
return false;
1190 is_redundant_.
resize(size,
false);
1192 int num_equivalences = 0;
1193 reverse_topological_order_.clear();
1194 for (std::vector<int32_t>& component : scc) {
1202 bool all_fixed =
false;
1203 bool all_true =
false;
1204 for (
const int32_t i : component) {
1213 for (
const int32_t i : component) {
1215 if (!is_redundant_[l.
Index()]) {
1216 ++num_redundant_literals_;
1217 is_redundant_[l.
Index()] =
true;
1222 ++num_fixed_during_scc;
1223 if (!FixLiteral(l))
return false;
1232 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1241 std::sort(component.begin(), component.end());
1245 if (component.size() == 1) {
1248 if (num_equivalences > 0) {
1250 for (
Literal& ref : representative_list) {
1251 const LiteralIndex rep = representative_of_[ref.Index()];
1262 for (
int i = 1; i < component.size(); ++i) {
1264 if (!is_redundant_[
literal.Index()]) {
1265 ++num_redundant_literals_;
1266 is_redundant_[
literal.Index()] =
true;
1272 if (
Literal(LiteralIndex(component[i - 1])).Negated() ==
literal) {
1273 LOG_IF(
INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
1282 for (
const Literal l : representative_list) {
1285 representative_list[new_size++] = rep;
1287 representative_list.resize(new_size);
1288 for (
int i = 1; i < component.size(); ++i) {
1290 auto& ref = implications_[
literal.Index()];
1301 representative_list.push_back(
literal);
1306 num_equivalences += component.size() - 1;
1310 if (num_equivalences != 0) {
1314 at_most_ones_.
clear();
1315 CleanUpAndAddAtMostOnes(0);
1317 num_implications_ = 0;
1318 for (LiteralIndex i(0); i < size; ++i) {
1319 num_implications_ += implications_[i].size();
1321 dtime += 2e-8 * num_implications_;
1325 LOG_IF(
INFO, log_info) <<
"SCC. " << num_equivalences
1326 <<
" redundant equivalent literals. " 1327 << num_fixed_during_scc <<
" fixed. " 1328 << num_implications_ <<
" implications left. " 1329 << implications_.size() <<
" literals." 1330 <<
" size of at_most_one buffer = " 1331 << at_most_one_buffer_.size() <<
"." 1332 <<
" dtime: " << dtime
1356 int64_t num_fixed = 0;
1357 int64_t num_new_redundant_implications = 0;
1358 bool aborted =
false;
1359 work_done_in_mark_descendants_ = 0;
1360 int marked_index = 0;
1378 const LiteralIndex size(implications_.size());
1380 for (
const LiteralIndex root : reverse_topological_order_) {
1385 if (is_redundant_[root])
continue;
1388 auto& direct_implications = implications_[root];
1389 if (direct_implications.empty())
continue;
1398 bool clear_previous_reachability =
true;
1399 for (
const Literal direct_child : direct_implications) {
1400 if (direct_child.Index() == previous) {
1401 clear_previous_reachability =
false;
1402 is_marked_.
Clear(previous);
1406 if (clear_previous_reachability) {
1412 for (
const Literal direct_child : direct_implications) {
1413 if (is_redundant_[direct_child.Index()])
continue;
1414 if (is_marked_[direct_child.Index()])
continue;
1418 if (direct_child.Index() == root)
continue;
1422 if (direct_child.NegatedIndex() == root) {
1423 is_marked_.
Set(direct_child.Index());
1427 MarkDescendants(direct_child);
1430 is_marked_.
Clear(direct_child.Index());
1432 CHECK(!is_marked_[root])
1433 <<
"DetectEquivalences() should have removed cycles!";
1434 is_marked_.
Set(root);
1440 for (; marked_index < marked_positions.size(); ++marked_index) {
1441 const LiteralIndex i = marked_positions[marked_index];
1450 if (!FixLiteral(
Literal(root).Negated()))
return false;
1464 for (
const Literal l : direct_implications) {
1465 if (!is_marked_[l.Index()]) {
1466 direct_implications[new_size++] = l;
1468 CHECK(!is_redundant_[l.Index()]);
1471 const int diff = direct_implications.size() - new_size;
1472 direct_implications.resize(new_size);
1473 direct_implications.shrink_to_fit();
1474 num_new_redundant_implications += diff;
1475 num_implications_ -= diff;
1478 if (work_done_in_mark_descendants_ > 1e8) {
1486 const double dtime = 1e-8 * work_done_in_mark_descendants_;
1488 num_redundant_implications_ += num_new_redundant_implications;
1489 LOG_IF(
INFO, log_info) <<
"Transitive reduction removed " 1490 << num_new_redundant_implications <<
" literals. " 1491 << num_fixed <<
" fixed. " << num_implications_
1492 <<
" implications left. " << implications_.size()
1494 <<
" dtime: " << dtime
1496 << (aborted ?
" Aborted." :
"");
1502 bool IntersectionIsEmpty(
const std::vector<int>&
a,
const std::vector<int>&
b) {
1503 DCHECK(std::is_sorted(
a.begin(),
a.end()));
1504 DCHECK(std::is_sorted(
b.begin(),
b.end()));
1507 for (; i <
a.size() && j <
b.size();) {
1508 if (
a[i] ==
b[j])
return false;
1520 std::size_t operator()(
const std::vector<Literal>& at_most_one)
const {
1522 for (Literal
literal : at_most_one) {
1532 std::vector<std::vector<Literal>>* at_most_ones,
1533 int64_t max_num_explored_nodes) {
1536 work_done_in_mark_descendants_ = 0;
1538 int num_extended = 0;
1539 int num_removed = 0;
1542 absl::flat_hash_set<std::vector<Literal>, VectorHash> max_cliques;
1544 implications_.size());
1547 std::sort(at_most_ones->begin(), at_most_ones->end(),
1548 [](
const std::vector<Literal>
a,
const std::vector<Literal>
b) {
1549 return a.size() >
b.size();
1551 for (std::vector<Literal>& clique : *at_most_ones) {
1552 const int old_size = clique.
size();
1562 const LiteralIndex rep = representative_of_[ref.Index()];
1571 if (old_size == 2 && clique[0] != clique[1]) {
1572 if (!IntersectionIsEmpty(max_cliques_containing[clique[0].
Index()],
1573 max_cliques_containing[clique[1].
Index()])) {
1581 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1582 clique = ExpandAtMostOne(clique);
1584 std::sort(clique.begin(), clique.end());
1591 const int clique_index = max_cliques.size();
1592 for (
const Literal l : clique) {
1593 max_cliques_containing[l.Index()].push_back(clique_index);
1595 if (clique.size() > old_size) ++num_extended;
1599 if (num_extended > 0 || num_removed > 0 || num_added > 0) {
1600 VLOG(1) <<
"Clique Extended: " << num_extended
1601 <<
" Removed: " << num_removed <<
" Added: " << num_added
1602 << (work_done_in_mark_descendants_ > max_num_explored_nodes
1609 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOneWithWeight(
1610 const absl::Span<const Literal> at_most_one,
1613 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1614 std::vector<LiteralIndex> intersection;
1615 double clique_weight = 0.0;
1616 const int64_t old_work = work_done_in_mark_descendants_;
1617 for (
const Literal l : clique) clique_weight += expanded_lp_values[l.Index()];
1618 for (
int i = 0; i < clique.size(); ++i) {
1620 if (work_done_in_mark_descendants_ - old_work > 1e8)
break;
1623 MarkDescendants(clique[i]);
1626 if (can_be_included[
index]) intersection.push_back(
index);
1628 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1632 double intersection_weight = 0.0;
1634 is_marked_.
Clear(clique[i].NegatedIndex());
1635 for (
const LiteralIndex
index : intersection) {
1636 if (!is_marked_[
index])
continue;
1637 intersection[new_size++] =
index;
1638 intersection_weight += expanded_lp_values[
index];
1640 intersection.
resize(new_size);
1641 if (intersection.empty())
break;
1645 if (clique_weight + intersection_weight <= 1.0) {
1652 if (i + 1 == clique.size()) {
1655 double max_lp = 0.0;
1656 for (
int j = 0; j < intersection.size(); ++j) {
1657 const double lp = 1.0 - expanded_lp_values[intersection[j]] +
1658 absl::Uniform<double>(*random_, 0.0, 1e-4);
1659 if (
index == -1 || lp > max_lp) {
1665 clique.push_back(Literal(intersection[
index]).Negated());
1667 intersection.pop_back();
1668 clique_weight += expanded_lp_values[clique.back().Index()];
1675 const std::vector<std::vector<Literal>>&
1677 const std::vector<Literal>& literals,
1678 const std::vector<double>& lp_values) {
1680 const int num_literals = implications_.size();
1684 const int size = literals.size();
1685 for (
int i = 0; i < size; ++i) {
1686 const Literal l = literals[i];
1687 can_be_included[l.
Index()] =
true;
1690 const double value = lp_values[i];
1700 bool operator<(
const Candidate& other)
const {
return sum > other.sum; }
1702 std::vector<Candidate> candidates;
1710 for (
int i = 0; i < size; ++i) {
1711 Literal current_literal = literals[i];
1712 double current_value = lp_values[i];
1714 if (is_redundant_[current_literal.
Index()])
continue;
1716 if (current_value < 0.5) {
1717 current_literal = current_literal.
Negated();
1718 current_value = 1.0 - current_value;
1723 double best_value = 0.0;
1724 for (
const Literal l : implications_[current_literal.
Index()]) {
1725 if (!can_be_included[l.Index()])
continue;
1726 const double activity =
1727 current_value + expanded_lp_values[l.NegatedIndex()];
1728 if (activity <= 1.01)
continue;
1729 const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
1732 best = l.NegatedIndex();
1736 const double activity = current_value + expanded_lp_values[best];
1737 candidates.push_back({current_literal,
Literal(best), activity});
1742 const int kMaxNumberOfCutPerCall = 50;
1743 std::sort(candidates.begin(), candidates.end());
1744 if (candidates.size() > kMaxNumberOfCutPerCall) {
1745 candidates.resize(kMaxNumberOfCutPerCall);
1751 std::vector<Literal> at_most_one;
1752 for (
const Candidate& candidate : candidates) {
1753 at_most_one = ExpandAtMostOneWithWeight(
1754 {candidate.a, candidate.b}, can_be_included, expanded_lp_values);
1755 if (!at_most_one.empty()) tmp_cuts_.push_back(at_most_one);
1761 void BinaryImplicationGraph::MarkDescendants(
Literal root) {
1762 dfs_stack_ = {root};
1764 if (is_redundant_[root.
Index()])
return;
1765 for (
int j = 0; j < dfs_stack_.size(); ++j) {
1766 const Literal current = dfs_stack_[j];
1767 for (
const Literal l : implications_[current.Index()]) {
1768 if (!is_marked_[l.Index()] && !is_redundant_[l.Index()]) {
1769 dfs_stack_.push_back(l);
1770 is_marked_.
Set(l.Index());
1774 if (current.Index() >= at_most_ones_.
size())
continue;
1775 for (
const int start : at_most_ones_[current.Index()]) {
1776 for (
int i = start;; ++i) {
1777 const Literal l = at_most_one_buffer_[i];
1779 if (l == current)
continue;
1780 if (!is_marked_[l.NegatedIndex()] && !is_redundant_[l.NegatedIndex()]) {
1781 dfs_stack_.push_back(l.Negated());
1782 is_marked_.
Set(l.NegatedIndex());
1787 work_done_in_mark_descendants_ += dfs_stack_.size();
1790 std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
1791 const absl::Span<const Literal> at_most_one) {
1792 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1795 for (
int i = 0; i < clique.size(); ++i) {
1796 if (implications_[clique[i].
Index()].empty() ||
1797 is_redundant_[clique[i].Index()]) {
1802 std::vector<LiteralIndex> intersection;
1803 for (
int i = 0; i < clique.size(); ++i) {
1805 MarkDescendants(clique[i]);
1808 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1812 is_marked_.
Clear(clique[i].NegatedIndex());
1813 for (
const LiteralIndex
index : intersection) {
1814 if (is_marked_[
index]) intersection[new_size++] =
index;
1816 intersection.resize(new_size);
1817 if (intersection.empty())
break;
1820 if (i + 1 == clique.size()) {
1821 clique.push_back(Literal(intersection.back()).Negated());
1822 intersection.pop_back();
1835 for (
const Literal l : direct_implications_) {
1836 in_direct_implications_[l.Index()] =
false;
1838 direct_implications_.
clear();
1846 if (!is_removed_[l.Index()] && !in_direct_implications_[l.Index()]) {
1847 in_direct_implications_[l.Index()] =
true;
1852 if (is_redundant_[
literal.Index()]) {
1855 for (
const int start : at_most_ones_[
literal.Index()]) {
1856 for (
int i = start;; ++i) {
1857 const Literal l = at_most_one_buffer_[i];
1861 if (!is_removed_[l.
Index()] &&
1869 estimated_sizes_[
literal.Index()] = direct_implications_.
size();
1870 return direct_implications_;
1879 if (assignment.VariableIsAssigned(
var))
return false;
1882 direct_implications_of_negated_literal_ =
1885 for (
const Literal l : direct_implications_of_negated_literal_) {
1886 if (in_direct_implications_[l.Index()]) {
1888 if (!FixLiteral(l)) {
1899 BooleanVariable
var) {
1902 direct_implications_of_negated_literal_ =
1905 for (
const Literal l : direct_implications_of_negated_literal_) {
1909 CHECK(!in_direct_implications_[l.Index()]);
1912 if (in_direct_implications_[l.NegatedIndex()]) result--;
1919 BooleanVariable
var, std::deque<std::vector<Literal>>* postsolve_clauses) {
1921 direct_implications_of_negated_literal_ =
1924 estimated_sizes_[
b.NegatedIndex()]--;
1925 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1926 if (a_negated.Negated() ==
b)
continue;
1930 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1931 estimated_sizes_[a_negated.NegatedIndex()]--;
1936 for (
const Literal b : direct_implications_) {
1937 if (drat_proof_handler_ !=
nullptr) {
1940 postsolve_clauses->push_back({
Literal(
var,
false),
b});
1942 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1943 if (drat_proof_handler_ !=
nullptr) {
1946 postsolve_clauses->push_back({
Literal(
var,
true), a_negated});
1952 is_removed_[
index] =
true;
1953 if (!is_redundant_[
index]) {
1954 ++num_redundant_literals_;
1955 is_redundant_[
index] =
true;
1957 implications_[
index].clear();
1962 for (
auto& implication : implications_) {
1964 for (
const Literal l : implication) {
1965 if (!is_removed_[l.Index()]) implication[new_size++] = l;
1967 implication.resize(new_size);
1971 at_most_ones_.
clear();
1972 CleanUpAndAddAtMostOnes(0);
1980 SatClause* clause = reinterpret_cast<SatClause*>(
1982 clause->size_ = literals.size();
1983 for (
int i = 0; i < literals.size(); ++i) {
1984 clause->literals_[i] = literals[i];
2003 for (
int i = j; i < size_; ++i) {
2025 if (!result.empty()) result.append(
" ");
2026 result.append(
literal.DebugString());
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
uint64_t Hash(uint64_t num, uint64_t c)
SatClause * AddRemovableClause(const std::vector< Literal > &literals, Trail *trail)
void MinimizeConflictWithReachability(std::vector< Literal > *c)
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
std::string StatString() const
static SatClause * Create(absl::Span< const Literal > literals)
SccGraph(SccFinder *finder, Implication *graph, AtMostOne *at_most_ones, std::vector< Literal > *at_most_one_buffer)
void Resize(int num_variables)
const Literal *const begin() const
void Set(IntegerType index)
void LazyDetach(SatClause *clause)
#define CHECK_GE(val1, val2)
bool InsertIfNotPresent(Collection *const collection, const typename Collection::value_type &value)
Class that owns everything related to a particular optimization model.
bool AddClause(absl::Span< const Literal > literals, Trail *trail)
absl::Span< const Literal > AsSpan() const
bool Propagate(Trail *trail) final
#define VLOG(verboselevel)
bool LiteralIsFalse(Literal literal) const
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
bool LiteralIsTrue(Literal literal) const
void Resize(IntegerType size)
LiteralIndex Index() const
void Clear(IntegerType index)
Literal RepresentativeOf(Literal l) const
void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end)
#define SCOPED_TIME_STAT(stats)
void Detach(SatClause *clause)
void AddClause(absl::Span< const Literal > clause)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
bool ComputeTransitiveReduction(bool log_info=false)
void CleanupAllRemovedVariables()
bool TransformIntoMaxCliques(std::vector< std::vector< Literal >> *at_most_ones, int64_t max_num_explored_nodes=1e8)
void RemoveFixedVariables()
void Enqueue(Literal true_literal, int propagator_id)
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked, absl::BitGenRef random)
void EnqueueWithUnitReason(Literal true_literal)
void InprocessingRemoveClause(SatClause *clause)
const std::vector< int32_t > & operator[](int32_t node) const
void AddImplication(Literal a, Literal b)
bool DetectEquivalences(bool log_info=false)
void resize(size_type new_size)
LiteralIndex NegatedIndex() const
BooleanVariable Variable() const
void AddBinaryClause(Literal a, Literal b)
void DeleteClause(absl::Span< const Literal > clause)
bool VariableIsAssigned(BooleanVariable var) const
bool Propagate(Trail *trail) final
void STLDeleteElements(T *container)
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
void DeleteRemovedClauses()
Literal SecondLiteral() const
bool LiteralIsAssigned(Literal literal) const
void push_back(const value_type &x)
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal >> *postsolve_clauses)
~LiteralWatchers() override
const std::vector< Literal > & DirectImplications(Literal literal)
#define DCHECK_GE(val1, val2)
void STLClearObject(T *obj)
#define CHECK_EQ(val1, val2)
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
SatClause * ReasonClause(int trail_index) const
Literal FirstLiteral() const
#define LOG_IF(severity, condition)
#define DCHECK(condition)
std::string DebugString() const
#define DCHECK_EQ(val1, val2)
void ClearAndResize(IntegerType size)
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
#define DCHECK_LE(val1, val2)
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
void RegisterPropagator(SatPropagator *propagator)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(const std::vector< Literal > &literals, const std::vector< double > &lp_values)
std::vector< Literal > to_fix_
Collection of objects used to extend the Constraint Solver library.
bool NodeIsInCurrentDfsPath(NodeIndex node) const
bool AddBinaryClauseDuringSearch(Literal a, Literal b)
LiteralWatchers(Model *model)
const LiteralIndex kNoLiteralIndex(-1)
const VariablesAssignment & Assignment() const
int propagation_trail_index_
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
#define VLOG_IS_ON(verboselevel)
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
int CurrentDecisionLevel() const
void Attach(SatClause *clause, Trail *trail)
void Resize(int num_variables)
#define CHECK_NE(val1, val2)
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
const AssignmentInfo & Info(BooleanVariable var) const
#define DCHECK_LT(val1, val2)
#define IF_STATS_ENABLED(instructions)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
bool IsSatisfied(const VariablesAssignment &assignment) const