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/types/span.h"
39#include "ortools/sat/sat_parameters.pb.h"
51 Literal literal, absl::Span<const Literal> clause) {
53 clauses.emplace_back(clause.begin(), clause.end());
54 for (
int i = 0;
i < clause.size(); ++
i) {
55 if (clause[
i] == literal) {
64#define RETURN_IF_FALSE(f) \
65 if (!(f)) return false;
72 double probing_time = 0.0;
73 const bool log_round_info = VLOG_IS_ON(1);
80 const double start_dtime = time_limit_->GetElapsedDeterministicTime();
82 while (!time_limit_->LimitReached() &&
83 time_limit_->GetElapsedDeterministicTime() <= stop_dtime) {
84 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
88 implication_graph_->RemoveFixedVariables();
89 implication_graph_->RemoveDuplicates();
107 implication_graph_->RemoveAllRedundantVariables(&postsolve_->clauses);
114 !implication_graph_->IsDag()) {
120 !implication_graph_->IsDag()) {
127 if (!params_.fill_tightened_domains_in_response()) {
128 blocked_clause_simplifier_->DoOneRound(log_round_info);
133 RETURN_IF_FALSE(bounded_variable_elimination_->DoOneRound(log_round_info));
137 const double saved_wtime = wall_timer.
Get();
138 const double time_left =
139 stop_dtime - time_limit_->GetElapsedDeterministicTime();
140 if (time_left <= 0)
break;
142 probing_options.
log_info = log_round_info;
147 probing_time += wall_timer.
Get() - saved_wtime;
150 !implication_graph_->IsDag()) {
161 logger_,
"[Pure SAT presolve]",
" num_fixed: ", trail_->Index(),
162 " num_redundant: ", implication_graph_->num_redundant_literals() / 2,
"/",
163 sat_solver_->NumVariables(),
164 " num_implications: ", implication_graph_->num_implications(),
165 " num_watched_clauses: ", clause_manager_->num_watched_clauses(),
166 " dtime: ", time_limit_->GetElapsedDeterministicTime() - start_dtime,
"/",
168 " non-probing time: ", (wall_timer.
Get() - probing_time));
173 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
174 if (sat_solver_->ModelIsUnsat())
return false;
178 const bool log_info = VLOG_IS_ON(1);
179 const bool log_round_info = VLOG_IS_ON(2);
180 const double start_dtime = time_limit_->GetElapsedDeterministicTime();
183 double probing_time = 0.0;
187 if (first_inprocessing_call_) {
188 reference_dtime_ = start_dtime;
189 first_inprocessing_call_ =
false;
197 const double diff = start_dtime - reference_dtime_;
198 if (total_dtime_ > params_.inprocessing_dtime_ratio() * diff) {
207 std::vector<std::pair<LinearProgrammingConstraint*, bool>> saved_state;
209 saved_state.push_back({lp, lp->PropagationIsEnabled()});
210 lp->EnablePropagation(
false);
212 auto cleanup = absl::MakeCleanup([&saved_state]() {
213 for (
const auto [lp, old_value] : saved_state) {
214 lp->EnablePropagation(old_value);
223 decision_policy_->MaybeEnablePhaseSaving(
false);
225 implication_graph_->RemoveDuplicates();
231 if (params_.inprocessing_probing_dtime() > 0.0) {
232 const double saved_wtime = wall_timer.
Get();
234 probing_options.
log_info = log_round_info;
238 probing_time += wall_timer.
Get() - saved_wtime;
250 const auto old_counter = sat_solver_->counters();
251 if (params_.inprocessing_minimization_dtime() > 0.0) {
253 params_.inprocessing_minimization_dtime()));
255 const int64_t mini_num_clause =
256 sat_solver_->counters().minimization_num_clauses -
257 old_counter.minimization_num_clauses;
258 const int64_t mini_num_removed =
259 sat_solver_->counters().minimization_num_removed_literals -
260 old_counter.minimization_num_removed_literals;
269 blocked_clause_simplifier_->DoOneRound(log_round_info);
270 RETURN_IF_FALSE(bounded_variable_elimination_->DoOneRound(log_round_info));
274 sat_solver_->AdvanceDeterministicTime(time_limit_);
275 total_dtime_ += time_limit_->GetElapsedDeterministicTime() - start_dtime;
278 logger_,
"Inprocessing.",
" fixed:", trail_->Index(),
279 " equiv:", implication_graph_->num_redundant_literals() / 2,
280 " bools:", sat_solver_->NumVariables(),
281 " implications:", implication_graph_->num_implications(),
282 " watched:", clause_manager_->num_watched_clauses(),
283 " minimization:", mini_num_clause,
"|", mini_num_removed,
284 " dtime:", time_limit_->GetElapsedDeterministicTime() - start_dtime,
285 " wtime:", wall_timer.
Get(),
286 " np_wtime:", (wall_timer.
Get() - probing_time));
289 DCHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
290 decision_policy_->MaybeEnablePhaseSaving(
true);
294#undef RETURN_IF_FALSE
297 const int64_t new_num_fixed_variables = trail_->Index();
298 return last_num_fixed_variables_ < new_num_fixed_variables;
302 const int64_t new_num_redundant_literals =
303 implication_graph_->num_redundant_literals();
304 return last_num_redundant_literals_ < new_num_redundant_literals;
308 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
309 clause_manager_->AttachAllClauses();
310 return sat_solver_->Propagate();
318 implication_graph_->RemoveFixedVariables();
319 if (!implication_graph_->IsDag()) {
323 if (!implication_graph_->DetectEquivalences(log_info))
return false;
325 if (use_transitive_reduction) {
326 if (!implication_graph_->ComputeTransitiveReduction(log_info)) {
333 if (!stamping_simplifier_->ComputeStampsForNextRound(log_info))
return false;
343 CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
352 const int64_t new_num_redundant_literals =
353 implication_graph_->num_redundant_literals();
354 const int64_t new_num_fixed_variables = trail_->Index();
355 if (last_num_redundant_literals_ == new_num_redundant_literals &&
356 last_num_fixed_variables_ == new_num_fixed_variables) {
359 last_num_fixed_variables_ = new_num_fixed_variables;
360 last_num_redundant_literals_ = new_num_redundant_literals;
366 int64_t num_removed_literals = 0;
367 int64_t num_inspected_literals = 0;
371 std::vector<Literal> new_clause;
374 const int num_literals(sat_solver_->NumVariables() * 2);
377 clause_manager_->DeleteRemovedClauses();
378 clause_manager_->DetachAllClauses();
379 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
380 bool removed =
false;
381 bool need_rewrite =
false;
384 for (
const Literal l : clause->AsSpan()) {
385 if (assignment_.LiteralIsTrue(l)) {
389 if (!clause_manager_->InprocessingFixLiteral(l))
return false;
390 clause_manager_->InprocessingRemoveClause(clause);
391 num_removed_literals += clause->size();
395 if (assignment_.LiteralIsFalse(l) || implication_graph_->IsRedundant(l)) {
401 num_inspected_literals += clause->size();
402 if (removed || !need_rewrite)
continue;
403 num_inspected_literals += clause->size();
407 for (
const Literal l : clause->AsSpan()) {
408 const Literal r = implication_graph_->RepresentativeOf(l);
409 if (marked[r] || assignment_.LiteralIsFalse(r)) {
412 if (marked[r.
NegatedIndex()] || assignment_.LiteralIsTrue(r)) {
413 clause_manager_->InprocessingRemoveClause(clause);
414 num_removed_literals += clause->size();
419 new_clause.push_back(r);
423 for (
const Literal l : new_clause) marked[l] =
false;
424 if (removed)
continue;
426 num_removed_literals += clause->size() - new_clause.size();
427 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause)) {
433 const double dtime =
static_cast<double>(num_inspected_literals) * 1e-8;
434 time_limit_->AdvanceDeterministicTime(dtime);
435 LOG_IF(INFO, log_info) <<
"Cleanup. num_removed_literals: "
436 << num_removed_literals <<
" dtime: " << dtime
437 <<
" wtime: " << wall_timer.
Get();
450 int64_t num_subsumed_clauses = 0;
451 int64_t num_removed_literals = 0;
452 int64_t num_inspected_signatures = 0;
453 int64_t num_inspected_literals = 0;
457 std::vector<Literal> new_clause;
464 clause_manager_->DeleteRemovedClauses();
465 clause_manager_->DetachAllClauses();
469 std::vector<SatClause*> clauses =
470 clause_manager_->AllClausesInCreationOrder();
472 clauses.begin(), clauses.end(),
476 const LiteralIndex num_literals(sat_solver_->NumVariables() * 2);
482 one_watcher(num_literals.value());
485 std::vector<uint64_t> signatures(clauses.size());
487 std::vector<Literal> candidates_for_removal;
488 for (
int clause_index = 0; clause_index < clauses.size(); ++clause_index) {
489 SatClause* clause = clauses[clause_index];
495 if (num_inspected_literals + num_inspected_signatures > 1e9) {
510 uint64_t signature = 0;
513 marked.
Set(l.Index());
514 signature |= (uint64_t{1} << (l.Variable().value() % 64));
520 bool removed =
false;
521 candidates_for_removal.clear();
522 const uint64_t mask = ~signature;
524 num_inspected_signatures += one_watcher[l].size();
525 for (
const int i : one_watcher[l]) {
526 if ((mask & signatures[
i]) != 0)
continue;
528 bool subsumed =
true;
529 bool stengthen =
true;
531 num_inspected_literals += clauses[
i]->size();
532 for (
const Literal o : clauses[
i]->AsSpan()) {
536 to_remove = o.NegatedIndex();
544 ++num_subsumed_clauses;
545 num_removed_literals += clause->
size();
546 clause_manager_->InprocessingRemoveClause(clause);
552 candidates_for_removal.push_back(
Literal(to_remove));
557 if (removed)
continue;
561 num_inspected_signatures += one_watcher[l.NegatedIndex()].size();
562 for (
const int i : one_watcher[l.NegatedIndex()]) {
563 if ((mask & signatures[
i]) != 0)
continue;
565 bool stengthen =
true;
566 num_inspected_literals += clauses[
i]->size();
567 for (
const Literal o : clauses[
i]->AsSpan()) {
568 if (o == l.Negated())
continue;
575 candidates_for_removal.push_back(l);
586 if (!candidates_for_removal.empty()) {
589 if (l == candidates_for_removal[0])
continue;
590 new_clause.push_back(l);
592 CHECK_EQ(new_clause.size() + 1, clause->
size());
594 num_removed_literals += clause->
size() - new_clause.size();
595 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause)) {
598 if (clause->
size() == 0)
continue;
603 signature |= (uint64_t{1} << (l.Variable().value() % 64));
620 if (!clause_manager_->IsRemovable(clause)) {
621 int min_size = std::numeric_limits<int32_t>::max();
624 if (one_watcher[l].size() < min_size) {
625 min_size = one_watcher[l].size();
626 min_literal = l.Index();
636 signatures[clause_index] = signature;
637 one_watcher[min_literal].
push_back(clause_index);
645 const double dtime =
static_cast<double>(num_inspected_signatures) * 1e-8 +
646 static_cast<double>(num_inspected_literals) * 5e-9;
647 time_limit_->AdvanceDeterministicTime(dtime);
648 LOG_IF(INFO, log_info) <<
"Subsume. num_removed_literals: "
649 << num_removed_literals
650 <<
" num_subsumed: " << num_subsumed_clauses
651 <<
" dtime: " << dtime
652 <<
" wtime: " << wall_timer.
Get();
661 num_subsumed_clauses_ = 0;
662 num_removed_literals_ = 0;
665 if (implication_graph_->literal_size() == 0)
return true;
666 if (implication_graph_->num_implications() == 0)
return true;
668 if (!stamps_are_already_computed_) {
672 implication_graph_->RemoveFixedVariables();
673 if (!implication_graph_->DetectEquivalences(log_info))
return true;
677 stamps_are_already_computed_ =
false;
682 time_limit_->AdvanceDeterministicTime(dtime_);
683 LOG_IF(INFO, log_info) <<
"Stamping. num_removed_literals: "
684 << num_removed_literals_
685 <<
" num_subsumed: " << num_subsumed_clauses_
686 <<
" num_fixed: " << num_fixed_ <<
" dtime: " << dtime_
687 <<
" wtime: " << wall_timer.
Get();
697 if (implication_graph_->literal_size() == 0)
return true;
698 if (implication_graph_->num_implications() == 0)
return true;
700 implication_graph_->RemoveFixedVariables();
701 if (!implication_graph_->DetectEquivalences(log_info))
return true;
704 stamps_are_already_computed_ =
true;
707 time_limit_->AdvanceDeterministicTime(dtime_);
708 LOG_IF(INFO, log_info) <<
"Prestamping."
709 <<
" num_fixed: " << num_fixed_ <<
" dtime: " << dtime_
710 <<
" wtime: " << wall_timer.
Get();
715 const int size = implication_graph_->literal_size();
716 CHECK(implication_graph_->IsDag());
717 parents_.resize(size);
718 for (LiteralIndex
i(0);
i < size; ++
i) {
720 if (implication_graph_->IsRedundant(
Literal(
i)))
continue;
721 if (assignment_.LiteralIsAssigned(
Literal(
i)))
continue;
731 for (
int num_tries = 0; num_tries < 10; ++num_tries) {
734 const LiteralIndex index =
735 implication_graph_->RandomImpliedLiteral(
Literal(
i).Negated());
739 if (implication_graph_->IsRedundant(candidate))
continue;
740 if (
i == candidate.
Index())
continue;
743 parents_[
i] = candidate.
Index();
750 const int size = implication_graph_->literal_size();
753 sizes_.assign(size, 0);
754 for (LiteralIndex
i(0);
i < size; ++
i) {
755 if (parents_[
i] ==
i)
continue;
756 sizes_[parents_[
i]]++;
760 starts_.resize(size + 1);
761 starts_[LiteralIndex(0)] = 0;
762 for (LiteralIndex
i(1);
i <= size; ++
i) {
763 starts_[
i] = starts_[
i - 1] + sizes_[
i - 1];
767 children_.resize(size);
768 for (LiteralIndex
i(0);
i < size; ++
i) {
769 if (parents_[
i] ==
i)
continue;
770 children_[starts_[parents_[
i]]++] =
i;
774 for (LiteralIndex
i(0);
i < size; ++
i) {
775 starts_[
i] -= sizes_[
i];
779 CHECK_EQ(starts_[LiteralIndex(0)], 0);
780 for (LiteralIndex
i(1);
i <= size; ++
i) {
781 CHECK_EQ(starts_[
i], starts_[
i - 1] + sizes_[
i - 1]);
787 first_stamps_.resize(size);
788 last_stamps_.resize(size);
789 marked_.assign(size,
false);
790 for (LiteralIndex
i(0);
i < size; ++
i) {
791 if (parents_[
i] !=
i)
continue;
793 const LiteralIndex tree_root =
i;
794 dfs_stack_.push_back(
i);
795 while (!dfs_stack_.empty()) {
796 const LiteralIndex top = dfs_stack_.back();
798 dfs_stack_.pop_back();
799 last_stamps_[top] = stamp++;
802 first_stamps_[top] = stamp++;
807 if (marked_[
Literal(top).NegatedIndex()] &&
808 first_stamps_[
Literal(top).NegatedIndex()] >=
809 first_stamps_[tree_root]) {
812 LiteralIndex lca = top;
813 while (first_stamps_[lca] > first_stamp) {
817 if (!clause_manager_->InprocessingFixLiteral(
Literal(lca).Negated())) {
822 const int end = starts_[top + 1];
823 for (
int j = starts_[top]; j < end; ++j) {
824 DCHECK_NE(top, children_[j]);
825 DCHECK(!marked_[children_[j]]);
826 dfs_stack_.push_back(children_[j]);
830 DCHECK_EQ(stamp, 2 * size);
840 bool operator<(
const Entry& o)
const {
return start < o.start; }
842 std::vector<int> to_remove;
843 std::vector<Literal> new_clause;
844 std::vector<Entry> entries;
845 clause_manager_->DeleteRemovedClauses();
846 clause_manager_->DetachAllClauses();
847 for (
SatClause* clause : clause_manager_->AllClausesInCreationOrder()) {
848 const auto span = clause->AsSpan();
849 if (span.empty())
continue;
858 for (
int i = 0;
i < span.size(); ++
i) {
859 if (assignment_.LiteralIsTrue(span[
i])) {
860 clause_manager_->InprocessingRemoveClause(clause);
863 if (assignment_.LiteralIsFalse(span[
i]))
continue;
865 {
i,
false, first_stamps_[span[
i]], last_stamps_[span[
i]]});
866 entries.push_back({
i,
true, first_stamps_[span[
i].NegatedIndex()],
867 last_stamps_[span[
i].NegatedIndex()]});
869 if (clause->IsRemoved())
continue;
872 if (!entries.empty()) {
873 const double n =
static_cast<double>(entries.size());
874 dtime_ += 1.5e-8 * n * std::log(n);
875 std::sort(entries.begin(), entries.end());
881 for (
const Entry& e : entries) {
882 if (e.end < top_entry.end) {
884 const Literal lhs = top_entry.is_negated ? span[top_entry.i].Negated()
886 const Literal rhs = e.is_negated ? span[e.i].
Negated() : span[e.i];
889 if (top_entry.is_negated != e.is_negated) {
891 if (top_entry.i == e.i) {
893 if (top_entry.is_negated) {
896 if (!clause_manager_->InprocessingFixLiteral(span[top_entry.i])) {
901 if (!clause_manager_->InprocessingFixLiteral(
902 span[top_entry.i].Negated())) {
905 to_remove.push_back(top_entry.i);
914 if (top_entry.is_negated) {
915 num_subsumed_clauses_++;
916 clause_manager_->InprocessingRemoveClause(clause);
920 CHECK_NE(top_entry.i, e.i);
921 if (top_entry.is_negated) {
923 to_remove.push_back(e.i);
932 to_remove.push_back(top_entry.i);
940 if (clause->IsRemoved())
continue;
943 if (!to_remove.empty() || entries.size() < span.size()) {
946 int to_remove_index = 0;
947 for (
int i = 0;
i < span.size(); ++
i) {
948 if (to_remove_index < to_remove.size() &&
949 i == to_remove[to_remove_index]) {
953 if (assignment_.LiteralIsTrue(span[
i])) {
954 clause_manager_->InprocessingRemoveClause(clause);
957 if (assignment_.LiteralIsFalse(span[
i]))
continue;
958 new_clause.push_back(span[
i]);
960 num_removed_literals_ += span.size() - new_clause.size();
961 if (!clause_manager_->InprocessingRewriteClause(clause, new_clause)) {
974 num_blocked_clauses_ = 0;
975 num_inspected_literals_ = 0;
977 InitializeForNewRound();
979 while (!time_limit_->LimitReached() && !queue_.empty()) {
980 const Literal l = queue_.front();
981 in_queue_[l] =
false;
986 if (num_inspected_literals_ <= 1e9) ProcessLiteral(l);
990 literal_to_clauses_.clear();
992 dtime_ += 1e-8 * num_inspected_literals_;
993 time_limit_->AdvanceDeterministicTime(dtime_);
994 log_info |= VLOG_IS_ON(1);
995 LOG_IF(INFO, log_info) <<
"Blocked clause. num_blocked_clauses: "
996 << num_blocked_clauses_ <<
" dtime: " << dtime_
997 <<
" wtime: " << wall_timer.
Get();
1000void BlockedClauseSimplifier::InitializeForNewRound() {
1008 clauses_.push_back(c);
1010 const int num_literals = clause_manager_->
literal_size();
1014 in_queue_.
assign(num_literals,
true);
1015 for (LiteralIndex l(0); l < num_literals; ++l) {
1016 queue_.push_back(Literal(l));
1019 marked_.
resize(num_literals);
1021 std::all_of(marked_.begin(), marked_.end(), [](
bool b) { return !b; }));
1025 literal_to_clauses_.clear();
1026 literal_to_clauses_.
resize(num_literals);
1027 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
1028 for (
const Literal l : clauses_[
i]->AsSpan()) {
1031 num_inspected_literals_ += clauses_[
i]->size();
1035void BlockedClauseSimplifier::ProcessLiteral(
Literal current_literal) {
1036 if (assignment_.LiteralIsAssigned(current_literal))
return;
1037 if (implication_graph_->IsRemoved(current_literal))
return;
1052 const std::vector<Literal>& implications =
1053 implication_graph_->DirectImplications(current_literal);
1054 for (
const Literal l : implications) {
1055 if (l == current_literal)
continue;
1063 std::vector<ClauseIndex> clauses_to_process;
1064 for (
const ClauseIndex
i : literal_to_clauses_[current_literal]) {
1065 if (clauses_[
i]->IsRemoved())
continue;
1071 if (num_binary > 0) {
1072 if (clauses_[
i]->size() <= num_binary)
continue;
1073 int num_with_negation_marked = 0;
1074 for (
const Literal l : clauses_[
i]->AsSpan()) {
1075 if (l == current_literal)
continue;
1076 if (marked_[l.NegatedIndex()]) {
1077 ++num_with_negation_marked;
1080 num_inspected_literals_ += clauses_[
i]->size();
1081 if (num_with_negation_marked < num_binary)
continue;
1083 clauses_to_process.push_back(
i);
1087 for (
const Literal l : implications) {
1099 for (
const ClauseIndex
i : clauses_to_process) {
1100 const auto c = clauses_[
i]->AsSpan();
1101 if (ClauseIsBlocked(current_literal, c)) {
1108 for (
const Literal l : c) {
1109 if (!in_queue_[l.NegatedIndex()]) {
1110 in_queue_[l.NegatedIndex()] =
true;
1111 queue_.push_back(l.Negated());
1116 postsolve_->AddClauseWithSpecialLiteral(current_literal, c);
1119 ++num_blocked_clauses_;
1120 clause_manager_->InprocessingRemoveClause(clauses_[
i]);
1126bool BlockedClauseSimplifier::ClauseIsBlocked(
1127 Literal current_literal, absl::Span<const Literal> clause) {
1128 bool is_blocked =
true;
1129 for (
const Literal l : clause) marked_[l] =
true;
1133 for (
const ClauseIndex
i :
1134 literal_to_clauses_[current_literal.NegatedIndex()]) {
1135 if (clauses_[
i]->IsRemoved())
continue;
1136 bool some_marked =
false;
1137 for (
const Literal l : clauses_[
i]->AsSpan()) {
1139 ++num_inspected_literals_;
1141 if (l == current_literal.Negated())
continue;
1142 if (marked_[l.NegatedIndex()]) {
1153 for (
const Literal l : clause) marked_[l] =
false;
1162 num_inspected_literals_ = 0;
1163 num_eliminated_variables_ = 0;
1164 num_literals_diff_ = 0;
1165 num_clauses_diff_ = 0;
1166 num_simplifications_ = 0;
1167 num_blocked_clauses_ = 0;
1170 clause_manager_->DeleteRemovedClauses();
1171 clause_manager_->DetachAllClauses();
1172 for (
SatClause* c : clause_manager_->AllClausesInCreationOrder()) {
1176 if (clause_manager_->IsRemovable(c))
continue;
1178 clauses_.push_back(c);
1180 const int num_literals = clause_manager_->literal_size();
1181 const int num_variables = num_literals / 2;
1183 literal_to_clauses_.clear();
1184 literal_to_clauses_.resize(num_literals);
1185 literal_to_num_clauses_.assign(num_literals, 0);
1186 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
1187 for (
const Literal l : clauses_[
i]->AsSpan()) {
1188 literal_to_clauses_[l].push_back(
i);
1189 literal_to_num_clauses_[l]++;
1191 num_inspected_literals_ += clauses_[
i]->size();
1194 const int saved_trail_index = trail_->Index();
1195 propagation_index_ = trail_->Index();
1197 need_to_be_updated_.clear();
1198 in_need_to_be_updated_.resize(num_variables);
1199 DCHECK(absl::c_find(in_need_to_be_updated_,
true) ==
1200 in_need_to_be_updated_.end());
1201 queue_.Reserve(num_variables);
1202 for (BooleanVariable v(0); v < num_variables; ++v) {
1203 if (assignment_.VariableIsAssigned(v))
continue;
1204 UpdatePriorityQueue(v);
1207 marked_.resize(num_literals);
1209 std::all_of(marked_.begin(), marked_.end(), [](
bool b) { return !b; }));
1214 while (!time_limit_->LimitReached() && !queue_.IsEmpty()) {
1215 const BooleanVariable top = queue_.Top().var;
1223 bool is_unsat =
false;
1224 if (!Propagate())
return false;
1225 while (implication_graph_->FindFailedLiteralAroundVar(top, &is_unsat)) {
1226 if (!Propagate())
return false;
1228 if (is_unsat)
return false;
1230 if (!CrossProduct(top))
return false;
1232 for (
const BooleanVariable v : need_to_be_updated_) {
1233 in_need_to_be_updated_[v] =
false;
1236 if (v != top) UpdatePriorityQueue(v);
1238 need_to_be_updated_.clear();
1241 if (!Propagate())
return false;
1242 implication_graph_->CleanupAllRemovedAndFixedVariables();
1246 for (
SatClause* c : clause_manager_->AllClausesInCreationOrder()) {
1247 bool remove =
false;
1248 for (
const Literal l : c->AsSpan()) {
1249 if (implication_graph_->IsRemoved(l)) {
1254 if (remove) clause_manager_->InprocessingRemoveClause(c);
1258 literal_to_clauses_.clear();
1259 literal_to_num_clauses_.clear();
1261 dtime_ += 1e-8 * num_inspected_literals_;
1262 time_limit_->AdvanceDeterministicTime(dtime_);
1263 log_info |= VLOG_IS_ON(1);
1264 LOG_IF(INFO, log_info) <<
"BVE."
1266 << trail_->Index() - saved_trail_index
1267 <<
" num_simplified_literals: " << num_simplifications_
1268 <<
" num_blocked_clauses_: " << num_blocked_clauses_
1269 <<
" num_eliminations: " << num_eliminated_variables_
1270 <<
" num_literals_diff: " << num_literals_diff_
1271 <<
" num_clause_diff: " << num_clauses_diff_
1272 <<
" dtime: " << dtime_
1273 <<
" wtime: " << wall_timer.
Get();
1277bool BoundedVariableElimination::RemoveLiteralFromClause(
1279 num_literals_diff_ -= sat_clause->
size();
1283 literal_to_num_clauses_[l]--;
1287 num_clauses_diff_--;
1291 resolvant_.push_back(l);
1293 if (!clause_manager_->InprocessingRewriteClause(sat_clause, resolvant_)) {
1297 --num_clauses_diff_;
1298 for (
const Literal l : resolvant_) literal_to_num_clauses_[l]--;
1300 num_literals_diff_ += sat_clause->
size();
1305bool BoundedVariableElimination::Propagate() {
1306 for (; propagation_index_ < trail_->Index(); ++propagation_index_) {
1308 if (!implication_graph_->Propagate(trail_))
return false;
1310 const Literal l = (*trail_)[propagation_index_];
1311 for (
const ClauseIndex index : literal_to_clauses_[l]) {
1312 if (clauses_[index]->IsRemoved())
continue;
1313 num_clauses_diff_--;
1314 num_literals_diff_ -= clauses_[index]->size();
1315 clause_manager_->InprocessingRemoveClause(clauses_[index]);
1317 literal_to_clauses_[l].clear();
1318 for (
const ClauseIndex index : literal_to_clauses_[l.NegatedIndex()]) {
1319 if (clauses_[index]->IsRemoved())
continue;
1320 if (!RemoveLiteralFromClause(l.Negated(), clauses_[index]))
return false;
1322 literal_to_clauses_[l.NegatedIndex()].clear();
1329int BoundedVariableElimination::NumClausesContaining(
Literal l) {
1330 return literal_to_num_clauses_[l] +
1331 implication_graph_->DirectImplicationsEstimatedSize(l.Negated());
1335void BoundedVariableElimination::UpdatePriorityQueue(BooleanVariable var) {
1336 if (assignment_.VariableIsAssigned(var))
return;
1337 if (implication_graph_->IsRemoved(Literal(var,
true)))
return;
1338 if (implication_graph_->IsRedundant(Literal(var,
true)))
return;
1339 const int priority = -NumClausesContaining(Literal(var,
true)) -
1340 NumClausesContaining(Literal(var,
false));
1341 if (queue_.Contains(var.value())) {
1342 queue_.ChangePriority({var, priority});
1344 queue_.Add({var, priority});
1348void BoundedVariableElimination::DeleteClause(
SatClause* sat_clause) {
1349 const auto clause = sat_clause->AsSpan();
1351 num_clauses_diff_--;
1352 num_literals_diff_ -= clause.size();
1355 for (
const Literal l : clause) {
1356 literal_to_num_clauses_[l]--;
1357 if (!in_need_to_be_updated_[l.Variable()]) {
1358 in_need_to_be_updated_[l.Variable()] =
true;
1359 need_to_be_updated_.push_back(l.Variable());
1364 clause_manager_->InprocessingRemoveClause(sat_clause);
1367void BoundedVariableElimination::DeleteAllClausesContaining(
Literal literal) {
1368 for (
const ClauseIndex
i : literal_to_clauses_[literal]) {
1369 const auto clause = clauses_[
i]->AsSpan();
1370 if (clause.empty())
continue;
1371 postsolve_->AddClauseWithSpecialLiteral(literal, clause);
1372 DeleteClause(clauses_[
i]);
1374 literal_to_clauses_[literal].clear();
1377void BoundedVariableElimination::AddClause(absl::Span<const Literal> clause) {
1378 SatClause* pt = clause_manager_->InprocessingAddClause(clause);
1379 if (pt ==
nullptr)
return;
1381 num_clauses_diff_++;
1382 num_literals_diff_ += clause.size();
1384 const ClauseIndex index(clauses_.size());
1385 clauses_.push_back(pt);
1386 for (
const Literal l : clause) {
1387 literal_to_num_clauses_[l]++;
1388 literal_to_clauses_[l].push_back(index);
1389 if (!in_need_to_be_updated_[l.Variable()]) {
1390 in_need_to_be_updated_[l.Variable()] =
true;
1391 need_to_be_updated_.push_back(l.Variable());
1396template <
bool score_only,
bool with_binary_only>
1397bool BoundedVariableElimination::ResolveAllClauseContaining(
Literal lit) {
1398 const int clause_weight = parameters_.presolve_bve_clause_weight();
1400 const std::vector<Literal>& implications =
1401 implication_graph_->DirectImplications(lit);
1402 auto& clause_containing_lit = literal_to_clauses_[lit];
1403 for (
int i = 0;
i < clause_containing_lit.size(); ++
i) {
1404 const ClauseIndex clause_index = clause_containing_lit[
i];
1405 const auto clause = clauses_[clause_index]->AsSpan();
1406 if (clause.empty())
continue;
1408 if (!score_only) resolvant_.clear();
1409 for (
const Literal l : clause) {
1410 if (!score_only && l != lit) resolvant_.push_back(l);
1413 DCHECK(marked_[lit]);
1414 num_inspected_literals_ += clause.size() + implications.size();
1418 bool clause_can_be_simplified =
false;
1419 const int64_t saved_score = new_score_;
1422 for (
const Literal l : implications) {
1424 if (marked_[l.NegatedIndex()])
continue;
1426 clause_can_be_simplified =
true;
1430 new_score_ += clause_weight + clause.size();
1432 resolvant_.push_back(l);
1433 AddClause(resolvant_);
1434 resolvant_.pop_back();
1440 if (!with_binary_only && !clause_can_be_simplified) {
1441 auto& clause_containing_not_lit = literal_to_clauses_[lit.NegatedIndex()];
1442 for (
int j = 0; j < clause_containing_not_lit.size(); ++j) {
1443 if (score_only && new_score_ > score_threshold_)
break;
1444 const ClauseIndex other_index = clause_containing_not_lit[j];
1445 const auto other = clauses_[other_index]->AsSpan();
1446 if (other.empty())
continue;
1447 bool trivial =
false;
1449 for (
const Literal l : other) {
1451 ++num_inspected_literals_;
1452 if (l == lit.Negated())
continue;
1453 if (marked_[l.NegatedIndex()]) {
1459 if (!score_only) resolvant_.push_back(l);
1463 if (!score_only) resolvant_.resize(resolvant_.size() - extra_size);
1469 if (score_only && clause.size() + extra_size <= other.size()) {
1475 if (
false) DCHECK_EQ(clause.size() + extra_size, other.size());
1476 ++num_simplifications_;
1480 score_threshold_ -= clause_weight + other.size();
1482 if (extra_size == 0) {
1486 DeleteClause(clauses_[other_index]);
1488 if (!RemoveLiteralFromClause(lit.Negated(),
1489 clauses_[other_index])) {
1492 std::swap(clause_containing_not_lit[j],
1493 clause_containing_not_lit.back());
1494 clause_containing_not_lit.pop_back();
1500 if (extra_size == 0) {
1501 clause_can_be_simplified =
true;
1506 if (clause.size() - 1 + extra_size > 100) {
1507 new_score_ = score_threshold_ + 1;
1511 new_score_ += clause_weight + clause.size() - 1 + extra_size;
1513 AddClause(resolvant_);
1514 resolvant_.resize(resolvant_.size() - extra_size);
1521 for (
const Literal l : clause) marked_[l] =
false;
1524 if (clause_can_be_simplified) {
1525 ++num_simplifications_;
1528 new_score_ = saved_score;
1529 score_threshold_ -= clause_weight + clause.size();
1531 if (!RemoveLiteralFromClause(lit, clauses_[clause_index]))
return false;
1532 std::swap(clause_containing_lit[
i], clause_containing_lit.back());
1533 clause_containing_lit.pop_back();
1537 if (score_only && new_score_ > score_threshold_)
return true;
1549 if (score_only && !with_binary_only && !clause_can_be_simplified &&
1550 new_score_ == saved_score) {
1551 ++num_blocked_clauses_;
1552 score_threshold_ -= clause_weight + clause.size();
1553 postsolve_->AddClauseWithSpecialLiteral(lit, clause);
1554 DeleteClause(clauses_[clause_index]);
1560bool BoundedVariableElimination::CrossProduct(BooleanVariable var) {
1561 if (assignment_.VariableIsAssigned(var))
return true;
1563 const Literal lit(var,
true);
1564 const Literal not_lit(var,
false);
1565 DCHECK(!implication_graph_->IsRedundant(lit));
1567 const int s1 = NumClausesContaining(lit);
1568 const int s2 = NumClausesContaining(not_lit);
1569 if (s1 == 0 && s2 == 0)
return true;
1570 if (s1 > 0 && s2 == 0) {
1571 num_eliminated_variables_++;
1572 if (!clause_manager_->InprocessingFixLiteral(lit))
return false;
1573 DeleteAllClausesContaining(lit);
1576 if (s1 == 0 && s2 > 0) {
1577 num_eliminated_variables_++;
1578 if (!clause_manager_->InprocessingFixLiteral(not_lit))
return false;
1579 DeleteAllClausesContaining(not_lit);
1585 if (s1 > 1 && s2 > 1 && s1 * s2 > parameters_.presolve_bve_threshold()) {
1598 const int clause_weight = parameters_.presolve_bve_clause_weight();
1600 implication_graph_->DirectImplications(lit).size() * (clause_weight + 2);
1601 score += implication_graph_->DirectImplications(not_lit).size() *
1602 (clause_weight + 2);
1603 for (
const ClauseIndex
i : literal_to_clauses_[lit]) {
1604 const auto c = clauses_[
i]->AsSpan();
1605 if (!
c.empty()) score += clause_weight +
c.size();
1607 for (
const ClauseIndex
i : literal_to_clauses_[not_lit]) {
1608 const auto c = clauses_[
i]->AsSpan();
1609 if (!
c.empty()) score += clause_weight +
c.size();
1620 score_threshold_ = score;
1621 new_score_ = implication_graph_->NumImplicationOnVariableRemoval(var) *
1622 (clause_weight + 2);
1623 if (new_score_ > score_threshold_)
return true;
1624 if (!ResolveAllClauseContaining<
true,
1628 if (new_score_ > score_threshold_)
return true;
1629 if (!ResolveAllClauseContaining<
true,
1633 if (new_score_ > score_threshold_)
return true;
1636 if (new_score_ > 0) {
1637 if (!ResolveAllClauseContaining<
false,
1641 if (!ResolveAllClauseContaining<
false,
1647 ++num_eliminated_variables_;
1648 implication_graph_->RemoveBooleanVariable(var, &postsolve_->clauses);
1649 DeleteAllClausesContaining(lit);
1650 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.
#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,...)