24#include "absl/algorithm/container.h"
25#include "absl/cleanup/cleanup.h"
26#include "absl/container/inlined_vector.h"
27#include "absl/log/check.h"
28#include "absl/log/log.h"
29#include "absl/log/vlog_is_on.h"
30#include "absl/types/span.h"
53 Literal literal, absl::Span<const Literal> clause) {
55 clauses.emplace_back(clause.begin(), clause.end());
56 for (
int i = 0;
i < clause.size(); ++
i) {
57 if (clause[
i] == literal) {
66#define RETURN_IF_FALSE(f) \
67 if (!(f)) return false;
74 double probing_time = 0.0;
75 const bool log_round_info = VLOG_IS_ON(1);
82 const double start_dtime = time_limit_->GetElapsedDeterministicTime();
84 while (!time_limit_->LimitReached() &&
85 time_limit_->GetElapsedDeterministicTime() <= stop_dtime) {
86 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
90 implication_graph_->RemoveFixedVariables();
91 implication_graph_->RemoveDuplicates();
109 implication_graph_->RemoveAllRedundantVariables(&postsolve_->clauses);
116 !implication_graph_->IsDag()) {
122 !implication_graph_->IsDag()) {
129 if (!params_.fill_tightened_domains_in_response()) {
130 blocked_clause_simplifier_->DoOneRound(log_round_info);
135 RETURN_IF_FALSE(bounded_variable_elimination_->DoOneRound(log_round_info));
139 const double saved_wtime = wall_timer.
Get();
140 const double time_left =
141 stop_dtime - time_limit_->GetElapsedDeterministicTime();
142 if (time_left <= 0)
break;
144 probing_options.
log_info = log_round_info;
149 probing_time += wall_timer.
Get() - saved_wtime;
152 !implication_graph_->IsDag()) {
166 logger_,
"[Pure SAT presolve]",
" num_fixed: ", trail_->Index(),
167 " num_redundant: ", implication_graph_->num_redundant_literals() / 2,
"/",
168 sat_solver_->NumVariables(),
169 " num_implications: ", implication_graph_->ComputeNumImplicationsForLog(),
170 " num_watched_clauses: ", clause_manager_->num_watched_clauses(),
171 " dtime: ", time_limit_->GetElapsedDeterministicTime() - start_dtime,
"/",
173 " non-probing time: ", (wall_timer.
Get() - probing_time));
178 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
179 if (sat_solver_->ModelIsUnsat())
return false;
183 const bool log_info = VLOG_IS_ON(1);
184 const bool log_round_info = VLOG_IS_ON(2);
185 const double start_dtime = time_limit_->GetElapsedDeterministicTime();
188 double probing_time = 0.0;
192 if (first_inprocessing_call_) {
193 reference_dtime_ = start_dtime;
194 first_inprocessing_call_ =
false;
202 const double diff = start_dtime - reference_dtime_;
203 if (total_dtime_ > params_.inprocessing_dtime_ratio() * diff) {
212 std::vector<std::pair<LinearProgrammingConstraint*, bool>> saved_state;
214 saved_state.push_back({lp, lp->PropagationIsEnabled()});
215 lp->EnablePropagation(
false);
217 auto cleanup = absl::MakeCleanup([&saved_state]() {
218 for (
const auto [lp, old_value] : saved_state) {
219 lp->EnablePropagation(old_value);
228 decision_policy_->MaybeEnablePhaseSaving(
false);
230 implication_graph_->RemoveDuplicates();
236 if (params_.inprocessing_probing_dtime() > 0.0) {
237 const double saved_wtime = wall_timer.
Get();
239 probing_options.
log_info = log_round_info;
243 probing_time += wall_timer.
Get() - saved_wtime;
255 const auto old_counter = sat_solver_->counters();
256 if (params_.inprocessing_minimization_dtime() > 0.0) {
258 params_.inprocessing_minimization_dtime()));
260 const int64_t mini_num_clause =
261 sat_solver_->counters().minimization_num_clauses -
262 old_counter.minimization_num_clauses;
263 const int64_t mini_num_removed =
264 sat_solver_->counters().minimization_num_removed_literals -
265 old_counter.minimization_num_removed_literals;
274 blocked_clause_simplifier_->DoOneRound(log_round_info);
275 RETURN_IF_FALSE(bounded_variable_elimination_->DoOneRound(log_round_info));
279 sat_solver_->AdvanceDeterministicTime(time_limit_);
280 total_dtime_ += time_limit_->GetElapsedDeterministicTime() - start_dtime;
283 logger_,
"Inprocessing.",
" fixed:", trail_->Index(),
284 " equiv:", implication_graph_->num_redundant_literals() / 2,
285 " bools:", sat_solver_->NumVariables(),
286 " implications:", implication_graph_->ComputeNumImplicationsForLog(),
287 " watched:", clause_manager_->num_watched_clauses(),
288 " minimization:", mini_num_clause,
"|", mini_num_removed,
289 " dtime:", time_limit_->GetElapsedDeterministicTime() - start_dtime,
290 " wtime:", wall_timer.
Get(),
291 " np_wtime:", (wall_timer.
Get() - probing_time));
294 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
295 decision_policy_->MaybeEnablePhaseSaving(
true);
299#undef RETURN_IF_FALSE
302 const int64_t new_num_fixed_variables = trail_->Index();
303 return last_num_fixed_variables_ < new_num_fixed_variables;
307 const int64_t new_num_redundant_literals =
308 implication_graph_->num_redundant_literals();
309 return last_num_redundant_literals_ < new_num_redundant_literals;
313 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
314 clause_manager_->AttachAllClauses();
315 return sat_solver_->Propagate();
323 implication_graph_->RemoveFixedVariables();
324 if (!implication_graph_->IsDag()) {
328 if (!implication_graph_->DetectEquivalences(log_info))
return false;
330 if (use_transitive_reduction) {
331 if (!implication_graph_->ComputeTransitiveReduction(log_info)) {
338 if (!stamping_simplifier_->ComputeStampsForNextRound(log_info))
return false;
348 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
357 const int64_t new_num_redundant_literals =
358 implication_graph_->num_redundant_literals();
359 const int64_t new_num_fixed_variables = trail_->Index();
360 if (last_num_redundant_literals_ == new_num_redundant_literals &&
361 last_num_fixed_variables_ == new_num_fixed_variables) {
364 last_num_fixed_variables_ = new_num_fixed_variables;
365 last_num_redundant_literals_ = new_num_redundant_literals;
371 int64_t num_removed_literals = 0;
372 int64_t num_inspected_literals = 0;
376 std::vector<Literal> new_clause;
379 const int num_literals(sat_solver_->NumVariables() * 2);
382 clause_manager_->DeleteRemovedClauses();
383 clause_manager_->DetachAllClauses();
384 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
385 bool removed =
false;
386 bool need_rewrite =
false;
389 for (
const Literal l : clause->AsSpan()) {
390 if (assignment_.LiteralIsTrue(l)) {
394 if (!clause_manager_->InprocessingFixLiteral(l))
return false;
395 clause_manager_->InprocessingRemoveClause(clause);
396 num_removed_literals += clause->size();
400 if (assignment_.LiteralIsFalse(l) || implication_graph_->IsRedundant(l)) {
406 num_inspected_literals += clause->size();
407 if (removed || !need_rewrite)
continue;
408 num_inspected_literals += clause->size();
412 for (
const Literal l : clause->AsSpan()) {
413 const Literal r = implication_graph_->RepresentativeOf(l);
414 if (marked[r] || assignment_.LiteralIsFalse(r)) {
417 if (marked[r.
NegatedIndex()] || assignment_.LiteralIsTrue(r)) {
418 clause_manager_->InprocessingRemoveClause(clause);
419 num_removed_literals += clause->size();
424 new_clause.push_back(r);
428 for (
const Literal l : new_clause) marked[l] =
false;
429 if (removed)
continue;
431 num_removed_literals += clause->size() - new_clause.size();
432 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause)) {
438 const double dtime =
static_cast<double>(num_inspected_literals) * 1e-8;
439 time_limit_->AdvanceDeterministicTime(dtime);
440 LOG_IF(INFO, log_info) <<
"Cleanup. num_removed_literals: "
441 << num_removed_literals <<
" dtime: " << dtime
442 <<
" wtime: " << wall_timer.
Get();
455 int64_t num_subsumed_clauses = 0;
456 int64_t num_removed_literals = 0;
457 int64_t num_inspected_signatures = 0;
458 int64_t num_inspected_literals = 0;
462 std::vector<Literal> new_clause;
469 clause_manager_->DeleteRemovedClauses();
470 clause_manager_->DetachAllClauses();
474 std::vector<SatClause*> clauses =
475 clause_manager_->AllClausesInCreationOrder();
477 clauses.begin(), clauses.end(),
481 const LiteralIndex num_literals(sat_solver_->NumVariables() * 2);
487 one_watcher(num_literals.value());
490 std::vector<uint64_t> signatures(clauses.size());
492 std::vector<Literal> candidates_for_removal;
493 for (
int clause_index = 0; clause_index < clauses.size(); ++clause_index) {
494 SatClause* clause = clauses[clause_index];
500 if (num_inspected_literals + num_inspected_signatures > 1e9) {
515 uint64_t signature = 0;
518 marked.
Set(l.Index());
519 signature |= (uint64_t{1} << (l.Variable().value() % 64));
525 bool removed =
false;
526 candidates_for_removal.clear();
527 const uint64_t mask = ~signature;
529 num_inspected_signatures += one_watcher[l].size();
530 for (
const int i : one_watcher[l]) {
531 if ((mask & signatures[
i]) != 0)
continue;
533 bool subsumed =
true;
534 bool stengthen =
true;
536 num_inspected_literals += clauses[
i]->size();
537 for (
const Literal o : clauses[
i]->AsSpan()) {
541 to_remove = o.NegatedIndex();
549 ++num_subsumed_clauses;
550 num_removed_literals += clause->
size();
551 clause_manager_->InprocessingRemoveClause(clause);
557 candidates_for_removal.push_back(
Literal(to_remove));
562 if (removed)
continue;
566 num_inspected_signatures += one_watcher[l.NegatedIndex()].size();
567 for (
const int i : one_watcher[l.NegatedIndex()]) {
568 if ((mask & signatures[
i]) != 0)
continue;
570 bool stengthen =
true;
571 num_inspected_literals += clauses[
i]->size();
572 for (
const Literal o : clauses[
i]->AsSpan()) {
573 if (o == l.Negated())
continue;
580 candidates_for_removal.push_back(l);
591 if (!candidates_for_removal.empty()) {
594 if (l == candidates_for_removal[0])
continue;
595 new_clause.push_back(l);
597 CHECK_EQ(new_clause.size() + 1, clause->
size());
599 num_removed_literals += clause->
size() - new_clause.size();
600 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause)) {
603 if (clause->
size() == 0)
continue;
608 signature |= (uint64_t{1} << (l.Variable().value() % 64));
625 if (!clause_manager_->IsRemovable(clause)) {
626 int min_size = std::numeric_limits<int32_t>::max();
629 if (one_watcher[l].size() < min_size) {
630 min_size = one_watcher[l].size();
631 min_literal = l.Index();
641 signatures[clause_index] = signature;
642 one_watcher[min_literal].
push_back(clause_index);
650 const double dtime =
static_cast<double>(num_inspected_signatures) * 1e-8 +
651 static_cast<double>(num_inspected_literals) * 5e-9;
652 time_limit_->AdvanceDeterministicTime(dtime);
653 LOG_IF(INFO, log_info) <<
"Subsume. num_removed_literals: "
654 << num_removed_literals
655 <<
" num_subsumed: " << num_subsumed_clauses
656 <<
" dtime: " << dtime
657 <<
" wtime: " << wall_timer.
Get();
666 num_subsumed_clauses_ = 0;
667 num_removed_literals_ = 0;
670 if (implication_graph_->IsEmpty())
return true;
672 if (!stamps_are_already_computed_) {
676 implication_graph_->RemoveFixedVariables();
677 if (!implication_graph_->DetectEquivalences(log_info))
return true;
681 stamps_are_already_computed_ =
false;
686 time_limit_->AdvanceDeterministicTime(dtime_);
687 LOG_IF(INFO, log_info) <<
"Stamping. num_removed_literals: "
688 << num_removed_literals_
689 <<
" num_subsumed: " << num_subsumed_clauses_
690 <<
" num_fixed: " << num_fixed_ <<
" dtime: " << dtime_
691 <<
" wtime: " << wall_timer.
Get();
701 if (implication_graph_->IsEmpty())
return true;
703 implication_graph_->RemoveFixedVariables();
704 if (!implication_graph_->DetectEquivalences(log_info))
return true;
707 stamps_are_already_computed_ =
true;
710 time_limit_->AdvanceDeterministicTime(dtime_);
711 LOG_IF(INFO, log_info) <<
"Prestamping."
712 <<
" num_fixed: " << num_fixed_ <<
" dtime: " << dtime_
713 <<
" wtime: " << wall_timer.
Get();
718 const int size = implication_graph_->literal_size();
719 CHECK(implication_graph_->IsDag());
720 parents_.resize(size);
721 for (LiteralIndex
i(0);
i < size; ++
i) {
723 if (implication_graph_->IsRedundant(
Literal(
i)))
continue;
724 if (assignment_.LiteralIsAssigned(
Literal(
i)))
continue;
734 for (
int num_tries = 0; num_tries < 10; ++num_tries) {
737 const LiteralIndex index =
738 implication_graph_->RandomImpliedLiteral(
Literal(
i).Negated());
742 if (implication_graph_->IsRedundant(candidate))
continue;
743 if (
i == candidate.
Index())
continue;
746 parents_[
i] = candidate.
Index();
753 const int size = implication_graph_->literal_size();
756 sizes_.assign(size, 0);
757 for (LiteralIndex
i(0);
i < size; ++
i) {
758 if (parents_[
i] ==
i)
continue;
759 sizes_[parents_[
i]]++;
763 starts_.resize(size + 1);
764 starts_[LiteralIndex(0)] = 0;
765 for (LiteralIndex
i(1);
i <= size; ++
i) {
766 starts_[
i] = starts_[
i - 1] + sizes_[
i - 1];
770 children_.resize(size);
771 for (LiteralIndex
i(0);
i < size; ++
i) {
772 if (parents_[
i] ==
i)
continue;
773 children_[starts_[parents_[
i]]++] =
i;
777 for (LiteralIndex
i(0);
i < size; ++
i) {
778 starts_[
i] -= sizes_[
i];
782 CHECK_EQ(starts_[LiteralIndex(0)], 0);
783 for (LiteralIndex
i(1);
i <= size; ++
i) {
784 CHECK_EQ(starts_[
i], starts_[
i - 1] + sizes_[
i - 1]);
790 first_stamps_.resize(size);
791 last_stamps_.resize(size);
792 marked_.assign(size,
false);
793 for (LiteralIndex
i(0);
i < size; ++
i) {
794 if (parents_[
i] !=
i)
continue;
796 const LiteralIndex tree_root =
i;
797 dfs_stack_.push_back(
i);
798 while (!dfs_stack_.empty()) {
799 const LiteralIndex top = dfs_stack_.back();
801 dfs_stack_.pop_back();
802 last_stamps_[top] = stamp++;
805 first_stamps_[top] = stamp++;
810 if (marked_[
Literal(top).NegatedIndex()] &&
811 first_stamps_[
Literal(top).NegatedIndex()] >=
812 first_stamps_[tree_root]) {
815 LiteralIndex lca = top;
816 while (first_stamps_[lca] > first_stamp) {
820 if (!clause_manager_->InprocessingFixLiteral(
Literal(lca).Negated())) {
825 const int end = starts_[top + 1];
826 for (
int j = starts_[top]; j <
end; ++j) {
827 DCHECK_NE(top, children_[j]);
828 DCHECK(!marked_[children_[j]]);
829 dfs_stack_.push_back(children_[j]);
833 DCHECK_EQ(stamp, 2 * size);
843 bool operator<(
const Entry& o)
const {
return start < o.start; }
845 std::vector<int> to_remove;
846 std::vector<Literal> new_clause;
847 std::vector<Entry> entries;
848 clause_manager_->DeleteRemovedClauses();
849 clause_manager_->DetachAllClauses();
850 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
851 const auto span = clause->AsSpan();
852 if (span.empty())
continue;
861 for (
int i = 0;
i < span.size(); ++
i) {
862 if (assignment_.LiteralIsTrue(span[
i])) {
863 clause_manager_->InprocessingRemoveClause(clause);
866 if (assignment_.LiteralIsFalse(span[
i]))
continue;
868 {
i,
false, first_stamps_[span[
i]], last_stamps_[span[
i]]});
869 entries.push_back({
i,
true, first_stamps_[span[
i].NegatedIndex()],
870 last_stamps_[span[
i].NegatedIndex()]});
872 if (clause->IsRemoved())
continue;
875 if (!entries.empty()) {
876 const double n =
static_cast<double>(entries.size());
877 dtime_ += 1.5e-8 * n * std::log(n);
878 std::sort(entries.begin(), entries.end());
884 for (
const Entry& e : entries) {
885 if (e.end < top_entry.end) {
887 const Literal lhs = top_entry.is_negated ? span[top_entry.i].Negated()
889 const Literal rhs = e.is_negated ? span[e.i].
Negated() : span[e.i];
892 if (top_entry.is_negated != e.is_negated) {
894 if (top_entry.i == e.i) {
896 if (top_entry.is_negated) {
899 if (!clause_manager_->InprocessingFixLiteral(span[top_entry.i])) {
904 if (!clause_manager_->InprocessingFixLiteral(
905 span[top_entry.i].Negated())) {
908 to_remove.push_back(top_entry.i);
917 if (top_entry.is_negated) {
918 num_subsumed_clauses_++;
919 clause_manager_->InprocessingRemoveClause(clause);
923 CHECK_NE(top_entry.i, e.i);
924 if (top_entry.is_negated) {
926 to_remove.push_back(e.i);
935 to_remove.push_back(top_entry.i);
943 if (clause->IsRemoved())
continue;
946 if (!to_remove.empty() || entries.size() < span.size()) {
949 int to_remove_index = 0;
950 for (
int i = 0;
i < span.size(); ++
i) {
951 if (to_remove_index < to_remove.size() &&
952 i == to_remove[to_remove_index]) {
956 if (assignment_.LiteralIsTrue(span[
i])) {
957 clause_manager_->InprocessingRemoveClause(clause);
960 if (assignment_.LiteralIsFalse(span[
i]))
continue;
961 new_clause.push_back(span[
i]);
963 num_removed_literals_ += span.size() - new_clause.size();
964 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause)) {
977 num_blocked_clauses_ = 0;
978 num_inspected_literals_ = 0;
980 InitializeForNewRound();
982 while (!time_limit_->LimitReached() && !queue_.empty()) {
983 const Literal l = queue_.front();
984 in_queue_[l] =
false;
989 if (num_inspected_literals_ <= 1e9) ProcessLiteral(l);
993 literal_to_clauses_.clear();
995 dtime_ += 1e-8 * num_inspected_literals_;
996 time_limit_->AdvanceDeterministicTime(dtime_);
997 log_info |= VLOG_IS_ON(1);
998 LOG_IF(INFO, log_info) <<
"Blocked clause. num_blocked_clauses: "
999 << num_blocked_clauses_ <<
" dtime: " << dtime_
1000 <<
" wtime: " << wall_timer.
Get();
1003void BlockedClauseSimplifier::InitializeForNewRound() {
1011 clauses_.push_back(c);
1013 const int num_literals = clause_manager_->
literal_size();
1017 in_queue_.
assign(num_literals,
true);
1018 for (LiteralIndex l(0); l < num_literals; ++l) {
1019 queue_.push_back(Literal(l));
1022 marked_.
resize(num_literals);
1024 std::all_of(marked_.begin(), marked_.end(), [](
bool b) { return !b; }));
1028 literal_to_clauses_.clear();
1029 literal_to_clauses_.
resize(num_literals);
1030 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
1031 for (
const Literal l : clauses_[
i]->AsSpan()) {
1034 num_inspected_literals_ += clauses_[
i]->size();
1038void BlockedClauseSimplifier::ProcessLiteral(
Literal current_literal) {
1039 if (assignment_.LiteralIsAssigned(current_literal))
return;
1040 if (implication_graph_->IsRemoved(current_literal))
return;
1055 const std::vector<Literal>& implications =
1056 implication_graph_->DirectImplications(current_literal);
1057 for (
const Literal l : implications) {
1058 if (l == current_literal)
continue;
1066 std::vector<ClauseIndex> clauses_to_process;
1067 for (
const ClauseIndex
i : literal_to_clauses_[current_literal]) {
1068 if (clauses_[
i]->IsRemoved())
continue;
1074 if (num_binary > 0) {
1075 if (clauses_[
i]->size() <= num_binary)
continue;
1076 int num_with_negation_marked = 0;
1077 for (
const Literal l : clauses_[
i]->AsSpan()) {
1078 if (l == current_literal)
continue;
1079 if (marked_[l.NegatedIndex()]) {
1080 ++num_with_negation_marked;
1083 num_inspected_literals_ += clauses_[
i]->size();
1084 if (num_with_negation_marked < num_binary)
continue;
1086 clauses_to_process.push_back(
i);
1090 for (
const Literal l : implications) {
1102 for (
const ClauseIndex
i : clauses_to_process) {
1103 const auto c = clauses_[
i]->AsSpan();
1104 if (ClauseIsBlocked(current_literal, c)) {
1111 for (
const Literal l : c) {
1112 if (!in_queue_[l.NegatedIndex()]) {
1113 in_queue_[l.NegatedIndex()] =
true;
1114 queue_.push_back(l.Negated());
1119 postsolve_->AddClauseWithSpecialLiteral(current_literal, c);
1122 ++num_blocked_clauses_;
1123 clause_manager_->InprocessingRemoveClause(clauses_[
i]);
1129bool BlockedClauseSimplifier::ClauseIsBlocked(
1130 Literal current_literal, absl::Span<const Literal> clause) {
1131 bool is_blocked =
true;
1132 for (
const Literal l : clause) marked_[l] =
true;
1136 for (
const ClauseIndex
i :
1137 literal_to_clauses_[current_literal.NegatedIndex()]) {
1138 if (clauses_[
i]->IsRemoved())
continue;
1139 bool some_marked =
false;
1140 for (
const Literal l : clauses_[
i]->AsSpan()) {
1142 ++num_inspected_literals_;
1144 if (l == current_literal.Negated())
continue;
1145 if (marked_[l.NegatedIndex()]) {
1156 for (
const Literal l : clause) marked_[l] =
false;
1165 num_inspected_literals_ = 0;
1166 num_eliminated_variables_ = 0;
1167 num_literals_diff_ = 0;
1168 num_clauses_diff_ = 0;
1169 num_simplifications_ = 0;
1170 num_blocked_clauses_ = 0;
1173 clause_manager_->DeleteRemovedClauses();
1174 clause_manager_->DetachAllClauses();
1175 for (
SatClause* c : clause_manager_->AllClausesInCreationOrder()) {
1179 if (clause_manager_->IsRemovable(c))
continue;
1181 clauses_.push_back(c);
1183 const int num_literals = clause_manager_->literal_size();
1184 const int num_variables = num_literals / 2;
1186 literal_to_clauses_.clear();
1187 literal_to_clauses_.resize(num_literals);
1188 literal_to_num_clauses_.assign(num_literals, 0);
1189 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
1190 for (
const Literal l : clauses_[
i]->AsSpan()) {
1191 literal_to_clauses_[l].push_back(
i);
1192 literal_to_num_clauses_[l]++;
1194 num_inspected_literals_ += clauses_[
i]->size();
1197 const int saved_trail_index = trail_->Index();
1198 propagation_index_ = trail_->Index();
1200 need_to_be_updated_.clear();
1201 in_need_to_be_updated_.resize(num_variables);
1202 DCHECK(absl::c_find(in_need_to_be_updated_,
true) ==
1203 in_need_to_be_updated_.end());
1204 queue_.Reserve(num_variables);
1205 for (BooleanVariable v(0); v < num_variables; ++v) {
1206 if (assignment_.VariableIsAssigned(v))
continue;
1207 UpdatePriorityQueue(v);
1210 marked_.resize(num_literals);
1212 std::all_of(marked_.begin(), marked_.end(), [](
bool b) { return !b; }));
1217 while (!time_limit_->LimitReached() && !queue_.IsEmpty()) {
1218 const BooleanVariable top = queue_.Top().var;
1226 bool is_unsat =
false;
1227 if (!Propagate())
return false;
1228 while (implication_graph_->FindFailedLiteralAroundVar(top, &is_unsat)) {
1229 if (!Propagate())
return false;
1231 if (is_unsat)
return false;
1233 if (!CrossProduct(top))
return false;
1235 for (
const BooleanVariable v : need_to_be_updated_) {
1236 in_need_to_be_updated_[v] =
false;
1239 if (v != top) UpdatePriorityQueue(v);
1241 need_to_be_updated_.clear();
1244 if (!Propagate())
return false;
1245 implication_graph_->CleanupAllRemovedAndFixedVariables();
1249 for (
SatClause* c : clause_manager_->AllClausesInCreationOrder()) {
1250 bool remove =
false;
1251 for (
const Literal l : c->AsSpan()) {
1252 if (implication_graph_->IsRemoved(l)) {
1257 if (remove) clause_manager_->InprocessingRemoveClause(c);
1261 literal_to_clauses_.clear();
1262 literal_to_num_clauses_.clear();
1264 dtime_ += 1e-8 * num_inspected_literals_;
1265 time_limit_->AdvanceDeterministicTime(dtime_);
1266 log_info |= VLOG_IS_ON(1);
1267 LOG_IF(INFO, log_info) <<
"BVE."
1269 << trail_->Index() - saved_trail_index
1270 <<
" num_simplified_literals: " << num_simplifications_
1271 <<
" num_blocked_clauses_: " << num_blocked_clauses_
1272 <<
" num_eliminations: " << num_eliminated_variables_
1273 <<
" num_literals_diff: " << num_literals_diff_
1274 <<
" num_clause_diff: " << num_clauses_diff_
1275 <<
" dtime: " << dtime_
1276 <<
" wtime: " << wall_timer.
Get();
1280bool BoundedVariableElimination::RemoveLiteralFromClause(
1282 num_literals_diff_ -= sat_clause->
size();
1286 literal_to_num_clauses_[l]--;
1290 num_clauses_diff_--;
1294 resolvant_.push_back(l);
1296 if (!clause_manager_->InprocessingRewriteClause(sat_clause, resolvant_)) {
1300 --num_clauses_diff_;
1301 for (
const Literal l : resolvant_) literal_to_num_clauses_[l]--;
1303 num_literals_diff_ += sat_clause->
size();
1308bool BoundedVariableElimination::Propagate() {
1309 for (; propagation_index_ < trail_->Index(); ++propagation_index_) {
1311 if (!implication_graph_->Propagate(trail_))
return false;
1313 const Literal l = (*trail_)[propagation_index_];
1314 for (
const ClauseIndex index : literal_to_clauses_[l]) {
1315 if (clauses_[index]->IsRemoved())
continue;
1316 num_clauses_diff_--;
1317 num_literals_diff_ -= clauses_[index]->size();
1318 clause_manager_->InprocessingRemoveClause(clauses_[index]);
1320 literal_to_clauses_[l].clear();
1321 for (
const ClauseIndex index : literal_to_clauses_[l.NegatedIndex()]) {
1322 if (clauses_[index]->IsRemoved())
continue;
1323 if (!RemoveLiteralFromClause(l.Negated(), clauses_[index]))
return false;
1325 literal_to_clauses_[l.NegatedIndex()].clear();
1332int BoundedVariableElimination::NumClausesContaining(
Literal l) {
1333 return literal_to_num_clauses_[l] +
1334 implication_graph_->DirectImplicationsEstimatedSize(l.Negated());
1338void BoundedVariableElimination::UpdatePriorityQueue(BooleanVariable var) {
1339 if (assignment_.VariableIsAssigned(var))
return;
1340 if (implication_graph_->IsRemoved(Literal(var,
true)))
return;
1341 if (implication_graph_->IsRedundant(Literal(var,
true)))
return;
1342 const int priority = -NumClausesContaining(Literal(var,
true)) -
1343 NumClausesContaining(Literal(var,
false));
1344 if (queue_.Contains(var.value())) {
1345 queue_.ChangePriority({var, priority});
1347 queue_.Add({var, priority});
1351void BoundedVariableElimination::DeleteClause(
SatClause* sat_clause) {
1352 const auto clause = sat_clause->AsSpan();
1354 num_clauses_diff_--;
1355 num_literals_diff_ -= clause.size();
1358 for (
const Literal l : clause) {
1359 literal_to_num_clauses_[l]--;
1360 if (!in_need_to_be_updated_[l.Variable()]) {
1361 in_need_to_be_updated_[l.Variable()] =
true;
1362 need_to_be_updated_.push_back(l.Variable());
1367 clause_manager_->InprocessingRemoveClause(sat_clause);
1370void BoundedVariableElimination::DeleteAllClausesContaining(
Literal literal) {
1371 for (
const ClauseIndex
i : literal_to_clauses_[literal]) {
1372 const auto clause = clauses_[
i]->AsSpan();
1373 if (clause.empty())
continue;
1374 postsolve_->AddClauseWithSpecialLiteral(literal, clause);
1375 DeleteClause(clauses_[
i]);
1377 literal_to_clauses_[literal].clear();
1380void BoundedVariableElimination::AddClause(absl::Span<const Literal> clause) {
1381 SatClause* pt = clause_manager_->InprocessingAddClause(clause);
1382 if (pt ==
nullptr)
return;
1384 num_clauses_diff_++;
1385 num_literals_diff_ += clause.size();
1387 const ClauseIndex index(clauses_.size());
1388 clauses_.push_back(pt);
1389 for (
const Literal l : clause) {
1390 literal_to_num_clauses_[l]++;
1391 literal_to_clauses_[l].push_back(index);
1392 if (!in_need_to_be_updated_[l.Variable()]) {
1393 in_need_to_be_updated_[l.Variable()] =
true;
1394 need_to_be_updated_.push_back(l.Variable());
1399template <
bool score_only,
bool with_binary_only>
1400bool BoundedVariableElimination::ResolveAllClauseContaining(
Literal lit) {
1401 const int clause_weight = parameters_.presolve_bve_clause_weight();
1403 const std::vector<Literal>& implications =
1404 implication_graph_->DirectImplications(lit);
1405 auto& clause_containing_lit = literal_to_clauses_[lit];
1406 for (
int i = 0;
i < clause_containing_lit.size(); ++
i) {
1407 const ClauseIndex clause_index = clause_containing_lit[
i];
1408 const auto clause = clauses_[clause_index]->AsSpan();
1409 if (clause.empty())
continue;
1411 if (!score_only) resolvant_.clear();
1412 for (
const Literal l : clause) {
1413 if (!score_only && l != lit) resolvant_.push_back(l);
1416 DCHECK(marked_[lit]);
1417 num_inspected_literals_ += clause.size() + implications.size();
1421 bool clause_can_be_simplified =
false;
1422 const int64_t saved_score = new_score_;
1425 for (
const Literal l : implications) {
1427 if (marked_[l.NegatedIndex()])
continue;
1429 clause_can_be_simplified =
true;
1433 new_score_ += clause_weight + clause.size();
1435 resolvant_.push_back(l);
1436 AddClause(resolvant_);
1437 resolvant_.pop_back();
1443 if (!with_binary_only && !clause_can_be_simplified) {
1444 auto& clause_containing_not_lit = literal_to_clauses_[lit.NegatedIndex()];
1445 for (
int j = 0; j < clause_containing_not_lit.size(); ++j) {
1446 if (score_only && new_score_ > score_threshold_)
break;
1447 const ClauseIndex other_index = clause_containing_not_lit[j];
1448 const auto other = clauses_[other_index]->AsSpan();
1449 if (other.empty())
continue;
1450 bool trivial =
false;
1452 for (
const Literal l : other) {
1454 ++num_inspected_literals_;
1455 if (l == lit.Negated())
continue;
1456 if (marked_[l.NegatedIndex()]) {
1462 if (!score_only) resolvant_.push_back(l);
1466 if (!score_only) resolvant_.resize(resolvant_.size() - extra_size);
1472 if (score_only && clause.size() + extra_size <= other.size()) {
1478 if (
false) DCHECK_EQ(clause.size() + extra_size, other.size());
1479 ++num_simplifications_;
1483 score_threshold_ -= clause_weight + other.size();
1485 if (extra_size == 0) {
1489 DeleteClause(clauses_[other_index]);
1491 if (!RemoveLiteralFromClause(lit.Negated(),
1492 clauses_[other_index])) {
1495 std::swap(clause_containing_not_lit[j],
1496 clause_containing_not_lit.back());
1497 clause_containing_not_lit.pop_back();
1503 if (extra_size == 0) {
1504 clause_can_be_simplified =
true;
1509 if (clause.size() - 1 + extra_size > 100) {
1510 new_score_ = score_threshold_ + 1;
1514 new_score_ += clause_weight + clause.size() - 1 + extra_size;
1516 AddClause(resolvant_);
1517 resolvant_.resize(resolvant_.size() - extra_size);
1524 for (
const Literal l : clause) marked_[l] =
false;
1527 if (clause_can_be_simplified) {
1528 ++num_simplifications_;
1531 new_score_ = saved_score;
1532 score_threshold_ -= clause_weight + clause.size();
1534 if (!RemoveLiteralFromClause(lit, clauses_[clause_index]))
return false;
1535 std::swap(clause_containing_lit[
i], clause_containing_lit.back());
1536 clause_containing_lit.pop_back();
1540 if (score_only && new_score_ > score_threshold_)
return true;
1552 if (score_only && !with_binary_only && !clause_can_be_simplified &&
1553 new_score_ == saved_score) {
1554 ++num_blocked_clauses_;
1555 score_threshold_ -= clause_weight + clause.size();
1556 postsolve_->AddClauseWithSpecialLiteral(lit, clause);
1557 DeleteClause(clauses_[clause_index]);
1563bool BoundedVariableElimination::CrossProduct(BooleanVariable var) {
1564 if (assignment_.VariableIsAssigned(var))
return true;
1566 const Literal lit(var,
true);
1567 const Literal not_lit(var,
false);
1568 DCHECK(!implication_graph_->IsRedundant(lit));
1570 const int s1 = NumClausesContaining(lit);
1571 const int s2 = NumClausesContaining(not_lit);
1572 if (s1 == 0 && s2 == 0)
return true;
1573 if (s1 > 0 && s2 == 0) {
1574 num_eliminated_variables_++;
1575 if (!clause_manager_->InprocessingFixLiteral(lit))
return false;
1576 DeleteAllClausesContaining(lit);
1579 if (s1 == 0 && s2 > 0) {
1580 num_eliminated_variables_++;
1581 if (!clause_manager_->InprocessingFixLiteral(not_lit))
return false;
1582 DeleteAllClausesContaining(not_lit);
1588 if (s1 > 1 && s2 > 1 && s1 * s2 > parameters_.presolve_bve_threshold()) {
1601 const int clause_weight = parameters_.presolve_bve_clause_weight();
1603 implication_graph_->DirectImplications(lit).size() * (clause_weight + 2);
1604 score += implication_graph_->DirectImplications(not_lit).size() *
1605 (clause_weight + 2);
1606 for (
const ClauseIndex
i : literal_to_clauses_[lit]) {
1607 const auto c = clauses_[
i]->AsSpan();
1608 if (!
c.empty()) score += clause_weight +
c.size();
1610 for (
const ClauseIndex
i : literal_to_clauses_[not_lit]) {
1611 const auto c = clauses_[
i]->AsSpan();
1612 if (!
c.empty()) score += clause_weight +
c.size();
1623 score_threshold_ = score;
1624 new_score_ = implication_graph_->NumImplicationOnVariableRemoval(var) *
1625 (clause_weight + 2);
1626 if (new_score_ > score_threshold_)
return true;
1627 if (!ResolveAllClauseContaining<
true,
1631 if (new_score_ > score_threshold_)
return true;
1632 if (!ResolveAllClauseContaining<
true,
1636 if (new_score_ > score_threshold_)
return true;
1642 if (new_score_ > 0) {
1643 if (!ResolveAllClauseContaining<
false,
1647 if (!ResolveAllClauseContaining<
false,
1653 ++num_eliminated_variables_;
1654 implication_graph_->RemoveBooleanVariable(var, &postsolve_->clauses);
1655 DeleteAllClausesContaining(lit);
1656 DeleteAllClausesContaining(not_lit);
void Start()
When Start() is called multiple times, only the most recent is used.
void Set(IntegerType index)
void DoOneRound(bool log_info)
bool DoOneRound(bool log_info)
void InprocessingRemoveClause(SatClause *clause)
These must only be called between [Detach/Attach]AllClauses() calls.
bool IsRemovable(SatClause *const clause) const
void DeleteRemovedClauses()
int64_t literal_size() const
The number of different literals (always twice the number of variables).
const std::vector< SatClause * > & AllClausesInCreationOrder() const
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)
bool LevelZeroPropagate()
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
absl::Span< const Literal > AsSpan() const
Returns a Span<> representation of the clause.
int size() const
Number of literals in the clause.
bool ComputeStampsForNextRound(bool log_info)
bool ImplicationIsInTree(Literal a, Literal b) const
void SampleTreeAndFillParent()
Visible for testing.
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)
const LiteralIndex kNoLiteralIndex(-1)
bool FailedLiteralProbingRound(ProbingOptions options, Model *model)
In SWIG mode, we don't want anything besides these top-level includes.
ClosedInterval::Iterator end(ClosedInterval interval)
#define RETURN_IF_FALSE(f)
std::deque< std::vector< Literal > > clauses
void AddClauseWithSpecialLiteral(Literal literal, absl::Span< const Literal > clause)
double deterministic_limit
bool log_info
We assume this is also true if –v 1 is activated.
bool extract_binary_clauses
double deterministic_time_limit
The time budget to spend.
bool use_transitive_reduction
bool extract_binary_clauses_in_probing
See ProbingOptions in probing.h.
#define SOLVER_LOG(logger,...)