26#include "absl/algorithm/container.h"
27#include "absl/cleanup/cleanup.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/flat_hash_set.h"
30#include "absl/container/inlined_vector.h"
31#include "absl/functional/function_ref.h"
32#include "absl/log/check.h"
33#include "absl/log/log.h"
34#include "absl/log/vlog_is_on.h"
35#include "absl/types/span.h"
65 absl::Span<const Literal> literals) {
66 return absl::c_any_of(
67 literals, [&](
Literal l) {
return assignment.LiteralIsAssigned(l); });
73 Literal literal, absl::Span<const Literal> clause) {
75 clauses.emplace_back(clause.begin(), clause.end());
76 for (
int i = 0;
i < clause.size(); ++
i) {
77 if (clause[
i] == literal) {
86#define RETURN_IF_FALSE(f) \
87 if (!(f)) return false;
94 double probing_time = 0.0;
95 const bool log_round_info = VLOG_IS_ON(2);
115 const double start_dtime = time_limit_->GetElapsedDeterministicTime();
117 while (!time_limit_->LimitReached() &&
118 time_limit_->GetElapsedDeterministicTime() <= stop_dtime) {
119 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
124 RETURN_IF_FALSE(implication_graph_->RemoveDuplicatesAndFixedVariables());
142 implication_graph_->RemoveAllRedundantVariables(&postsolve_->clauses);
149 !implication_graph_->IsDag()) {
155 !implication_graph_->IsDag()) {
162 stop_dtime > time_limit_->GetElapsedDeterministicTime()) {
163 auto inner_model_inprocessing = [&](
Model* inner_model) {
165 ->set_inprocessing_use_sat_sweeping(
false);
170 equivalence_sat_sweeping_->DoOneRound(inner_model_inprocessing));
172 implication_graph_->RemoveAllRedundantVariables(&postsolve_->clauses);
178 if (!params_.fill_tightened_domains_in_response()) {
179 blocked_clause_simplifier_->DoOneRound(log_round_info);
184 RETURN_IF_FALSE(bounded_variable_elimination_->DoOneRound(log_round_info));
188 const double saved_wtime = wall_timer.
Get();
189 const double time_left =
190 stop_dtime - time_limit_->GetElapsedDeterministicTime();
191 if (time_left <= 0)
break;
193 probing_options.
log_info = log_round_info;
198 probing_time += wall_timer.
Get() - saved_wtime;
201 !implication_graph_->IsDag()) {
215 logger_,
"[Pure SAT presolve]",
216 " num_fixed: ",
FormatCounter(trail_->Index()),
" num_equiv: ",
217 FormatCounter(implication_graph_->num_current_equivalences() / 2),
"/",
218 FormatCounter(sat_solver_->NumVariables()),
" num_implications: ",
219 FormatCounter(implication_graph_->ComputeNumImplicationsForLog()),
220 " num_watched_clauses: ",
222 " dtime: ", time_limit_->GetElapsedDeterministicTime() - start_dtime,
"/",
224 " non-probing time: ", (wall_timer.
Get() - probing_time));
229 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
230 if (sat_solver_->ModelIsUnsat())
return false;
234 const bool log_info = VLOG_IS_ON(2);
235 const bool log_round_info = VLOG_IS_ON(3);
236 const double start_dtime = time_limit_->GetElapsedDeterministicTime();
239 double probing_time = 0.0;
243 if (first_inprocessing_call_) {
244 reference_dtime_ = start_dtime;
245 first_inprocessing_call_ =
false;
253 const double diff = start_dtime - reference_dtime_;
254 if (total_dtime_ > params_.inprocessing_dtime_ratio() * diff) {
263 std::vector<std::pair<LinearProgrammingConstraint*, bool>> saved_state;
265 saved_state.push_back({lp, lp->PropagationIsEnabled()});
266 lp->EnablePropagation(
false);
268 auto cleanup = absl::MakeCleanup([&saved_state]() {
269 for (
const auto [lp, old_value] : saved_state) {
270 lp->EnablePropagation(old_value);
279 decision_policy_->MaybeEnablePhaseSaving(
false);
281 RETURN_IF_FALSE(implication_graph_->RemoveDuplicatesAndFixedVariables());
290 if (params_.inprocessing_probing_dtime() > 0.0 &&
291 sat_solver_->AssumptionLevel() == 0) {
292 const double saved_wtime = wall_timer.
Get();
294 probing_options.
log_info = log_round_info;
298 probing_time += wall_timer.
Get() - saved_wtime;
309 if (params_.inprocessing_minimization_dtime() > 0.0) {
311 log_round_info, params_.inprocessing_minimization_dtime()));
315 if (params_.inprocessing_use_congruence_closure()) {
317 RETURN_IF_FALSE(implication_graph_->RemoveDuplicatesAndFixedVariables());
328 blocked_clause_simplifier_->DoOneRound(log_round_info);
329 RETURN_IF_FALSE(bounded_variable_elimination_->DoOneRound(log_round_info));
333 if (params_.inprocessing_use_sat_sweeping()) {
334 auto inner_model_inprocessing = [&](
Model* inner_model) {
336 ->set_inprocessing_use_sat_sweeping(
false);
340 equivalence_sat_sweeping_->DoOneRound(inner_model_inprocessing));
343 sat_solver_->AdvanceDeterministicTime(time_limit_);
344 total_dtime_ += time_limit_->GetElapsedDeterministicTime() - start_dtime;
346 const int num_fixed = trail_->Index();
347 const int num_equiv = implication_graph_->num_current_equivalences() / 2;
350 logger_,
"Inprocessing.",
" fixed:",
FormatCounter(trail_->Index()),
352 FormatCounter(sat_solver_->NumVariables() - num_fixed - num_equiv),
354 FormatCounter(implication_graph_->ComputeNumImplicationsForLog()),
357 clause_manager_->num_removable_clauses()),
358 "|",
FormatCounter(clause_manager_->num_removable_clauses()),
"|",
359 FormatCounter(sat_solver_->counters().num_deleted_clauses),
"|",
361 " minimization:",
FormatCounter(vivifier_->last_num_vivified()),
"|",
363 " dtime:", time_limit_->GetElapsedDeterministicTime() - start_dtime,
364 " wtime:", wall_timer.
Get(),
365 " np_wtime:", (wall_timer.
Get() - probing_time));
368 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
369 decision_policy_->MaybeEnablePhaseSaving(
true);
373#undef RETURN_IF_FALSE
376 const int64_t new_num_fixed_variables = trail_->Index();
377 return last_num_fixed_variables_ < new_num_fixed_variables;
381 const int64_t new_num_redundant_literals =
382 implication_graph_->num_redundant_literals();
383 return last_num_redundant_literals_ < new_num_redundant_literals;
387 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
388 clause_manager_->AttachAllClauses();
389 if (!sat_solver_->Propagate()) {
391 sat_solver_->ProcessCurrentConflict();
402 implication_graph_->RemoveFixedVariables();
403 if (!implication_graph_->IsDag()) {
407 if (!implication_graph_->DetectEquivalences(log_info))
return false;
409 if (use_transitive_reduction) {
410 if (!implication_graph_->ComputeTransitiveReduction(log_info)) {
417 if (!stamping_simplifier_->ComputeStampsForNextRound(log_info))
return false;
427 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
434 int64_t num_removed_literals = 0;
435 int64_t num_inspected_literals = 0;
439 while (num_inspected_literals < 1e10) {
446 const int64_t new_num_redundant_literals =
447 implication_graph_->num_redundant_literals();
448 const int64_t new_num_fixed_variables = trail_->Index();
449 if (last_num_redundant_literals_ == new_num_redundant_literals &&
450 last_num_fixed_variables_ == new_num_fixed_variables) {
453 last_num_fixed_variables_ = new_num_fixed_variables;
454 last_num_redundant_literals_ = new_num_redundant_literals;
458 std::vector<Literal> new_clause;
461 const int num_literals(sat_solver_->NumVariables() * 2);
464 clause_manager_->DeleteRemovedClauses();
465 clause_manager_->DetachAllClauses();
466 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
467 bool removed =
false;
468 bool need_rewrite =
false;
471 for (
const Literal l : clause->AsSpan()) {
472 if (assignment_.LiteralIsTrue(l)) {
473 DCHECK(lrat_proof_handler_ ==
nullptr ||
474 trail_->GetUnitClauseId(l.Variable()) !=
kNoClauseId);
475 clause_manager_->LazyDelete(clause,
477 num_removed_literals += clause->size();
481 if (assignment_.LiteralIsFalse(l) ||
482 implication_graph_->IsRedundant(l)) {
488 num_inspected_literals += clause->size();
489 if (removed || !need_rewrite)
continue;
490 num_inspected_literals += clause->size();
495 for (
const Literal l : clause->AsSpan()) {
496 const Literal r = implication_graph_->RepresentativeOf(l);
497 if (lrat_proof_handler_ !=
nullptr) {
498 if (!marked[r] && assignment_.LiteralIsFalse(r)) {
499 clause_ids_.push_back(trail_->GetUnitClauseId(r.
Variable()));
502 clause_ids_.push_back(
503 implication_graph_->GetClauseId(l.Negated(), r));
506 if (marked[r] || assignment_.LiteralIsFalse(r)) {
509 if (marked[r.
NegatedIndex()] || assignment_.LiteralIsTrue(r)) {
510 clause_manager_->LazyDelete(
512 num_removed_literals += clause->size();
517 new_clause.push_back(r);
521 for (
const Literal l : new_clause) marked[l] =
false;
522 if (removed)
continue;
524 if (lrat_proof_handler_ !=
nullptr) {
525 clause_ids_.push_back(clause_manager_->GetClauseId(clause));
527 num_removed_literals += clause->size() - new_clause.size();
528 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause,
540 if (!implication_graph_->RemoveDuplicatesAndFixedVariables())
return false;
545 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
546 CHECK(!SomeLiteralAreAssigned(trail_->Assignment(), clause->AsSpan()));
551 const double dtime =
static_cast<double>(num_inspected_literals) * 1e-8;
552 time_limit_->AdvanceDeterministicTime(dtime);
553 LOG_IF(INFO, log_info) <<
"Cleanup. num_removed_literals: "
554 << num_removed_literals <<
" dtime: " << dtime
555 <<
" wtime: " << wall_timer.
Get();
569 int64_t num_subsumed_clauses = 0;
570 int64_t num_removed_literals = 0;
571 int64_t num_inspected_signatures = 0;
572 int64_t num_inspected_literals = 0;
576 std::vector<Literal> new_clause;
583 clause_manager_->DeleteRemovedClauses();
584 clause_manager_->DetachAllClauses();
588 std::vector<SatClause*> clauses =
589 clause_manager_->AllClausesInCreationOrder();
591 return a->
size() <
b->size();
595 const LiteralIndex num_literals(sat_solver_->NumVariables() * 2);
601 one_watcher(num_literals.value());
604 std::vector<uint64_t> signatures(clauses.size());
607 std::vector<std::pair<Literal, SatClause*>> candidates_for_removal;
608 for (
int clause_index = 0; clause_index < clauses.size(); ++clause_index) {
609 SatClause* clause = clauses[clause_index];
610 DCHECK(!SomeLiteralAreAssigned(trail_->Assignment(), clause->
AsSpan()));
616 if (num_inspected_literals + num_inspected_signatures > 1e9) {
631 uint64_t signature = 0;
634 marked.
Set(l.Index());
635 signature |= (uint64_t{1} << (l.Variable().value() % 64));
641 bool removed =
false;
642 candidates_for_removal.clear();
643 const uint64_t mask = ~signature;
645 num_inspected_signatures += one_watcher[l].size();
646 for (
const int i : one_watcher[l]) {
647 if ((mask & signatures[
i]) != 0)
continue;
649 bool subsumed =
true;
650 bool stengthen =
true;
652 num_inspected_literals += clauses[
i]->size();
653 for (
const Literal o : clauses[
i]->AsSpan()) {
657 to_remove = o.NegatedIndex();
665 ++num_subsumed_clauses;
666 num_removed_literals += clause->
size();
667 clause_manager_->LazyDelete(
674 candidates_for_removal.emplace_back(
Literal(to_remove), clauses[
i]);
679 if (removed)
continue;
683 num_inspected_signatures += one_watcher[l.NegatedIndex()].size();
684 for (
const int i : one_watcher[l.NegatedIndex()]) {
685 if ((mask & signatures[
i]) != 0)
continue;
687 bool stengthen =
true;
688 num_inspected_literals += clauses[
i]->size();
689 for (
const Literal o : clauses[
i]->AsSpan()) {
690 if (o == l.Negated())
continue;
697 candidates_for_removal.emplace_back(l, clauses[
i]);
708 if (!candidates_for_removal.empty()) {
711 if (l == candidates_for_removal[0].first)
continue;
712 new_clause.push_back(l);
714 CHECK_EQ(new_clause.size() + 1, clause->
size());
715 CHECK_GE(new_clause.size(), 2);
717 num_removed_literals += clause->
size() - new_clause.size();
718 if (lrat_proof_handler_ !=
nullptr) {
719 if (!clause_manager_->InprocessingRewriteClause(
721 {clause_manager_->GetClauseId(candidates_for_removal[0].second),
722 clause_manager_->GetClauseId(clause)})) {
725 }
else if (!clause_manager_->InprocessingRewriteClause(clause,
729 if (clause->
size() == 0)
continue;
734 signature |= (uint64_t{1} << (l.Variable().value() % 64));
751 if (!clause_manager_->IsRemovable(clause)) {
752 int min_size = std::numeric_limits<int32_t>::max();
755 if (one_watcher[l].size() < min_size) {
756 min_size = one_watcher[l].size();
757 min_literal = l.Index();
767 signatures[clause_index] = signature;
768 one_watcher[min_literal].
push_back(clause_index);
776 const double dtime =
static_cast<double>(num_inspected_signatures) * 1e-8 +
777 static_cast<double>(num_inspected_literals) * 5e-9;
778 time_limit_->AdvanceDeterministicTime(dtime);
779 LOG_IF(INFO, log_info) <<
"Subsume. num_removed_literals: "
780 << num_removed_literals
781 <<
" num_subsumed: " << num_subsumed_clauses
782 <<
" dtime: " << dtime
783 <<
" wtime: " << wall_timer.
Get();
792 num_subsumed_clauses_ = 0;
793 num_removed_literals_ = 0;
796 if (implication_graph_->IsEmpty())
return true;
798 if (!stamps_are_already_computed_) {
802 implication_graph_->RemoveFixedVariables();
803 if (!implication_graph_->DetectEquivalences(log_info))
return true;
807 stamps_are_already_computed_ =
false;
812 time_limit_->AdvanceDeterministicTime(dtime_);
813 LOG_IF(INFO, log_info) <<
"Stamping. num_removed_literals: "
814 << num_removed_literals_
815 <<
" num_subsumed: " << num_subsumed_clauses_
816 <<
" num_fixed: " << num_fixed_ <<
" dtime: " << dtime_
817 <<
" wtime: " << wall_timer.
Get();
827 if (implication_graph_->IsEmpty())
return true;
829 implication_graph_->RemoveFixedVariables();
830 if (!implication_graph_->DetectEquivalences(log_info))
return true;
833 stamps_are_already_computed_ =
true;
836 time_limit_->AdvanceDeterministicTime(dtime_);
837 LOG_IF(INFO, log_info) <<
"Prestamping."
838 <<
" num_fixed: " << num_fixed_ <<
" dtime: " << dtime_
839 <<
" wtime: " << wall_timer.
Get();
844 const int size = implication_graph_->literal_size();
845 CHECK(implication_graph_->IsDag());
846 parents_.resize(size);
847 for (LiteralIndex
i(0);
i < size; ++
i) {
849 if (implication_graph_->IsRedundant(
Literal(
i)))
continue;
850 if (assignment_.LiteralIsAssigned(
Literal(
i)))
continue;
860 for (
int num_tries = 0; num_tries < 10; ++num_tries) {
863 const LiteralIndex index =
864 implication_graph_->RandomImpliedLiteral(
Literal(
i).Negated());
868 if (implication_graph_->IsRedundant(candidate))
continue;
869 if (
i == candidate.
Index())
continue;
872 parents_[
i] = candidate.
Index();
879 const int size = implication_graph_->literal_size();
884 std::vector<LiteralIndex> children;
888 for (LiteralIndex
i(0);
i < size; ++
i) {
889 if (parents_[
i] ==
i)
continue;
890 sizes[parents_[
i]]++;
895 starts[LiteralIndex(0)] = 0;
896 for (LiteralIndex
i(1);
i <= size; ++
i) {
897 starts[
i] = starts[
i - 1] + sizes[
i - 1];
901 children.resize(size);
902 for (LiteralIndex
i(0);
i < size; ++
i) {
903 if (parents_[
i] ==
i)
continue;
904 children[starts[parents_[
i]]++] =
i;
908 for (LiteralIndex
i(0);
i < size; ++
i) {
909 starts[
i] -= sizes[
i];
913 CHECK_EQ(starts[LiteralIndex(0)], 0);
914 for (LiteralIndex
i(1);
i <= size; ++
i) {
915 CHECK_EQ(starts[
i], starts[
i - 1] + sizes[
i - 1]);
921 first_stamps_.resize(size);
922 last_stamps_.resize(size);
923 marked_.assign(size,
false);
924 for (LiteralIndex
i(0);
i < size; ++
i) {
925 if (parents_[
i] !=
i)
continue;
927 const LiteralIndex tree_root =
i;
928 dfs_stack_.push_back(
i);
929 while (!dfs_stack_.empty()) {
930 const LiteralIndex top = dfs_stack_.back();
932 dfs_stack_.pop_back();
933 last_stamps_[top] = stamp++;
936 first_stamps_[top] = stamp++;
941 if (marked_[
Literal(top).NegatedIndex()] &&
942 first_stamps_[
Literal(top).NegatedIndex()] >=
943 first_stamps_[tree_root]) {
946 LiteralIndex lca = top;
947 while (first_stamps_[lca] > first_stamp) {
951 if (lrat_proof_handler_ !=
nullptr) {
957 if (!clause_manager_->InprocessingFixLiteral(
Literal(lca).Negated(),
963 const int end = starts[top + 1];
964 for (
int j = starts[top]; j <
end; ++j) {
965 DCHECK_NE(top, children[j]);
966 DCHECK(!marked_[children[j]]);
967 dfs_stack_.push_back(children[j]);
971 DCHECK_EQ(stamp, 2 * size);
976class LratStampingHelper {
978 void NewClause(absl::Span<const Literal> clause) {
980 has_literals_to_remove_ =
false;
983 void AddToRemove(
int index,
int reason,
bool negated) {
984 if (!has_literals_to_remove_) {
985 has_literals_to_remove_ =
true;
986 status_.assign(clause_.size(), {0, 0, false, false});
988 status_[index].reason = reason;
989 status_[index].negated = negated;
990 status_[index].to_remove =
true;
993 void AppendImplicationChains(
994 absl::FunctionRef<
void(Literal, Literal,
bool)> append_chain) {
995 if (!has_literals_to_remove_)
return;
1000 for (
const Status& status : status_) {
1001 if (status.to_remove && status_[status.reason].to_remove) {
1002 status_[status.reason].num_children++;
1005 reverse_removal_order_.clear();
1006 for (
int i = 0;
i < status_.size(); ++
i) {
1007 if (status_[
i].to_remove && status_[
i].num_children == 0) {
1008 reverse_removal_order_.push_back(
i);
1011 int num_visited = 0;
1012 while (num_visited < reverse_removal_order_.size()) {
1013 int parent = status_[reverse_removal_order_[num_visited++]].reason;
1014 if (--status_[parent].num_children == 0) {
1015 reverse_removal_order_.push_back(parent);
1018 for (
int i = reverse_removal_order_.size() - 1;
i >= 0; --
i) {
1019 const int index = reverse_removal_order_[
i];
1020 const Status& status = status_[index];
1021 DCHECK(status.to_remove);
1022 if (status.negated) {
1023 append_chain(clause_[status.reason].Negated(), clause_[index].Negated(),
1026 append_chain(clause_[index], clause_[status.reason],
true);
1051 absl::Span<const Literal> clause_;
1052 bool has_literals_to_remove_;
1054 std::vector<Status> status_;
1056 std::vector<int> reverse_removal_order_;
1066 bool operator<(
const Entry& o)
const {
return start < o.start; }
1068 std::vector<int> to_remove;
1069 std::vector<Literal> new_clause;
1070 std::vector<Entry> entries;
1071 LratStampingHelper lrat_helper;
1073 clause_manager_->DeleteRemovedClauses();
1074 clause_manager_->DetachAllClauses();
1075 clause_ids_.clear();
1076 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
1077 const auto span = clause->AsSpan();
1078 if (span.empty())
continue;
1087 for (
int i = 0;
i < span.size(); ++
i) {
1088 if (assignment_.LiteralIsTrue(span[
i])) {
1089 clause_manager_->LazyDelete(clause,
1093 if (assignment_.LiteralIsFalse(span[
i]))
continue;
1095 {
i,
false, first_stamps_[span[
i]], last_stamps_[span[
i]]});
1096 entries.push_back({
i,
true, first_stamps_[span[
i].NegatedIndex()],
1097 last_stamps_[span[
i].NegatedIndex()]});
1099 if (clause->IsRemoved())
continue;
1102 if (!entries.empty()) {
1103 const double n =
static_cast<double>(entries.size());
1104 dtime_ += 1.5e-8 * n * std::log(n);
1105 std::sort(entries.begin(), entries.end());
1111 if (lrat_proof_handler_ !=
nullptr) {
1112 lrat_helper.NewClause(span);
1114 for (
const Entry& e : entries) {
1115 if (e.end < top_entry.end) {
1117 const Literal lhs = top_entry.is_negated ? span[top_entry.i].Negated()
1118 : span[top_entry.i];
1119 const Literal rhs = e.is_negated ? span[e.i].
Negated() : span[e.i];
1122 if (top_entry.is_negated != e.is_negated) {
1124 if (top_entry.i == e.i) {
1126 if (top_entry.is_negated) {
1129 if (lrat_proof_handler_ !=
nullptr) {
1130 clause_ids_.clear();
1131 AppendImplicationChain(lhs, rhs, clause_ids_);
1133 if (!clause_manager_->InprocessingFixLiteral(span[top_entry.i],
1139 if (lrat_proof_handler_ !=
nullptr) {
1140 clause_ids_.clear();
1141 AppendImplicationChain(lhs, rhs, clause_ids_);
1143 if (!clause_manager_->InprocessingFixLiteral(
1144 span[top_entry.i].Negated(), clause_ids_)) {
1147 to_remove.push_back(top_entry.i);
1156 if (top_entry.is_negated) {
1157 num_subsumed_clauses_++;
1158 clause_manager_->LazyDelete(
1163 CHECK_NE(top_entry.i, e.i);
1164 if (top_entry.is_negated) {
1166 to_remove.push_back(e.i);
1167 if (lrat_proof_handler_ !=
nullptr) {
1168 lrat_helper.AddToRemove(e.i, top_entry.i,
true);
1178 to_remove.push_back(top_entry.i);
1179 if (lrat_proof_handler_ !=
nullptr) {
1180 lrat_helper.AddToRemove(top_entry.i, e.i,
false);
1189 if (clause->IsRemoved())
continue;
1192 if (!to_remove.empty() || entries.size() < span.size()) {
1195 clause_ids_.clear();
1196 int to_remove_index = 0;
1197 for (
int i = 0;
i < span.size(); ++
i) {
1198 if (to_remove_index < to_remove.size() &&
1199 i == to_remove[to_remove_index]) {
1203 if (assignment_.LiteralIsTrue(span[
i])) {
1204 clause_manager_->LazyDelete(clause,
1208 if (assignment_.LiteralIsFalse(span[
i])) {
1209 if (lrat_proof_handler_ !=
nullptr) {
1210 clause_ids_.push_back(trail_->GetUnitClauseId(span[
i].Variable()));
1214 new_clause.push_back(span[
i]);
1216 if (lrat_proof_handler_ !=
nullptr) {
1217 lrat_helper.AppendImplicationChains(
1219 AppendImplicationChain(a,
b, clause_ids_, reversed);
1221 clause_ids_.push_back(clause_manager_->GetClauseId(clause));
1223 num_removed_literals_ += span.size() - new_clause.size();
1224 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause,
1234 std::vector<ClauseId>& chain,
1236 const int initial_size = chain.size();
1244 std::reverse(clause_ids_.begin() + initial_size, clause_ids_.end());
1253 num_blocked_clauses_ = 0;
1254 num_inspected_literals_ = 0;
1256 InitializeForNewRound();
1258 while (!time_limit_->LimitReached() && !queue_.empty()) {
1259 const Literal l = queue_.front();
1260 in_queue_[l] =
false;
1265 if (num_inspected_literals_ <= 1e9) ProcessLiteral(l);
1269 literal_to_clauses_.clear();
1271 dtime_ += 1e-8 * num_inspected_literals_;
1272 time_limit_->AdvanceDeterministicTime(dtime_);
1273 log_info |= VLOG_IS_ON(2);
1274 LOG_IF(INFO, log_info) <<
"Blocked clause. num_blocked_clauses: "
1275 << num_blocked_clauses_ <<
" dtime: " << dtime_
1276 <<
" wtime: " << wall_timer.
Get();
1279void BlockedClauseSimplifier::InitializeForNewRound() {
1287 clauses_.push_back(c);
1289 const int num_literals = clause_manager_->
literal_size();
1293 in_queue_.
assign(num_literals,
true);
1294 for (LiteralIndex l(0); l < num_literals; ++l) {
1295 queue_.push_back(Literal(l));
1298 marked_.
resize(num_literals);
1300 std::all_of(marked_.begin(), marked_.end(), [](
bool b) { return !b; }));
1304 literal_to_clauses_.clear();
1305 literal_to_clauses_.
resize(num_literals);
1306 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
1307 for (
const Literal l : clauses_[
i]->AsSpan()) {
1310 num_inspected_literals_ += clauses_[
i]->size();
1314void BlockedClauseSimplifier::ProcessLiteral(
Literal current_literal) {
1315 if (assignment_.LiteralIsAssigned(current_literal))
return;
1316 if (implication_graph_->IsRemoved(current_literal))
return;
1331 const std::vector<Literal>& implications =
1332 implication_graph_->DirectImplications(current_literal);
1333 for (
const Literal l : implications) {
1334 if (l == current_literal)
continue;
1342 std::vector<ClauseIndex> clauses_to_process;
1343 for (
const ClauseIndex
i : literal_to_clauses_[current_literal]) {
1344 if (clauses_[
i]->IsRemoved())
continue;
1350 if (num_binary > 0) {
1351 if (clauses_[
i]->size() <= num_binary)
continue;
1352 int num_with_negation_marked = 0;
1353 for (
const Literal l : clauses_[
i]->AsSpan()) {
1354 if (l == current_literal)
continue;
1355 if (marked_[l.NegatedIndex()]) {
1356 ++num_with_negation_marked;
1359 num_inspected_literals_ += clauses_[
i]->size();
1360 if (num_with_negation_marked < num_binary)
continue;
1362 clauses_to_process.push_back(
i);
1366 for (
const Literal l : implications) {
1378 for (
const ClauseIndex
i : clauses_to_process) {
1379 const auto c = clauses_[
i]->AsSpan();
1380 if (ClauseIsBlocked(current_literal, c)) {
1387 for (
const Literal l : c) {
1388 if (!in_queue_[l.NegatedIndex()]) {
1389 in_queue_[l.NegatedIndex()] =
true;
1390 queue_.push_back(l.Negated());
1395 postsolve_->AddClauseWithSpecialLiteral(current_literal, c);
1398 ++num_blocked_clauses_;
1405bool BlockedClauseSimplifier::ClauseIsBlocked(
1406 Literal current_literal, absl::Span<const Literal> clause) {
1407 bool is_blocked =
true;
1408 for (
const Literal l : clause) marked_[l] =
true;
1412 for (
const ClauseIndex
i :
1413 literal_to_clauses_[current_literal.NegatedIndex()]) {
1414 if (clauses_[
i]->IsRemoved())
continue;
1415 bool some_marked =
false;
1416 for (
const Literal l : clauses_[
i]->AsSpan()) {
1418 ++num_inspected_literals_;
1420 if (l == current_literal.Negated())
continue;
1421 if (marked_[l.NegatedIndex()]) {
1432 for (
const Literal l : clause) marked_[l] =
false;
1441 num_inspected_literals_ = 0;
1442 num_eliminated_variables_ = 0;
1443 num_literals_diff_ = 0;
1444 num_clauses_diff_ = 0;
1445 num_simplifications_ = 0;
1446 num_blocked_clauses_ = 0;
1449 clause_manager_->DeleteRemovedClauses();
1450 clause_manager_->DetachAllClauses();
1451 for (
SatClause* c : clause_manager_->AllClausesInCreationOrder()) {
1455 if (clause_manager_->IsRemovable(c))
continue;
1457 clauses_.push_back(c);
1459 const int num_literals = clause_manager_->literal_size();
1460 const int num_variables = num_literals / 2;
1462 literal_to_clauses_.clear();
1463 literal_to_clauses_.resize(num_literals);
1464 literal_to_num_clauses_.assign(num_literals, 0);
1465 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
1466 for (
const Literal l : clauses_[
i]->AsSpan()) {
1467 literal_to_clauses_[l].push_back(
i);
1468 literal_to_num_clauses_[l]++;
1470 num_inspected_literals_ += clauses_[
i]->size();
1473 const int saved_trail_index = trail_->Index();
1474 propagation_index_ = trail_->Index();
1476 need_to_be_updated_.clear();
1477 in_need_to_be_updated_.resize(num_variables);
1478 DCHECK(absl::c_find(in_need_to_be_updated_,
true) ==
1479 in_need_to_be_updated_.end());
1480 queue_.Reserve(num_variables);
1481 for (BooleanVariable v(0); v < num_variables; ++v) {
1482 if (assignment_.VariableIsAssigned(v))
continue;
1483 UpdatePriorityQueue(v);
1486 marked_.resize(num_literals);
1488 std::all_of(marked_.begin(), marked_.end(), [](
bool b) { return !b; }));
1492 while (!time_limit_->LimitReached() && !queue_.IsEmpty() && dtime_ < 10.0) {
1493 const BooleanVariable top = queue_.Top().var;
1501 bool is_unsat =
false;
1502 if (!Propagate())
return false;
1503 while (implication_graph_->FindFailedLiteralAroundVar(top, &is_unsat)) {
1504 if (!Propagate())
return false;
1506 if (is_unsat)
return false;
1508 if (!CrossProduct(top))
return false;
1510 for (
const BooleanVariable v : need_to_be_updated_) {
1511 in_need_to_be_updated_[v] =
false;
1514 if (v != top) UpdatePriorityQueue(v);
1516 need_to_be_updated_.clear();
1519 if (!Propagate())
return false;
1520 implication_graph_->CleanupAllRemovedAndFixedVariables();
1524 for (
SatClause* c : clause_manager_->AllClausesInCreationOrder()) {
1525 bool remove =
false;
1526 for (
const Literal l : c->AsSpan()) {
1527 if (implication_graph_->IsRemoved(l)) {
1537 literal_to_clauses_.clear();
1538 literal_to_num_clauses_.clear();
1540 time_limit_->AdvanceDeterministicTime(dtime_);
1541 log_info |= VLOG_IS_ON(2);
1542 LOG_IF(INFO, log_info) <<
"BVE."
1544 << trail_->Index() - saved_trail_index
1545 <<
" num_simplified_literals: " << num_simplifications_
1546 <<
" num_blocked_clauses_: " << num_blocked_clauses_
1547 <<
" num_eliminations: " << num_eliminated_variables_
1548 <<
" num_literals_diff: " << num_literals_diff_
1549 <<
" num_clause_diff: " << num_clauses_diff_
1550 <<
" dtime: " << dtime_
1551 <<
" wtime: " << wall_timer.
Get();
1555bool BoundedVariableElimination::RemoveLiteralFromClause(
1557 num_literals_diff_ -= sat_clause->
size();
1561 literal_to_num_clauses_[l]--;
1565 num_clauses_diff_--;
1570 resolvant_.push_back(l);
1572 if (!clause_manager_->InprocessingRewriteClause(sat_clause, resolvant_)) {
1576 --num_clauses_diff_;
1577 for (
const Literal l : resolvant_) literal_to_num_clauses_[l]--;
1579 num_literals_diff_ += sat_clause->
size();
1584bool BoundedVariableElimination::Propagate() {
1585 for (; propagation_index_ < trail_->Index(); ++propagation_index_) {
1587 if (!implication_graph_->Propagate(trail_))
return false;
1589 const Literal l = (*trail_)[propagation_index_];
1590 for (
const ClauseIndex index : literal_to_clauses_[l]) {
1591 if (clauses_[index]->IsRemoved())
continue;
1592 num_clauses_diff_--;
1593 num_literals_diff_ -= clauses_[index]->size();
1594 clause_manager_->LazyDelete(clauses_[index],
1597 literal_to_clauses_[l].clear();
1598 for (
const ClauseIndex index : literal_to_clauses_[l.NegatedIndex()]) {
1599 if (clauses_[index]->IsRemoved())
continue;
1600 if (!RemoveLiteralFromClause(l.Negated(), clauses_[index]))
return false;
1602 literal_to_clauses_[l.NegatedIndex()].clear();
1609int BoundedVariableElimination::NumClausesContaining(
Literal l) {
1610 return literal_to_num_clauses_[l] +
1611 implication_graph_->DirectImplicationsEstimatedSize(l.Negated());
1615void BoundedVariableElimination::UpdatePriorityQueue(BooleanVariable var) {
1616 if (assignment_.VariableIsAssigned(var))
return;
1617 if (implication_graph_->IsRemoved(Literal(var,
true)))
return;
1618 if (implication_graph_->IsRedundant(Literal(var,
true)))
return;
1619 const int priority = -NumClausesContaining(Literal(var,
true)) -
1620 NumClausesContaining(Literal(var,
false));
1621 if (queue_.Contains(var.value())) {
1622 queue_.ChangePriority({var, priority});
1624 queue_.Add({var, priority});
1628void BoundedVariableElimination::DeleteClause(
SatClause* sat_clause) {
1629 const auto clause = sat_clause->AsSpan();
1631 num_clauses_diff_--;
1632 num_literals_diff_ -= clause.size();
1635 for (
const Literal l : clause) {
1636 literal_to_num_clauses_[l]--;
1637 if (!in_need_to_be_updated_[l.Variable()]) {
1638 in_need_to_be_updated_[l.Variable()] =
true;
1639 need_to_be_updated_.push_back(l.Variable());
1647void BoundedVariableElimination::DeleteAllClausesContaining(
Literal literal) {
1648 for (
const ClauseIndex
i : literal_to_clauses_[literal]) {
1649 const auto clause = clauses_[
i]->AsSpan();
1650 if (clause.empty())
continue;
1651 postsolve_->AddClauseWithSpecialLiteral(literal, clause);
1652 DeleteClause(clauses_[
i]);
1654 literal_to_clauses_[literal].clear();
1657void BoundedVariableElimination::AddClause(absl::Span<const Literal> clause) {
1658 SatClause* pt = clause_manager_->InprocessingAddClause(clause);
1659 if (pt ==
nullptr)
return;
1661 num_clauses_diff_++;
1662 num_literals_diff_ += clause.size();
1664 const ClauseIndex index(clauses_.size());
1665 clauses_.push_back(pt);
1666 for (
const Literal l : clause) {
1667 literal_to_num_clauses_[l]++;
1668 literal_to_clauses_[l].push_back(index);
1669 if (!in_need_to_be_updated_[l.Variable()]) {
1670 in_need_to_be_updated_[l.Variable()] =
true;
1671 need_to_be_updated_.push_back(l.Variable());
1676template <
bool score_only,
bool with_binary_only>
1677bool BoundedVariableElimination::ResolveAllClauseContaining(
Literal lit) {
1678 const int clause_weight = parameters_.presolve_bve_clause_weight();
1680 const std::vector<Literal>& implications =
1681 implication_graph_->DirectImplications(lit);
1682 auto& clause_containing_lit = literal_to_clauses_[lit];
1683 for (
int i = 0;
i < clause_containing_lit.size(); ++
i) {
1684 const ClauseIndex clause_index = clause_containing_lit[
i];
1685 const auto clause = clauses_[clause_index]->AsSpan();
1686 if (clause.empty())
continue;
1688 if (!score_only) resolvant_.clear();
1689 for (
const Literal l : clause) {
1690 if (!score_only && l != lit) resolvant_.push_back(l);
1693 DCHECK(marked_[lit]);
1694 num_inspected_literals_ += clause.size() + implications.size();
1698 bool clause_can_be_simplified =
false;
1699 const int64_t saved_score = new_score_;
1702 for (
const Literal l : implications) {
1704 if (marked_[l.NegatedIndex()])
continue;
1706 clause_can_be_simplified =
true;
1710 new_score_ += clause_weight + clause.size();
1712 resolvant_.push_back(l);
1713 AddClause(resolvant_);
1714 resolvant_.pop_back();
1720 if (!with_binary_only && !clause_can_be_simplified) {
1721 auto& clause_containing_not_lit = literal_to_clauses_[lit.NegatedIndex()];
1722 for (
int j = 0; j < clause_containing_not_lit.size(); ++j) {
1723 if (score_only && new_score_ > score_threshold_)
break;
1724 const ClauseIndex other_index = clause_containing_not_lit[j];
1725 const auto other = clauses_[other_index]->AsSpan();
1726 if (other.empty())
continue;
1727 bool trivial =
false;
1729 for (
const Literal l : other) {
1731 ++num_inspected_literals_;
1732 if (l == lit.Negated())
continue;
1733 if (marked_[l.NegatedIndex()]) {
1739 if (!score_only) resolvant_.push_back(l);
1743 if (!score_only) resolvant_.resize(resolvant_.size() - extra_size);
1749 if (score_only && clause.size() + extra_size <= other.size()) {
1755 if (
false) DCHECK_EQ(clause.size() + extra_size, other.size());
1756 ++num_simplifications_;
1760 score_threshold_ -= clause_weight + other.size();
1762 if (extra_size == 0) {
1766 DeleteClause(clauses_[other_index]);
1768 if (!RemoveLiteralFromClause(lit.Negated(),
1769 clauses_[other_index])) {
1772 std::swap(clause_containing_not_lit[j],
1773 clause_containing_not_lit.back());
1774 clause_containing_not_lit.pop_back();
1780 if (extra_size == 0) {
1781 clause_can_be_simplified =
true;
1786 if (clause.size() - 1 + extra_size > 100) {
1787 new_score_ = score_threshold_ + 1;
1791 new_score_ += clause_weight + clause.size() - 1 + extra_size;
1793 AddClause(resolvant_);
1794 resolvant_.resize(resolvant_.size() - extra_size);
1801 for (
const Literal l : clause) marked_[l] =
false;
1804 if (clause_can_be_simplified) {
1805 ++num_simplifications_;
1808 new_score_ = saved_score;
1809 score_threshold_ -= clause_weight + clause.size();
1811 if (!RemoveLiteralFromClause(lit, clauses_[clause_index]))
return false;
1812 std::swap(clause_containing_lit[
i], clause_containing_lit.back());
1813 clause_containing_lit.pop_back();
1817 if (score_only && new_score_ > score_threshold_)
return true;
1829 if (score_only && !with_binary_only && !clause_can_be_simplified &&
1830 new_score_ == saved_score) {
1831 ++num_blocked_clauses_;
1832 score_threshold_ -= clause_weight + clause.size();
1833 postsolve_->AddClauseWithSpecialLiteral(lit, clause);
1834 DeleteClause(clauses_[clause_index]);
1840bool BoundedVariableElimination::CrossProduct(BooleanVariable var) {
1841 if (assignment_.VariableIsAssigned(var))
return true;
1843 const Literal lit(var,
true);
1844 const Literal not_lit(var,
false);
1845 DCHECK(!implication_graph_->IsRedundant(lit));
1847 const int s1 = NumClausesContaining(lit);
1848 const int s2 = NumClausesContaining(not_lit);
1849 if (s1 == 0 && s2 == 0)
return true;
1850 if (s1 > 0 && s2 == 0) {
1851 num_eliminated_variables_++;
1852 if (!clause_manager_->InprocessingFixLiteral(lit))
return false;
1853 DeleteAllClausesContaining(lit);
1856 if (s1 == 0 && s2 > 0) {
1857 num_eliminated_variables_++;
1858 if (!clause_manager_->InprocessingFixLiteral(not_lit))
return false;
1859 DeleteAllClausesContaining(not_lit);
1865 if (s1 > 1 && s2 > 1 && s1 * s2 > parameters_.presolve_bve_threshold()) {
1878 const int clause_weight = parameters_.presolve_bve_clause_weight();
1880 implication_graph_->DirectImplications(lit).size() * (clause_weight + 2);
1881 score += implication_graph_->DirectImplications(not_lit).size() *
1882 (clause_weight + 2);
1883 for (
const ClauseIndex
i : literal_to_clauses_[lit]) {
1884 const auto c = clauses_[
i]->AsSpan();
1885 if (!
c.empty()) score += clause_weight +
c.size();
1886 dtime_ += 1e-8 *
c.size();
1888 for (
const ClauseIndex
i : literal_to_clauses_[not_lit]) {
1889 const auto c = clauses_[
i]->AsSpan();
1890 if (!
c.empty()) score += clause_weight +
c.size();
1891 dtime_ += 1.0e-8 *
c.size();
1902 score_threshold_ = score;
1903 new_score_ = implication_graph_->NumImplicationOnVariableRemoval(var) *
1904 (clause_weight + 2);
1905 if (new_score_ > score_threshold_)
return true;
1906 if (!ResolveAllClauseContaining<
true,
1910 if (new_score_ > score_threshold_)
return true;
1911 if (!ResolveAllClauseContaining<
true,
1915 if (new_score_ > score_threshold_)
return true;
1921 if (new_score_ > 0) {
1922 if (!ResolveAllClauseContaining<
false,
1926 if (!ResolveAllClauseContaining<
false,
1932 ++num_eliminated_variables_;
1933 implication_graph_->RemoveBooleanVariable(var, &postsolve_->clauses);
1934 DeleteAllClausesContaining(lit);
1935 DeleteAllClausesContaining(not_lit);
1940 if (!VLOG_IS_ON(1))
return;
1941 shared_stats_->AddStats({
1942 {
"GateCongruenceClosure/dtime(int)",
static_cast<int64_t
>(total_dtime_)},
1943 {
"GateCongruenceClosure/walltime(int)",
1944 static_cast<int64_t
>(total_wtime_)},
1945 {
"GateCongruenceClosure/gates", total_gates_},
1946 {
"GateCongruenceClosure/units", total_num_units_},
1947 {
"GateCongruenceClosure/equivalences", total_equivalences_},
1952void GateCongruenceClosure::AddToTruthTable(
1954 absl::flat_hash_map<std::array<BooleanVariable, arity>, TruthTableId>&
1956 CHECK_EQ(clause->
size(), arity);
1957 std::array<BooleanVariable, arity> key;
1961 TruthTableId next_id(truth_tables_bitset_.size());
1962 auto [it, inserted] = ids.insert({key, next_id});
1963 const TruthTableId
id = it->second;
1965 truth_tables_inputs_.
Add(key);
1966 truth_tables_bitset_.
push_back(bitmask);
1967 if (lrat_proof_handler_ !=
nullptr) {
1968 tmp_ids_.push_back(
id);
1969 tmp_clauses_.push_back(clause);
1975 truth_tables_bitset_[id] &= bitmask;
1976 if (lrat_proof_handler_ !=
nullptr && old != truth_tables_bitset_[
id]) {
1977 tmp_ids_.push_back(
id);
1978 tmp_clauses_.push_back(clause);
1987void AppendBinaryClausesFromTruthTable(
1988 absl::Span<const BooleanVariable> vars,
SmallBitset table,
1989 std::vector<std::pair<Literal, Literal>>* binary_used) {
1990 DCHECK_EQ(vars.size(), 2);
1991 for (
int b = 0;
b < 4; ++
b) {
1992 if (((table >>
b) & 1) == 0) {
1993 binary_used->emplace_back(
Literal(vars[0], (
b & 1) == 0),
2003void GateCongruenceClosure::ExtractAndGatesAndFillShortTruthTables(
2004 PresolveTimer& timer) {
2008 truth_tables_inputs_.clear();
2009 truth_tables_bitset_.clear();
2010 truth_tables_clauses_.clear();
2012 tmp_clauses_.clear();
2022 std::vector<std::pair<Literal, Literal>> binary_used;
2023 for (LiteralIndex a(0); a < implication_graph_->literal_size(); ++a) {
2026 if (timer.WorkLimitIsReached())
break;
2027 if (implication_graph_->IsRedundant(Literal(a)))
continue;
2028 const absl::Span<const Literal> implied =
2029 implication_graph_->Implications(Literal(a));
2030 timer.TrackHashLookups(implied.size());
2031 for (
const Literal
b : implied) {
2032 if (implication_graph_->IsRedundant(
b))
continue;
2034 std::array<BooleanVariable, 2> key2;
2038 auto [it, inserted] = ids2_.insert({key2, bitmask});
2041 it->second &= bitmask;
2042 if (it->second != old) {
2049 if (lrat_proof_handler_ !=
nullptr) {
2050 binary_used.clear();
2051 AppendBinaryClausesFromTruthTable(key2, bitset2, &binary_used);
2055 const int num_added =
2056 ProcessTruthTable(key2, bitset2, {}, binary_used);
2057 CHECK_GE(num_added, 1) << std::bitset<4>(bitset2);
2058 num_fn1 += num_added;
2063 timer.AddCounter(
"t2", ids2_.size());
2064 timer.AddCounter(
"fn1", num_fn1);
2066 std::vector<Literal> candidates;
2067 for (SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
2068 if (timer.WorkLimitIsReached())
break;
2069 if (clause->
size() == 0)
continue;
2071 if (clause->
size() == 3) {
2072 AddToTruthTable<3>(clause, ids3_);
2077 }
else if (clause->
size() == 4) {
2078 AddToTruthTable<4>(clause, ids4_);
2079 }
else if (clause->
size() == 5) {
2080 AddToTruthTable<5>(clause, ids5_);
2084 int min_num_implications = std::numeric_limits<int>::max();
2085 Literal lit_with_less_implications;
2087 const int clause_size = clause->
size();
2088 timer.TrackSimpleLoop(clause_size);
2090 for (
const Literal l : clause->
AsSpan()) {
2095 const int num_implications = implication_graph_->Implications(l).size();
2096 if (num_implications < min_num_implications) {
2097 min_num_implications = num_implications;
2098 lit_with_less_implications = l;
2101 if (num_implications >= clause_size - 1) {
2102 candidates.push_back(l);
2105 if (candidates.empty())
continue;
2106 if (min_num_implications == 0)
continue;
2108 marked_.ResetAllToFalse();
2109 for (
const Literal l : clause->
AsSpan()) marked_.Set(l);
2110 const auto is_clause_literal = marked_.const_view();
2114 CHECK_EQ(marked_.PositionsSetAtLeastOnce().size(), clause_size);
2132 auto* is_potential_target = &seen_;
2133 auto* next_is_potential_target = &next_seen_;
2138 is_potential_target->ResetAllToFalse();
2139 is_potential_target->Set(lit_with_less_implications);
2140 const absl::Span<const Literal> implications =
2141 implication_graph_->Implications(lit_with_less_implications);
2142 timer.TrackFastLoop(implications.size());
2143 for (
const Literal implied : implications) {
2144 is_potential_target->Set(implied.Negated());
2148 for (
const Literal target : candidates) {
2149 if (!(*is_potential_target)[target])
continue;
2152 next_is_potential_target->ResetAllToFalse();
2153 const absl::Span<const Literal> implications =
2154 implication_graph_->Implications(target);
2155 timer.TrackFastLoop(implications.size());
2156 for (
const Literal implied : implications) {
2157 CHECK_NE(implied.Variable(), target.Variable());
2159 if (is_clause_literal[implied.Negated()]) {
2162 if ((*is_potential_target)[implied.Negated()]) {
2163 next_is_potential_target->Set(implied.Negated());
2168 std::swap(is_potential_target, next_is_potential_target);
2171 if (count < clause_size - 1)
continue;
2176 int second_count = 0;
2177 for (
const Literal implied : implications) {
2178 if (implied.Variable() == target.Variable())
continue;
2179 if (is_clause_literal[implied.Negated()]) {
2181 marked_.Clear(implied.Negated());
2186 for (
const Literal l : clause->
AsSpan()) {
2189 if (second_count != clause_size - 1)
continue;
2194 gates_target_.push_back(target.Index());
2195 gates_type_.push_back(kAndGateType);
2197 const GateId gate_id = GateId(gates_inputs_.Add({}));
2198 for (
const Literal l : clause->
AsSpan()) {
2199 if (l == target)
continue;
2200 gates_inputs_.AppendToLastVector(l.NegatedIndex());
2202 if (lrat_proof_handler_ !=
nullptr) {
2203 gates_clauses_.Add({clause});
2206 for (
const Literal l : clause->
AsSpan()) {
2207 if (l == target)
continue;
2208 tmp_binary_clauses_.emplace_back(
2210 gates_clauses_.AppendToLastVector(tmp_binary_clauses_.back().get());
2215 absl::Span<LiteralIndex> gate = gates_inputs_[gate_id];
2216 std::sort(gate.begin(), gate.end());
2224 timer.AddCounter(
"and_gates", gates_inputs_.size());
2227int GateCongruenceClosure::CanonicalizeShortGate(GateId
id) {
2229 absl::Span<LiteralIndex> inputs = gates_inputs_[id];
2230 int new_size = inputs.size();
2232 for (
int i = 0;
i < new_size;) {
2233 if (assignment_.LiteralIsAssigned(Literal(inputs[
i]))) {
2236 inputs.subspan(0, new_size), gates_type_[
id]);
2244 gates_target_[
id], inputs.subspan(0, new_size), gates_type_[
id]);
2247 if (new_size < gates_inputs_[
id].size()) {
2248 gates_inputs_.Shrink(
id, new_size);
2250 DCHECK_EQ(gates_type_[
id] >> (1 << (gates_inputs_[
id].size())), 0);
2254int GateCongruenceClosure::ProcessTruthTable(
2255 absl::Span<const BooleanVariable> inputs,
SmallBitset truth_table,
2256 absl::Span<const TruthTableId> ids_for_proof,
2257 absl::Span<
const std::pair<Literal, Literal>> binary_used) {
2258 int num_detected = 0;
2259 for (
int i = 0;
i < inputs.size(); ++
i) {
2260 if (!
IsFunction(
i, inputs.size(), truth_table))
continue;
2261 const int num_bits = inputs.size() - 1;
2264 const GateId new_id(gates_target_.size());
2265 gates_target_.push_back(Literal(inputs[
i],
true));
2266 gates_inputs_.Add({});
2267 for (
int j = 0; j < inputs.size(); ++j) {
2269 gates_inputs_.AppendToLastVector(Literal(inputs[j],
true));
2275 unsigned int type = 0;
2276 for (
int p = 0; p < (1 << num_bits); ++p) {
2281 if ((truth_table >> (bigger_p + (1 <<
i))) & 1) {
2284 DCHECK_NE((truth_table >> bigger_p) & 1, 1);
2298 gates_type_.push_back(type);
2299 if (lrat_proof_handler_ !=
nullptr) {
2300 gates_clauses_.Add({});
2301 for (
const TruthTableId
id : ids_for_proof) {
2302 gates_clauses_.AppendToLastVector(truth_tables_clauses_[
id]);
2304 for (
const auto [a,
b] : binary_used) {
2306 gates_clauses_.AppendToLastVector(tmp_binary_clauses_.back().get());
2311 CanonicalizeShortGate(new_id);
2313 return num_detected;
2319BooleanVariable FindMissing(absl::Span<const BooleanVariable> vars_a,
2320 absl::Span<const BooleanVariable> vars_b) {
2321 for (
const BooleanVariable
b : vars_b) {
2323 for (
const BooleanVariable a : vars_a) {
2329 if (!found)
return b;
2338void GateCongruenceClosure::ExtractShortGates(PresolveTimer& timer) {
2339 if (lrat_proof_handler_ !=
nullptr) {
2340 truth_tables_clauses_.ResetFromFlatMapping(
2341 tmp_ids_, tmp_clauses_,
2342 truth_tables_bitset_.size());
2343 CHECK_EQ(truth_tables_bitset_.size(), truth_tables_clauses_.size());
2348 absl::flat_hash_map<std::array<BooleanVariable, 2>,
int> binary_index_map;
2349 std::vector<int> flat_binary_index;
2350 std::vector<TruthTableId> flat_table_id;
2354 std::vector<int> num_tables(6);
2355 std::vector<int> num_functions(6);
2359 CHECK_EQ(truth_tables_bitset_.size(), truth_tables_inputs_.size());
2364 const auto merge3_into_4 = [
this, &num_merges](
2365 absl::Span<const BooleanVariable> inputs,
2367 std::vector<TruthTableId>& ids_for_proof) {
2368 DCHECK_EQ(inputs.size(), 4);
2369 for (
int i_to_remove = 0; i_to_remove < inputs.size(); ++i_to_remove) {
2371 std::array<BooleanVariable, 3> key3;
2372 for (
int i = 0;
i < inputs.size(); ++
i) {
2373 if (
i == i_to_remove)
continue;
2374 key3[pos++] = inputs[
i];
2376 const auto it = ids3_.find(key3);
2377 if (it == ids3_.end())
continue;
2382 const TruthTableId id3 = it->second;
2383 std::array<BooleanVariable, 4> key4;
2384 for (
int i = 0;
i < 3; ++
i) key4[
i] = key3[
i];
2385 key4[3] = FindMissing(key3, inputs);
2387 bitset |= bitset << (1 << 3);
2389 CHECK_EQ(inputs, absl::MakeSpan(key4));
2390 truth_table &= bitset;
2393 if (lrat_proof_handler_ !=
nullptr) {
2394 ids_for_proof.push_back(id3);
2399 int num_merges2 = 0;
2400 const auto merge2_into_n =
2401 [
this, &num_merges2](
2402 absl::Span<const BooleanVariable> inputs,
SmallBitset& truth_table,
2403 std::vector<std::pair<Literal, Literal>>& binary_used) {
2404 for (
int i = 0;
i < inputs.size(); ++
i) {
2405 for (
int j =
i + 1; j < inputs.size(); ++j) {
2406 std::array<BooleanVariable, 2> key2 = {inputs[
i], inputs[j]};
2407 const auto it = ids2_.find(key2);
2408 if (it == ids2_.end())
continue;
2413 std::vector<BooleanVariable> key(inputs.size());
2414 key[new_size++] = inputs[
i];
2415 key[new_size++] = inputs[j];
2416 for (
int t = 0; t < inputs.size(); ++t) {
2417 if (t !=
i && t != j) {
2418 key[new_size] = inputs[t];
2419 bitset |= bitset << (1 << new_size);
2425 CHECK_EQ(inputs, absl::MakeSpan(key));
2428 truth_table &= bitset;
2429 if (old != truth_table) {
2430 AppendBinaryClausesFromTruthTable(key2, bitset2, &binary_used);
2443 std::vector<TruthTableId> ids_for_proof;
2444 std::vector<std::pair<Literal, Literal>> binary_used;
2445 for (TruthTableId t_id(0); t_id < truth_tables_inputs_.size(); ++t_id) {
2446 ids_for_proof.clear();
2447 ids_for_proof.push_back(t_id);
2448 const absl::Span<const BooleanVariable> inputs = truth_tables_inputs_[t_id];
2449 SmallBitset truth_table = truth_tables_bitset_[t_id];
2454 binary_used.clear();
2455 merge2_into_n(inputs, truth_table, binary_used);
2459 if (inputs.size() == 4) {
2460 merge3_into_4(inputs, truth_table, ids_for_proof);
2463 ++num_tables[inputs.size()];
2464 const int num_detected =
2465 ProcessTruthTable(inputs, truth_table, ids_for_proof, binary_used);
2466 num_functions[inputs.size() - 1] += num_detected;
2470 if (inputs.size() == 3 && num_detected == 0) {
2471 for (
int i = 0;
i < 3; ++
i) {
2472 std::array<BooleanVariable, 2> key{inputs[
i != 0 ? 0 : 1],
2473 inputs[
i != 2 ? 2 : 1]};
2474 DCHECK(std::is_sorted(key.begin(), key.end()));
2475 const auto [it, inserted] =
2476 binary_index_map.insert({key, binary_index_map.size()});
2477 flat_binary_index.push_back(it->second);
2478 flat_table_id.push_back(t_id);
2487 CompactVectorVector<int, TruthTableId> candidates;
2488 candidates.ResetFromFlatMapping(flat_binary_index, flat_table_id);
2491 int num_combinations = 0;
2492 for (
int c = 0;
c < candidates.size(); ++
c) {
2493 if (candidates[c].size() < 2)
continue;
2494 if (candidates[c].size() > 10)
continue;
2496 for (
int a = 0; a < candidates[
c].size(); ++a) {
2497 for (
int b = a + 1;
b < candidates[
c].size(); ++
b) {
2498 const absl::Span<const BooleanVariable> inputs_a =
2499 truth_tables_inputs_[candidates[
c][a]];
2500 const absl::Span<const BooleanVariable> inputs_b =
2501 truth_tables_inputs_[candidates[
c][
b]];
2503 std::array<BooleanVariable, 4> key;
2504 for (
int i = 0;
i < 3; ++
i) key[
i] = inputs_a[
i];
2505 key[3] = FindMissing(inputs_a, inputs_b);
2518 if (!ids4_.contains(key)) {
2521 ids_for_proof.clear();
2522 binary_used.clear();
2523 merge2_into_n(key, bitmask, binary_used);
2524 merge3_into_4(key, bitmask, ids_for_proof);
2526 ProcessTruthTable(key, bitmask, ids_for_proof, binary_used);
2532 timer.AddCounter(
"combine3", num_combinations);
2533 timer.AddCounter(
"merges", num_merges);
2534 timer.AddCounter(
"merges2", num_merges2);
2537 for (
int i = 0;
i < num_tables.size(); ++
i) {
2538 timer.AddCounter(absl::StrCat(
"t",
i), num_tables[
i]);
2540 for (
int i = 0;
i < num_functions.size(); ++
i) {
2541 timer.AddCounter(absl::StrCat(
"fn",
i), num_functions[
i]);
2547class LratGateCongruenceHelper {
2549 LratGateCongruenceHelper(
2550 const Trail* trail,
const BinaryImplicationGraph* implication_graph,
2551 ClauseManager* clause_manager, ClauseIdGenerator* clause_id_generator,
2552 LratProofHandler* lrat_proof_handler,
2553 const util_intops::StrongVector<GateId, LiteralIndex>& gates_target,
2554 const CompactVectorVector<GateId, const SatClause*>& gates_clauses,
2555 DenseConnectedComponentsFinder& union_find)
2557 implication_graph_(implication_graph),
2558 clause_manager_(clause_manager),
2559 clause_id_generator_(clause_id_generator),
2560 lrat_proof_handler_(lrat_proof_handler),
2561 gates_target_(gates_target),
2562 gates_clauses_(gates_clauses),
2563 union_find_(union_find) {}
2565 ~LratGateCongruenceHelper() {
2566 if (lrat_proof_handler_ !=
nullptr) {
2567 if (lrat_proof_handler_->drat_check_enabled() ||
2568 lrat_proof_handler_->drat_output_enabled()) {
2569 for (
int i = 0;
i < to_delete_.size(); ++
i) {
2570 lrat_proof_handler_->DeleteClause(
2572 {clauses_to_delete_[
i].first, clauses_to_delete_[
i].second});
2575 for (
const ClauseId
id : to_delete_) {
2576 lrat_proof_handler_->DeleteClause(
id, {});
2585 void ShortenEquivalencesWithRepresentative(Literal l) {
2586 std::vector<Literal> literals;
2587 Literal representative;
2590 if (IsRepresentative(l)) {
2594 literals.push_back(l);
2602 for (
int i = literals.size() - 2;
i >= 0; --
i) {
2603 const Literal parent = literals[
i + 1];
2604 const Literal child = literals[
i];
2605 DCHECK(parent_equivalence_.contains(parent));
2606 DCHECK(parent_equivalence_.contains(child));
2607 GateEquivalenceClauses& parent_clauses = parent_equivalence_[parent];
2608 GateEquivalenceClauses& child_clauses = parent_equivalence_[child];
2610 const ClauseId rep_implies_child = clause_id_generator_->GetNextId();
2611 lrat_proof_handler_->AddInferredClause(
2612 rep_implies_child, {representative.Negated(), child},
2613 {parent_clauses.parent_implies_child,
2614 child_clauses.parent_implies_child});
2615 const ClauseId child_implies_rep = clause_id_generator_->GetNextId();
2616 lrat_proof_handler_->AddInferredClause(
2617 child_implies_rep, {child.Negated(), representative},
2618 {child_clauses.child_implies_parent,
2619 parent_clauses.child_implies_parent});
2621 child_clauses.parent_implies_child = rep_implies_child;
2622 child_clauses.child_implies_parent = child_implies_rep;
2623 to_delete_.push_back(rep_implies_child);
2624 to_delete_.push_back(child_implies_rep);
2625 if (lrat_proof_handler_->drat_check_enabled() ||
2626 lrat_proof_handler_->drat_output_enabled()) {
2627 clauses_to_delete_.push_back({representative.Negated(), child});
2628 clauses_to_delete_.push_back({child.Negated(), representative});
2631 if (!literals.empty()) {
2634 union_find_.FindRoot(literals[0].
Index().value());
2640 ClauseId AddAndGateTargetImplication(GateId gate_a_id, GateId gate_b_id) {
2641 const Literal a = Literal(gates_target_[gate_a_id]);
2642 const Literal
b = Literal(gates_target_[gate_b_id]);
2643 const Literal rep_a = GetRepresentativeWithProofSupport(a);
2644 const Literal rep_b = GetRepresentativeWithProofSupport(
b);
2645 DCHECK(IsRepresentative(rep_a));
2646 DCHECK(IsRepresentative(rep_b));
2649 std::vector<ClauseId> clause_ids;
2651 Append(clause_ids, GetRepresentativeImpliesLiteralClauseId(a));
2656 for (
const Literal lit : gates_clauses_[gate_a_id][0]->AsSpan()) {
2657 if (lit == a)
continue;
2658 const Literal l = lit.Negated();
2659 clause_ids.push_back(implication_graph_->GetClauseId(a.Negated(), l));
2660 Append(clause_ids, GetLiteralImpliesRepresentativeClauseId(l));
2664 for (
const Literal lit : gates_clauses_[gate_b_id][0]->AsSpan()) {
2665 if (lit ==
b)
continue;
2666 const Literal l = lit.Negated();
2667 Append(clause_ids, GetRepresentativeImpliesLiteralClauseId(l));
2670 clause_ids.push_back(
2671 clause_manager_->GetClauseId(gates_clauses_[gate_b_id][0]));
2673 Append(clause_ids, GetLiteralImpliesRepresentativeClauseId(
b));
2675 const ClauseId clause_id = clause_id_generator_->GetNextId();
2676 lrat_proof_handler_->AddInferredClause(clause_id, {rep_a.Negated(), rep_b},
2681 void ClearTemporaryProof() {
2682 CHECK(lrat_proof_handler_ !=
nullptr);
2683 tmp_index_to_delete_.clear();
2684 tmp_proof_clauses_id_.clear();
2685 tmp_proof_clauses_.clear();
2686 marked_.ClearAndResize(LiteralIndex(clause_manager_->literal_size()));
2689 Literal GetRepresentativeWithProofSupport(Literal lit) {
2690 const int lit_index_as_int = lit.Index().value();
2691 if (union_find_.GetParent(lit_index_as_int) == lit_index_as_int) {
2694 if (lrat_proof_handler_ !=
nullptr) {
2695 ShortenEquivalencesWithRepresentative(lit);
2697 return Literal(LiteralIndex(union_find_.FindRoot(lit_index_as_int)));
2700 void AddGateClausesToTemporaryProof(GateId
id) {
2701 CHECK(lrat_proof_handler_ !=
nullptr);
2702 const auto& assignment = trail_->Assignment();
2703 std::vector<Literal> fixed;
2704 for (
const SatClause* clause : gates_clauses_[
id]) {
2706 marked_.ResetAllToFalse();
2707 tmp_literals_.clear();
2708 tmp_clause_ids_.clear();
2709 bool clause_is_trivial =
false;
2710 bool some_change =
false;
2711 for (
const Literal lit : clause->
AsSpan()) {
2712 const Literal rep = GetRepresentativeWithProofSupport(lit);
2713 if (assignment.LiteralIsAssigned(rep)) {
2714 fixed.push_back(rep);
2720 tmp_clause_ids_.push_back(
2721 GetLiteralImpliesRepresentativeClauseId(lit));
2723 if (marked_[rep])
continue;
2724 if (marked_[rep.Negated()]) {
2725 clause_is_trivial =
true;
2729 tmp_literals_.push_back(rep);
2733 if (clause_is_trivial)
continue;
2737 ? implication_graph_->GetClauseId(clause->
FirstLiteral(),
2739 : clause_manager_->GetClauseId(clause);
2744 tmp_index_to_delete_.push_back(tmp_proof_clauses_.size());
2745 tmp_clause_ids_.push_back(new_id);
2746 new_id = clause_id_generator_->GetNextId();
2747 lrat_proof_handler_->AddInferredClause(new_id, tmp_literals_,
2752 tmp_proof_clauses_id_.push_back(new_id);
2753 tmp_proof_clauses_.Add(tmp_literals_);
2758 for (
const Literal lit : fixed) {
2759 if (assignment.LiteralIsAssigned(lit)) {
2760 tmp_proof_clauses_id_.push_back(
2761 trail_->GetUnitClauseId(lit.Variable()));
2762 tmp_proof_clauses_.Add(
2763 {assignment.LiteralIsTrue(lit) ? lit : lit.Negated()});
2769 std::pair<ClauseId, ClauseId> AddShortGateEquivalence(
2770 Literal rep_a, Literal rep_b, absl::Span<const GateId> gate_ids) {
2775 ClearTemporaryProof();
2776 for (
const GateId
id : gate_ids) {
2777 AddGateClausesToTemporaryProof(
id);
2782 DCHECK(IsRepresentative(rep_a));
2783 DCHECK(IsRepresentative(rep_b));
2784 CHECK_NE(rep_a, rep_b);
2786 if (rep_a.Negated() == rep_b) {
2792 const ClauseId not_rep_a_id =
2793 lrat_proof_handler_->AddAndProveInferredClauseByEnumeration(
2794 {rep_a.Negated()}, tmp_proof_clauses_id_, tmp_proof_clauses_);
2795 const ClauseId rep_a_id =
2796 lrat_proof_handler_->AddAndProveInferredClauseByEnumeration(
2797 {rep_a}, tmp_proof_clauses_id_, tmp_proof_clauses_);
2798 return {not_rep_a_id, rep_a_id};
2801 const ClauseId rep_a_implies_rep_b =
2802 lrat_proof_handler_->AddAndProveInferredClauseByEnumeration(
2803 {rep_a.Negated(), rep_b}, tmp_proof_clauses_id_,
2804 tmp_proof_clauses_);
2805 const ClauseId rep_b_implies_rep_a =
2806 lrat_proof_handler_->AddAndProveInferredClauseByEnumeration(
2807 {rep_b.Negated(), rep_a}, tmp_proof_clauses_id_,
2808 tmp_proof_clauses_);
2810 for (
const int i : tmp_index_to_delete_) {
2812 if (tmp_proof_clauses_id_[
i] == rep_a_implies_rep_b)
continue;
2813 if (tmp_proof_clauses_id_[
i] == rep_b_implies_rep_a)
continue;
2814 lrat_proof_handler_->DeleteClause(tmp_proof_clauses_id_[
i],
2815 tmp_proof_clauses_[
i]);
2817 return {rep_a_implies_rep_b, rep_b_implies_rep_a};
2820 ClauseId ProofForFixingLiteral(Literal to_fix, GateId
id) {
2821 if (lrat_proof_handler_ ==
nullptr)
return kNoClauseId;
2822 CHECK(IsRepresentative(to_fix));
2823 ClearTemporaryProof();
2824 AddGateClausesToTemporaryProof(
id);
2825 const ClauseId new_id =
2826 lrat_proof_handler_->AddAndProveInferredClauseByEnumeration(
2827 {to_fix}, tmp_proof_clauses_id_, tmp_proof_clauses_);
2829 for (
const int i : tmp_index_to_delete_) {
2831 if (tmp_proof_clauses_id_[
i] == new_id)
continue;
2832 lrat_proof_handler_->DeleteClause(tmp_proof_clauses_id_[
i],
2833 tmp_proof_clauses_[
i]);
2838 void AddGateEquivalenceClauses(Literal child, ClauseId child_implies_parent,
2839 ClauseId parent_implies_child) {
2840 DCHECK(!parent_equivalence_.contains(child));
2841 parent_equivalence_[child] = {
2842 .parent_implies_child = parent_implies_child,
2843 .child_implies_parent = child_implies_parent,
2852 void AppendFixAndGateTargetClauses(
2853 GateId gate_id, Literal rep,
2854 absl::InlinedVector<ClauseId, 4>& clause_ids) {
2855 const Literal target = Literal(gates_target_[gate_id]);
2860 for (
const Literal lit : gates_clauses_[gate_id][0]->AsSpan()) {
2862 const Literal l = lit.Negated();
2863 const Literal rep_l = GetRepresentativeWithProofSupport(l);
2864 if (rep_l == rep) l_index = l.Index();
2865 if (rep_l == rep.Negated()) m_index = l.Index();
2867 clause_ids.push_back(
2868 implication_graph_->GetClauseId(target.Negated(), Literal(l_index)));
2870 GetLiteralImpliesRepresentativeClauseId(Literal(l_index)));
2871 clause_ids.push_back(
2872 implication_graph_->GetClauseId(target.Negated(), Literal(m_index)));
2874 GetLiteralImpliesRepresentativeClauseId(Literal(m_index)));
2875 Append(clause_ids, GetLiteralImpliesRepresentativeClauseId(target));
2880 struct GateEquivalenceClauses {
2881 ClauseId parent_implies_child;
2882 ClauseId child_implies_parent;
2885 bool IsRepresentative(Literal l)
const {
return GetParent(l) == l; }
2887 Literal GetParent(Literal l)
const {
2888 return Literal(LiteralIndex(union_find_.GetParent(l.Index().value())));
2891 ClauseId GetLiteralImpliesRepresentativeClauseId(Literal l) {
2892 ShortenEquivalencesWithRepresentative(l);
2893 const auto it = parent_equivalence_.find(l);
2894 if (it == parent_equivalence_.end())
return kNoClauseId;
2895 return it->second.child_implies_parent;
2898 ClauseId GetRepresentativeImpliesLiteralClauseId(Literal l) {
2899 ShortenEquivalencesWithRepresentative(l);
2900 const auto it = parent_equivalence_.find(l);
2901 if (it == parent_equivalence_.end())
return kNoClauseId;
2902 return it->second.parent_implies_child;
2905 template <
typename Vector>
2906 static void Append(Vector& clauses, ClauseId new_clause) {
2908 clauses.push_back(new_clause);
2912 const Trail* trail_;
2913 const BinaryImplicationGraph* implication_graph_;
2914 ClauseManager* clause_manager_;
2915 ClauseIdGenerator* clause_id_generator_;
2916 LratProofHandler* lrat_proof_handler_;
2917 const util_intops::StrongVector<GateId, LiteralIndex>& gates_target_;
2918 const CompactVectorVector<GateId, const SatClause*>& gates_clauses_;
2919 DenseConnectedComponentsFinder& union_find_;
2923 absl::flat_hash_map<Literal, GateEquivalenceClauses> parent_equivalence_;
2925 std::vector<ClauseId> to_delete_;
2928 std::vector<std::pair<Literal, Literal>> clauses_to_delete_;
2931 std::vector<int> tmp_index_to_delete_;
2932 std::vector<ClauseId> tmp_proof_clauses_id_;
2933 CompactVectorVector<int, Literal> tmp_proof_clauses_;
2937 SparseBitset<LiteralIndex> marked_;
2938 std::vector<ClauseId> tmp_clause_ids_;
2939 std::vector<Literal> tmp_literals_;
2947 if (implication_graph_->IsEmpty())
return true;
2949 PresolveTimer timer(
"GateCongruenceClosure", logger_, time_limit_);
2952 const int num_variables(sat_solver_->NumVariables());
2953 const int num_literals(num_variables * 2);
2954 marked_.ClearAndResize(
Literal(num_literals));
2955 seen_.ClearAndResize(
Literal(num_literals));
2956 next_seen_.ClearAndResize(
Literal(num_literals));
2958 gates_target_.clear();
2959 gates_inputs_.clear();
2960 gates_type_.clear();
2961 gates_clauses_.clear();
2964 CHECK(tmp_binary_clauses_.empty());
2965 absl::Cleanup binary_cleanup = [
this] { tmp_binary_clauses_.clear(); };
2967 ExtractAndGatesAndFillShortTruthTables(timer);
2968 ExtractShortGates(timer);
2972 CHECK_EQ(gates_target_.size(), gates_type_.size());
2973 CHECK_EQ(gates_target_.size(), gates_inputs_.size());
2974 if (lrat_proof_handler_ !=
nullptr) {
2975 CHECK_EQ(gates_target_.size(), gates_clauses_.size());
2980 absl::flat_hash_set<GateId, GateHash, GateEq> gate_set(
2981 gates_inputs_.size(), GateHash(&gates_type_, &gates_inputs_),
2982 GateEq(&gates_type_, &gates_inputs_));
2996 struct GetVarMapper {
2997 BooleanVariable operator()(LiteralIndex l)
const {
3004 LratGateCongruenceHelper lrat_helper(
3005 trail_, implication_graph_, clause_manager_, clause_id_generator_,
3006 lrat_proof_handler_, gates_target_, gates_clauses_, union_find);
3010 int num_equivalences = 0;
3011 int num_processed = 0;
3012 int arity1_equivalences = 0;
3013 absl::Cleanup stat_cleanup = [&] {
3014 total_wtime_ += timer.
wtime();
3016 total_equivalences_ += num_equivalences;
3017 total_num_units_ += num_units;
3018 timer.
AddCounter(
"processed", num_processed);
3020 timer.
AddCounter(
"f1_equiv", arity1_equivalences);
3025 const int num_gates = gates_inputs_.size();
3026 total_gates_ += num_gates;
3027 std::vector<bool> in_queue(num_gates,
true);
3028 std::vector<GateId> queue(num_gates);
3029 for (GateId
id(0);
id < num_gates; ++id) queue[
id.value()] = id;
3031 int num_processed_fixed_variables = trail_->Index();
3033 const auto fix_literal = [&,
this](
Literal to_fix,
3034 absl::Span<const ClauseId> clause_ids) {
3035 DCHECK_EQ(to_fix, lrat_helper.GetRepresentativeWithProofSupport(to_fix));
3036 if (assignment_.LiteralIsTrue(to_fix))
return true;
3037 if (!clause_manager_->InprocessingFixLiteral(to_fix, clause_ids)) {
3046 for (; num_processed_fixed_variables < trail_->Index();
3047 ++num_processed_fixed_variables) {
3048 const Literal to_update = lrat_helper.GetRepresentativeWithProofSupport(
3049 (*trail_)[num_processed_fixed_variables]);
3050 for (
const GateId gate_id : input_var_to_gate[to_update.
Variable()]) {
3051 if (in_queue[gate_id.value()])
continue;
3052 queue.push_back(gate_id);
3053 in_queue[gate_id.value()] =
true;
3060 const auto get_unit_clause = [
this](
Literal a) {
3061 if (lrat_proof_handler_ ==
nullptr)
return kNoClauseId;
3062 return trail_->GetUnitClauseId(a.Variable());
3066 ClauseId a_implies_b,
3067 ClauseId b_implies_a) {
3069 if (assignment_.LiteralIsAssigned(a)) {
3070 if (assignment_.LiteralIsTrue(a)) {
3071 return fix_literal(
b, {a_implies_b, get_unit_clause(a)});
3073 return fix_literal(
b.Negated(), {b_implies_a, get_unit_clause(a)});
3075 }
else if (assignment_.LiteralIsAssigned(
b)) {
3076 if (assignment_.LiteralIsTrue(
b)) {
3077 return fix_literal(a, {b_implies_a, get_unit_clause(
b)});
3079 return fix_literal(a.
Negated(), {a_implies_b, get_unit_clause(b)});
3084 DCHECK(!implication_graph_->IsRedundant(a));
3085 DCHECK(!implication_graph_->IsRedundant(
b));
3086 if (!implication_graph_->AddBinaryClause(a_implies_b, a.
Negated(),
b) ||
3087 !implication_graph_->AddBinaryClause(b_implies_a,
b.Negated(), a)) {
3093 for (
const bool negate : {
false,
true}) {
3095 const LiteralIndex y = negate ?
b.NegatedIndex() :
b.Index();
3096 const ClauseId x_implies_y = negate ? b_implies_a : a_implies_b;
3097 const ClauseId y_implies_x = negate ? a_implies_b : b_implies_a;
3102 union_find.
AddEdge(x.value(), y.value());
3103 const LiteralIndex rep(union_find.
FindRoot(y.value()));
3104 const LiteralIndex to_merge = rep == x ? y : x;
3114 if (lrat_proof_handler_ !=
nullptr) {
3116 lrat_helper.AddGateEquivalenceClauses(
Literal(y), y_implies_x,
3119 lrat_helper.AddGateEquivalenceClauses(
Literal(x), x_implies_y,
3127 lrat_helper.GetRepresentativeWithProofSupport(a),
3128 lrat_helper.GetRepresentativeWithProofSupport(a.
Negated()).Negated());
3130 lrat_helper.GetRepresentativeWithProofSupport(
b),
3131 lrat_helper.GetRepresentativeWithProofSupport(
b.Negated()).Negated());
3138 input_var_to_gate.
MergeInto(to_merge_var, rep_var);
3139 for (
const GateId gate_id : input_var_to_gate[rep_var]) {
3140 if (in_queue[gate_id.value()])
continue;
3141 queue.push_back(gate_id);
3142 in_queue[gate_id.value()] =
true;
3149 while (!queue.empty()) {
3151 const GateId
id = queue.back();
3153 CHECK(in_queue[
id.value()]);
3154 in_queue[
id.value()] =
false;
3162 for (
int pass = 0; pass < 2; ++pass) {
3163 GateId other_id(-1);
3164 bool is_equivalent =
false;
3166 const auto it = gate_set.find(
id);
3167 if (it != gate_set.end()) {
3169 if (
id == other_id) {
3174 is_equivalent =
true;
3178 const auto [it, inserted] = gate_set.insert(
id);
3181 is_equivalent =
true;
3185 if (is_equivalent) {
3186 CHECK_NE(
id, other_id);
3187 CHECK_GE(other_id, 0);
3188 CHECK_EQ(gates_type_[
id], gates_type_[other_id]);
3189 CHECK_EQ(gates_inputs_[
id], gates_inputs_[other_id]);
3194 const Literal a(gates_target_[
id]);
3195 const Literal b(gates_target_[other_id]);
3196 const Literal rep_a = lrat_helper.GetRepresentativeWithProofSupport(a);
3197 const Literal rep_b = lrat_helper.GetRepresentativeWithProofSupport(
b);
3198 if (rep_a != rep_b) {
3201 if (lrat_proof_handler_ !=
nullptr) {
3202 if (gates_type_[
id] == kAndGateType) {
3203 rep_a_implies_rep_b =
3204 lrat_helper.AddAndGateTargetImplication(
id, other_id);
3205 rep_b_implies_rep_a =
3206 lrat_helper.AddAndGateTargetImplication(other_id,
id);
3208 const auto [x, y] = lrat_helper.AddShortGateEquivalence(
3209 rep_a, rep_b, {id, other_id});
3210 rep_a_implies_rep_b = x;
3211 rep_b_implies_rep_a = y;
3214 if (!new_equivalence(rep_a, rep_b, rep_a_implies_rep_b,
3215 rep_b_implies_rep_a)) {
3229 if (pass > 0)
continue;
3231 if (gates_type_[
id] == kAndGateType) {
3232 absl::Span<LiteralIndex> inputs = gates_inputs_[id];
3233 marked_.ResetAllToFalse();
3235 bool is_unit =
false;
3236 for (
const LiteralIndex l : inputs) {
3237 if (lrat_proof_handler_ !=
nullptr) {
3238 lrat_helper.ShortenEquivalencesWithRepresentative(
Literal(l));
3240 const LiteralIndex rep(union_find.
FindRoot(l.value()));
3241 if (marked_[rep])
continue;
3245 if (marked_[
Literal(rep).Negated()]) {
3251 lrat_helper.GetRepresentativeWithProofSupport(initial_to_fix);
3252 if (!assignment_.LiteralIsTrue(to_fix)) {
3253 absl::InlinedVector<ClauseId, 4> clause_ids;
3254 if (lrat_proof_handler_ !=
nullptr) {
3255 lrat_helper.AppendFixAndGateTargetClauses(
id,
Literal(rep),
3258 if (!fix_literal(to_fix, clause_ids))
return false;
3264 inputs[new_size++] = rep;
3273 CHECK_GT(new_size, 0);
3274 std::sort(inputs.begin(), inputs.begin() + new_size);
3275 gates_inputs_.Shrink(
id, new_size);
3292 if (new_size > 4)
continue;
3293 gates_type_[id] = 1 << ((1 << new_size) - 1);
3298 DCHECK_GE(gates_type_[
id], 0);
3299 DCHECK_EQ(gates_type_[
id] >> (1 << (gates_inputs_[
id].size())), 0);
3300 for (LiteralIndex& lit_ref : gates_inputs_[
id]) {
3302 lrat_helper.GetRepresentativeWithProofSupport(
Literal(lit_ref))
3306 const int new_size = CanonicalizeShortGate(
id);
3307 if (new_size == 1) {
3312 const Literal rep_a = lrat_helper.GetRepresentativeWithProofSupport(a);
3313 const Literal rep_b = lrat_helper.GetRepresentativeWithProofSupport(
b);
3314 if (rep_a != rep_b) {
3315 ++arity1_equivalences;
3316 const auto [a_to_b, b_to_a] =
3317 lrat_helper.AddShortGateEquivalence(rep_a, rep_b, {
id});
3318 if (!new_equivalence(rep_a, rep_b, a_to_b, b_to_a)) {
3323 }
else if (new_size == 0) {
3326 const Literal initial_to_fix =
3327 (gates_type_[id] & 1) == 1 ?
Literal(gates_target_[
id])
3330 lrat_helper.GetRepresentativeWithProofSupport(initial_to_fix);
3331 if (!assignment_.LiteralIsTrue(to_fix)) {
3332 const ClauseId clause_id =
3333 lrat_helper.ProofForFixingLiteral(to_fix,
id);
3334 if (!fix_literal(to_fix, {clause_id}))
return false;
3343 CHECK(queue.empty());
3345 for (GateId
id(0);
id < num_gates; ++id) {
3346 if (gates_type_[
id] == kAndGateType)
continue;
3347 if (assignment_.LiteralIsAssigned(
Literal(gates_target_[
id])))
continue;
3349 const int new_size = CanonicalizeShortGate(
id);
3350 if (new_size == 0) {
3351 CHECK_EQ(gates_type_[
id] & 1, 0);
3354 lrat_helper.GetRepresentativeWithProofSupport(initial_to_fix);
3355 CHECK(assignment_.LiteralIsTrue(to_fix));
3356 }
else if (new_size == 1) {
3357 CHECK(!assignment_.LiteralIsAssigned(
Literal(gates_target_[
id])));
3358 CHECK(!assignment_.LiteralIsAssigned(
Literal(gates_inputs_[
id][0])));
3359 CHECK_EQ(lrat_helper.GetRepresentativeWithProofSupport(
3361 lrat_helper.GetRepresentativeWithProofSupport(
3362 Literal(gates_inputs_[
id][0])))
3365 const auto [it, inserted] = gate_set.insert(
id);
3367 const GateId other_id = *it;
3368 CHECK_EQ(lrat_helper.GetRepresentativeWithProofSupport(
3370 lrat_helper.GetRepresentativeWithProofSupport(
3371 Literal(gates_target_[other_id])))
3372 <<
id <<
" " << gates_inputs_[id] <<
" " << other_id <<
" "
3373 << gates_inputs_[other_id];
bool AddEdge(int node1, int node2)
void SetNumberOfNodes(int num_nodes)
void OverrideLogging(bool value)
double deterministic_time() const
void AddCounter(std::string name, int64_t count)
void Set(IntegerType index)
ClauseId GetClauseId(Literal a, Literal b) const
void DoOneRound(bool log_info)
bool DoOneRound(bool log_info)
void LazyDelete(SatClause *clause, DeletionSourceForStat source)
bool IsRemovable(SatClause *const clause) const
void DeleteRemovedClauses()
int64_t literal_size() const
const std::vector< SatClause * > & AllClausesInCreationOrder() const
int Add(absl::Span< const V > values)
bool DoOneRound(bool log_info)
bool SubsumeAndStrenghtenRound(bool log_info)
bool DetectEquivalencesAndStamp(bool use_transitive_reduction, bool log_info)
bool MoreFixedVariableToClean() const
bool RemoveFixedAndEquivalentVariables(bool log_info)
bool MoreRedundantVariableToClean() const
bool PresolveLoop(SatPresolveOptions options)
Inprocessing(Model *model)
bool LevelZeroPropagate()
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
void RemoveFromFutureOutput(V value)
void ResetFromTransposeMap(const Container &input, int min_transpose_size=0)
void MergeInto(K to_merge, K representative)
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
absl::Span< const Literal > AsSpan() const
Literal SecondLiteral() const
bool ComputeStampsForNextRound(bool log_info)
bool ImplicationIsInTree(Literal a, Literal b) const
void SampleTreeAndFillParent()
bool DoOneRound(bool log_info)
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 STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLClearObject(T *obj)
int RemoveFixedInput(int i, bool at_true, absl::Span< LiteralIndex > inputs, int &int_function_values)
const LiteralIndex kNoLiteralIndex(-1)
bool IsFunction(int i, int num_bits, SmallBitset truth_table)
SmallBitset GetNumBitsAtOne(int num_bits)
void CanonicalizeTruthTable(absl::Span< VarOrLiteral > key, SmallBitset &bitmask)
constexpr ClauseId kNoClauseId(0)
int AddHoleAtPosition(int i, int bitset)
int CanonicalizeFunctionTruthTable(LiteralIndex &target, absl::Span< LiteralIndex > inputs, int &int_function_values)
void FillKeyAndBitmask(absl::Span< const Literal > clause, absl::Span< BooleanVariable > key, SmallBitset &bitmask)
@ SUBSUMPTION_INPROCESSING
const BooleanVariable kNoBooleanVariable(-1)
ClosedInterval::Iterator end(ClosedInterval interval)
std::string FormatCounter(int64_t num)
#define RETURN_IF_FALSE(f)
std::deque< std::vector< Literal > > clauses
void AddClauseWithSpecialLiteral(Literal literal, absl::Span< const Literal > clause)
double deterministic_limit
bool extract_binary_clauses
double deterministic_time_limit
bool use_transitive_reduction
bool extract_binary_clauses_in_probing
bool use_equivalence_sat_sweeping
#define FORCED_SOLVER_LOG(logger,...)
#define SOLVER_LOG(logger,...)