28#include "absl/algorithm/container.h"
29#include "absl/container/flat_hash_map.h"
30#include "absl/container/flat_hash_set.h"
31#include "absl/container/inlined_vector.h"
32#include "absl/functional/function_ref.h"
33#include "absl/log/check.h"
34#include "absl/log/log.h"
35#include "absl/log/vlog_is_on.h"
36#include "absl/random/distributions.h"
37#include "absl/types/span.h"
61template <
typename Watcher>
62bool WatcherListContains(
const std::vector<Watcher>& list,
64 for (
const Watcher& watcher : list) {
65 if (watcher.clause == &candidate)
return true;
70bool WatchersAreValid(
const Trail& trail, absl::Span<const Literal> literals) {
71 int min_watcher_level = trail.CurrentDecisionLevel();
72 for (
Literal w : literals.subspan(0, 2)) {
73 if (trail.Assignment().LiteralIsAssigned(
w)) {
74 min_watcher_level = std::min(min_watcher_level, trail.AssignmentLevel(
w));
77 for (
Literal l : literals.subspan(2)) {
78 if (trail.Assignment().LiteralIsFalse(l) &&
79 trail.AssignmentLevel(l) > min_watcher_level) {
86bool ClauseIsSatisfiedAtRoot(
const Trail& trail,
87 absl::Span<const Literal> literals) {
88 return absl::c_any_of(literals, [&](
Literal l) {
89 return trail.Assignment().LiteralIsTrue(l) && trail.AssignmentLevel(l) == 0;
93bool LiteralsAreFixedAtRoot(
const Trail& trail,
94 absl::Span<const Literal> literals) {
95 return absl::c_any_of(literals, [&](
Literal l) {
96 return trail.Assignment().LiteralIsAssigned(l) &&
97 trail.AssignmentLevel(l) == 0;
111 trail_(model->GetOrCreate<
Trail>()),
112 num_inspected_clauses_(0),
113 num_inspected_clause_literals_(0),
114 num_watched_clauses_(0),
115 stats_(
"ClauseManager"),
117 trail_->RegisterPropagator(
this);
118 deletion_counters_.resize(
128 watchers_on_false_.resize(num_variables << 1);
129 reasons_.resize(num_variables);
130 needs_cleaning_.Resize(LiteralIndex(num_variables << 1));
135void ClauseManager::AttachOnFalse(
Literal literal,
Literal blocking_literal,
138 DCHECK(!WatcherListContains(watchers_on_false_[literal], *clause));
139 watchers_on_false_[literal].push_back(Watcher(clause, blocking_literal));
146 const int old_index = trail->
Index();
150 std::vector<Watcher>& watchers = watchers_on_false_[false_literal];
155 auto new_it = watchers.begin();
156 const auto end = watchers.end();
160 for (
auto it = new_it; it !=
end; ++it) {
166 ++num_inspected_clauses_;
168 const int size = it->clause->size();
170 if (size == 0)
continue;
175 Literal* literals = it->clause->literals();
176 const Literal other_watched_literal(LiteralIndex(
177 literals[0].Index().value() ^ literals[1].Index().value() ^
178 false_literal.
Index().value()));
181 new_it->blocking_literal = other_watched_literal;
183 ++num_inspected_clause_literals_;
191 const int start = it->start_index;
196 num_inspected_clause_literals_ +=
i - start + 2;
200 num_inspected_clause_literals_ +=
i - 2;
201 if (
i >= start)
i = size;
207 literals[0] = other_watched_literal;
208 literals[1] = literals[
i];
209 literals[
i] = false_literal;
210 watchers_on_false_[literals[1]].emplace_back(
211 it->clause, other_watched_literal,
i + 1);
226 num_inspected_clause_literals_ += it - watchers.begin() + 1;
227 watchers.erase(new_it, it);
234 literals[0] = other_watched_literal;
235 literals[1] = false_literal;
239 const int size = it->clause->size();
241 for (
int i = 2;
i < size; ++
i) {
242 propagation_level = std::max<int>(
247 if (propagation_level == 0) {
248 if (lrat_proof_handler_ !=
nullptr) {
249 std::vector<ClauseId>& unit_ids = clause_ids_scratchpad_;
251 const int size = it->clause->size();
252 for (
int i = 1;
i < size; ++
i) {
254 trail_->GetUnitClauseId(literals[
i].
Variable()));
257 const ClauseId new_clause_id = clause_id_generator_->GetNextId();
258 lrat_proof_handler_->AddInferredClause(
259 new_clause_id, {other_watched_literal}, unit_ids);
262 trail_->EnqueueWithUnitReason(other_watched_literal);
265 reasons_[trail->
Index()] = it->clause;
271 num_inspected_clause_literals_ += watchers.size();
272 watchers.erase(new_it,
end);
280 return reasons_[trail_index]->PropagationReason();
284 const Literal literal = (*trail)[old_trail_index];
286 CHECK_LE(trail->
Index(), old_trail_index);
287 reasons_[trail->
Index()] = reasons_[old_trail_index];
288 DCHECK(absl::c_all_of(
289 reasons_[trail->
Index()]->PropagationReason(),
290 [&](
Literal l) { return trail->AssignmentLevel(l) <= level; }));
291 DCHECK_EQ(reasons_[trail->
Index()]->FirstLiteral(), literal);
296 return reasons_[trail_index];
300 if (!trail_->Assignment().VariableIsAssigned(var))
return nullptr;
301 if (trail_->AssignmentType(var) !=
propagator_id_)
return nullptr;
302 SatClause* result = reasons_[trail_->Info(var).trail_index];
303 DCHECK(result !=
nullptr) << trail_->Info(var).
DebugString();
307 if (result->
size() == 0)
return nullptr;
313 DCHECK(clause !=
nullptr);
314 if (clause->
empty())
return false;
323 Trail* trail,
int lbd) {
325 clauses_.push_back(clause);
327 clause_id_[clause] = id;
329 if (add_clause_callback_ !=
nullptr) add_clause_callback_(lbd,
id, literals);
330 return AttachAndPropagate(clause, trail);
334 absl::Span<const Literal> literals,
335 Trail* trail,
int lbd) {
337 clauses_.push_back(clause);
339 clause_id_[clause] = id;
341 if (add_clause_callback_ !=
nullptr) add_clause_callback_(lbd,
id, literals);
342 CHECK(AttachAndPropagate(clause, trail));
345 clauses_info_[clause].lbd = lbd;
354bool ClauseManager::AttachAndPropagate(
SatClause* clause,
Trail* trail) {
357 const int size = clause->
size();
358 Literal* literals = clause->literals();
362 int num_literal_not_false = 0;
363 for (
int i = 0;
i < size; ++
i) {
365 std::swap(literals[
i], literals[num_literal_not_false]);
366 ++num_literal_not_false;
367 if (num_literal_not_false == 2) {
376 if (num_literal_not_false == 0)
return false;
378 if (num_literal_not_false == 1) {
382 for (
int i = 2;
i < size; ++
i) {
384 if (level > max_level) {
386 std::swap(literals[1], literals[
i]);
393 return trail->AssignmentLevel(l) <= max_level &&
394 trail->Assignment().LiteralIsFalse(l);
396 reasons_[trail->
Index()] = clause;
401 ++num_watched_clauses_;
402 AttachOnFalse(literals[0], literals[1], clause);
403 AttachOnFalse(literals[1], literals[0], clause);
408 Literal* literals = clause->literals();
412 ++num_watched_clauses_;
413 AttachOnFalse(literals[0], literals[1], clause);
414 AttachOnFalse(literals[1], literals[0], clause);
417void ClauseManager::InternalDetach(
SatClause* clause,
421 if (clause->
size() == 0)
return;
423 --num_watched_clauses_;
424 if (lrat_proof_handler_ !=
nullptr) {
425 const auto it = clause_id_.find(clause);
426 if (it != clause_id_.end()) {
428 clause_id_.erase(it);
431 deletion_counters_[
static_cast<int>(source)]++;
432 clauses_info_.erase(clause);
438 InternalDetach(clause, source);
439 if (all_clauses_are_attached_) {
447 if (!all_clauses_are_attached_)
return;
448 all_clauses_are_attached_ =
false;
453 num_watched_clauses_ = 0;
454 watchers_on_false_.clear();
458 if (all_clauses_are_attached_)
return;
459 all_clauses_are_attached_ =
true;
461 needs_cleaning_.ResetAllToFalse();
462 watchers_on_false_.resize(needs_cleaning_.size().value());
466 ++num_watched_clauses_;
467 DCHECK_GE(clause->
size(), 2);
475 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
476 if (trail_->Assignment().LiteralIsTrue(true_literal))
return true;
477 if (trail_->Assignment().LiteralIsFalse(true_literal)) {
478 if (lrat_proof_handler_ !=
nullptr) {
479 lrat_proof_handler_->AddInferredClause(
480 clause_id_generator_->GetNextId(), {},
481 {unit_clause_id, trail_->GetUnitClauseId(true_literal.Variable())});
486 trail_->EnqueueWithUnitReason(unit_clause_id, true_literal);
490 return implication_graph_->Propagate(trail_);
494 Literal true_literal, absl::Span<const ClauseId> clause_ids) {
495 return implication_graph_->FixLiteral(true_literal, clause_ids);
499 auto it = clauses_info_.find(clause);
500 if (it == clauses_info_.end())
return;
503 if (new_lbd > it->second.lbd)
return;
505 ++num_lbd_promotions_;
506 if (new_lbd <= parameters_.clause_cleanup_lbd_bound()) {
508 clauses_info_.erase(it);
510 it->second.lbd = new_lbd;
515 if (clause->RemoveFixedLiteralsAndTestIfTrue(assignment_)) {
522 CHECK_GE(clause->
size(), 2);
528 SatClause* clause, absl::Span<const Literal> new_clause,
529 absl::Span<const ClauseId> clause_ids) {
531 if (lrat_proof_handler_ !=
nullptr) {
532 new_clause_id = clause_id_generator_->GetNextId();
533 lrat_proof_handler_->AddInferredClause(new_clause_id, new_clause,
539 << new_clause <<
" old " << clause->
AsSpan();
541 if (new_clause.empty())
return false;
543 if (new_clause.size() == 1) {
549 DCHECK(WatchersAreValid(*trail_, new_clause));
550 DCHECK(!ClauseIsSatisfiedAtRoot(*trail_, new_clause));
551 DCHECK(!LiteralsAreFixedAtRoot(*trail_, new_clause));
553 if (new_clause.size() == 2) {
555 CHECK(implication_graph_->AddBinaryClauseAndChangeReason(
556 new_clause_id, new_clause[0], new_clause[1]));
558 CHECK(implication_graph_->AddBinaryClause(new_clause_id, new_clause[0],
565 if (lrat_proof_handler_ !=
nullptr) {
566 const auto it = clause_id_.find(clause);
567 if (it != clause_id_.end()) {
568 lrat_proof_handler_->DeleteClause(it->second, clause->
AsSpan());
573 if (all_clauses_are_attached_) {
579 needs_cleaning_.Clear(l);
586 clause->Rewrite(new_clause);
590 if (all_clauses_are_attached_) {
591 AttachOnFalse(new_clause[0], new_clause[1], clause);
592 AttachOnFalse(new_clause[1], new_clause[0], clause);
598 trail_->ChangeReason(trail_->Info(new_clause[0].Variable()).trail_index,
606 absl::Span<const Literal> new_clause) {
607 DCHECK(!new_clause.empty());
608 DCHECK(!all_clauses_are_attached_);
610 for (
const Literal l : new_clause) {
611 DCHECK(!trail_->Assignment().LiteralIsAssigned(l));
615 if (new_clause.size() == 1) {
621 if (new_clause.size() == 2) {
622 implication_graph_->AddBinaryClause(new_clause[0], new_clause[1]);
627 clauses_.push_back(clause);
633 for (
const LiteralIndex index : needs_cleaning_.PositionsSetAtLeastOnce()) {
634 if (!needs_cleaning_[index])
continue;
638 needs_cleaning_.Clear(index);
640 needs_cleaning_.NotifyAllClear();
654 for (
int i = 0;
i < reasons_.size(); ++
i) {
655 if (reasons_[
i] !=
nullptr && reasons_[
i]->empty()) {
656 reasons_[
i] =
nullptr;
662 const int old_size = clauses_.size();
663 for (
int i = 0;
i < old_size; ++
i) {
664 if (
i == to_minimize_index_) to_minimize_index_ = new_size;
665 if (
i == to_first_minimize_index_) to_first_minimize_index_ = new_size;
666 if (
i == to_probe_index_) to_probe_index_ = new_size;
667 if (clauses_[
i]->IsRemoved()) {
668 DCHECK(!clauses_info_.contains(clauses_[
i]));
671 clauses_[new_size++] = clauses_[
i];
674 clauses_.resize(new_size);
676 if (to_minimize_index_ > new_size) to_minimize_index_ = new_size;
677 if (to_first_minimize_index_ > new_size) to_first_minimize_index_ = new_size;
678 if (to_probe_index_ > new_size) to_probe_index_ = new_size;
685 to_first_minimize_index_ =
686 std::max(to_first_minimize_index_, to_minimize_index_);
687 for (; to_first_minimize_index_ < clauses_.size();
688 ++to_first_minimize_index_) {
692 if (to_minimize_index_ == to_first_minimize_index_) ++to_minimize_index_;
694 if (clauses_[to_first_minimize_index_]->IsRemoved())
continue;
695 if (!
IsRemovable(clauses_[to_first_minimize_index_])) {
696 return clauses_[to_first_minimize_index_++];
703 const int old = to_first_minimize_index_;
704 for (; to_minimize_index_ < clauses_.size(); ++to_minimize_index_) {
705 if (clauses_[to_minimize_index_]->IsRemoved())
continue;
707 return clauses_[to_minimize_index_++];
712 to_minimize_index_ = 0;
713 ++num_to_minimize_index_resets_;
714 for (; to_minimize_index_ < old; ++to_minimize_index_) {
715 if (clauses_[to_minimize_index_]->IsRemoved())
continue;
717 return clauses_[to_minimize_index_++];
725 for (; to_probe_index_ < clauses_.size(); ++to_probe_index_) {
726 if (clauses_[to_probe_index_]->IsRemoved())
continue;
727 return clauses_[to_probe_index_++];
733 const BooleanVariable var = literal.
Variable();
734 DCHECK(trail_->Assignment().VariableIsAssigned(var));
735 const int assignment_type = trail_->AssignmentType(var);
736 const int trail_index = trail_->Info(var).trail_index;
738 return trail_->GetStoredReasonClauseId(var);
740 return trail_->GetUnitClauseId(var);
741 }
else if (assignment_type == implication_graph_->PropagatorId()) {
742 absl::Span<const Literal> reason =
743 implication_graph_->Reason(*trail_, trail_index,
745 CHECK_EQ(reason.size(), 1);
746 return implication_graph_->GetClauseId(literal, reason[0]);
749 if (reason !=
nullptr) {
757 absl::Span<const Literal> literals, std::vector<ClauseId>* clause_ids,
758 LiteralIndex decision,
759 std::optional<absl::FunctionRef<ClauseId(
int,
int)>> root_literals) {
761 const auto& assignment = trail_->Assignment();
764 tmp_mark_.ClearAndResize(BooleanVariable(trail_->NumVariables()));
765 marked_trail_indices_heap_.clear();
766 for (
const Literal lit : literals) {
767 CHECK(assignment.LiteralIsAssigned(lit));
768 tmp_mark_.Set(lit.Variable());
769 marked_trail_indices_heap_.push_back(
770 trail_->Info(lit.Variable()).trail_index);
772 absl::c_make_heap(marked_trail_indices_heap_);
773 const int current_level = trail_->CurrentDecisionLevel();
776 int min_level = current_level;
780 std::vector<ClauseId>& non_unit_clause_ids =
781 tmp_clause_ids_for_append_clauses_fixing_;
782 non_unit_clause_ids.clear();
784 const auto& decisions = trail_->Decisions();
785 while (!marked_trail_indices_heap_.empty()) {
786 absl::c_pop_heap(marked_trail_indices_heap_);
787 const int trail_index = marked_trail_indices_heap_.back();
788 marked_trail_indices_heap_.pop_back();
789 const Literal marked_literal = (*trail_)[trail_index];
793 const int level = trail_->Info(marked_literal.
Variable()).level;
794 if (level > 0) min_level = std::min(min_level, level);
795 if (trail_->AssignmentType(marked_literal.
Variable()) ==
800 clause_ids->push_back(trail_->GetUnitClauseId(marked_literal.
Variable()));
803 const Literal level_decision = decisions[level - 1].literal;
804 ClauseId clause_id = implication_graph_->GetClauseId(
805 level_decision.
Negated(), marked_literal);
806 if (clause_id ==
kNoClauseId && root_literals.has_value()) {
807 clause_id = (*root_literals)(level, trail_index);
810 non_unit_clause_ids.push_back(clause_id);
815 for (
const Literal literal : trail_->Reason(marked_literal.
Variable())) {
816 const BooleanVariable var = literal.
Variable();
817 if (!tmp_mark_[var]) {
820 if (info.
level > 0) {
821 marked_trail_indices_heap_.push_back(info.
trail_index);
822 absl::c_push_heap(marked_trail_indices_heap_);
824 clause_ids->push_back(trail_->GetUnitClauseId(var));
834 if (
Literal(decision) != decisions[current_level - 1].literal) {
836 clause_ids->push_back(implication_graph_->GetClauseId(
837 Literal(decision).Negated(), decisions[current_level - 1].literal));
839 for (
int level = current_level - 1; level >= min_level; --level) {
840 clause_ids->push_back(implication_graph_->GetClauseId(
841 decisions[level].literal.Negated(), decisions[level - 1].literal));
845 clause_ids->insert(clause_ids->end(), non_unit_clause_ids.rbegin(),
846 non_unit_clause_ids.rend());
853 const int num_literals = 2 * num_variables;
854 bfs_stack_.resize(num_literals);
855 implications_and_amos_.resize(num_literals);
856 implies_something_.resize(num_literals);
857 might_have_dups_.resize(num_literals);
858 is_redundant_.resize(num_literals);
859 is_removed_.resize(num_literals,
false);
860 estimated_sizes_.resize(num_literals, 0);
861 implied_by_.resize(num_literals);
862 in_direct_implications_.resize(num_literals,
false);
863 reasons_.resize(num_variables);
866void BinaryImplicationGraph::NotifyPossibleDuplicate(
Literal a) {
867 if (might_have_dups_[a.
Index()])
return;
868 might_have_dups_[a.
Index()] =
true;
869 to_clean_.push_back(a);
872bool BinaryImplicationGraph::CleanUpImplicationList(Literal a) {
873 const absl::Span<Literal> range = implications_and_amos_[a].literals();
874 if (range.empty())
return true;
876 std::sort(range.begin(), range.end());
881 for (
int i = 1;
i < range.size(); ++
i) {
882 if (range[
i] == range[
i - 1])
continue;
890 if (range[
i] == range[
i - 1].Negated() &&
892 if (lrat_proof_handler_ !=
nullptr) {
894 {GetClauseId(a.Negated(), range[i]),
895 GetClauseId(a.Negated(), range[i - 1])})) {
902 range[new_size++] = range[
i];
904 implications_and_amos_[a].TruncateLiterals(new_size);
911 if (to_clean_.empty()) {
917 while (!to_clean_.empty()) {
918 for (
const Literal l : to_clean_) {
919 might_have_dups_[l.Index()] =
false;
920 if (!CleanUpImplicationList(l))
return false;
940 tmp_bitset_.ClearAndResize(implications_and_amos_.end_index());
941 for (
const LiteralIndex l : implications_and_amos_.index_range()) {
942 for (
const Literal b : implications_and_amos_[l].literals()) {
946 if (tmp_bitset_[
Literal(
b.Variable(),
true)]) {
949 tmp_bitset_.Set(
Literal(
b.Variable(),
true));
951 tmp_bitset_.ResetAllToFalse();
960bool BinaryImplicationGraph::AddBinaryClauseInternal(
962 bool delete_non_representative_id) {
964 DCHECK_GE(a.
Index(), 0);
965 DCHECK_GE(
b.Index(), 0);
970 no_constraint_ever_added_ =
false;
975 if (rep_a == rep_b.
Negated())
return true;
977 if (lrat_proof_handler_ !=
nullptr) {
982 if (rep_a != a || rep_b !=
b) {
983 absl::InlinedVector<ClauseId, 3> clause_ids;
990 clause_ids.push_back(
id);
991 rep_id = clause_id_generator_->
GetNextId();
996 changed_reasons_on_trail_.emplace_back(std::minmax(a,
b));
997 AddClauseId(
id, a,
b);
998 }
else if (delete_non_representative_id) {
999 lrat_proof_handler_->DeleteClause(
id, {a,
b});
1002 AddClauseId(rep_id, rep_a, rep_b);
1004 if (change_reason) {
1005 CHECK(trail_->Assignment().LiteralIsFalse(
b));
1006 CHECK(trail_->Assignment().LiteralIsTrue(a));
1007 const int trail_index = trail_->Info(a.
Variable()).trail_index;
1008 reasons_[trail_index] =
b;
1013 if (rep_a == rep_b) {
1015 }
else if (trail_->CurrentDecisionLevel() == 0) {
1016 const auto& assignment = trail_->Assignment();
1020 if (lrat_proof_handler_ !=
nullptr) {
1021 if (assignment.LiteralIsFalse(rep_a)) {
1023 {rep_id, trail_->GetUnitClauseId(rep_a.
Variable())});
1024 }
else if (assignment.LiteralIsFalse(rep_b)) {
1026 {rep_id, trail_->GetUnitClauseId(rep_b.
Variable())});
1029 if (assignment.LiteralIsFalse(rep_a)) {
1031 }
else if (assignment.LiteralIsFalse(rep_b)) {
1039 DCHECK(!is_removed_[a]);
1040 DCHECK(!is_removed_[
b]);
1042 estimated_sizes_[
b.NegatedIndex()]++;
1043 implications_and_amos_[a.
NegatedIndex()].PushBackLiteral(
b);
1044 implications_and_amos_[
b.NegatedIndex()].PushBackLiteral(a);
1046 implies_something_.Set(
b.NegatedIndex());
1047 NotifyPossibleDuplicate(a.
Negated());
1048 NotifyPossibleDuplicate(
b.Negated());
1051 if (enable_sharing_ && add_binary_callback_ !=
nullptr) {
1052 add_binary_callback_(a,
b);
1055 const auto& assignment = trail_->Assignment();
1056 if (assignment.LiteralIsFalse(a)) {
1057 if (assignment.LiteralIsAssigned(
b)) {
1058 if (assignment.LiteralIsFalse(
b))
return false;
1060 reasons_[trail_->Index()] = a;
1061 trail_->EnqueueAtLevel(
b,
propagator_id_, trail_->AssignmentLevel(a));
1063 }
else if (assignment.LiteralIsFalse(
b)) {
1064 if (assignment.LiteralIsAssigned(a)) {
1065 if (assignment.LiteralIsFalse(a))
return false;
1067 reasons_[trail_->Index()] =
b;
1068 trail_->EnqueueAtLevel(a,
propagator_id_, trail_->AssignmentLevel(
b));
1076 absl::Span<const Literal> at_most_one) {
1077 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1078 if (at_most_one.size() <= 1)
return true;
1082 no_constraint_ever_added_ =
false;
1087 const int base_index = at_most_one_buffer_.size();
1088 at_most_one_buffer_.push_back(
Literal(LiteralIndex(at_most_one.size())));
1089 at_most_one_buffer_.insert(at_most_one_buffer_.end(), at_most_one.begin(),
1093 return CleanUpAndAddAtMostOnes(base_index);
1101 absl::Span<const ClauseId> clause_ids) {
1102 CHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1103 if (trail_->Assignment().LiteralIsTrue(true_literal))
return true;
1104 if (trail_->Assignment().LiteralIsFalse(true_literal)) {
1105 if (lrat_proof_handler_ !=
nullptr) {
1106 std::vector<ClauseId> proof = {clause_ids.begin(), clause_ids.end()};
1107 proof.push_back(trail_->GetUnitClauseId(true_literal.
Variable()));
1108 lrat_proof_handler_->AddInferredClause(clause_id_generator_->GetNextId(),
1115 if (lrat_proof_handler_ !=
nullptr) {
1116 new_clause_id = clause_id_generator_->GetNextId();
1117 lrat_proof_handler_->AddInferredClause(new_clause_id, {true_literal},
1121 trail_->EnqueueWithUnitReason(new_clause_id, true_literal);
1128bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(
int base_index) {
1130 int local_end = base_index;
1131 const int buffer_size = at_most_one_buffer_.size();
1132 for (
int i = base_index;
i < buffer_size;) {
1136 const int local_start = local_end;
1141 if (
i == at_most_one_iterator_) {
1142 at_most_one_iterator_ = local_start;
1146 const absl::Span<const Literal> initial_amo = AtMostOne(
i);
1147 i += initial_amo.size() + 1;
1151 bool set_all_left_to_false =
false;
1152 for (
const Literal l : initial_amo) {
1154 if (is_removed_[l])
continue;
1155 if (!set_all_left_to_false && assignment.
LiteralIsTrue(l)) {
1156 set_all_left_to_false =
true;
1161 at_most_one_buffer_[local_start] =
1162 Literal(LiteralIndex(local_end - local_start - 1));
1166 bool some_duplicates =
false;
1168 if (!set_all_left_to_false) {
1171 Literal* pt = &at_most_one_buffer_[local_start + 1];
1172 std::sort(pt, pt + AtMostOne(local_start).size());
1175 bool remove_previous =
false;
1176 int new_local_end = local_start + 1;
1177 for (
const Literal l : AtMostOne(local_start)) {
1178 if (l.Index() == previous) {
1183 remove_previous =
true;
1184 some_duplicates =
true;
1192 trivializer = l.Variable();
1197 if (remove_previous) {
1199 remove_previous =
false;
1201 previous = l.Index();
1202 at_most_one_buffer_[new_local_end++] = l;
1204 if (remove_previous) --new_local_end;
1207 local_end = new_local_end;
1208 at_most_one_buffer_[local_start] =
1209 Literal(LiteralIndex(local_end - local_start - 1));
1214 if (some_duplicates) {
1215 int new_local_end = local_start + 1;
1216 for (
const Literal l : AtMostOne(local_start)) {
1218 if (!set_all_left_to_false && assignment.
LiteralIsTrue(l)) {
1219 set_all_left_to_false =
true;
1222 at_most_one_buffer_[new_local_end++] = l;
1226 local_end = new_local_end;
1227 at_most_one_buffer_[local_start] =
1228 Literal(LiteralIndex(local_end - local_start - 1));
1233 for (
const Literal l : AtMostOne(local_start)) {
1234 if (l.Variable() == trivializer)
continue;
1241 local_end = local_start;
1246 const absl::Span<const Literal> at_most_one = AtMostOne(local_start);
1250 if (at_most_one.size() <= std::max(2, at_most_one_max_expansion_size_)) {
1251 if (at_most_one.size() > 1) {
1252 for (
const Literal a : at_most_one) {
1253 implies_something_.Set(a);
1254 NotifyPossibleDuplicate(a);
1255 for (
const Literal
b : at_most_one) {
1256 if (a ==
b)
continue;
1257 implications_and_amos_[a].PushBackLiteral(
b.Negated());
1263 local_end = local_start;
1268 for (
const Literal l : at_most_one) {
1269 DCHECK_LT(l.Index(), implications_and_amos_.size());
1270 DCHECK(!is_redundant_[l]);
1271 implications_and_amos_[l].InsertOffset(local_start);
1272 implies_something_.Set(l);
1276 at_most_one_buffer_.resize(local_end);
1289 const auto implies_something = implies_something_.view();
1290 auto* implications = implications_and_amos_.data();
1295 if (!implies_something[true_literal])
continue;
1302 const auto implied = implications[true_literal.
Index().value()].literals();
1303 num_inspections_ += implied.size();
1304 for (
const Literal literal : implied) {
1314 ++num_propagations_;
1318 if (lrat_proof_handler_ !=
nullptr && level == 0 &&
1320 lrat_proof_handler_->AddInferredClause(
1321 clause_id_generator_->GetNextId(), {},
1322 {trail->GetUnitClauseId(true_literal.Variable()),
1323 GetClauseId(true_literal.Negated(), literal),
1324 trail->GetUnitClauseId(literal.Variable())});
1330 if (level == 0 && lrat_proof_handler_ !=
nullptr) {
1331 const ClauseId new_clause_id = clause_id_generator_->GetNextId();
1332 lrat_proof_handler_->AddInferredClause(
1333 new_clause_id, {literal},
1344 for (
const int start :
1345 implications[true_literal.
Index().value()].offsets()) {
1347 for (
const Literal literal : AtMostOne(start)) {
1349 if (literal == true_literal) {
1358 ++num_propagations_;
1378 const Trail& ,
int trail_index, int64_t )
const {
1379 return {&reasons_[trail_index], 1};
1383 const Literal literal = (*trail)[old_trail_index];
1385 reasons_[trail->
Index()] = reasons_[old_trail_index];
1398 const Trail& trail, std::vector<Literal>* conflict,
1400 bool also_use_decisions) {
1402 DCHECK(!conflict->empty());
1403 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
1405 tmp_to_keep_.clear();
1406 tmp_to_keep_.push_back(conflict->front().Negated());
1407 if (lrat_proof_handler_ !=
nullptr) {
1408 MarkDescendants<
true>(conflict->front().Negated());
1410 MarkDescendants(conflict->front().Negated());
1417 std::vector<std::pair<Literal, int>> decisions;
1418 if (also_use_decisions) {
1419 for (
int i = 1;
i < conflict->size(); ++
i) {
1420 const auto& info = trail_->Info((*conflict)[
i].
Variable());
1422 decisions.push_back({(*conflict)[
i].Negated(), info.level});
1425 absl::c_stable_sort(decisions, [](
const std::pair<LiteralIndex, int>& a,
1426 const std::pair<LiteralIndex, int>&
b) {
1427 return a.second >
b.second;
1433 for (
const auto [literal, unused_level] : decisions) {
1434 if (is_marked_[literal])
continue;
1435 tmp_to_keep_.push_back(literal);
1436 if (lrat_proof_handler_ !=
nullptr) {
1437 MarkDescendants<
true>(literal);
1439 MarkDescendants(literal);
1443 for (
const LiteralIndex
i : is_marked_.PositionsSetAtLeastOnce()) {
1454 for (
const Literal l : tmp_to_keep_) is_marked_.Clear(l);
1455 if (lrat_proof_handler_ !=
nullptr) {
1456 RemoveRedundantLiterals<
true>(conflict, clause_ids);
1458 RemoveRedundantLiterals(conflict);
1462template <
bool fill_clause_
ids>
1463void BinaryImplicationGraph::RemoveRedundantLiterals(
1464 std::vector<Literal>* conflict, std::vector<ClauseId>* clause_ids) {
1467 if constexpr (fill_clause_ids) {
1468 clause_ids->clear();
1470 for (
int i = 1;
i < conflict->size(); ++
i) {
1471 const Literal literal = (*conflict)[
i];
1473 if (!is_marked_[negated_index]) {
1474 (*conflict)[new_index] = (*conflict)[
i];
1476 }
else if constexpr (fill_clause_ids) {
1477 AppendImplicationChain(literal, clause_ids);
1480 if (new_index < conflict->size()) {
1481 ++num_minimization_;
1482 num_literals_removed_ += conflict->size() - new_index;
1483 conflict->resize(new_index);
1488 absl::Span<const Literal> literals, std::vector<ClauseId>* clause_ids) {
1489 for (
const Literal literal : literals) {
1490 if (trail_->Info(literal.Variable()).level == 0) {
1491 const ClauseId clause_id = trail_->GetUnitClauseId(literal.Variable());
1493 if (!processed_unit_clauses_[literal]) {
1494 processed_unit_clauses_.Set(literal);
1495 clause_ids->push_back(clause_id);
1498 }
else if (is_marked_[literal.NegatedIndex()]) {
1499 AppendImplicationChain(literal, clause_ids);
1504void BinaryImplicationGraph::AppendImplicationChain(
1505 Literal literal, std::vector<ClauseId>* clause_ids) {
1507 const int initial_size = clause_ids->size();
1508 while (implied_by_[negated_index] !=
Literal(negated_index)) {
1510 Literal(negated_index), implied_by_[negated_index].Negated());
1512 clause_ids->push_back(clause_id);
1513 const LiteralIndex next_negated_index = implied_by_[negated_index];
1515 implied_by_[negated_index] =
Literal(negated_index);
1516 negated_index = next_negated_index;
1518 std::reverse(clause_ids->begin() + initial_size, clause_ids->end());
1523 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
1527 const int new_num_fixed = trail_->Index();
1529 if (num_processed_fixed_variables_ == new_num_fixed)
return;
1532 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
1533 for (; num_processed_fixed_variables_ < new_num_fixed;
1534 ++num_processed_fixed_variables_) {
1535 const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
1540 implications_and_amos_[true_literal].literals()) {
1541 DCHECK(trail_->Assignment().LiteralIsTrue(lit));
1554 implications_and_amos_[true_literal.
NegatedIndex()].literals()) {
1555 if (lit.NegatedIndex() < representative_of_.size() &&
1558 is_marked_.Set(representative_of_[lit.Negated()]);
1560 is_marked_.Set(lit.NegatedIndex());
1563 implications_and_amos_[true_literal].ClearAndReleaseMemory();
1564 implications_and_amos_[true_literal.
NegatedIndex()].ClearAndReleaseMemory();
1566 for (
const LiteralIndex
i : is_marked_.PositionsSetAtLeastOnce()) {
1567 implications_and_amos_[
i].RemoveLiteralsIf(
1568 [&assignment](
const Literal lit) {
1575 for (
auto& v : implications_and_amos_) {
1578 CHECK(CleanUpAndAddAtMostOnes(0));
1579 DCHECK(InvariantsAreOk());
1591 implications_and_offsets,
1592 std::vector<Literal>* at_most_one_buffer,
1594 std::vector<Literal>* to_fix, std::vector<Literal>* parent_of_to_fix)
1596 implications_and_offsets_(*implications_and_offsets),
1597 at_most_one_buffer_(*at_most_one_buffer),
1600 parent_of_to_fix_(parent_of_to_fix) {
1601 if (parents_ !=
nullptr) {
1602 parents_->resize(implications_and_offsets->size());
1603 for (
int i = 0;
i < implications_and_offsets->size(); ++
i) {
1604 parents_->push_back(LiteralIndex(
i));
1612 implications_and_offsets_[LiteralIndex(node)].literals()) {
1613 if (parents_ !=
nullptr &&
1614 finder_.NodeIsNotYetExplored(l.Index().value())) {
1615 (*parents_)[l.Index()] = LiteralIndex(node);
1617 tmp_.push_back(l.Index().value());
1618 if (finder_.NodeIsInCurrentDfsPath(l.NegatedIndex().value())) {
1619 to_fix_.push_back(l);
1625 if (parent_of_to_fix_ !=
nullptr) {
1626 parent_of_to_fix_->push_back(
Literal(LiteralIndex(node)));
1630 for (
const int start :
1631 implications_and_offsets_[LiteralIndex(node)].offsets()) {
1632 if (start >= at_most_one_already_explored_.size()) {
1633 at_most_one_already_explored_.resize(start + 1,
false);
1634 previous_node_to_explore_at_most_one_.resize(start + 1);
1644 if (at_most_one_already_explored_[start]) {
1646 const int first_node = previous_node_to_explore_at_most_one_[start];
1647 DCHECK_NE(node, first_node);
1649 if (finder_.NodeIsInCurrentDfsPath(first_node)) {
1665 previous_node_to_explore_at_most_one_[start] = node;
1670 Literal(LiteralIndex(first_node)).NegatedIndex().value());
1674 at_most_one_already_explored_[start] =
true;
1675 previous_node_to_explore_at_most_one_[start] = node;
1678 const absl::Span<const Literal> amo =
1679 absl::MakeSpan(&at_most_one_buffer_[start + 1],
1680 at_most_one_buffer_[start].Index().value());
1682 if (l.Index() == node)
continue;
1683 tmp_.push_back(l.NegatedIndex().value());
1684 if (finder_.NodeIsInCurrentDfsPath(l.Index().value())) {
1685 to_fix_.push_back(l.Negated());
1699 implications_and_offsets_;
1700 const std::vector<Literal>& at_most_one_buffer_;
1705 std::vector<Literal>& to_fix_;
1706 std::vector<Literal>* parent_of_to_fix_;
1708 mutable std::vector<int32_t> tmp_;
1711 mutable std::vector<bool> at_most_one_already_explored_;
1712 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1722 trail_(graph->trail_),
1723 implications_and_amos_(graph->implications_and_amos_),
1724 clause_id_generator_(graph->clause_id_generator_),
1725 lrat_proof_handler_(graph->lrat_proof_handler_) {}
1733 component_ = component;
1737 in_component_.ClearAndResize(LiteralIndex(graph_->literal_size()));
1738 for (
int i = 0;
i < component.size(); ++
i) {
1739 in_component_.Set(LiteralIndex(component[
i]));
1748 DCHECK(stack_.empty());
1749 stack_.push(all_true ? fixed_literal :
Literal(fixed_literal).Negated());
1750 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
1751 is_marked_.Set(stack_.top());
1752 while (!stack_.empty()) {
1753 const LiteralIndex node = stack_.top();
1755 for (
const Literal l : implications_and_amos_[node].literals()) {
1756 if (!in_component_[l.Index()] || is_marked_[l.Index()])
continue;
1757 stack_.push(l.Index());
1758 is_marked_.Set(l.Index());
1760 if (trail_->Assignment().LiteralIsTrue(to_fix))
continue;
1761 clause_ids_.clear();
1762 clause_ids_.push_back(
1764 clause_ids_.push_back(graph_->GetClauseId(
Literal(node).Negated(), l));
1765 if (!graph_->FixLiteral(l, clause_ids_))
return false;
1775 LiteralIndex representative = LiteralIndex(component_[0]);
1776 for (
Literal& ref : implications_and_amos_[representative].literals()) {
1777 const LiteralIndex rep = graph_->RepresentativeOf(ref);
1778 if (rep == representative)
continue;
1780 MaybeAddLratImplicationChain(
1798 for (
const Literal l : implications_and_amos_[representative].literals()) {
1799 const Literal rep = graph_->RepresentativeOf(l);
1800 if (rep.
Index() == representative)
continue;
1802 MaybeAddLratImplicationChain({
Literal(representative), l, rep});
1805 DCHECK(stack_.empty());
1806 stack_.push(representative);
1807 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
1808 is_marked_.Set(representative);
1809 while (!stack_.empty()) {
1810 const LiteralIndex node = stack_.top();
1812 for (
const Literal l : implications_and_amos_[node].literals()) {
1813 if (is_marked_[l] || !in_component_[l.Index()])
continue;
1814 stack_.push(l.Index());
1815 is_marked_.Set(l.Index());
1817 MaybeAddLratImplicationChain(
1819 for (
const Literal m : implications_and_amos_[l].literals()) {
1820 const Literal rep = graph_->RepresentativeOf(m);
1821 if (rep.
Index() != representative) {
1823 MaybeAddLratImplicationChain({
Literal(representative), l, m, rep});
1830 const LiteralIndex negated_representative =
1832 DCHECK(stack_.empty());
1833 stack_.push(negated_representative);
1834 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
1835 is_marked_.Set(negated_representative);
1836 while (!stack_.empty()) {
1837 const LiteralIndex node = stack_.top();
1839 for (
const Literal l : implications_and_amos_[node].literals()) {
1840 if (is_marked_[l] || !in_component_[l.NegatedIndex()])
continue;
1841 stack_.push(l.Index());
1842 is_marked_.Set(l.Index());
1844 MaybeAddLratImplicationChain(
1855 const ClauseId clause_id = clause_id_generator_->GetNextId();
1856 clause_ids_.clear();
1857 AppendLratImplicationChain({literal, representative, literal.
Negated()},
1859 AddInferredClause(clause_id, {literal.
Negated()}, clause_ids_);
1862 clause_ids_.clear();
1863 clause_ids_.push_back(clause_id);
1864 AppendLratImplicationChain({literal.
Negated(), representative, literal},
1866 AddInferredClause(clause_id_generator_->GetNextId(), {}, clause_ids_);
1876 std::vector<ClauseId>& clause_ids) {
1879 std::vector<Literal>& steps = tmp_literals_;
1882 const Literal a_rep = graph_->RepresentativeOf(a);
1883 while (graph_->RepresentativeOf(steps.back()) != a_rep) {
1884 DCHECK_NE(parents[steps.back().Index()], steps.back().Index());
1885 steps.push_back(
Literal(parents[steps.back().Index()]));
1887 steps.push_back(a_rep);
1890 std::reverse(steps.begin(), steps.end());
1892 AppendLratImplicationChain(steps, clause_ids);
1899 void MaybeAddLratImplicationChain(absl::Span<const Literal> steps) {
1903 clause_ids_.clear();
1904 AppendLratImplicationChain(steps, clause_ids_);
1905 if (clause_ids_.empty())
return;
1906 const ClauseId new_clause_id = clause_id_generator_->
GetNextId();
1907 AddInferredClause(new_clause_id, {a,
b}, clause_ids_);
1908 graph_->AddClauseId(new_clause_id, a,
b);
1911 void AppendLratImplicationChain(absl::Span<const Literal> steps,
1912 std::vector<ClauseId>& clause_ids) {
1913 for (
int i = 1;
i < steps.size(); ++
i) {
1914 if (steps[
i] == steps[
i - 1])
continue;
1915 clause_ids.push_back(
1920 void AddInferredClause(ClauseId new_clause_id,
1921 absl::Span<const Literal> literals,
1922 absl::Span<const ClauseId> clause_ids) {
1923 if (lrat_proof_handler_ !=
nullptr) {
1924 lrat_proof_handler_->AddInferredClause(new_clause_id, literals,
1929 BinaryImplicationGraph* graph_;
1931 util_intops::StrongVector<LiteralIndex, LiteralsOrOffsets>&
1932 implications_and_amos_;
1933 ClauseIdGenerator* clause_id_generator_;
1934 LratProofHandler* lrat_proof_handler_;
1942 absl::Span<const int32_t> component_;
1943 SparseBitset<LiteralIndex> in_component_;
1944 std::stack<LiteralIndex> stack_;
1945 SparseBitset<LiteralIndex> is_marked_;
1946 std::vector<ClauseId> clause_ids_;
1947 std::vector<Literal> tmp_literals_;
1953 if (is_dag_)
return true;
1956 log_info |= VLOG_IS_ON(1);
1958 if (trail_->CurrentDecisionLevel() == 0) {
1959 for (std::pair<Literal, Literal> clause : changed_reasons_on_trail_) {
1960 auto it = clause_id_.find(clause);
1961 lrat_proof_handler_->DeleteClause(it->second,
1962 {clause.first, clause.second});
1963 clause_id_.erase(it);
1965 changed_reasons_on_trail_.clear();
1971 DCHECK(InvariantsAreOk());
1974 const int32_t size(implications_and_amos_.size());
1978 std::vector<Literal> to_fix;
1979 std::vector<Literal> parent_of_to_fix;
1983 SccGraph graph(&finder, &implications_and_amos_, &at_most_one_buffer_,
1984 lrat_helper_ !=
nullptr ? &parents :
nullptr, &to_fix,
1985 lrat_helper_ !=
nullptr ? &parent_of_to_fix :
nullptr);
1992 is_redundant_.resize(size);
1994 int num_equivalences = 0;
1995 int num_new_redundant_literals = 0;
1999 reverse_topological_order_.clear();
2000 for (
int index = 0; index < scc.
size(); ++index) {
2001 const absl::Span<int32_t> component = scc[index];
2003 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
2012 std::sort(component.begin(), component.end());
2013 const LiteralIndex representative(component[0]);
2014 reverse_topological_order_.push_back(representative);
2016 if (lrat_helper_ !=
nullptr) {
2017 lrat_helper_->NewComponent(component);
2020 if (component.size() == 1) {
2023 if (num_equivalences > 0) {
2024 if (lrat_helper_ !=
nullptr) {
2025 lrat_helper_->ProcessSingletonComponent();
2027 auto& representative_list = implications_and_amos_[representative];
2028 for (
Literal& ref : representative_list.literals()) {
2029 const LiteralIndex rep = representative_of_[ref];
2030 if (rep == representative)
continue;
2036 CHECK(CleanUpImplicationList(
Literal(representative)));
2041 if (lrat_helper_ !=
nullptr) {
2042 lrat_helper_->ProcessComponent(representative);
2046 for (
int i = 1;
i < component.size(); ++
i) {
2048 if (!is_redundant_[literal]) {
2049 ++num_new_redundant_literals;
2050 is_redundant_.Set(literal);
2052 representative_of_[literal] = representative;
2056 if (
Literal(LiteralIndex(component[
i - 1])).Negated() == literal) {
2057 LOG_IF(INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
2058 if (lrat_helper_ !=
nullptr) {
2059 lrat_helper_->ProcessUnsatComponent(literal,
Literal(representative));
2066 auto& representative_list = implications_and_amos_[representative];
2068 for (
const Literal l : representative_list.literals()) {
2070 if (rep.
Index() == representative)
continue;
2071 representative_list.literals()[new_size++] = rep;
2073 representative_list.TruncateLiterals(new_size);
2074 for (
int i = 1;
i < component.size(); ++
i) {
2076 auto& ref = implications_and_amos_[literal];
2077 for (
const Literal l : ref.literals()) {
2079 if (rep.
Index() != representative) {
2080 representative_list.PushBackLiteral(rep);
2083 dtime += 1e-8 *
static_cast<double>(ref.num_literals());
2090 representative_list.PushBackLiteral(literal);
2091 ref.ClearLiterals();
2092 ref.PushBackLiteral(
Literal(representative));
2096 CHECK(CleanUpImplicationList(
Literal(representative)));
2097 num_equivalences += component.size() - 1;
2100 std::vector<ClauseId> clause_ids;
2102 for (
int i = 0;
i < to_fix.size(); ++
i) {
2105 if (lrat_helper_ !=
nullptr) {
2106 const Literal parent_of_l = parent_of_to_fix[
i];
2107 lrat_helper_->ComputeImplicationChain(l.
Negated(), parent_of_l, parents,
2111 if (!
FixLiteral(l, clause_ids))
return false;
2119 for (
int index = 0; index < scc.
size(); ++index) {
2120 const absl::Span<int32_t> component = scc[index];
2121 if (component.size() == 1 || is_removed_[LiteralIndex(component[0])]) {
2124 bool all_fixed =
false;
2125 bool all_true =
false;
2127 for (
const int32_t
i : component) {
2132 fixed_literal = l.
Index();
2136 if (!all_fixed)
continue;
2137 for (
const int32_t
i : component) {
2139 if (!is_redundant_[l]) {
2140 ++num_new_redundant_literals;
2141 is_redundant_.Set(l);
2142 representative_of_[l] = l.
Index();
2145 if (lrat_helper_ !=
nullptr) {
2146 lrat_helper_->NewComponent(component);
2147 if (!lrat_helper_->FixAllLiteralsInComponent(fixed_literal, all_true)) {
2151 for (
const int32_t
i : component) {
2162 if (num_equivalences != 0) {
2166 for (
auto& v : implications_and_amos_) {
2170 if (!CleanUpAndAddAtMostOnes(0))
return false;
2185 num_redundant_literals_ += num_new_redundant_literals;
2186 num_current_equivalences_ = num_equivalences;
2187 time_limit_->AdvanceDeterministicTime(dtime);
2188 const int num_fixed_during_scc =
2189 trail_->Index() - num_processed_fixed_variables_;
2195 DCHECK(InvariantsAreOk());
2196 LOG_IF(INFO, log_info) <<
"SCC. " << num_equivalences
2197 <<
" redundant equivalent literals. "
2198 << num_fixed_during_scc <<
" fixed. "
2200 <<
" implications left. "
2201 << implications_and_amos_.size() <<
" literals."
2202 <<
" size of at_most_one buffer = "
2203 << at_most_one_buffer_.size() <<
"."
2204 <<
" dtime: " << dtime
2205 <<
" wtime: " << wall_timer.
Get();
2215 DCHECK_EQ(trail_->CurrentDecisionLevel(), 0);
2216 if (time_limit_->LimitReached())
return true;
2222 if (time_limit_->LimitReached())
return true;
2225 DCHECK(InvariantsAreOk());
2226 if (time_limit_->LimitReached())
return true;
2228 log_info |= VLOG_IS_ON(1);
2232 int64_t num_fixed = 0;
2233 int64_t num_new_redundant_implications = 0;
2234 bool aborted =
false;
2235 tmp_removed_.clear();
2236 work_done_in_mark_descendants_ = 0;
2237 int marked_index = 0;
2238 std::vector<ClauseId> clause_ids;
2254 const LiteralIndex size(implications_and_amos_.size());
2256 for (
const LiteralIndex root : reverse_topological_order_) {
2261 if (is_redundant_[root])
continue;
2262 if (trail_->Assignment().LiteralIsAssigned(
Literal(root)))
continue;
2264 auto& direct_implications = implications_and_amos_[root];
2265 if (direct_implications.literals().empty())
continue;
2274 bool clear_previous_reachability =
true;
2275 for (
const Literal direct_child : direct_implications.literals()) {
2276 if (direct_child.Index() == previous) {
2277 clear_previous_reachability =
false;
2278 is_marked_.Clear(previous);
2282 if (clear_previous_reachability) {
2283 is_marked_.ClearAndResize(size);
2288 for (
const Literal direct_child : direct_implications.literals()) {
2289 if (is_redundant_[direct_child])
continue;
2290 if (is_marked_[direct_child])
continue;
2294 if (direct_child.Index() == root)
continue;
2298 if (direct_child.NegatedIndex() == root) {
2299 is_marked_.Set(direct_child);
2303 if (lrat_proof_handler_ !=
nullptr) {
2304 MarkDescendants<
true>(direct_child);
2306 MarkDescendants(direct_child);
2310 is_marked_.Clear(direct_child);
2311 implied_by_[direct_child] =
Literal(root);
2313 DCHECK(!is_marked_[root])
2314 <<
"DetectEquivalences() should have removed cycles!";
2315 is_marked_.Set(root);
2316 implied_by_[root] =
Literal(root);
2320 auto is_marked = is_marked_.BitsetView();
2321 for (
const int start : implications_and_amos_[root].offsets()) {
2322 for (
const Literal l : AtMostOne(start)) {
2323 if (l.Index() == root)
continue;
2324 if (!is_marked[l.Negated()] && !is_redundant_[l.Negated()]) {
2325 is_marked_.SetUnsafe(is_marked, l.Negated());
2326 if (lrat_proof_handler_ !=
nullptr) {
2327 MarkDescendants<
true>(l.Negated());
2329 MarkDescendants(l.Negated());
2339 const auto& marked_positions = is_marked_.PositionsSetAtLeastOnce();
2340 for (; marked_index < marked_positions.size(); ++marked_index) {
2341 const LiteralIndex
i = marked_positions[marked_index];
2342 if (is_marked_[
Literal(
i).NegatedIndex()]) {
2344 DCHECK(!trail_->Assignment().LiteralIsAssigned(
Literal(root)));
2350 if (lrat_proof_handler_ !=
nullptr) {
2352 AppendImplicationChain(
Literal(
i), &clause_ids);
2353 AppendImplicationChain(
Literal(
i).Negated(), &clause_ids);
2363 if (trail_->Assignment().LiteralIsAssigned(
Literal(root)))
continue;
2369 for (
const Literal l : direct_implications.literals()) {
2370 if (!is_marked_[l]) {
2371 direct_implications.literals()[new_size++] = l;
2373 tmp_removed_.push_back({
Literal(root), l});
2374 DCHECK(!is_redundant_[l]);
2377 const int diff = direct_implications.num_literals() - new_size;
2378 direct_implications.TruncateLiterals(new_size);
2379 direct_implications.ShrinkToFit();
2380 num_new_redundant_implications += diff;
2383 if (work_done_in_mark_descendants_ > 1e8) {
2389 is_marked_.ClearAndResize(size);
2395 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> removed;
2396 for (
const auto [a,
b] : tmp_removed_) {
2397 removed.insert({a.
Index(),
b.Index()});
2399 for (LiteralIndex
i(0);
i < implications_and_amos_.size(); ++
i) {
2402 auto& implication = implications_and_amos_[
i];
2403 for (
const Literal l : implication.literals()) {
2404 if (removed.contains({l.NegatedIndex(), negated_i}))
continue;
2405 implication.literals()[new_size++] = l;
2407 implication.TruncateLiterals(new_size);
2410 if (num_fixed > 0) {
2413 DCHECK(InvariantsAreOk());
2416 const double dtime = 1e-8 * work_done_in_mark_descendants_;
2417 time_limit_->AdvanceDeterministicTime(dtime);
2418 num_redundant_implications_ += num_new_redundant_implications;
2419 LOG_IF(INFO, log_info) <<
"Transitive reduction removed "
2420 << num_new_redundant_implications <<
" literals. "
2421 << num_fixed <<
" fixed. "
2423 <<
" implications left. "
2424 << implications_and_amos_.size() <<
" literals."
2425 <<
" dtime: " << dtime
2426 <<
" wtime: " << wall_timer.
Get()
2427 << (aborted ?
" Aborted." :
"");
2433int ElementInIntersectionOrMinusOne(absl::Span<const int> a,
2434 absl::Span<const int>
b) {
2435 DCHECK(std::is_sorted(a.begin(), a.end()));
2436 DCHECK(std::is_sorted(
b.begin(),
b.end()));
2437 if (a.empty() ||
b.empty())
return -1;
2441 if (a[
i] ==
b[j])
return a[
i];
2443 if (++
i == a.size())
return -1;
2445 if (++j ==
b.size())
return -1;
2452std::vector<std::pair<int, int>>
2453BinaryImplicationGraph::FilterAndSortAtMostOnes(
2454 absl::Span<std::vector<Literal>> at_most_ones) {
2457 std::vector<std::pair<int, int>> index_size_vector;
2458 const int num_amos = at_most_ones.size();
2459 index_size_vector.reserve(num_amos);
2460 for (
int index = 0; index < num_amos; ++index) {
2461 std::vector<Literal>& clique = at_most_ones[index];
2462 if (clique.size() <= 1)
continue;
2470 for (Literal& ref : clique) {
2471 DCHECK_LT(ref.Index(), representative_of_.size());
2472 const LiteralIndex rep = representative_of_[ref];
2482 std::sort(clique.begin(), clique.end());
2483 for (
int i = 1;
i < clique.size(); ++
i) {
2484 if (clique[
i] == clique[
i - 1] || clique[
i] == clique[
i -
i].Negated()) {
2491 index_size_vector.push_back({index, clique.size()});
2493 absl::c_stable_sort(index_size_vector, [](
const std::pair<int, int> a,
2494 const std::pair<int, int>
b) {
2495 return a.second >
b.second;
2497 return index_size_vector;
2501 std::vector<std::vector<Literal>>* at_most_ones,
2502 int64_t max_num_explored_nodes) {
2505 work_done_in_mark_descendants_ = 0;
2507 int num_extended = 0;
2508 int num_removed = 0;
2512 std::vector<int> detector_clique_index;
2517 std::vector<int> dense_index_to_index;
2519 max_cliques_containing(implications_and_amos_.size());
2521 const std::vector<std::pair<int, int>> index_size_vector =
2522 FilterAndSortAtMostOnes(absl::MakeSpan(*at_most_ones));
2524 absl::flat_hash_set<int> cannot_be_removed;
2525 std::vector<bool> was_extended(at_most_ones->size(),
false);
2526 for (
const auto& [index, old_size] : index_size_vector) {
2527 std::vector<Literal>& clique = (*at_most_ones)[index];
2528 if (time_limit_->LimitReached())
break;
2532 if (clique.size() == 2) {
2533 DCHECK_NE(clique[0], clique[1]);
2534 const int dense_index = ElementInIntersectionOrMinusOne(
2535 max_cliques_containing[clique[0]], max_cliques_containing[clique[1]]);
2536 if (dense_index >= 0) {
2537 const int superset_index = dense_index_to_index[dense_index];
2538 if (was_extended[superset_index]) {
2539 cannot_be_removed.insert(superset_index);
2549 detector_clique_index.push_back(index);
2553 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
2554 clique = ExpandAtMostOne(clique, max_num_explored_nodes);
2558 detector_clique_index.push_back(index);
2562 const int dense_index = dense_index_to_index.size();
2563 dense_index_to_index.push_back(index);
2564 for (
const Literal l : clique) {
2565 max_cliques_containing[l].
push_back(dense_index);
2568 if (clique.size() > old_size) {
2569 was_extended[index] =
true;
2577 const int subset_index = detector_clique_index[subset];
2578 const int superset_index = detector_clique_index[superset];
2579 if (subset_index == superset_index)
return;
2582 if ((*at_most_ones)[subset_index].empty())
return;
2583 if ((*at_most_ones)[superset_index].empty())
return;
2587 if (cannot_be_removed.contains(subset_index))
return;
2590 (*at_most_ones)[subset_index].clear();
2591 if (was_extended[superset_index]) cannot_be_removed.insert(superset_index);
2594 if (num_extended > 0 || num_removed > 0 || num_added > 0) {
2595 VLOG(1) <<
"Clique Extended: " << num_extended
2596 <<
" Removed: " << num_removed <<
" Added: " << num_added
2597 << (work_done_in_mark_descendants_ > max_num_explored_nodes
2605 absl::Span<std::vector<Literal>> at_most_ones,
2606 int64_t max_num_explored_nodes,
double* dtime) {
2609 work_done_in_mark_descendants_ = 0;
2611 const std::vector<std::pair<int, int>> index_size_vector =
2612 FilterAndSortAtMostOnes(at_most_ones);
2615 std::vector<int> detector_clique_index;
2617 for (
const auto& [index, old_size] : index_size_vector) {
2618 if (time_limit_->LimitReached())
break;
2619 detector_clique_index.push_back(index);
2626 detector.IndexAllStorageAsSubsets();
2633 std::vector<int> intersection;
2634 const int num_to_consider = index_size_vector.size();
2635 for (
int subset_index = 0; subset_index < num_to_consider; ++subset_index) {
2636 const int index = index_size_vector[subset_index].first;
2637 std::vector<Literal>& clique = at_most_ones[index];
2638 if (clique.empty())
continue;
2640 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
2641 if (detector.
Stopped())
break;
2646 int next_index_to_try = 0;
2647 intersection.clear();
2648 tmp_bitset_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
2649 for (
const Literal l : clique) {
2650 intersection.push_back(l.Index().value());
2655 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
2656 if (detector.
Stopped())
break;
2663 if (clique_i > 0 && intersection.size() == clique.size()) {
2664 clique_i = clique.size();
2666 for (; clique_i < clique.size(); ++clique_i) {
2667 const Literal l = clique[clique_i];
2669 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
2672 if (clique_i == 0) {
2675 for (
const LiteralIndex index :
2676 is_marked_.PositionsSetAtLeastOnce()) {
2678 if (!tmp_bitset_[lit]) {
2679 intersection.push_back(lit.
Index().value());
2687 const int old_size = intersection.size();
2688 for (
int i = 0;
i < old_size; ++
i) {
2689 if (
i == next_index_to_try) {
2690 next_index_to_try = new_size;
2692 const int index = intersection[
i];
2694 if (tmp_bitset_[lit] || is_marked_[lit.
Negated()]) {
2695 intersection[new_size++] = index;
2698 intersection.resize(new_size);
2703 if (intersection.size() <= clique.size())
break;
2709 CHECK_GE(intersection.size(), clique.size());
2718 detector.
FindSubsets(intersection, &next_index_to_try, [&](
int subset) {
2719 if (subset == subset_index) {
2725 for (
const int index : storage[subset]) {
2726 const LiteralIndex lit_index = LiteralIndex(index);
2727 if (tmp_bitset_[lit_index])
continue;
2728 tmp_bitset_.Set(lit_index);
2729 clique.push_back(
Literal(lit_index));
2732 if (num_extra == 0) {
2734 at_most_ones[detector_clique_index[subset]].clear();
2743 if (num_extra == 0)
break;
2746 if (dtime !=
nullptr) {
2748 1e-8 * work_done_in_mark_descendants_ + 1e-9 * detector.
work_done();
2753template <
bool use_weight>
2755 const absl::Span<const Literal> at_most_one,
2758 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
2759 std::vector<LiteralIndex> intersection;
2760 const int64_t old_work = work_done_in_mark_descendants_;
2761 for (
int i = 0;
i < clique.size(); ++
i) {
2763 if (work_done_in_mark_descendants_ - old_work > 1e8)
break;
2765 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
2766 MarkDescendants(clique[
i]);
2768 for (
const LiteralIndex index : is_marked_.PositionsSetAtLeastOnce()) {
2769 if (can_be_included[
Literal(index).NegatedIndex()]) {
2770 intersection.push_back(index);
2773 for (
const Literal l : clique) is_marked_.Clear(l.NegatedIndex());
2777 is_marked_.Clear(clique[
i]);
2778 is_marked_.Clear(clique[
i].NegatedIndex());
2779 for (
const LiteralIndex index : intersection) {
2780 if (!is_marked_[index])
continue;
2781 intersection[new_size++] = index;
2783 intersection.resize(new_size);
2784 if (intersection.empty())
break;
2788 if (
i + 1 == clique.size()) {
2791 double max_lp = 0.0;
2792 for (
int j = 0; j < intersection.size(); ++j) {
2797 absl::Uniform<double>(*random_, 0.0, 1e-4)
2798 : can_be_included.size() - intersection[j].value();
2799 if (index == -1 || lp > max_lp) {
2805 clique.push_back(
Literal(intersection[index]).Negated());
2806 std::swap(intersection.back(), intersection[index]);
2807 intersection.pop_back();
2815template std::vector<Literal>
2817 const absl::Span<const Literal> at_most_one,
2820template std::vector<Literal>
2822 const absl::Span<const Literal> at_most_one,
2834const std::vector<std::vector<Literal>>&
2836 absl::Span<const Literal> literals, absl::Span<const double> lp_values,
2837 absl::Span<const double> reduced_costs) {
2839 const int num_literals = implications_and_amos_.size();
2846 const int size = literals.size();
2847 for (
int i = 0;
i < size; ++
i) {
2849 can_be_included[l] =
true;
2852 const double value = lp_values[
i];
2853 expanded_lp_values[l] = value;
2869 const double rc = reduced_costs[
i];
2870 heuristic_weights[l] = -rc;
2879 bool operator<(
const Candidate& other)
const {
return sum > other.sum; }
2881 std::vector<Candidate> candidates;
2889 std::vector<Literal> fractional_literals;
2890 for (
int i = 0;
i < size; ++
i) {
2891 Literal current_literal = literals[
i];
2892 double current_value = lp_values[
i];
2893 if (trail_->Assignment().LiteralIsAssigned(current_literal))
continue;
2894 if (is_redundant_[current_literal])
continue;
2896 if (current_value < 0.5) {
2897 current_literal = current_literal.
Negated();
2898 current_value = 1.0 - current_value;
2901 if (current_value < 0.99) {
2902 fractional_literals.push_back(current_literal);
2907 double best_value = 0.0;
2908 for (
const Literal l : implications_and_amos_[current_literal].literals()) {
2909 if (!can_be_included[l])
continue;
2910 const double activity =
2911 current_value + expanded_lp_values[l.NegatedIndex()];
2912 if (activity <= 1.01)
continue;
2913 const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
2916 best = l.NegatedIndex();
2920 const double activity = current_value + expanded_lp_values[best];
2921 candidates.push_back({current_literal,
Literal(best), activity});
2926 const int kMaxNumberOfCutPerCall = 10;
2927 std::sort(candidates.begin(), candidates.end());
2928 if (candidates.size() > kMaxNumberOfCutPerCall) {
2929 candidates.resize(kMaxNumberOfCutPerCall);
2935 for (
const Candidate& candidate : candidates) {
2937 {candidate.a, candidate.b}, can_be_included, heuristic_weights));
2942 if (fractional_literals.size() > 1) {
2947 const int max_graph_size = 1024;
2948 if (fractional_literals.size() > max_graph_size) {
2949 std::shuffle(fractional_literals.begin(), fractional_literals.end(),
2951 fractional_literals.resize(max_graph_size);
2954 bron_kerbosch_.Initialize(fractional_literals.size() * 2);
2958 tmp_mapping_.resize(implications_and_amos_.size(), -1);
2959 for (
const Literal l : fractional_literals) {
2960 bron_kerbosch_.SetWeight(
i, expanded_lp_values[l]);
2961 tmp_mapping_[l] =
i++;
2962 bron_kerbosch_.SetWeight(
i, expanded_lp_values[l.Negated()]);
2963 tmp_mapping_[l.Negated()] =
i++;
2972 const int from = tmp_mapping_[l];
2975 const int to = tmp_mapping_[next.Negated()];
2977 bron_kerbosch_.AddEdge(from,
to);
2985 bron_kerbosch_.TakeTransitiveClosureOfImplicationGraph();
2987 bron_kerbosch_.SetWorkLimit(1e8);
2988 bron_kerbosch_.SetMinimumWeight(1.001);
2989 std::vector<std::vector<int>> cliques = bron_kerbosch_.Run();
2993 const int max_num_per_batch = 5;
2994 std::vector<std::pair<int, double>> with_weight =
2995 bron_kerbosch_.GetMutableIndexAndWeight();
2996 if (with_weight.size() > max_num_per_batch) {
2998 with_weight.begin(), with_weight.end(),
2999 [](
const std::pair<int, double>& a,
const std::pair<int, double>&
b) {
3000 return a.second > b.second;
3002 with_weight.resize(max_num_per_batch);
3005 std::vector<Literal> at_most_one;
3006 for (
const auto [index, weight] : with_weight) {
3008 at_most_one.clear();
3009 for (
const int i : cliques[index]) {
3010 const Literal l = fractional_literals[
i / 2];
3011 at_most_one.push_back(
i % 2 == 1 ? l.
Negated() : l);
3020 at_most_one, can_be_included, heuristic_weights));
3024 for (
const Literal l : fractional_literals) {
3025 tmp_mapping_[l] = -1;
3026 tmp_mapping_[l.Negated()] = -1;
3036std::vector<absl::Span<const Literal>>
3038 std::vector<absl::Span<const Literal>> result;
3041 implications_and_amos_.size(),
false);
3042 for (
const Literal l : *literals) to_consider[l] =
true;
3045 std::priority_queue<std::pair<int, int>> pq;
3051 absl::flat_hash_set<int> explored_amo;
3052 for (
const Literal l : *literals) {
3053 for (
const int start : implications_and_amos_[l].offsets()) {
3054 const auto [_, inserted] = explored_amo.insert(start);
3055 if (!inserted)
continue;
3057 int intersection_size = 0;
3058 for (
const Literal l : AtMostOne(start)) {
3059 if (to_consider[l]) ++intersection_size;
3061 if (intersection_size > 1) {
3062 pq.push({intersection_size, start});
3066 if (intersection_size == literals->size())
break;
3071 int num_processed = 0;
3072 while (!pq.empty()) {
3073 const auto [old_size, start] = pq.top();
3077 int intersection_size = 0;
3078 for (
const Literal l : AtMostOne(start)) {
3079 if (to_consider[l]) ++intersection_size;
3081 if (intersection_size != old_size) {
3083 if (intersection_size > 1) {
3084 pq.push({intersection_size, start});
3090 for (
const Literal l : AtMostOne(start)) {
3091 to_consider[l] =
false;
3096 const int span_start = num_processed;
3097 for (
int i = num_processed;
i < literals->size(); ++
i) {
3098 if (to_consider[(*literals)[
i]])
continue;
3099 std::swap((*literals)[num_processed], (*literals)[
i]);
3102 result.push_back(absl::MakeSpan(literals->data() + span_start,
3103 num_processed - span_start));
3110 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
3111 return MarkDescendants(root);
3115 const LiteralIndex size(implications_and_amos_.size());
3116 is_marked_.ClearAndResize(size);
3117 processed_unit_clauses_.ClearAndResize(size);
3120template <
bool fill_implied_by>
3121absl::Span<const Literal> BinaryImplicationGraph::MarkDescendants(
3123 auto*
const stack = bfs_stack_.data();
3125 auto is_redundant = is_redundant_.
const_view();
3129 if (is_redundant[root])
return absl::MakeSpan(stack, 1);
3130 is_marked_.
Set(root);
3131 if constexpr (fill_implied_by) {
3132 implied_by_[root] = root;
3134 auto implies_something = implies_something_.
const_view();
3135 for (
int j = 0; j < stack_size; ++j) {
3136 const Literal current = stack[j];
3137 if (!implies_something[current])
continue;
3139 work_done_in_mark_descendants_ +=
3140 implications_and_amos_[current].num_literals();
3141 for (
const Literal l : implications_and_amos_[current].literals()) {
3142 if (!is_marked[l] && !is_redundant[l]) {
3144 if constexpr (fill_implied_by) {
3145 implied_by_[l] = current;
3147 stack[stack_size++] = l;
3151 for (
const int start : implications_and_amos_[current].offsets()) {
3152 work_done_in_mark_descendants_ += AtMostOne(start).size();
3153 for (
const Literal l : AtMostOne(start)) {
3154 if (l == current)
continue;
3155 if (!is_marked[l.NegatedIndex()] && !is_redundant[l.NegatedIndex()]) {
3156 is_marked_.SetUnsafe(is_marked, l.NegatedIndex());
3157 if constexpr (fill_implied_by) {
3158 implied_by_[l.Negated()] = current;
3160 stack[stack_size++] = l.Negated();
3165 work_done_in_mark_descendants_ += stack_size;
3166 return absl::MakeSpan(stack, stack_size);
3169std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
3170 const absl::Span<const Literal> at_most_one,
3171 int64_t max_num_explored_nodes) {
3172 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
3175 for (
const Literal l : clique) {
3176 if (!implies_something_[l]) {
3184 std::vector<LiteralIndex> intersection;
3185 for (
int i = 0;
i < clique.size(); ++
i) {
3186 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
3187 is_marked_.ClearAndResize(LiteralIndex(implications_and_amos_.size()));
3188 MarkDescendants(clique[
i]);
3191 intersection = is_marked_.PositionsSetAtLeastOnce();
3192 for (
const Literal l : clique) is_marked_.Clear(l.NegatedIndex());
3196 is_marked_.Clear(clique[
i].NegatedIndex());
3197 for (
const LiteralIndex index : intersection) {
3198 if (is_marked_[index]) intersection[new_size++] = index;
3200 intersection.resize(new_size);
3201 if (intersection.empty())
break;
3208 if (
i + 1 == clique.size()) {
3209 clique.push_back(Literal(intersection.back()).Negated());
3210 intersection.pop_back();
3220 DCHECK(!is_removed_[literal]);
3223 for (
const Literal l : direct_implications_) {
3224 in_direct_implications_[l] =
false;
3226 direct_implications_.clear();
3231 for (
const Literal l : implications_and_amos_[literal].literals()) {
3232 if (l == literal)
continue;
3234 if (!is_removed_[l] && !in_direct_implications_[l]) {
3235 in_direct_implications_[l] =
true;
3236 direct_implications_.push_back(l);
3239 if (is_redundant_[literal]) {
3240 DCHECK(implications_and_amos_[literal].offsets().empty());
3242 for (
const int start : implications_and_amos_[literal].offsets()) {
3243 for (
const Literal l : AtMostOne(start)) {
3244 if (l == literal)
continue;
3246 if (!is_removed_[l] && !in_direct_implications_[l.NegatedIndex()]) {
3247 in_direct_implications_[l.NegatedIndex()] =
true;
3248 direct_implications_.push_back(l.Negated());
3252 estimated_sizes_[literal] = direct_implications_.size();
3253 return direct_implications_;
3256absl::Span<const Literal> BinaryImplicationGraph::AtMostOne(
int start)
const {
3257 const int size = at_most_one_buffer_[start].Index().value();
3258 return absl::MakeSpan(&at_most_one_buffer_[start + 1], size);
3262 const int size1 = implications_and_amos_[lhs].num_literals();
3263 const int size2 = implications_and_amos_[lhs].num_offsets();
3266 const int choice = absl::Uniform<int>(*random_, 0, size1 + size2);
3267 if (choice < size1) {
3268 return implications_and_amos_[lhs].literals()[choice].Index();
3271 const absl::Span<const Literal> amo =
3272 AtMostOne(implications_and_amos_[lhs].offsets()[choice - size1]);
3273 CHECK_GE(amo.size(), 2);
3274 const int first_choice = absl::Uniform<int>(*random_, 0, amo.size());
3275 const Literal lit = amo[first_choice];
3279 int next_choice = absl::Uniform<int>(*random_, 0, amo.size() - 1);
3280 if (next_choice >= first_choice) {
3283 CHECK_NE(amo[next_choice], lhs);
3284 return amo[next_choice].NegatedIndex();
3295 const Literal literal(var,
true);
3296 direct_implications_of_negated_literal_ =
3299 for (
const Literal l : direct_implications_of_negated_literal_) {
3300 if (in_direct_implications_[l]) {
3313 BooleanVariable var) {
3314 const Literal literal(var,
true);
3316 direct_implications_of_negated_literal_ =
3319 for (
const Literal l : direct_implications_of_negated_literal_) {
3323 DCHECK(!in_direct_implications_[l]);
3326 if (in_direct_implications_[l.NegatedIndex()]) result--;
3333 BooleanVariable var, std::deque<std::vector<Literal>>* postsolve_clauses) {
3334 const Literal literal(var,
true);
3335 DCHECK(!is_removed_[literal.
Index()]);
3336 DCHECK(!is_redundant_[literal.
Index()]);
3338 direct_implications_of_negated_literal_ =
3341 if (is_removed_[
b])
continue;
3342 DCHECK(!is_redundant_[
b]);
3343 estimated_sizes_[
b.NegatedIndex()]--;
3344 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
3345 if (a_negated.Negated() ==
b)
continue;
3346 if (is_removed_[a_negated])
continue;
3350 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
3351 if (is_removed_[a_negated])
continue;
3352 DCHECK(!is_redundant_[a_negated]);
3353 estimated_sizes_[a_negated.NegatedIndex()]--;
3358 for (
const Literal b : direct_implications_) {
3359 postsolve_clauses->push_back({
Literal(var,
false),
b});
3361 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
3362 postsolve_clauses->push_back({
Literal(var,
true), a_negated});
3368 is_removed_[index] =
true;
3369 implications_and_amos_[index].ClearLiterals();
3370 implications_and_amos_[index].ShrinkToFit();
3371 if (!is_redundant_[index]) {
3372 ++num_redundant_literals_;
3373 is_redundant_.Set(index);
3379 std::deque<std::vector<Literal>>* postsolve_clauses) {
3380 for (LiteralIndex a(0); a < implications_and_amos_.size(); ++a) {
3381 if (is_redundant_[a] && !is_removed_[a]) {
3382 postsolve_clauses->push_back(
3384 is_removed_[a] =
true;
3385 implications_and_amos_[a].ClearLiterals();
3386 implications_and_amos_[a].ShrinkToFit();
3391 auto& implication = implications_and_amos_[a];
3392 for (
const Literal l : implication.literals()) {
3393 if (!is_redundant_[l]) {
3394 implication.literals()[new_size++] = l;
3397 implication.TruncateLiterals(new_size);
3403 for (LiteralIndex a(0); a < implications_and_amos_.size(); ++a) {
3408 for (
const Literal lit : implications_and_amos_[a].literals()) {
3409 DCHECK(trail_->Assignment().LiteralIsTrue(lit));
3413 implications_and_amos_[a].ClearLiterals();
3414 implications_and_amos_[a].ShrinkToFit();
3419 auto& implication = implications_and_amos_[a];
3420 for (
const Literal l : implication.literals()) {
3422 implication.literals()[new_size++] = l;
3425 implication.TruncateLiterals(new_size);
3429 for (
auto& v : implications_and_amos_) {
3432 CHECK(CleanUpAndAddAtMostOnes(0));
3435 DCHECK(InvariantsAreOk());
3438bool BinaryImplicationGraph::InvariantsAreOk() {
3441 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> seen;
3442 int num_redundant = 0;
3445 for (LiteralIndex a_index(0); a_index < implications_and_amos_.size();
3447 if (time_limit_check.LimitReached())
return true;
3450 if (!implications_and_amos_[a_index].literals().empty()) {
3451 LOG(ERROR) <<
"Fixed literal has non-cleared implications";
3456 if (is_removed_[a_index]) {
3457 if (!implications_and_amos_[a_index].literals().empty()) {
3458 LOG(ERROR) <<
"Removed literal has non-cleared implications";
3463 if (is_redundant_[a_index]) {
3465 if (implications_and_amos_[a_index].num_literals() != 1) {
3467 <<
"Redundant literal should only point to its representative "
3468 << Literal(a_index) <<
" => "
3469 << implications_and_amos_[a_index].literals();
3473 for (
const Literal
b : implications_and_amos_[a_index].literals()) {
3474 seen.insert({a_index,
b.Index()});
3475 if (lrat_proof_handler_ !=
nullptr &&
3477 LOG(ERROR) <<
"No clause id for " << Literal(a_index) <<
" => " <<
b;
3484 util_intops::StrongVector<LiteralIndex, int> lit_to_order;
3486 lit_to_order.
assign(implications_and_amos_.size(), -1);
3487 for (
int i = 0;
i < reverse_topological_order_.size(); ++
i) {
3488 lit_to_order[reverse_topological_order_[
i]] =
i;
3492 VLOG(2) <<
"num_redundant " << num_redundant;
3493 VLOG(2) <<
"num_fixed " << num_fixed;
3494 for (LiteralIndex a_index(0); a_index < implications_and_amos_.size();
3496 if (time_limit_check.LimitReached())
return true;
3497 const LiteralIndex not_a_index = Literal(a_index).NegatedIndex();
3498 for (
const Literal
b : implications_and_amos_[a_index].literals()) {
3499 if (is_removed_[
b]) {
3500 LOG(ERROR) <<
"A removed literal still appear! " << Literal(a_index)
3505 if (!seen.contains({b.NegatedIndex(), not_a_index})) {
3506 LOG(ERROR) <<
"We have " << Literal(a_index) <<
" => " <<
b
3507 <<
" but not the reverse implication!";
3508 LOG(ERROR) <<
"redundant[a]: " << is_redundant_[a_index]
3510 << trail_->Assignment().LiteralIsAssigned(Literal(a_index))
3511 <<
" removed[a]: " << is_removed_[a_index]
3512 <<
" redundant[b]: " << is_redundant_[
b] <<
" assigned[b]: "
3513 << trail_->Assignment().LiteralIsAssigned(
b)
3514 <<
" removed[b]: " << is_removed_[
b];
3520 if (is_dag_ && !is_redundant_[
b] && !is_redundant_[a_index]) {
3521 DCHECK_NE(lit_to_order[
b], -1);
3522 DCHECK_LE(lit_to_order[
b], lit_to_order[a_index]);
3528 absl::flat_hash_set<std::pair<LiteralIndex, int>> lit_to_start;
3529 for (LiteralIndex
i(0);
i < implications_and_amos_.size(); ++
i) {
3530 for (
const int start : implications_and_amos_[
i].offsets()) {
3531 lit_to_start.insert({
i, start});
3535 for (
int start = 0; start < at_most_one_buffer_.size();) {
3536 const absl::Span<const Literal> amo = AtMostOne(start);
3537 for (
const Literal l : amo) {
3538 if (is_removed_[l]) {
3539 LOG(ERROR) <<
"A removed literal still appear in an amo " << l;
3542 if (!lit_to_start.contains({l, start})) {
3546 start += amo.size() + 1;
3553 if (at_most_one_iterator_ >= at_most_one_buffer_.size()) {
3554 return absl::Span<const Literal>();
3557 const absl::Span<const Literal> result = AtMostOne(at_most_one_iterator_);
3558 DCHECK(!result.empty());
3559 at_most_one_iterator_ += result.size() + 1;
3565 stats_(
"BinaryImplicationGraph"),
3566 time_limit_(model->GetOrCreate<
TimeLimit>()),
3568 trail_(model->GetOrCreate<
Trail>()),
3571 at_most_one_max_expansion_size_(model->GetOrCreate<
SatParameters>()
3572 ->at_most_one_max_expansion_size()) {
3573 trail_->RegisterPropagator(
this);
3574 if (lrat_proof_handler_ !=
nullptr) {
3581 LOG(INFO) << stats_.StatString();
3582 LOG(INFO) <<
"num_redundant_implications " << num_redundant_implications_;
3584 if (lrat_helper_ !=
nullptr)
delete lrat_helper_;
3591 DCHECK_GE(literals.size(), 2);
3594 clause->size_ = literals.size();
3595 for (
int i = 0;
i < literals.size(); ++
i) {
3596 clause->literals_[
i] = literals[
i];
3603bool SatClause::RemoveFixedLiteralsAndTestIfTrue(
3615 for (
int i = j;
i < size_; ++
i) {
3619 std::swap(literals_[j], literals_[
i]);
3628 for (
const Literal literal : *
this) {
3636 for (
const Literal literal : *
this) {
3637 if (!result.empty()) result.append(
" ");
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
ConstView const_view() const
void Set(IntegerType index)
Bitset64< IntegerType >::View BitsetView()
void SetUnsafe(typename Bitset64< IntegerType >::View view, IntegerType index)
void AppendImplicationChains(absl::Span< const Literal > literals, std::vector< ClauseId > *clause_ids)
bool ComputeTransitiveReduction(bool log_info=false)
Literal RepresentativeOf(Literal l) const
void ClearImpliedLiterals()
void Resize(int num_variables)
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(absl::Span< const Literal > literals, absl::Span< const double > lp_values, absl::Span< const double > reduced_costs)
void Reimply(Trail *trail, int old_trail_index) final
bool Propagate(Trail *trail) final
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)
bool TransformIntoMaxCliques(std::vector< std::vector< Literal > > *at_most_ones, int64_t max_num_explored_nodes=1e8)
absl::Span< const Literal > GetAllImpliedLiterals(Literal root)
void CleanupAllRemovedAndFixedVariables()
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
bool IsEmpty() const final
bool RemoveDuplicatesAndFixedVariables()
void RemoveFixedVariables()
absl::Span< const Literal > NextAtMostOne()
bool FixLiteral(Literal true_literal, absl::Span< const ClauseId > clause_ids={})
int64_t ComputeNumImplicationsForLog() const
BinaryImplicationGraph(Model *model)
bool AddImplication(Literal a, Literal b)
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked, std::vector< ClauseId > *clause_ids, bool also_use_decisions)
bool DetectEquivalences(bool log_info=false)
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)
ClauseId GetClauseId(Literal a, Literal b) const
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
~BinaryImplicationGraph() override
friend class LratEquivalenceHelper
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
bool AddClause(ClauseId id, absl::Span< const Literal > literals, Trail *trail, int lbd)
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause, absl::Span< const ClauseId > clause_ids={})
SatClause * NextClauseToMinimize()
void LazyDelete(SatClause *clause, DeletionSourceForStat source)
bool IsRemovable(SatClause *const clause) const
bool RemoveFixedLiteralsAndTestIfTrue(SatClause *clause)
~ClauseManager() override
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal, absl::Span< const ClauseId > clause_ids={})
void DeleteRemovedClauses()
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
ABSL_MUST_USE_RESULT bool InprocessingAddUnitClause(ClauseId unit_clause_id, Literal true_literal)
SatClause * AddRemovableClause(ClauseId id, absl::Span< const Literal > literals, Trail *trail, int lbd)
void ChangeLbdIfBetter(SatClause *clause, int new_lbd)
SatClause * NextClauseToProbe()
ClauseId ReasonClauseId(Literal literal) const
void Reimply(Trail *trail, int old_trail_index) final
ClauseManager(Model *model)
SatClause * ReasonClauseOrNull(BooleanVariable var) const
SatClause * ReasonClause(int trail_index) const
void Resize(int num_variables)
void Attach(SatClause *clause, Trail *trail)
SatClause * NextNewClauseToMinimize()
void SetClauseId(const SatClause *clause, ClauseId id)
void AppendClauseIdsFixing(absl::Span< const Literal > literals, std::vector< ClauseId > *clause_ids, LiteralIndex decision=kNoLiteralIndex, std::optional< absl::FunctionRef< ClauseId(int, int)> > root_literals={})
ClauseId GetClauseId(const SatClause *clause) const
bool ClauseIsUsedAsReason(SatClause *clause) const
bool Propagate(Trail *trail) final
int AddLiterals(const std::vector< L > &wrapped_values)
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
void NewComponent(absl::Span< const int32_t > component)
LratEquivalenceHelper(BinaryImplicationGraph *graph)
void ProcessSingletonComponent()
void ProcessComponent(LiteralIndex representative)
void ProcessUnsatComponent(Literal literal, Literal representative)
bool FixAllLiteralsInComponent(LiteralIndex fixed_literal, bool all_true)
void ComputeImplicationChain(Literal a, Literal b, util_intops::StrongVector< LiteralIndex, LiteralIndex > &parents, std::vector< ClauseId > &clause_ids)
void DeleteClause(ClauseId id, absl::Span< const Literal > clause)
bool AddInferredClause(ClauseId id, absl::Span< const Literal > clause, absl::Span< const ClauseId > unit_ids, bool exported=false)
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
absl::Span< const Literal > AsSpan() const
absl::Span< const Literal > PropagationReason() const
bool IsSatisfied(const VariablesAssignment &assignment) const
Literal PropagatedLiteral() const
std::string DebugString() const
Literal SecondLiteral() const
SatPropagator(const std::string &name)
int propagation_trail_index_
SccGraph(SccFinder *finder, util_intops::StrongVector< LiteralIndex, LiteralsOrOffsets > *implications_and_offsets, std::vector< Literal > *at_most_one_buffer, util_intops::StrongVector< LiteralIndex, LiteralIndex > *parents, std::vector< Literal > *to_fix, std::vector< Literal > *parent_of_to_fix)
StronglyConnectedComponentsFinder< int32_t, SccGraph, CompactVectorVector< int32_t, int32_t > > SccFinder
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
void EnqueueWithUnitReason(Literal true_literal, ClauseId clause_id)
void EnqueueAtLevel(Literal true_literal, int level)
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
void EnqueueAtLevel(Literal true_literal, int propagator_id, int level)
std::vector< Literal > * MutableConflict()
ClauseId GetUnitClauseId(BooleanVariable var) const
int CurrentDecisionLevel() const
EnqueueHelper GetEnqueueHelper(int propagator_id)
int AssignmentLevel(Literal lit) const
void SetFailingSatClause(SatClause *clause)
bool ChronologicalBacktrackingEnabled() const
const VariablesAssignment & Assignment() const
bool VariableIsAssigned(BooleanVariable var) const
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)
void resize(size_type new_size)
void STLDeleteElements(T *container)
void STLClearObject(T *obj)
void OpenSourceEraseIf(Container &c, Pred pred)
const LiteralIndex kNoLiteralIndex(-1)
constexpr ClauseId kNoClauseId(0)
const BooleanVariable kNoBooleanVariable(-1)
ClosedInterval::Iterator end(ClosedInterval interval)
trees with all degrees equal w the current value of w
trees with all degrees equal to
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
static constexpr int kSearchDecision
static constexpr int kUnitReason
static constexpr int kCachedReason