27#include "absl/container/flat_hash_map.h"
28#include "absl/container/flat_hash_set.h"
29#include "absl/container/inlined_vector.h"
30#include "absl/log/check.h"
31#include "absl/random/bit_gen_ref.h"
32#include "absl/random/distributions.h"
33#include "absl/types/span.h"
54template <
typename Watcher>
55bool WatcherListContains(
const std::vector<Watcher>& list,
57 for (
const Watcher& watcher : list) {
58 if (watcher.clause == &candidate)
return true;
64template <
typename Container,
typename Predicate>
65void RemoveIf(Container c, Predicate p) {
66 c->erase(std::remove_if(
c->begin(),
c->end(), p),
c->end());
76 trail_(model->GetOrCreate<
Trail>()),
77 num_inspected_clauses_(0),
78 num_inspected_clause_literals_(0),
79 num_watched_clauses_(0),
80 stats_(
"ClauseManager") {
81 trail_->RegisterPropagator(
this);
91 watchers_on_false_.resize(num_variables << 1);
92 reasons_.resize(num_variables);
93 needs_cleaning_.Resize(LiteralIndex(num_variables << 1));
98void ClauseManager::AttachOnFalse(
Literal literal,
Literal blocking_literal,
102 DCHECK(!WatcherListContains(watchers_on_false_[literal], *clause));
103 watchers_on_false_[literal].push_back(Watcher(clause, blocking_literal));
106bool ClauseManager::PropagateOnFalse(Literal false_literal,
Trail* trail) {
109 std::vector<Watcher>& watchers = watchers_on_false_[false_literal];
110 const auto assignment = AssignmentView(trail->Assignment());
115 auto new_it = watchers.begin();
116 const auto end = watchers.end();
117 while (new_it != end && assignment.LiteralIsTrue(new_it->blocking_literal)) {
120 for (
auto it = new_it; it != end; ++it) {
122 if (assignment.LiteralIsTrue(it->blocking_literal)) {
126 ++num_inspected_clauses_;
131 Literal* literals = it->clause->literals();
132 const Literal other_watched_literal(
133 LiteralIndex(literals[0].
Index().value() ^ literals[1].
Index().value() ^
134 false_literal.Index().value()));
135 if (assignment.LiteralIsTrue(other_watched_literal)) {
137 new_it->blocking_literal = other_watched_literal;
139 ++num_inspected_clause_literals_;
147 const int start = it->start_index;
148 const int size = it->clause->size();
152 while (
i < size && assignment.LiteralIsFalse(literals[
i])) ++
i;
153 num_inspected_clause_literals_ +=
i - start + 2;
156 while (
i < start && assignment.LiteralIsFalse(literals[
i])) ++
i;
157 num_inspected_clause_literals_ +=
i - 2;
158 if (
i >= start)
i = size;
164 literals[0] = other_watched_literal;
165 literals[1] = literals[
i];
166 literals[
i] = false_literal;
167 watchers_on_false_[literals[1]].emplace_back(
168 it->clause, other_watched_literal,
i + 1);
175 if (assignment.LiteralIsFalse(other_watched_literal)) {
180 trail->MutableConflict()->assign(it->clause->begin(), it->clause->end());
181 trail->SetFailingSatClause(it->clause);
182 num_inspected_clause_literals_ += it - watchers.begin() + 1;
183 watchers.erase(new_it, it);
190 literals[0] = other_watched_literal;
191 literals[1] = false_literal;
192 reasons_[trail->Index()] = it->clause;
197 num_inspected_clause_literals_ += watchers.size();
198 watchers.erase(new_it, end);
203 const int old_index = trail->
Index();
206 if (!PropagateOnFalse(literal.
Negated(), trail))
return false;
214 return reasons_[trail_index]->PropagationReason();
218 return reasons_[trail_index];
228 clauses_.push_back(clause);
229 if (add_clause_callback_ !=
nullptr) add_clause_callback_(lbd, literals);
230 return AttachAndPropagate(clause, trail);
234 Trail* trail,
int lbd) {
236 clauses_.push_back(clause);
237 if (add_clause_callback_ !=
nullptr) add_clause_callback_(lbd, literals);
238 CHECK(AttachAndPropagate(clause, trail));
247bool ClauseManager::AttachAndPropagate(
SatClause* clause,
Trail* trail) {
250 const int size = clause->
size();
251 Literal* literals = clause->literals();
255 int num_literal_not_false = 0;
256 for (
int i = 0;
i < size; ++
i) {
258 std::swap(literals[
i], literals[num_literal_not_false]);
259 ++num_literal_not_false;
260 if (num_literal_not_false == 2) {
269 if (num_literal_not_false == 0)
return false;
271 if (num_literal_not_false == 1) {
274 int max_level = trail->
Info(literals[1].Variable()).
level;
275 for (
int i = 2;
i < size; ++
i) {
276 const int level = trail->
Info(literals[
i].Variable()).
level;
277 if (level > max_level) {
279 std::swap(literals[1], literals[
i]);
285 reasons_[trail->
Index()] = clause;
290 ++num_watched_clauses_;
291 AttachOnFalse(literals[0], literals[1], clause);
292 AttachOnFalse(literals[1], literals[0], clause);
297 Literal* literals = clause->literals();
301 ++num_watched_clauses_;
302 AttachOnFalse(literals[0], literals[1], clause);
303 AttachOnFalse(literals[1], literals[0], clause);
306void ClauseManager::InternalDetach(
SatClause* clause) {
307 --num_watched_clauses_;
308 const size_t size = clause->
size();
309 if (drat_proof_handler_ !=
nullptr && size > 2) {
312 clauses_info_.erase(clause);
317 InternalDetach(clause);
324 InternalDetach(clause);
326 needs_cleaning_.Clear(l);
327 RemoveIf(&(watchers_on_false_[l]), [](
const Watcher& watcher) {
334 if (!all_clauses_are_attached_)
return;
335 all_clauses_are_attached_ =
false;
340 num_watched_clauses_ = 0;
341 watchers_on_false_.clear();
345 if (all_clauses_are_attached_)
return;
346 all_clauses_are_attached_ =
true;
348 needs_cleaning_.ClearAll();
349 watchers_on_false_.resize(needs_cleaning_.size().value());
353 ++num_watched_clauses_;
354 DCHECK_GE(clause->
size(), 2);
362 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
363 if (drat_proof_handler_ !=
nullptr) {
364 drat_proof_handler_->AddClause({true_literal});
368 if (!trail_->Assignment().LiteralIsTrue(true_literal)) {
369 trail_->EnqueueWithUnitReason(true_literal);
373 return implication_graph_->Propagate(trail_);
381 DCHECK(!all_clauses_are_attached_);
382 if (drat_proof_handler_ !=
nullptr) {
383 drat_proof_handler_->DeleteClause(clause->
AsSpan());
385 clauses_info_.erase(clause);
390 SatClause* clause, absl::Span<const Literal> new_clause) {
391 if (new_clause.empty())
return false;
394 for (
const Literal l : new_clause) {
395 DCHECK(!trail_->Assignment().LiteralIsAssigned(l));
399 if (new_clause.size() == 1) {
405 if (new_clause.size() == 2) {
406 CHECK(implication_graph_->AddBinaryClause(new_clause[0], new_clause[1]));
411 if (drat_proof_handler_ !=
nullptr) {
413 drat_proof_handler_->AddClause(new_clause);
414 drat_proof_handler_->DeleteClause(clause->
AsSpan());
417 if (all_clauses_are_attached_) {
420 --num_watched_clauses_;
423 needs_cleaning_.Clear(l);
424 RemoveIf(&(watchers_on_false_[l]), [](
const Watcher& watcher) {
430 clause->Rewrite(new_clause);
433 if (all_clauses_are_attached_)
Attach(clause, trail_);
438 absl::Span<const Literal> new_clause) {
439 DCHECK(!new_clause.empty());
440 DCHECK(!all_clauses_are_attached_);
442 for (
const Literal l : new_clause) {
443 DCHECK(!trail_->Assignment().LiteralIsAssigned(l));
447 if (new_clause.size() == 1) {
453 if (new_clause.size() == 2) {
454 implication_graph_->AddBinaryClause(new_clause[0], new_clause[1]);
459 clauses_.push_back(clause);
465 for (
const LiteralIndex index : needs_cleaning_.PositionsSetAtLeastOnce()) {
466 DCHECK(needs_cleaning_[index]);
467 RemoveIf(&(watchers_on_false_[index]), [](
const Watcher& watcher) {
470 needs_cleaning_.Clear(index);
472 needs_cleaning_.NotifyAllClear();
484 const int old_size = clauses_.size();
485 for (
int i = 0;
i < old_size; ++
i) {
486 if (
i == to_minimize_index_) to_minimize_index_ = new_size;
487 if (
i == to_first_minimize_index_) to_first_minimize_index_ = new_size;
488 if (
i == to_probe_index_) to_probe_index_ = new_size;
489 if (clauses_[
i]->IsRemoved()) {
492 clauses_[new_size++] = clauses_[
i];
495 clauses_.resize(new_size);
497 if (to_minimize_index_ > new_size) to_minimize_index_ = new_size;
498 if (to_first_minimize_index_ > new_size) to_first_minimize_index_ = new_size;
499 if (to_probe_index_ > new_size) to_probe_index_ = new_size;
503 for (; to_first_minimize_index_ < clauses_.size();
504 ++to_first_minimize_index_) {
505 if (clauses_[to_first_minimize_index_]->IsRemoved())
continue;
506 if (!
IsRemovable(clauses_[to_first_minimize_index_])) {
510 if (to_minimize_index_ == to_first_minimize_index_) {
511 ++to_minimize_index_;
513 return clauses_[to_first_minimize_index_++];
520 for (; to_minimize_index_ < clauses_.size(); ++to_minimize_index_) {
521 if (clauses_[to_minimize_index_]->IsRemoved())
continue;
523 return clauses_[to_minimize_index_++];
530 for (; to_probe_index_ < clauses_.size(); ++to_probe_index_) {
531 if (clauses_[to_probe_index_]->IsRemoved())
continue;
532 return clauses_[to_probe_index_++];
541 bfs_stack_.resize(num_variables << 1);
542 implications_.resize(num_variables << 1);
543 implies_something_.resize(num_variables << 1);
544 might_have_dups_.resize(num_variables << 1);
545 is_redundant_.resize(implications_.size());
546 is_removed_.resize(implications_.size(),
false);
547 estimated_sizes_.resize(implications_.size(), 0);
548 in_direct_implications_.resize(implications_.size(),
false);
549 reasons_.resize(num_variables);
552void BinaryImplicationGraph::NotifyPossibleDuplicate(
Literal a) {
553 if (might_have_dups_[a.
Index()])
return;
554 might_have_dups_[a.
Index()] =
true;
555 to_clean_.push_back(a);
559 for (
const Literal l : to_clean_) {
560 might_have_dups_[l.Index()] =
false;
577 if (drat_proof_handler_ !=
nullptr) {
582 drat_proof_handler_->AddClause({a,
b});
586 if (is_redundant_[
b.Index()])
b =
Literal(representative_of_[
b.Index()]);
587 if (a ==
b.Negated())
return true;
588 if (a ==
b)
return FixLiteral(a);
590 DCHECK(!is_removed_[a]);
591 DCHECK(!is_removed_[
b]);
593 estimated_sizes_[
b.NegatedIndex()]++;
595 implications_[
b.NegatedIndex()].push_back(a);
597 implies_something_.Set(
b.NegatedIndex());
598 NotifyPossibleDuplicate(a);
599 NotifyPossibleDuplicate(
b);
601 num_implications_ += 2;
603 if (enable_sharing_ && add_binary_callback_ !=
nullptr) {
604 add_binary_callback_(a,
b);
607 const auto& assignment = trail_->Assignment();
608 if (trail_->CurrentDecisionLevel() == 0) {
609 DCHECK(!assignment.LiteralIsAssigned(a));
610 DCHECK(!assignment.LiteralIsAssigned(
b));
612 if (assignment.LiteralIsFalse(a)) {
613 if (assignment.LiteralIsAssigned(
b)) {
614 if (assignment.LiteralIsFalse(
b))
return false;
616 reasons_[trail_->Index()] = a;
619 }
else if (assignment.LiteralIsFalse(
b)) {
620 if (!assignment.LiteralIsAssigned(a)) {
621 reasons_[trail_->Index()] =
b;
631 absl::Span<const Literal> at_most_one) {
632 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
633 if (at_most_one.size() <= 1)
return true;
638 const int base_index = at_most_one_buffer_.size();
639 at_most_one_buffer_.push_back(
Literal(LiteralIndex(at_most_one.size())));
640 at_most_one_buffer_.insert(at_most_one_buffer_.end(), at_most_one.begin(),
644 return CleanUpAndAddAtMostOnes(base_index);
651bool BinaryImplicationGraph::FixLiteral(
Literal true_literal) {
656 if (drat_proof_handler_ !=
nullptr) {
657 drat_proof_handler_->
AddClause({true_literal});
667bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(
int base_index) {
668 const VariablesAssignment& assignment = trail_->Assignment();
669 int local_end = base_index;
670 const int buffer_size = at_most_one_buffer_.size();
671 for (
int i = base_index;
i < buffer_size;) {
675 const int local_start = local_end;
680 if (
i == at_most_one_iterator_) {
681 at_most_one_iterator_ = local_start;
685 const absl::Span<const Literal> initial_amo = AtMostOne(
i);
686 i += initial_amo.size() + 1;
690 bool set_all_left_to_false =
false;
691 for (
const Literal l : initial_amo) {
692 if (assignment.LiteralIsFalse(l))
continue;
693 if (is_removed_[l])
continue;
694 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
695 set_all_left_to_false =
true;
700 at_most_one_buffer_[local_start] =
701 Literal(LiteralIndex(local_end - local_start - 1));
705 bool some_duplicates =
false;
706 if (!set_all_left_to_false) {
709 Literal* pt = &at_most_one_buffer_[local_start + 1];
710 std::sort(pt, pt + AtMostOne(local_start).size());
713 bool remove_previous =
false;
714 int new_local_end = local_start + 1;
715 for (
const Literal l : AtMostOne(local_start)) {
716 if (l.Index() == previous) {
717 if (assignment.LiteralIsTrue(l))
return false;
718 if (!assignment.LiteralIsFalse(l)) {
719 if (!FixLiteral(l.Negated()))
return false;
721 remove_previous =
true;
722 some_duplicates =
true;
728 if (remove_previous) {
730 remove_previous =
false;
732 previous = l.Index();
733 at_most_one_buffer_[new_local_end++] = l;
735 if (remove_previous) --new_local_end;
738 local_end = new_local_end;
739 at_most_one_buffer_[local_start] =
740 Literal(LiteralIndex(local_end - local_start - 1));
745 if (some_duplicates) {
746 int new_local_end = local_start + 1;
747 for (
const Literal l : AtMostOne(local_start)) {
748 if (assignment.LiteralIsFalse(l))
continue;
749 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
750 set_all_left_to_false =
true;
753 at_most_one_buffer_[new_local_end++] = l;
757 local_end = new_local_end;
758 at_most_one_buffer_[local_start] =
759 Literal(LiteralIndex(local_end - local_start - 1));
763 if (set_all_left_to_false) {
764 for (
const Literal l : AtMostOne(local_start)) {
765 if (assignment.LiteralIsFalse(l))
continue;
766 if (assignment.LiteralIsTrue(l))
return false;
767 if (!FixLiteral(l.Negated()))
return false;
771 local_end = local_start;
776 const absl::Span<const Literal> at_most_one = AtMostOne(local_start);
780 if (at_most_one.size() <= std::max(2, at_most_one_max_expansion_size_)) {
781 for (
const Literal a : at_most_one) {
782 for (
const Literal
b : at_most_one) {
783 if (a ==
b)
continue;
784 implications_[a].push_back(
b.Negated());
785 implies_something_.Set(a);
786 NotifyPossibleDuplicate(a);
789 num_implications_ += at_most_one.size() * (at_most_one.size() - 1);
792 local_end = local_start;
797 for (
const Literal l : at_most_one) {
798 if (l.Index() >= at_most_ones_.size()) {
799 at_most_ones_.resize(l.Index().value() + 1);
801 DCHECK(!is_redundant_[l]);
802 at_most_ones_[l].push_back(local_start);
803 implies_something_.Set(l);
807 at_most_one_buffer_.resize(local_end);
821 const auto implies_something = implies_something_.view();
822 auto* implications = implications_.data();
826 DCHECK(assignment.LiteralIsTrue(true_literal));
827 if (!implies_something[true_literal])
continue;
832 const absl::Span<const Literal> implied =
833 implications[true_literal.
Index().value()];
834 num_inspections_ += implied.size();
835 for (
const Literal literal : implied) {
836 if (assignment.LiteralIsTrue(literal)) {
846 if (assignment.LiteralIsFalse(literal)) {
858 if (true_literal.
Index() < at_most_ones_.size()) {
859 for (
const int start : at_most_ones_[true_literal]) {
861 for (
const Literal literal : AtMostOne(start)) {
863 if (literal == true_literal) {
870 if (assignment.LiteralIsFalse(literal))
continue;
873 if (assignment.LiteralIsTrue(literal)) {
880 reasons_[trail->
Index()] = true_literal.Negated();
892 const Trail& ,
int trail_index, int64_t )
const {
893 return {&reasons_[trail_index], 1};
904 std::vector<Literal>* conflict) {
910 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
911 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
912 is_marked_.Set(root_literal_index);
921 const bool also_prune_direct_implication_list =
false;
925 auto& direct_implications = implications_[root_literal_index];
926 for (
const Literal l : direct_implications) {
927 if (is_marked_[l])
continue;
928 dfs_stack_.push_back(l);
929 while (!dfs_stack_.empty()) {
930 const LiteralIndex index = dfs_stack_.back().Index();
931 dfs_stack_.pop_back();
932 if (!is_marked_[index]) {
933 is_marked_.Set(index);
934 for (
const Literal implied : implications_[index]) {
935 if (!is_marked_[implied]) dfs_stack_.push_back(implied);
951 if (also_prune_direct_implication_list) {
958 if (also_prune_direct_implication_list) {
960 for (
const Literal l : direct_implications) {
961 if (!is_marked_[l]) {
963 direct_implications[new_size] = l;
967 if (new_size < direct_implications.size()) {
968 num_redundant_implications_ += direct_implications.size() - new_size;
969 direct_implications.resize(new_size);
973 RemoveRedundantLiterals(conflict);
981 const Trail& trail, std::vector<Literal>* conflict,
984 DCHECK(!conflict->empty());
985 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
986 MarkDescendants(conflict->front().Negated());
987 for (
const LiteralIndex
i : is_marked_.PositionsSetAtLeastOnce()) {
996 RemoveRedundantLiterals(conflict);
1003 const Trail& , std::vector<Literal>* conflict,
1004 absl::BitGenRef random) {
1006 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
1007 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
1008 is_marked_.Set(root_literal_index);
1011 auto& direct_implications = implications_[root_literal_index];
1017 std::shuffle(direct_implications.begin(), direct_implications.end(), random);
1019 for (
const Literal l : direct_implications) {
1020 if (is_marked_[l]) {
1026 direct_implications[new_size++] = l;
1027 dfs_stack_.push_back(l);
1028 while (!dfs_stack_.empty()) {
1029 const LiteralIndex index = dfs_stack_.back().Index();
1030 dfs_stack_.pop_back();
1031 if (!is_marked_[index]) {
1032 is_marked_.Set(index);
1033 for (
const Literal implied : implications_[index]) {
1034 if (!is_marked_[implied]) dfs_stack_.push_back(implied);
1039 if (new_size < direct_implications.size()) {
1040 num_redundant_implications_ += direct_implications.size() - new_size;
1041 direct_implications.resize(new_size);
1043 RemoveRedundantLiterals(conflict);
1046void BinaryImplicationGraph::RemoveRedundantLiterals(
1047 std::vector<Literal>* conflict) {
1050 for (
int i = 1;
i < conflict->size(); ++
i) {
1051 if (!is_marked_[(*conflict)[
i].NegatedIndex()]) {
1052 (*conflict)[new_index] = (*conflict)[
i];
1056 if (new_index < conflict->size()) {
1057 ++num_minimization_;
1058 num_literals_removed_ += conflict->size() - new_index;
1059 conflict->resize(new_index);
1065 const Trail& trail, std::vector<Literal>* conflict) {
1067 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
1068 is_simplified_.ClearAndResize(LiteralIndex(implications_.size()));
1069 for (
const Literal lit : *conflict) {
1070 is_marked_.Set(lit);
1086 for (
int i = 1;
i < conflict->size(); ++
i) {
1087 const Literal lit = (*conflict)[
i];
1089 bool keep_literal =
true;
1090 for (
const Literal implied : implications_[lit]) {
1091 if (is_marked_[implied]) {
1092 DCHECK_LE(lit_level, trail.
Info(implied.Variable()).
level);
1093 if (lit_level == trail.
Info(implied.Variable()).
level &&
1094 is_simplified_[implied]) {
1097 keep_literal =
false;
1102 (*conflict)[index] = lit;
1105 is_simplified_.Set(lit);
1108 if (index < conflict->size()) {
1109 ++num_minimization_;
1110 num_literals_removed_ += conflict->size() - index;
1111 conflict->erase(conflict->begin() + index, conflict->end());
1117 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1121 const int new_num_fixed = trail_->Index();
1123 if (num_processed_fixed_variables_ == new_num_fixed)
return;
1126 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
1127 for (; num_processed_fixed_variables_ < new_num_fixed;
1128 ++num_processed_fixed_variables_) {
1129 const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
1133 for (
const Literal lit : implications_[true_literal]) {
1134 DCHECK(trail_->Assignment().LiteralIsTrue(lit));
1147 if (lit.NegatedIndex() < representative_of_.size() &&
1150 is_marked_.Set(representative_of_[lit.Negated()]);
1152 is_marked_.Set(lit.NegatedIndex());
1158 if (true_literal.
Index() < at_most_ones_.size()) {
1161 if (true_literal.
NegatedIndex() < at_most_ones_.size()) {
1165 for (
const LiteralIndex
i : is_marked_.PositionsSetAtLeastOnce()) {
1166 RemoveIf(&implications_[
i], [&assignment](
const Literal& lit) {
1173 at_most_ones_.clear();
1174 CleanUpAndAddAtMostOnes(0);
1175 DCHECK(InvariantsAreOk());
1190 std::vector<Literal>* at_most_one_buffer)
1192 implications_(*graph),
1193 at_most_ones_(*at_most_ones),
1194 at_most_one_buffer_(*at_most_one_buffer) {}
1198 for (
const Literal l : implications_[LiteralIndex(node)]) {
1199 tmp_.push_back(l.Index().value());
1200 if (finder_.NodeIsInCurrentDfsPath(l.NegatedIndex().value())) {
1204 if (node < at_most_ones_.size()) {
1205 for (
const int start : at_most_ones_[LiteralIndex(node)]) {
1206 if (start >= at_most_one_already_explored_.size()) {
1207 at_most_one_already_explored_.resize(start + 1,
false);
1208 previous_node_to_explore_at_most_one_.resize(start + 1);
1218 if (at_most_one_already_explored_[start]) {
1220 const int first_node = previous_node_to_explore_at_most_one_[start];
1221 DCHECK_NE(node, first_node);
1223 if (finder_.NodeIsInCurrentDfsPath(first_node)) {
1239 previous_node_to_explore_at_most_one_[start] = node;
1244 Literal(LiteralIndex(first_node)).NegatedIndex().value());
1248 at_most_one_already_explored_[start] =
true;
1249 previous_node_to_explore_at_most_one_[start] = node;
1252 const absl::Span<const Literal> amo =
1253 absl::MakeSpan(&at_most_one_buffer_[start + 1],
1254 at_most_one_buffer_[start].Index().value());
1256 if (l.Index() == node)
continue;
1257 tmp_.push_back(l.NegatedIndex().value());
1258 if (finder_.NodeIsInCurrentDfsPath(l.Index().value())) {
1259 to_fix_.push_back(l.Negated());
1278 const std::vector<Literal>& at_most_one_buffer_;
1280 mutable std::vector<int32_t> tmp_;
1283 mutable std::vector<bool> at_most_one_already_explored_;
1284 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1290 if (is_dag_)
return true;
1293 log_info |= VLOG_IS_ON(1);
1299 DCHECK(InvariantsAreOk());
1302 const int32_t size(implications_.size());
1308 SccGraph graph(&finder, &implications_, &at_most_ones_,
1309 &at_most_one_buffer_);
1316 if (!FixLiteral(l))
return false;
1322 is_redundant_.resize(size);
1324 int num_equivalences = 0;
1325 reverse_topological_order_.clear();
1326 for (
int index = 0; index < scc.
size(); ++index) {
1327 const absl::Span<int32_t> component = scc[index];
1336 bool all_fixed =
false;
1337 bool all_true =
false;
1338 for (
const int32_t
i : component) {
1340 if (trail_->Assignment().LiteralIsAssigned(l)) {
1342 all_true = trail_->Assignment().LiteralIsTrue(l);
1347 for (
const int32_t
i : component) {
1349 if (!is_redundant_[l]) {
1350 ++num_redundant_literals_;
1351 is_redundant_.Set(l);
1352 representative_of_[l] = l.
Index();
1357 if (!FixLiteral(l))
return false;
1366 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1375 std::sort(component.begin(), component.end());
1376 const LiteralIndex representative(component[0]);
1377 reverse_topological_order_.push_back(representative);
1379 if (component.size() == 1) {
1382 if (num_equivalences > 0) {
1383 auto& representative_list = implications_[representative];
1384 for (
Literal& ref : representative_list) {
1385 const LiteralIndex rep = representative_of_[ref];
1386 if (rep == representative)
continue;
1396 for (
int i = 1;
i < component.size(); ++
i) {
1398 if (!is_redundant_[literal]) {
1399 ++num_redundant_literals_;
1400 is_redundant_.Set(literal);
1402 representative_of_[literal] = representative;
1406 if (
Literal(LiteralIndex(component[
i - 1])).Negated() == literal) {
1407 LOG_IF(INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
1414 auto& representative_list = implications_[representative];
1416 for (
const Literal l : representative_list) {
1418 if (rep.
Index() == representative)
continue;
1419 representative_list[new_size++] = rep;
1421 representative_list.resize(new_size);
1422 for (
int i = 1;
i < component.size(); ++
i) {
1424 auto& ref = implications_[literal];
1427 if (rep.
Index() != representative) representative_list.push_back(rep);
1435 representative_list.push_back(literal);
1437 ref.push_back(
Literal(representative));
1440 num_equivalences += component.size() - 1;
1444 if (num_equivalences != 0) {
1448 at_most_ones_.clear();
1450 CleanUpAndAddAtMostOnes(0);
1461 num_implications_ = 0;
1462 for (LiteralIndex
i(0);
i < size; ++
i) {
1463 num_implications_ += implications_[
i].size();
1465 dtime += 2e-8 * num_implications_;
1468 time_limit_->AdvanceDeterministicTime(dtime);
1469 const int num_fixed_during_scc =
1470 trail_->Index() - num_processed_fixed_variables_;
1472 DCHECK(InvariantsAreOk());
1473 LOG_IF(INFO, log_info) <<
"SCC. " << num_equivalences
1474 <<
" redundant equivalent literals. "
1475 << num_fixed_during_scc <<
" fixed. "
1476 << num_implications_ <<
" implications left. "
1477 << implications_.size() <<
" literals."
1478 <<
" size of at_most_one buffer = "
1479 << at_most_one_buffer_.size() <<
"."
1480 <<
" dtime: " << dtime
1481 <<
" wtime: " << wall_timer.
Get();
1491 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1492 if (time_limit_->LimitReached())
return true;
1498 if (time_limit_->LimitReached())
return true;
1501 DCHECK(InvariantsAreOk());
1502 if (time_limit_->LimitReached())
return true;
1504 log_info |= VLOG_IS_ON(1);
1508 int64_t num_fixed = 0;
1509 int64_t num_new_redundant_implications = 0;
1510 bool aborted =
false;
1511 tmp_removed_.clear();
1512 work_done_in_mark_descendants_ = 0;
1513 int marked_index = 0;
1529 const LiteralIndex size(implications_.size());
1531 for (
const LiteralIndex root : reverse_topological_order_) {
1536 if (is_redundant_[root])
continue;
1537 if (trail_->Assignment().LiteralIsAssigned(
Literal(root)))
continue;
1539 auto& direct_implications = implications_[root];
1540 if (direct_implications.empty())
continue;
1549 bool clear_previous_reachability =
true;
1550 for (
const Literal direct_child : direct_implications) {
1551 if (direct_child.Index() == previous) {
1552 clear_previous_reachability =
false;
1553 is_marked_.Clear(previous);
1557 if (clear_previous_reachability) {
1558 is_marked_.ClearAndResize(size);
1563 for (
const Literal direct_child : direct_implications) {
1564 if (is_redundant_[direct_child])
continue;
1565 if (is_marked_[direct_child])
continue;
1569 if (direct_child.Index() == root)
continue;
1573 if (direct_child.NegatedIndex() == root) {
1574 is_marked_.Set(direct_child);
1578 MarkDescendants(direct_child);
1581 is_marked_.Clear(direct_child);
1583 DCHECK(!is_marked_[root])
1584 <<
"DetectEquivalences() should have removed cycles!";
1585 is_marked_.Set(root);
1588 if (root < at_most_ones_.size()) {
1589 auto is_marked = is_marked_.BitsetView();
1590 for (
const int start : at_most_ones_[root]) {
1591 for (
const Literal l : AtMostOne(start)) {
1592 if (l.Index() == root)
continue;
1593 if (!is_marked[l.Negated()] && !is_redundant_[l.Negated()]) {
1594 is_marked_.SetUnsafe(is_marked, l.Negated());
1595 MarkDescendants(l.Negated());
1604 const auto& marked_positions = is_marked_.PositionsSetAtLeastOnce();
1605 for (; marked_index < marked_positions.size(); ++marked_index) {
1606 const LiteralIndex
i = marked_positions[marked_index];
1607 if (is_marked_[
Literal(
i).NegatedIndex()]) {
1609 DCHECK(!trail_->Assignment().LiteralIsAssigned(
Literal(root)));
1615 if (!FixLiteral(
Literal(root).Negated()))
return false;
1623 if (trail_->Assignment().LiteralIsAssigned(
Literal(root)))
continue;
1629 for (
const Literal l : direct_implications) {
1630 if (!is_marked_[l]) {
1631 direct_implications[new_size++] = l;
1633 tmp_removed_.push_back({
Literal(root), l});
1634 DCHECK(!is_redundant_[l]);
1637 const int diff = direct_implications.size() - new_size;
1638 direct_implications.resize(new_size);
1639 direct_implications.shrink_to_fit();
1640 num_new_redundant_implications += diff;
1641 num_implications_ -= diff;
1644 if (work_done_in_mark_descendants_ > 1e8) {
1650 is_marked_.ClearAndResize(size);
1656 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> removed;
1657 for (
const auto [a,
b] : tmp_removed_) {
1658 removed.insert({a.
Index(),
b.Index()});
1660 for (LiteralIndex
i(0);
i < implications_.size(); ++
i) {
1663 auto& implication = implications_[
i];
1664 for (
const Literal l : implication) {
1665 if (removed.contains({l.NegatedIndex(), negated_i}))
continue;
1666 implication[new_size++] = l;
1668 implication.resize(new_size);
1671 if (num_fixed > 0) {
1674 DCHECK(InvariantsAreOk());
1677 const double dtime = 1e-8 * work_done_in_mark_descendants_;
1678 time_limit_->AdvanceDeterministicTime(dtime);
1679 num_redundant_implications_ += num_new_redundant_implications;
1680 LOG_IF(INFO, log_info) <<
"Transitive reduction removed "
1681 << num_new_redundant_implications <<
" literals. "
1682 << num_fixed <<
" fixed. " << num_implications_
1683 <<
" implications left. " << implications_.size()
1684 <<
" literals." <<
" dtime: " << dtime
1685 <<
" wtime: " << wall_timer.
Get()
1686 << (aborted ?
" Aborted." :
"");
1692int ElementInIntersectionOrMinusOne(absl::Span<const int> a,
1693 absl::Span<const int>
b) {
1694 DCHECK(std::is_sorted(a.begin(), a.end()));
1695 DCHECK(std::is_sorted(
b.begin(),
b.end()));
1696 if (a.empty() ||
b.empty())
return -1;
1700 if (a[
i] ==
b[j])
return a[
i];
1702 if (++
i == a.size())
return -1;
1704 if (++j ==
b.size())
return -1;
1711std::vector<std::pair<int, int>>
1712BinaryImplicationGraph::FilterAndSortAtMostOnes(
1713 absl::Span<std::vector<Literal>> at_most_ones) {
1716 std::vector<std::pair<int, int>> index_size_vector;
1717 const int num_amos = at_most_ones.size();
1718 index_size_vector.reserve(num_amos);
1719 for (
int index = 0; index < num_amos; ++index) {
1720 std::vector<Literal>& clique = at_most_ones[index];
1721 if (clique.size() <= 1)
continue;
1729 for (Literal& ref : clique) {
1730 DCHECK_LT(ref.Index(), representative_of_.size());
1731 const LiteralIndex rep = representative_of_[ref];
1741 std::sort(clique.begin(), clique.end());
1742 for (
int i = 1;
i < clique.size(); ++
i) {
1743 if (clique[
i] == clique[
i - 1] || clique[
i] == clique[
i -
i].Negated()) {
1750 index_size_vector.push_back({index, clique.size()});
1753 index_size_vector.begin(), index_size_vector.end(),
1754 [](
const std::pair<int, int> a,
const std::pair<int, int>&
b) {
1755 return a.second > b.second;
1757 return index_size_vector;
1761 std::vector<std::vector<Literal>>* at_most_ones,
1762 int64_t max_num_explored_nodes) {
1765 work_done_in_mark_descendants_ = 0;
1767 int num_extended = 0;
1768 int num_removed = 0;
1772 std::vector<int> detector_clique_index;
1777 std::vector<int> dense_index_to_index;
1779 max_cliques_containing(implications_.size());
1781 const std::vector<std::pair<int, int>> index_size_vector =
1782 FilterAndSortAtMostOnes(absl::MakeSpan(*at_most_ones));
1784 absl::flat_hash_set<int> cannot_be_removed;
1785 std::vector<bool> was_extended(at_most_ones->size(),
false);
1786 for (
const auto& [index, old_size] : index_size_vector) {
1787 std::vector<Literal>& clique = (*at_most_ones)[index];
1788 if (time_limit_->LimitReached())
break;
1792 if (clique.size() == 2) {
1793 DCHECK_NE(clique[0], clique[1]);
1794 const int dense_index = ElementInIntersectionOrMinusOne(
1795 max_cliques_containing[clique[0]], max_cliques_containing[clique[1]]);
1796 if (dense_index >= 0) {
1797 const int superset_index = dense_index_to_index[dense_index];
1798 if (was_extended[superset_index]) {
1799 cannot_be_removed.insert(superset_index);
1809 detector_clique_index.push_back(index);
1813 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1814 clique = ExpandAtMostOne(clique, max_num_explored_nodes);
1818 detector_clique_index.push_back(index);
1822 const int dense_index = dense_index_to_index.size();
1823 dense_index_to_index.push_back(index);
1824 for (
const Literal l : clique) {
1825 max_cliques_containing[l].
push_back(dense_index);
1828 if (clique.size() > old_size) {
1829 was_extended[index] =
true;
1837 const int subset_index = detector_clique_index[subset];
1838 const int superset_index = detector_clique_index[superset];
1839 if (subset_index == superset_index)
return;
1842 if ((*at_most_ones)[subset_index].empty())
return;
1843 if ((*at_most_ones)[superset_index].empty())
return;
1847 if (cannot_be_removed.contains(subset_index))
return;
1850 (*at_most_ones)[subset_index].clear();
1851 if (was_extended[superset_index]) cannot_be_removed.insert(superset_index);
1854 if (num_extended > 0 || num_removed > 0 || num_added > 0) {
1855 VLOG(1) <<
"Clique Extended: " << num_extended
1856 <<
" Removed: " << num_removed <<
" Added: " << num_added
1857 << (work_done_in_mark_descendants_ > max_num_explored_nodes
1865 absl::Span<std::vector<Literal>> at_most_ones,
1866 int64_t max_num_explored_nodes,
double* dtime) {
1869 work_done_in_mark_descendants_ = 0;
1871 const std::vector<std::pair<int, int>> index_size_vector =
1872 FilterAndSortAtMostOnes(at_most_ones);
1875 std::vector<int> detector_clique_index;
1877 for (
const auto& [index, old_size] : index_size_vector) {
1878 if (time_limit_->LimitReached())
break;
1879 detector_clique_index.push_back(index);
1886 detector.IndexAllStorageAsSubsets();
1893 std::vector<int> intersection;
1894 const int num_to_consider = index_size_vector.size();
1895 for (
int subset_index = 0; subset_index < num_to_consider; ++subset_index) {
1896 const int index = index_size_vector[subset_index].first;
1897 std::vector<Literal>& clique = at_most_ones[index];
1898 if (clique.empty())
continue;
1900 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
1901 if (detector.
Stopped())
break;
1906 int next_index_to_try = 0;
1907 intersection.clear();
1908 tmp_bitset_.ClearAndResize(LiteralIndex(implications_.size()));
1909 for (
const Literal l : clique) {
1910 intersection.push_back(l.Index().value());
1915 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
1916 if (detector.
Stopped())
break;
1923 if (clique_i > 0 && intersection.size() == clique.size()) {
1924 clique_i = clique.size();
1926 for (; clique_i < clique.size(); ++clique_i) {
1927 const Literal l = clique[clique_i];
1929 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
1932 if (clique_i == 0) {
1935 for (
const LiteralIndex index :
1936 is_marked_.PositionsSetAtLeastOnce()) {
1938 if (!tmp_bitset_[lit]) {
1939 intersection.push_back(lit.
Index().value());
1947 const int old_size = intersection.size();
1948 for (
int i = 0;
i < old_size; ++
i) {
1949 if (
i == next_index_to_try) {
1950 next_index_to_try = new_size;
1952 const int index = intersection[
i];
1954 if (tmp_bitset_[lit] || is_marked_[lit.
Negated()]) {
1955 intersection[new_size++] = index;
1958 intersection.resize(new_size);
1963 if (intersection.size() <= clique.size())
break;
1969 CHECK_GE(intersection.size(), clique.size());
1978 detector.
FindSubsets(intersection, &next_index_to_try, [&](
int subset) {
1979 if (subset == subset_index) {
1985 for (
const int index : storage[subset]) {
1986 const LiteralIndex lit_index = LiteralIndex(index);
1987 if (tmp_bitset_[lit_index])
continue;
1988 tmp_bitset_.Set(lit_index);
1989 clique.push_back(
Literal(lit_index));
1992 if (num_extra == 0) {
1994 at_most_ones[detector_clique_index[subset]].clear();
2003 if (num_extra == 0)
break;
2006 if (dtime !=
nullptr) {
2008 1e-8 * work_done_in_mark_descendants_ + 1e-9 * detector.
work_done();
2013template <
bool use_weight>
2015 const absl::Span<const Literal> at_most_one,
2018 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
2019 std::vector<LiteralIndex> intersection;
2020 const int64_t old_work = work_done_in_mark_descendants_;
2021 for (
int i = 0;
i < clique.size(); ++
i) {
2023 if (work_done_in_mark_descendants_ - old_work > 1e8)
break;
2025 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
2026 MarkDescendants(clique[
i]);
2028 for (
const LiteralIndex index : is_marked_.PositionsSetAtLeastOnce()) {
2029 if (can_be_included[
Literal(index).NegatedIndex()]) {
2030 intersection.push_back(index);
2033 for (
const Literal l : clique) is_marked_.Clear(l.NegatedIndex());
2037 is_marked_.Clear(clique[
i]);
2038 is_marked_.Clear(clique[
i].NegatedIndex());
2039 for (
const LiteralIndex index : intersection) {
2040 if (!is_marked_[index])
continue;
2041 intersection[new_size++] = index;
2043 intersection.resize(new_size);
2044 if (intersection.empty())
break;
2048 if (
i + 1 == clique.size()) {
2051 double max_lp = 0.0;
2052 for (
int j = 0; j < intersection.size(); ++j) {
2057 absl::Uniform<double>(*random_, 0.0, 1e-4)
2058 : can_be_included.size() - intersection[j].value();
2059 if (index == -1 || lp > max_lp) {
2065 clique.push_back(
Literal(intersection[index]).Negated());
2066 std::swap(intersection.back(), intersection[index]);
2067 intersection.pop_back();
2075template std::vector<Literal>
2077 const absl::Span<const Literal> at_most_one,
2080template std::vector<Literal>
2082 const absl::Span<const Literal> at_most_one,
2094const std::vector<std::vector<Literal>>&
2096 absl::Span<const Literal> literals, absl::Span<const double> lp_values,
2097 absl::Span<const double> reduced_costs) {
2099 const int num_literals = implications_.size();
2106 const int size = literals.size();
2107 for (
int i = 0;
i < size; ++
i) {
2109 can_be_included[l] =
true;
2112 const double value = lp_values[
i];
2113 expanded_lp_values[l] = value;
2129 const double rc = reduced_costs[
i];
2130 heuristic_weights[l] = -rc;
2139 bool operator<(
const Candidate& other)
const {
return sum > other.sum; }
2141 std::vector<Candidate> candidates;
2149 std::vector<Literal> fractional_literals;
2150 for (
int i = 0;
i < size; ++
i) {
2151 Literal current_literal = literals[
i];
2152 double current_value = lp_values[
i];
2153 if (trail_->Assignment().LiteralIsAssigned(current_literal))
continue;
2154 if (is_redundant_[current_literal])
continue;
2156 if (current_value < 0.5) {
2157 current_literal = current_literal.
Negated();
2158 current_value = 1.0 - current_value;
2161 if (current_value < 0.99) {
2162 fractional_literals.push_back(current_literal);
2167 double best_value = 0.0;
2168 for (
const Literal l : implications_[current_literal]) {
2169 if (!can_be_included[l])
continue;
2170 const double activity =
2171 current_value + expanded_lp_values[l.NegatedIndex()];
2172 if (activity <= 1.01)
continue;
2173 const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
2176 best = l.NegatedIndex();
2180 const double activity = current_value + expanded_lp_values[best];
2181 candidates.push_back({current_literal,
Literal(best), activity});
2186 const int kMaxNumberOfCutPerCall = 10;
2187 std::sort(candidates.begin(), candidates.end());
2188 if (candidates.size() > kMaxNumberOfCutPerCall) {
2189 candidates.resize(kMaxNumberOfCutPerCall);
2195 for (
const Candidate& candidate : candidates) {
2197 {candidate.a, candidate.b}, can_be_included, heuristic_weights));
2202 if (fractional_literals.size() > 1) {
2207 const int max_graph_size = 1024;
2208 if (fractional_literals.size() > max_graph_size) {
2209 std::shuffle(fractional_literals.begin(), fractional_literals.end(),
2211 fractional_literals.resize(max_graph_size);
2214 bron_kerbosch_.Initialize(fractional_literals.size() * 2);
2218 tmp_mapping_.resize(implications_.size(), -1);
2219 for (
const Literal l : fractional_literals) {
2220 bron_kerbosch_.SetWeight(
i, expanded_lp_values[l]);
2221 tmp_mapping_[l] =
i++;
2222 bron_kerbosch_.SetWeight(
i, expanded_lp_values[l.Negated()]);
2223 tmp_mapping_[l.Negated()] =
i++;
2232 const int from = tmp_mapping_[l];
2235 const int to = tmp_mapping_[next.Negated()];
2237 bron_kerbosch_.AddEdge(from,
to);
2245 bron_kerbosch_.TakeTransitiveClosureOfImplicationGraph();
2247 bron_kerbosch_.SetWorkLimit(1e8);
2248 bron_kerbosch_.SetMinimumWeight(1.001);
2249 std::vector<std::vector<int>> cliques = bron_kerbosch_.Run();
2253 const int max_num_per_batch = 5;
2254 std::vector<std::pair<int, double>> with_weight =
2255 bron_kerbosch_.GetMutableIndexAndWeight();
2256 if (with_weight.size() > max_num_per_batch) {
2258 with_weight.begin(), with_weight.end(),
2259 [](
const std::pair<int, double>& a,
const std::pair<int, double>&
b) {
2260 return a.second > b.second;
2262 with_weight.resize(max_num_per_batch);
2265 std::vector<Literal> at_most_one;
2266 for (
const auto [index, weight] : with_weight) {
2268 at_most_one.clear();
2269 for (
const int i : cliques[index]) {
2270 const Literal l = fractional_literals[
i / 2];
2271 at_most_one.push_back(
i % 2 == 1 ? l.
Negated() : l);
2280 at_most_one, can_be_included, heuristic_weights));
2284 for (
const Literal l : fractional_literals) {
2285 tmp_mapping_[l] = -1;
2286 tmp_mapping_[l.Negated()] = -1;
2296std::vector<absl::Span<const Literal>>
2298 std::vector<absl::Span<const Literal>> result;
2301 implications_.size(),
false);
2302 for (
const Literal l : *literals) to_consider[l] =
true;
2305 std::priority_queue<std::pair<int, int>> pq;
2311 absl::flat_hash_set<int> explored_amo;
2312 for (
const Literal l : *literals) {
2313 if (l.Index() >= at_most_ones_.size())
continue;
2314 for (
const int start : at_most_ones_[l]) {
2315 const auto [_, inserted] = explored_amo.insert(start);
2316 if (!inserted)
continue;
2318 int intersection_size = 0;
2319 for (
const Literal l : AtMostOne(start)) {
2320 if (to_consider[l]) ++intersection_size;
2322 if (intersection_size > 1) {
2323 pq.push({intersection_size, start});
2327 if (intersection_size == literals->size())
break;
2332 int num_processed = 0;
2333 while (!pq.empty()) {
2334 const auto [old_size, start] = pq.top();
2338 int intersection_size = 0;
2339 for (
const Literal l : AtMostOne(start)) {
2340 if (to_consider[l]) ++intersection_size;
2342 if (intersection_size != old_size) {
2344 if (intersection_size > 1) {
2345 pq.push({intersection_size, start});
2351 for (
const Literal l : AtMostOne(start)) {
2352 to_consider[l] =
false;
2357 const int span_start = num_processed;
2358 for (
int i = num_processed;
i < literals->size(); ++
i) {
2359 if (to_consider[(*literals)[
i]])
continue;
2360 std::swap((*literals)[num_processed], (*literals)[
i]);
2363 result.push_back(absl::MakeSpan(literals->data() + span_start,
2364 num_processed - span_start));
2369void BinaryImplicationGraph::MarkDescendants(
Literal root) {
2370 auto*
const stack = bfs_stack_.data();
2372 auto is_redundant = is_redundant_.
const_view();
2373 if (is_redundant[root])
return;
2377 is_marked_.
Set(root);
2378 const int amo_size =
static_cast<int>(at_most_ones_.size());
2379 auto implies_something = implies_something_.
const_view();
2380 for (
int j = 0; j < stack_size; ++j) {
2381 const Literal current = stack[j];
2382 if (!implies_something[current])
continue;
2384 work_done_in_mark_descendants_ += implications_[current].size();
2385 for (
const Literal l : implications_[current]) {
2386 if (!is_marked[l] && !is_redundant[l]) {
2388 stack[stack_size++] = l;
2392 if (current.Index() >= amo_size)
continue;
2393 for (
const int start : at_most_ones_[current]) {
2394 work_done_in_mark_descendants_ += AtMostOne(start).size();
2395 for (
const Literal l : AtMostOne(start)) {
2396 if (l == current)
continue;
2397 if (!is_marked[l.NegatedIndex()] && !is_redundant[l.NegatedIndex()]) {
2398 is_marked_.
SetUnsafe(is_marked, l.NegatedIndex());
2399 stack[stack_size++] = l.Negated();
2404 work_done_in_mark_descendants_ += stack_size;
2407std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
2408 const absl::Span<const Literal> at_most_one,
2409 int64_t max_num_explored_nodes) {
2410 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
2413 for (
const Literal l : clique) {
2414 if (!implies_something_[l]) {
2422 std::vector<LiteralIndex> intersection;
2423 for (
int i = 0;
i < clique.size(); ++
i) {
2424 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
2425 is_marked_.ClearAndResize(LiteralIndex(implications_.size()));
2426 MarkDescendants(clique[
i]);
2429 intersection = is_marked_.PositionsSetAtLeastOnce();
2430 for (
const Literal l : clique) is_marked_.Clear(l.NegatedIndex());
2434 is_marked_.Clear(clique[
i].NegatedIndex());
2435 for (
const LiteralIndex index : intersection) {
2436 if (is_marked_[index]) intersection[new_size++] = index;
2438 intersection.resize(new_size);
2439 if (intersection.empty())
break;
2446 if (
i + 1 == clique.size()) {
2447 clique.push_back(Literal(intersection.back()).Negated());
2448 intersection.pop_back();
2458 DCHECK(!is_removed_[literal]);
2461 for (
const Literal l : direct_implications_) {
2462 in_direct_implications_[l] =
false;
2464 direct_implications_.clear();
2469 for (
const Literal l : implications_[literal]) {
2470 if (l == literal)
continue;
2472 if (!is_removed_[l] && !in_direct_implications_[l]) {
2473 in_direct_implications_[l] =
true;
2474 direct_implications_.push_back(l);
2477 if (literal.
Index() < at_most_ones_.size()) {
2478 if (is_redundant_[literal]) {
2479 DCHECK(at_most_ones_[literal].empty());
2481 for (
const int start : at_most_ones_[literal]) {
2482 for (
const Literal l : AtMostOne(start)) {
2483 if (l == literal)
continue;
2485 if (!is_removed_[l] && !in_direct_implications_[l.NegatedIndex()]) {
2486 in_direct_implications_[l.NegatedIndex()] =
true;
2487 direct_implications_.push_back(l.Negated());
2492 estimated_sizes_[literal] = direct_implications_.size();
2493 return direct_implications_;
2496absl::Span<const Literal> BinaryImplicationGraph::AtMostOne(
int start)
const {
2497 const int size = at_most_one_buffer_[start].Index().value();
2498 return absl::MakeSpan(&at_most_one_buffer_[start + 1], size);
2502 const int size1 = implications_[lhs].size();
2504 lhs.
Index() < at_most_ones_.size() ? at_most_ones_[lhs].size() : 0;
2507 const int choice = absl::Uniform<int>(*random_, 0, size1 + size2);
2508 if (choice < size1) {
2509 return implications_[lhs][choice].Index();
2512 const absl::Span<const Literal> amo =
2513 AtMostOne(at_most_ones_[lhs][choice - size1]);
2514 CHECK_GE(amo.size(), 2);
2515 const int first_choice = absl::Uniform<int>(*random_, 0, amo.size());
2516 const Literal lit = amo[first_choice];
2520 int next_choice = absl::Uniform<int>(*random_, 0, amo.size() - 1);
2521 if (next_choice >= first_choice) {
2524 CHECK_NE(amo[next_choice], lhs);
2525 return amo[next_choice].NegatedIndex();
2536 const Literal literal(var,
true);
2537 direct_implications_of_negated_literal_ =
2540 for (
const Literal l : direct_implications_of_negated_literal_) {
2541 if (in_direct_implications_[l]) {
2543 if (!FixLiteral(l)) {
2554 BooleanVariable var) {
2555 const Literal literal(var,
true);
2557 direct_implications_of_negated_literal_ =
2560 for (
const Literal l : direct_implications_of_negated_literal_) {
2564 DCHECK(!in_direct_implications_[l]);
2567 if (in_direct_implications_[l.NegatedIndex()]) result--;
2574 BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses) {
2575 const Literal literal(var,
true);
2576 DCHECK(!is_removed_[literal.
Index()]);
2577 DCHECK(!is_redundant_[literal.
Index()]);
2579 direct_implications_of_negated_literal_ =
2582 if (is_removed_[
b])
continue;
2583 DCHECK(!is_redundant_[
b]);
2584 estimated_sizes_[
b.NegatedIndex()]--;
2585 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
2586 if (a_negated.Negated() ==
b)
continue;
2587 if (is_removed_[a_negated])
continue;
2591 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
2592 if (is_removed_[a_negated])
continue;
2593 DCHECK(!is_redundant_[a_negated]);
2594 estimated_sizes_[a_negated.NegatedIndex()]--;
2599 for (
const Literal b : direct_implications_) {
2600 if (drat_proof_handler_ !=
nullptr) {
2601 drat_proof_handler_->DeleteClause({
Literal(var,
false),
b});
2603 postsolve_clauses->push_back({
Literal(var,
false),
b});
2605 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
2606 if (drat_proof_handler_ !=
nullptr) {
2607 drat_proof_handler_->DeleteClause({
Literal(var,
true), a_negated});
2609 postsolve_clauses->push_back({
Literal(var,
true), a_negated});
2615 is_removed_[index] =
true;
2616 implications_[index].clear();
2617 if (!is_redundant_[index]) {
2618 ++num_redundant_literals_;
2619 is_redundant_.Set(index);
2625 std::deque<std::vector<Literal>>* postsolve_clauses) {
2626 for (LiteralIndex a(0); a < implications_.size(); ++a) {
2627 if (is_redundant_[a] && !is_removed_[a]) {
2628 postsolve_clauses->push_back(
2630 is_removed_[a] =
true;
2636 auto& implication = implications_[a];
2637 for (
const Literal l : implication) {
2638 if (!is_redundant_[l]) {
2639 implication[new_size++] = l;
2642 implication.resize(new_size);
2648 for (LiteralIndex a(0); a < implications_.size(); ++a) {
2653 for (
const Literal lit : implications_[a]) {
2654 DCHECK(trail_->Assignment().LiteralIsTrue(lit));
2663 auto& implication = implications_[a];
2664 for (
const Literal l : implication) {
2666 implication[new_size++] = l;
2669 implication.resize(new_size);
2673 at_most_ones_.clear();
2674 CleanUpAndAddAtMostOnes(0);
2677 DCHECK(InvariantsAreOk());
2680bool BinaryImplicationGraph::InvariantsAreOk() {
2683 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> seen;
2684 int num_redundant = 0;
2686 for (LiteralIndex a_index(0); a_index < implications_.size(); ++a_index) {
2689 if (!implications_[a_index].empty()) {
2690 LOG(ERROR) <<
"Fixed literal has non-cleared implications";
2695 if (is_removed_[a_index]) {
2696 if (!implications_[a_index].empty()) {
2697 LOG(ERROR) <<
"Removed literal has non-cleared implications";
2702 if (is_redundant_[a_index]) {
2704 if (implications_[a_index].size() != 1) {
2706 <<
"Redundant literal should only point to its representative "
2707 << Literal(a_index) <<
" => " << implications_[a_index];
2711 for (
const Literal
b : implications_[a_index]) {
2712 seen.insert({a_index,
b.Index()});
2717 util_intops::StrongVector<LiteralIndex, int> lit_to_order;
2719 lit_to_order.
assign(implications_.size(), -1);
2720 for (
int i = 0;
i < reverse_topological_order_.size(); ++
i) {
2721 lit_to_order[reverse_topological_order_[
i]] =
i;
2725 VLOG(2) <<
"num_redundant " << num_redundant;
2726 VLOG(2) <<
"num_fixed " << num_fixed;
2727 for (LiteralIndex a_index(0); a_index < implications_.size(); ++a_index) {
2728 const LiteralIndex not_a_index = Literal(a_index).NegatedIndex();
2729 for (
const Literal
b : implications_[a_index]) {
2730 if (is_removed_[
b]) {
2731 LOG(ERROR) <<
"A removed literal still appear! " << Literal(a_index)
2736 if (!seen.contains({b.NegatedIndex(), not_a_index})) {
2737 LOG(ERROR) <<
"We have " << Literal(a_index) <<
" => " <<
b
2738 <<
" but not the reverse implication!";
2739 LOG(ERROR) <<
"redundant[a]: " << is_redundant_[a_index]
2741 << trail_->Assignment().LiteralIsAssigned(Literal(a_index))
2742 <<
" removed[a]: " << is_removed_[a_index]
2743 <<
" redundant[b]: " << is_redundant_[
b] <<
" assigned[b]: "
2744 << trail_->Assignment().LiteralIsAssigned(
b)
2745 <<
" removed[b]: " << is_removed_[
b];
2751 if (is_dag_ && !is_redundant_[
b] && !is_redundant_[a_index]) {
2752 DCHECK_NE(lit_to_order[
b], -1);
2753 DCHECK_LE(lit_to_order[
b], lit_to_order[a_index]);
2759 absl::flat_hash_set<std::pair<LiteralIndex, int>> lit_to_start;
2760 for (LiteralIndex
i(0);
i < at_most_ones_.size(); ++
i) {
2761 for (
const int start : at_most_ones_[
i]) {
2762 lit_to_start.insert({
i, start});
2766 for (
int start = 0; start < at_most_one_buffer_.size();) {
2767 const absl::Span<const Literal> amo = AtMostOne(start);
2768 for (
const Literal l : amo) {
2769 if (is_removed_[l]) {
2770 LOG(ERROR) <<
"A removed literal still appear in an amo " << l;
2773 if (!lit_to_start.contains({l, start})) {
2777 start += amo.size() + 1;
2784 if (at_most_one_iterator_ >= at_most_one_buffer_.size()) {
2785 return absl::Span<const Literal>();
2788 const absl::Span<const Literal> result = AtMostOne(at_most_one_iterator_);
2789 at_most_one_iterator_ += result.size() + 1;
2797 DCHECK_GE(literals.size(), 2);
2800 clause->size_ = literals.size();
2801 for (
int i = 0;
i < literals.size(); ++
i) {
2802 clause->literals_[
i] = literals[
i];
2821 for (
int i = j;
i < size_; ++
i) {
2825 std::swap(literals_[j], literals_[
i]);
2834 for (
const Literal literal : *
this) {
2842 for (
const Literal literal : *
this) {
2843 if (!result.empty()) result.append(
" ");
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
void Start()
When Start() is called multiple times, only the most recent is used.
ConstView const_view() const
void Set(IntegerType index)
Bitset64< IntegerType >::View BitsetView()
A bit hacky for really hot loop.
void SetUnsafe(typename Bitset64< IntegerType >::View view, IntegerType index)
bool ComputeTransitiveReduction(bool log_info=false)
Literal RepresentativeOf(Literal l) const
void Resize(int num_variables)
Resizes the data structure.
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(absl::Span< const Literal > literals, absl::Span< const double > lp_values, absl::Span< const double > reduced_costs)
bool Propagate(Trail *trail) final
SatPropagator interface.
void RemoveDuplicates()
Clean up implications list that might have duplicates.
void MinimizeConflictWithReachability(std::vector< Literal > *c)
std::vector< Literal > ExpandAtMostOneWithWeight(absl::Span< const Literal > at_most_one, const util_intops::StrongVector< LiteralIndex, bool > &can_be_included, const util_intops::StrongVector< LiteralIndex, double > &expanded_lp_values)
Same as ExpandAtMostOne() but try to maximize the weight in the clique.
bool TransformIntoMaxCliques(std::vector< std::vector< Literal > > *at_most_ones, int64_t max_num_explored_nodes=1e8)
void CleanupAllRemovedAndFixedVariables()
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
bool IsEmpty() const final
Returns true if there is no constraints in this class.
bool AddBinaryClause(Literal a, Literal b)
void RemoveFixedVariables()
absl::Span< const Literal > NextAtMostOne()
Returns the next at_most_one, or a span of size 0 when finished.
bool AddImplication(Literal a, Literal b)
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
For all possible a => var => b, add a => b.
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, absl::BitGenRef random)
bool DetectEquivalences(bool log_info=false)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
const std::vector< Literal > & DirectImplications(Literal literal)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
bool MergeAtMostOnes(absl::Span< std::vector< Literal > > at_most_ones, int64_t max_num_explored_nodes=1e8, double *dtime=nullptr)
void RemoveAllRedundantVariables(std::deque< std::vector< Literal > > *postsolve_clauses)
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
LiteralIndex RandomImpliedLiteral(Literal lhs)
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
void LazyDetach(SatClause *clause)
void InprocessingRemoveClause(SatClause *clause)
These must only be called between [Detach/Attach]AllClauses() calls.
SatClause * NextClauseToMinimize()
bool IsRemovable(SatClause *const clause) const
void Detach(SatClause *clause)
~ClauseManager() override
void DeleteRemovedClauses()
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
bool AddClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
Adds a new clause and perform initial propagation for this clause only.
SatClause * NextClauseToProbe()
Returns the next clause to probe in round-robin order.
ClauseManager(Model *model)
--— ClauseManager --—
SatClause * ReasonClause(int trail_index) const
SatClause * AddRemovableClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
void Resize(int num_variables)
Must be called before adding clauses referring to such variables.
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
This one do not need the clause to be detached.
void Attach(SatClause *clause, Trail *trail)
SatClause * NextNewClauseToMinimize()
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
bool Propagate(Trail *trail) final
SatPropagator API.
int AddLiterals(const std::vector< L > &wrapped_values)
size_t size() const
Size of the "key" space, always in [0, size()).
void reserve(int size)
Reserve memory if it is already known or tightly estimated.
void AddClause(absl::Span< const Literal > clause)
void DeleteClause(absl::Span< const Literal > clause)
void AddPotentialSubset(int index)
void DetectInclusions(const std::function< void(int subset, int superset)> &process)
void SetWorkLimit(uint64_t work_limit)
void AddPotentialSuperset(int index)
std::string DebugString() const
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
--— SatClause --—
absl::Span< const Literal > AsSpan() const
Returns a Span<> representation of the clause.
const Literal * begin() const
Allows for range based iteration: for (Literal literal : clause) {}.
bool IsSatisfied(const VariablesAssignment &assignment) const
int size() const
Number of literals in the clause.
std::string DebugString() const
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
Literal SecondLiteral() const
SatPropagator(const std::string &name)
int propagation_trail_index_
util_intops::StrongVector< LiteralIndex, absl::InlinedVector< int32_t, 6 > > AtMostOnes
SccGraph(SccFinder *finder, Implications *graph, AtMostOnes *at_most_ones, std::vector< Literal > *at_most_one_buffer)
int64_t work_done_
For the deterministic time.
util_intops::StrongVector< LiteralIndex, absl::InlinedVector< Literal, 6 > > Implications
StronglyConnectedComponentsFinder< int32_t, SccGraph, CompactVectorVector< int32_t, int32_t > > SccFinder
std::vector< Literal > to_fix_
All these literals where detected to be true during the SCC computation.
const std::vector< int32_t > & operator[](int32_t node) const
void StopProcessingCurrentSubset()
void FindSubsets(absl::Span< const int > superset, int *next_index_to_try, const std::function< void(int subset)> &process)
void StopProcessingCurrentSuperset()
void SetWorkLimit(uint64_t work_limit)
uint64_t work_done() const
std::vector< Literal > * MutableConflict()
void Enqueue(Literal true_literal, int propagator_id)
void EnqueueWithUnitReason(Literal true_literal)
Specific Enqueue() version for a fixed variable.
int CurrentDecisionLevel() const
const AssignmentInfo & Info(BooleanVariable var) const
void SetCurrentPropagatorId(int propagator_id)
void FastEnqueue(Literal true_literal)
const VariablesAssignment & Assignment() const
bool VariableIsAssigned(BooleanVariable var) const
Returns true iff the given variable is assigned.
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void assign(size_type n, const value_type &val)
void push_back(const value_type &val)
int RemoveIf(RepeatedPtrField< T > *array, const Pred &pr)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLDeleteElements(T *container)
void STLClearObject(T *obj)
const LiteralIndex kNoLiteralIndex(-1)
In SWIG mode, we don't want anything besides these top-level includes.
trees with all degrees equal to
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)