26#include "absl/container/flat_hash_map.h"
27#include "absl/container/flat_hash_set.h"
28#include "absl/container/inlined_vector.h"
29#include "absl/log/check.h"
30#include "absl/random/bit_gen_ref.h"
31#include "absl/random/distributions.h"
32#include "absl/types/span.h"
53template <
typename Watcher>
54bool WatcherListContains(
const std::vector<Watcher>& list,
55 const SatClause& candidate) {
56 for (
const Watcher& watcher : list) {
57 if (watcher.clause == &candidate)
return true;
63template <
typename Container,
typename Predicate>
64void RemoveIf(Container c, Predicate p) {
65 c->erase(std::remove_if(
c->begin(),
c->end(), p),
c->end());
76 num_inspected_clauses_(0),
77 num_inspected_clause_literals_(0),
78 num_watched_clauses_(0),
79 stats_(
"ClauseManager") {
90 watchers_on_false_.resize(num_variables << 1);
91 reasons_.resize(num_variables);
92 needs_cleaning_.
Resize(LiteralIndex(num_variables << 1));
101 DCHECK(!WatcherListContains(watchers_on_false_[
literal], *clause));
102 watchers_on_false_[
literal].push_back(Watcher(clause, blocking_literal));
105bool ClauseManager::PropagateOnFalse(Literal false_literal,
Trail* trail) {
108 std::vector<Watcher>& watchers = watchers_on_false_[false_literal];
109 const auto assignment = AssignmentView(trail->Assignment());
114 auto new_it = watchers.begin();
115 const auto end = watchers.end();
116 while (new_it !=
end && assignment.LiteralIsTrue(new_it->blocking_literal)) {
119 for (
auto it = new_it; it !=
end; ++it) {
121 if (assignment.LiteralIsTrue(it->blocking_literal)) {
125 ++num_inspected_clauses_;
130 Literal* literals = it->clause->literals();
131 const Literal other_watched_literal(
133 false_literal.Index().value()));
134 if (assignment.LiteralIsTrue(other_watched_literal)) {
136 new_it->blocking_literal = other_watched_literal;
138 ++num_inspected_clause_literals_;
146 const int start = it->start_index;
147 const int size = it->clause->size();
151 while (
i <
size && assignment.LiteralIsFalse(literals[
i])) ++
i;
152 num_inspected_clause_literals_ +=
i -
start + 2;
155 while (
i <
start && assignment.LiteralIsFalse(literals[
i])) ++
i;
156 num_inspected_clause_literals_ +=
i - 2;
163 literals[0] = other_watched_literal;
164 literals[1] = literals[
i];
165 literals[
i] = false_literal;
166 watchers_on_false_[literals[1]].emplace_back(
167 it->clause, other_watched_literal,
i + 1);
174 if (assignment.LiteralIsFalse(other_watched_literal)) {
179 trail->MutableConflict()->assign(it->clause->begin(), it->clause->end());
180 trail->SetFailingSatClause(it->clause);
181 num_inspected_clause_literals_ += it - watchers.begin() + 1;
182 watchers.erase(new_it, it);
189 literals[0] = other_watched_literal;
190 literals[1] = false_literal;
191 reasons_[trail->Index()] = it->clause;
196 num_inspected_clause_literals_ += watchers.size();
197 watchers.erase(new_it,
end);
202 const int old_index = trail->
Index();
205 if (!PropagateOnFalse(
literal.Negated(), trail))
return false;
213 return reasons_[trail_index]->PropagationReason();
217 return reasons_[trail_index];
227 clauses_.push_back(clause);
228 if (add_clause_callback_ !=
nullptr) add_clause_callback_(lbd, literals);
229 return AttachAndPropagate(clause, trail);
233 Trail* trail,
int lbd) {
235 clauses_.push_back(clause);
236 if (add_clause_callback_ !=
nullptr) add_clause_callback_(lbd, literals);
237 CHECK(AttachAndPropagate(clause, trail));
246bool ClauseManager::AttachAndPropagate(
SatClause* clause,
Trail* trail) {
250 Literal* literals = clause->literals();
254 int num_literal_not_false = 0;
255 for (
int i = 0;
i <
size; ++
i) {
257 std::swap(literals[
i], literals[num_literal_not_false]);
258 ++num_literal_not_false;
259 if (num_literal_not_false == 2) {
268 if (num_literal_not_false == 0)
return false;
270 if (num_literal_not_false == 1) {
273 int max_level = trail->
Info(literals[1].Variable()).
level;
274 for (
int i = 2;
i <
size; ++
i) {
275 const int level = trail->
Info(literals[
i].Variable()).
level;
276 if (level > max_level) {
278 std::swap(literals[1], literals[
i]);
284 reasons_[trail->
Index()] = clause;
289 ++num_watched_clauses_;
290 AttachOnFalse(literals[0], literals[1], clause);
291 AttachOnFalse(literals[1], literals[0], clause);
296 Literal* literals = clause->literals();
300 ++num_watched_clauses_;
301 AttachOnFalse(literals[0], literals[1], clause);
302 AttachOnFalse(literals[1], literals[0], clause);
305void ClauseManager::InternalDetach(
SatClause* clause) {
306 --num_watched_clauses_;
308 if (drat_proof_handler_ !=
nullptr &&
size > 2) {
311 clauses_info_.erase(clause);
316 InternalDetach(clause);
323 InternalDetach(clause);
325 needs_cleaning_.
Clear(l);
326 RemoveIf(&(watchers_on_false_[l]), [](
const Watcher& watcher) {
333 if (!all_clauses_are_attached_)
return;
334 all_clauses_are_attached_ =
false;
339 num_watched_clauses_ = 0;
340 watchers_on_false_.clear();
344 if (all_clauses_are_attached_)
return;
345 all_clauses_are_attached_ =
true;
348 watchers_on_false_.resize(needs_cleaning_.
size().value());
352 ++num_watched_clauses_;
353 DCHECK_GE(clause->
size(), 2);
362 if (drat_proof_handler_ !=
nullptr) {
363 drat_proof_handler_->
AddClause({true_literal});
372 return implication_graph_->
Propagate(trail_);
380 DCHECK(!all_clauses_are_attached_);
381 if (drat_proof_handler_ !=
nullptr) {
384 clauses_info_.erase(clause);
389 SatClause* clause, absl::Span<const Literal> new_clause) {
390 if (new_clause.empty())
return false;
393 for (
const Literal l : new_clause) {
398 if (new_clause.size() == 1) {
404 if (new_clause.size() == 2) {
405 CHECK(implication_graph_->
AddBinaryClause(new_clause[0], new_clause[1]));
410 if (drat_proof_handler_ !=
nullptr) {
412 drat_proof_handler_->
AddClause(new_clause);
416 if (all_clauses_are_attached_) {
419 --num_watched_clauses_;
422 needs_cleaning_.
Clear(l);
423 RemoveIf(&(watchers_on_false_[l]), [](
const Watcher& watcher) {
429 clause->Rewrite(new_clause);
432 if (all_clauses_are_attached_)
Attach(clause, trail_);
437 absl::Span<const Literal> new_clause) {
438 DCHECK(!new_clause.empty());
439 DCHECK(!all_clauses_are_attached_);
441 for (
const Literal l : new_clause) {
446 if (new_clause.size() == 1) {
452 if (new_clause.size() == 2) {
458 clauses_.push_back(clause);
465 DCHECK(needs_cleaning_[
index]);
466 RemoveIf(&(watchers_on_false_[
index]), [](
const Watcher& watcher) {
483 const int old_size = clauses_.size();
484 for (
int i = 0;
i < old_size; ++
i) {
485 if (
i == to_minimize_index_) to_minimize_index_ = new_size;
486 if (
i == to_probe_index_) to_probe_index_ = new_size;
487 if (clauses_[
i]->IsRemoved()) {
490 clauses_[new_size++] = clauses_[
i];
493 clauses_.resize(new_size);
495 if (to_minimize_index_ > new_size) to_minimize_index_ = new_size;
496 if (to_probe_index_ > new_size) to_probe_index_ = new_size;
503 implications_.resize(num_variables << 1);
504 implies_something_.
resize(num_variables << 1);
505 might_have_dups_.
resize(num_variables << 1);
506 is_redundant_.
resize(implications_.size());
507 is_removed_.
resize(implications_.size(),
false);
508 estimated_sizes_.
resize(implications_.size(), 0);
509 in_direct_implications_.
resize(implications_.size(),
false);
510 reasons_.resize(num_variables);
513void BinaryImplicationGraph::NotifyPossibleDuplicate(
Literal a) {
514 if (might_have_dups_[
a.Index()])
return;
515 might_have_dups_[
a.Index()] =
true;
516 to_clean_.push_back(
a);
520 for (
const Literal l : to_clean_) {
521 might_have_dups_[l.Index()] =
false;
538 if (drat_proof_handler_ !=
nullptr) {
546 if (is_redundant_[
a.Index()])
a =
Literal(representative_of_[
a.Index()]);
547 if (is_redundant_[
b.Index()])
b =
Literal(representative_of_[
b.Index()]);
548 if (
a ==
b.Negated())
return true;
549 if (
a ==
b)
return FixLiteral(
a);
551 DCHECK(!is_removed_[
a]);
552 DCHECK(!is_removed_[
b]);
553 estimated_sizes_[
a.NegatedIndex()]++;
554 estimated_sizes_[
b.NegatedIndex()]++;
555 implications_[
a.NegatedIndex()].push_back(
b);
556 implications_[
b.NegatedIndex()].push_back(
a);
557 implies_something_.
Set(
a.NegatedIndex());
558 implies_something_.
Set(
b.NegatedIndex());
559 NotifyPossibleDuplicate(
a);
560 NotifyPossibleDuplicate(
b);
562 num_implications_ += 2;
564 if (enable_sharing_ && add_binary_callback_ !=
nullptr) {
565 add_binary_callback_(
a,
b);
568 const auto& assignment = trail_->
Assignment();
570 DCHECK(!assignment.LiteralIsAssigned(
a));
571 DCHECK(!assignment.LiteralIsAssigned(
b));
573 if (assignment.LiteralIsFalse(
a)) {
574 if (assignment.LiteralIsAssigned(
b)) {
575 if (assignment.LiteralIsFalse(
b))
return false;
577 reasons_[trail_->
Index()] =
a;
580 }
else if (assignment.LiteralIsFalse(
b)) {
581 if (!assignment.LiteralIsAssigned(
a)) {
582 reasons_[trail_->
Index()] =
b;
592 absl::Span<const Literal> at_most_one) {
594 if (at_most_one.size() <= 1)
return true;
599 const int base_index = at_most_one_buffer_.size();
600 at_most_one_buffer_.push_back(
Literal(LiteralIndex(at_most_one.size())));
601 at_most_one_buffer_.insert(at_most_one_buffer_.end(), at_most_one.begin(),
605 return CleanUpAndAddAtMostOnes(base_index);
612bool BinaryImplicationGraph::FixLiteral(
Literal true_literal) {
617 if (drat_proof_handler_ !=
nullptr) {
618 drat_proof_handler_->
AddClause({true_literal});
628bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(
int base_index) {
629 const VariablesAssignment& assignment = trail_->
Assignment();
630 int local_end = base_index;
631 const int buffer_size = at_most_one_buffer_.size();
632 for (
int i = base_index;
i < buffer_size;) {
636 const int local_start = local_end;
641 if (
i == at_most_one_iterator_) {
642 at_most_one_iterator_ = local_start;
646 const absl::Span<const Literal> initial_amo = AtMostOne(
i);
647 i += initial_amo.size() + 1;
651 bool set_all_left_to_false =
false;
652 for (
const Literal l : initial_amo) {
653 if (assignment.LiteralIsFalse(l))
continue;
654 if (is_removed_[l])
continue;
655 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
656 set_all_left_to_false =
true;
661 at_most_one_buffer_[local_start] =
662 Literal(LiteralIndex(local_end - local_start - 1));
666 bool some_duplicates =
false;
667 if (!set_all_left_to_false) {
670 Literal* pt = &at_most_one_buffer_[local_start + 1];
671 std::sort(pt, pt + AtMostOne(local_start).
size());
674 bool remove_previous =
false;
675 int new_local_end = local_start + 1;
676 for (
const Literal l : AtMostOne(local_start)) {
677 if (l.Index() == previous) {
678 if (assignment.LiteralIsTrue(l))
return false;
679 if (!assignment.LiteralIsFalse(l)) {
680 if (!FixLiteral(l.Negated()))
return false;
682 remove_previous =
true;
683 some_duplicates =
true;
689 if (remove_previous) {
691 remove_previous =
false;
693 previous = l.Index();
694 at_most_one_buffer_[new_local_end++] = l;
696 if (remove_previous) --new_local_end;
699 local_end = new_local_end;
700 at_most_one_buffer_[local_start] =
701 Literal(LiteralIndex(local_end - local_start - 1));
706 if (some_duplicates) {
707 int new_local_end = local_start + 1;
708 for (
const Literal l : AtMostOne(local_start)) {
709 if (assignment.LiteralIsFalse(l))
continue;
710 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
711 set_all_left_to_false =
true;
714 at_most_one_buffer_[new_local_end++] = l;
718 local_end = new_local_end;
719 at_most_one_buffer_[local_start] =
720 Literal(LiteralIndex(local_end - local_start - 1));
724 if (set_all_left_to_false) {
725 for (
const Literal l : AtMostOne(local_start)) {
726 if (assignment.LiteralIsFalse(l))
continue;
727 if (assignment.LiteralIsTrue(l))
return false;
728 if (!FixLiteral(l.Negated()))
return false;
732 local_end = local_start;
737 const absl::Span<const Literal> at_most_one = AtMostOne(local_start);
741 if (at_most_one.size() <= std::max(2, at_most_one_max_expansion_size_)) {
742 for (
const Literal
a : at_most_one) {
743 for (
const Literal
b : at_most_one) {
744 if (
a ==
b)
continue;
745 implications_[
a].push_back(
b.Negated());
746 implies_something_.
Set(
a);
747 NotifyPossibleDuplicate(
a);
750 num_implications_ += at_most_one.size() * (at_most_one.size() - 1);
753 local_end = local_start;
758 for (
const Literal l : at_most_one) {
759 if (l.Index() >= at_most_ones_.size()) {
760 at_most_ones_.
resize(l.Index().value() + 1);
762 DCHECK(!is_redundant_[l]);
764 implies_something_.
Set(l);
768 at_most_one_buffer_.resize(local_end);
772bool BinaryImplicationGraph::PropagateOnTrue(Literal true_literal,
776 const auto assignment = AssignmentView(trail->Assignment());
777 DCHECK(assignment.LiteralIsTrue(true_literal));
782 num_inspections_ += implications_[true_literal].size();
784 for (
const Literal
literal : implications_[true_literal]) {
785 if (assignment.LiteralIsTrue(
literal)) {
795 if (assignment.LiteralIsFalse(
literal)) {
797 *(trail->MutableConflict()) = {true_literal.Negated(),
literal};
801 reasons_[trail->Index()] = true_literal.Negated();
807 if (true_literal.Index() < at_most_ones_.size()) {
808 for (
const int start : at_most_ones_[true_literal]) {
819 if (assignment.LiteralIsFalse(
literal))
continue;
822 if (assignment.LiteralIsTrue(
literal)) {
824 *(trail->MutableConflict()) = {true_literal.Negated(),
829 reasons_[trail->Index()] = true_literal.Negated();
830 trail->FastEnqueue(
literal.Negated());
847 if (!PropagateOnTrue(
literal, trail))
return false;
853 const Trail& ,
int trail_index, int64_t )
const {
854 return {&reasons_[trail_index], 1};
865 std::vector<Literal>* conflict) {
871 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
873 is_marked_.
Set(root_literal_index);
882 const bool also_prune_direct_implication_list =
false;
886 auto& direct_implications = implications_[root_literal_index];
887 for (
const Literal l : direct_implications) {
888 if (is_marked_[l])
continue;
889 dfs_stack_.push_back(l);
890 while (!dfs_stack_.empty()) {
891 const LiteralIndex
index = dfs_stack_.back().Index();
892 dfs_stack_.pop_back();
893 if (!is_marked_[
index]) {
896 if (!is_marked_[implied]) dfs_stack_.push_back(implied);
912 if (also_prune_direct_implication_list) {
919 if (also_prune_direct_implication_list) {
921 for (
const Literal l : direct_implications) {
922 if (!is_marked_[l]) {
924 direct_implications[new_size] = l;
928 if (new_size < direct_implications.size()) {
929 num_redundant_implications_ += direct_implications.size() - new_size;
930 direct_implications.resize(new_size);
934 RemoveRedundantLiterals(conflict);
942 const Trail& trail, std::vector<Literal>* conflict,
945 DCHECK(!conflict->empty());
947 MarkDescendants(conflict->front().Negated());
957 RemoveRedundantLiterals(conflict);
964 const Trail& , std::vector<Literal>* conflict,
965 absl::BitGenRef random) {
967 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
969 is_marked_.
Set(root_literal_index);
972 auto& direct_implications = implications_[root_literal_index];
978 std::shuffle(direct_implications.begin(), direct_implications.end(), random);
980 for (
const Literal l : direct_implications) {
987 direct_implications[new_size++] = l;
988 dfs_stack_.push_back(l);
989 while (!dfs_stack_.empty()) {
990 const LiteralIndex
index = dfs_stack_.back().Index();
991 dfs_stack_.pop_back();
992 if (!is_marked_[
index]) {
995 if (!is_marked_[implied]) dfs_stack_.push_back(implied);
1000 if (new_size < direct_implications.size()) {
1001 num_redundant_implications_ += direct_implications.size() - new_size;
1002 direct_implications.resize(new_size);
1004 RemoveRedundantLiterals(conflict);
1007void BinaryImplicationGraph::RemoveRedundantLiterals(
1008 std::vector<Literal>* conflict) {
1011 for (
int i = 1;
i < conflict->size(); ++
i) {
1012 if (!is_marked_[(*conflict)[
i].NegatedIndex()]) {
1013 (*conflict)[new_index] = (*conflict)[
i];
1017 if (new_index < conflict->
size()) {
1018 ++num_minimization_;
1019 num_literals_removed_ += conflict->size() - new_index;
1020 conflict->resize(new_index);
1026 const Trail& trail, std::vector<Literal>* conflict) {
1029 is_simplified_.
ClearAndResize(LiteralIndex(implications_.size()));
1047 for (
int i = 1;
i < conflict->size(); ++
i) {
1049 const int lit_level = trail.
Info(
lit.Variable()).
level;
1050 bool keep_literal =
true;
1051 for (
const Literal implied : implications_[
lit]) {
1052 if (is_marked_[implied]) {
1053 DCHECK_LE(lit_level, trail.
Info(implied.Variable()).
level);
1054 if (lit_level == trail.
Info(implied.Variable()).
level &&
1055 is_simplified_[implied]) {
1058 keep_literal =
false;
1070 ++num_minimization_;
1071 num_literals_removed_ += conflict->size() -
index;
1072 conflict->erase(conflict->begin() +
index, conflict->end());
1082 const int new_num_fixed = trail_->
Index();
1084 if (num_processed_fixed_variables_ == new_num_fixed)
return;
1088 for (; num_processed_fixed_variables_ < new_num_fixed;
1089 ++num_processed_fixed_variables_) {
1090 const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
1094 for (
const Literal lit : implications_[true_literal]) {
1108 if (
lit.NegatedIndex() < representative_of_.size() &&
1111 is_marked_.
Set(representative_of_[
lit.Negated()]);
1113 is_marked_.
Set(
lit.NegatedIndex());
1119 if (true_literal.
Index() < at_most_ones_.size()) {
1122 if (true_literal.
NegatedIndex() < at_most_ones_.size()) {
1127 RemoveIf(&implications_[
i], [&assignment](
const Literal&
lit) {
1134 at_most_ones_.clear();
1135 CleanUpAndAddAtMostOnes(0);
1136 DCHECK(InvariantsAreOk());
1151 std::vector<Literal>* at_most_one_buffer)
1153 implications_(*
graph),
1154 at_most_ones_(*at_most_ones),
1155 at_most_one_buffer_(*at_most_one_buffer) {}
1159 for (
const Literal l : implications_[LiteralIndex(node)]) {
1160 tmp_.push_back(l.Index().value());
1165 if (node < at_most_ones_.size()) {
1166 for (
const int start : at_most_ones_[LiteralIndex(node)]) {
1167 if (
start >= at_most_one_already_explored_.size()) {
1168 at_most_one_already_explored_.resize(
start + 1,
false);
1169 previous_node_to_explore_at_most_one_.resize(
start + 1);
1179 if (at_most_one_already_explored_[
start]) {
1181 const int first_node = previous_node_to_explore_at_most_one_[
start];
1182 DCHECK_NE(node, first_node);
1200 previous_node_to_explore_at_most_one_[
start] = node;
1205 Literal(LiteralIndex(first_node)).NegatedIndex().
value());
1209 at_most_one_already_explored_[
start] =
true;
1210 previous_node_to_explore_at_most_one_[
start] = node;
1213 const absl::Span<const Literal> amo =
1214 absl::MakeSpan(&at_most_one_buffer_[
start + 1],
1217 if (l.Index() == node)
continue;
1218 tmp_.push_back(l.NegatedIndex().value());
1220 to_fix_.push_back(l.Negated());
1239 const std::vector<Literal>& at_most_one_buffer_;
1241 mutable std::vector<int32_t> tmp_;
1244 mutable std::vector<bool> at_most_one_already_explored_;
1245 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1251 if (is_dag_)
return true;
1254 log_info |= VLOG_IS_ON(1);
1260 DCHECK(InvariantsAreOk());
1263 const int32_t
size(implications_.size());
1270 &at_most_one_buffer_);
1272 dtime += 4e-8 *
graph.work_done_;
1277 if (!FixLiteral(l))
return false;
1285 int num_equivalences = 0;
1286 reverse_topological_order_.clear();
1288 const absl::Span<int32_t> component = scc[
index];
1297 bool all_fixed =
false;
1298 bool all_true =
false;
1299 for (
const int32_t
i : component) {
1308 for (
const int32_t
i : component) {
1310 if (!is_redundant_[l]) {
1311 ++num_redundant_literals_;
1312 is_redundant_.
Set(l);
1313 representative_of_[l] = l.
Index();
1318 if (!FixLiteral(l))
return false;
1327 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1336 std::sort(component.begin(), component.end());
1340 if (component.size() == 1) {
1343 if (num_equivalences > 0) {
1345 for (
Literal& ref : representative_list) {
1346 const LiteralIndex rep = representative_of_[ref];
1357 for (
int i = 1;
i < component.size(); ++
i) {
1359 if (!is_redundant_[
literal]) {
1360 ++num_redundant_literals_;
1368 LOG_IF(INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
1377 for (
const Literal l : representative_list) {
1380 representative_list[new_size++] = rep;
1382 representative_list.resize(new_size);
1383 for (
int i = 1;
i < component.size(); ++
i) {
1385 auto& ref = implications_[
literal];
1396 representative_list.push_back(
literal);
1401 num_equivalences += component.size() - 1;
1405 if (num_equivalences != 0) {
1409 at_most_ones_.clear();
1410 CleanUpAndAddAtMostOnes(0);
1412 num_implications_ = 0;
1413 for (LiteralIndex
i(0);
i <
size; ++
i) {
1414 num_implications_ += implications_[
i].size();
1416 dtime += 2e-8 * num_implications_;
1420 const int num_fixed_during_scc =
1421 trail_->
Index() - num_processed_fixed_variables_;
1423 DCHECK(InvariantsAreOk());
1424 LOG_IF(INFO, log_info) <<
"SCC. " << num_equivalences
1425 <<
" redundant equivalent literals. "
1426 << num_fixed_during_scc <<
" fixed. "
1427 << num_implications_ <<
" implications left. "
1428 << implications_.size() <<
" literals."
1429 <<
" size of at_most_one buffer = "
1430 << at_most_one_buffer_.size() <<
"."
1431 <<
" dtime: " << dtime
1432 <<
" wtime: " << wall_timer.
Get();
1450 DCHECK(InvariantsAreOk());
1452 log_info |= VLOG_IS_ON(1);
1456 int64_t num_fixed = 0;
1457 int64_t num_new_redundant_implications = 0;
1458 bool aborted =
false;
1459 tmp_removed_.clear();
1460 work_done_in_mark_descendants_ = 0;
1461 int marked_index = 0;
1477 const LiteralIndex
size(implications_.size());
1479 for (
const LiteralIndex root : reverse_topological_order_) {
1484 if (is_redundant_[root])
continue;
1487 auto& direct_implications = implications_[root];
1488 if (direct_implications.empty())
continue;
1497 bool clear_previous_reachability =
true;
1498 for (
const Literal direct_child : direct_implications) {
1499 if (direct_child.Index() == previous) {
1500 clear_previous_reachability =
false;
1501 is_marked_.
Clear(previous);
1505 if (clear_previous_reachability) {
1511 for (
const Literal direct_child : direct_implications) {
1512 if (is_redundant_[direct_child])
continue;
1513 if (is_marked_[direct_child])
continue;
1517 if (direct_child.Index() == root)
continue;
1521 if (direct_child.NegatedIndex() == root) {
1522 is_marked_.
Set(direct_child);
1526 MarkDescendants(direct_child);
1529 is_marked_.
Clear(direct_child);
1531 DCHECK(!is_marked_[root])
1532 <<
"DetectEquivalences() should have removed cycles!";
1533 is_marked_.
Set(root);
1536 if (root < at_most_ones_.size()) {
1537 for (
const int start : at_most_ones_[root]) {
1539 if (l.Index() == root)
continue;
1540 if (!is_marked_[l.Negated()] && !is_redundant_[l.Negated()]) {
1542 MarkDescendants(l.Negated());
1552 for (; marked_index < marked_positions.size(); ++marked_index) {
1553 const LiteralIndex
i = marked_positions[marked_index];
1554 if (is_marked_[
Literal(
i).NegatedIndex()]) {
1562 if (!FixLiteral(
Literal(root).Negated()))
return false;
1576 for (
const Literal l : direct_implications) {
1577 if (!is_marked_[l]) {
1578 direct_implications[new_size++] = l;
1580 tmp_removed_.push_back({
Literal(root), l});
1581 DCHECK(!is_redundant_[l]);
1584 const int diff = direct_implications.size() - new_size;
1585 direct_implications.resize(new_size);
1586 direct_implications.shrink_to_fit();
1587 num_new_redundant_implications += diff;
1588 num_implications_ -= diff;
1591 if (work_done_in_mark_descendants_ > 1e8) {
1603 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> removed;
1604 for (
const auto [
a,
b] : tmp_removed_) {
1605 removed.insert({
a.Index(),
b.Index()});
1607 for (LiteralIndex
i(0);
i < implications_.size(); ++
i) {
1610 auto& implication = implications_[
i];
1611 for (
const Literal l : implication) {
1612 if (removed.contains({l.NegatedIndex(), negated_i}))
continue;
1613 implication[new_size++] = l;
1615 implication.resize(new_size);
1618 if (num_fixed > 0) {
1621 DCHECK(InvariantsAreOk());
1624 const double dtime = 1e-8 * work_done_in_mark_descendants_;
1626 num_redundant_implications_ += num_new_redundant_implications;
1627 LOG_IF(INFO, log_info) <<
"Transitive reduction removed "
1628 << num_new_redundant_implications <<
" literals. "
1629 << num_fixed <<
" fixed. " << num_implications_
1630 <<
" implications left. " << implications_.size()
1631 <<
" literals." <<
" dtime: " << dtime
1632 <<
" wtime: " << wall_timer.
Get()
1633 << (aborted ?
" Aborted." :
"");
1639int ElementInIntersectionOrMinusOne(absl::Span<const int>
a,
1640 absl::Span<const int>
b) {
1641 DCHECK(std::is_sorted(
a.begin(),
a.end()));
1642 DCHECK(std::is_sorted(
b.begin(),
b.end()));
1643 if (
a.empty() ||
b.empty())
return -1;
1647 if (
a[
i] ==
b[j])
return a[
i];
1649 if (++
i ==
a.size())
return -1;
1651 if (++j ==
b.size())
return -1;
1659 std::vector<std::vector<Literal>>* at_most_ones,
1660 int64_t max_num_explored_nodes) {
1663 work_done_in_mark_descendants_ = 0;
1665 int num_extended = 0;
1666 int num_removed = 0;
1670 std::vector<int> detector_clique_index;
1675 std::vector<int> dense_index_to_index;
1677 max_cliques_containing(implications_.size());
1681 std::vector<std::pair<int, int>> index_size_vector;
1682 index_size_vector.reserve(at_most_ones->size());
1684 std::vector<Literal>& clique = (*at_most_ones)[
index];
1685 if (clique.size() <= 1)
continue;
1694 DCHECK_LT(ref.Index(), representative_of_.size());
1695 const LiteralIndex rep = representative_of_[ref];
1705 std::sort(clique.begin(), clique.end());
1706 for (
int i = 1;
i < clique.size(); ++
i) {
1707 if (clique[
i] == clique[
i - 1] || clique[
i] == clique[
i -
i].Negated()) {
1714 index_size_vector.push_back({
index, clique.size()});
1717 index_size_vector.begin(), index_size_vector.end(),
1718 [](
const std::pair<int, int>
a,
const std::pair<int, int>&
b) {
1719 return a.second > b.second;
1722 absl::flat_hash_set<int> cannot_be_removed;
1723 std::vector<bool> was_extended(at_most_ones->size(),
false);
1724 for (
const auto& [
index, old_size] : index_size_vector) {
1725 std::vector<Literal>& clique = (*at_most_ones)[
index];
1730 if (clique.size() == 2) {
1731 DCHECK_NE(clique[0], clique[1]);
1732 const int dense_index = ElementInIntersectionOrMinusOne(
1733 max_cliques_containing[clique[0]], max_cliques_containing[clique[1]]);
1734 if (dense_index >= 0) {
1735 const int superset_index = dense_index_to_index[dense_index];
1736 if (was_extended[superset_index])
1737 cannot_be_removed.insert(superset_index);
1746 detector_clique_index.push_back(
index);
1750 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1751 clique = ExpandAtMostOne(clique, max_num_explored_nodes);
1755 detector_clique_index.push_back(
index);
1759 const int dense_index = dense_index_to_index.size();
1760 dense_index_to_index.push_back(
index);
1761 for (
const Literal l : clique) {
1762 max_cliques_containing[l].
push_back(dense_index);
1765 if (clique.size() > old_size) {
1766 was_extended[
index] =
true;
1774 const int subset_index = detector_clique_index[subset];
1775 const int superset_index = detector_clique_index[superset];
1776 if (subset_index == superset_index)
return;
1779 if ((*at_most_ones)[subset_index].empty())
return;
1780 if ((*at_most_ones)[superset_index].empty())
return;
1784 if (cannot_be_removed.contains(subset_index))
return;
1787 (*at_most_ones)[subset_index].clear();
1788 if (was_extended[superset_index]) cannot_be_removed.insert(superset_index);
1791 if (num_extended > 0 || num_removed > 0 || num_added > 0) {
1792 VLOG(1) <<
"Clique Extended: " << num_extended
1793 <<
" Removed: " << num_removed <<
" Added: " << num_added
1794 << (work_done_in_mark_descendants_ > max_num_explored_nodes
1801template <
bool use_weight>
1803 const absl::Span<const Literal> at_most_one,
1806 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1807 std::vector<LiteralIndex> intersection;
1808 double clique_weight = 0.0;
1809 const int64_t old_work = work_done_in_mark_descendants_;
1811 for (
const Literal l : clique) {
1812 clique_weight += expanded_lp_values[l];
1815 for (
int i = 0;
i < clique.size(); ++
i) {
1817 if (work_done_in_mark_descendants_ - old_work > 1e8)
break;
1820 MarkDescendants(clique[
i]);
1824 intersection.push_back(
index);
1827 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1831 double intersection_weight = 0.0;
1832 is_marked_.
Clear(clique[
i]);
1833 is_marked_.
Clear(clique[
i].NegatedIndex());
1834 for (
const LiteralIndex
index : intersection) {
1835 if (!is_marked_[
index])
continue;
1836 intersection[new_size++] =
index;
1838 intersection_weight += expanded_lp_values[
index];
1841 intersection.resize(new_size);
1842 if (intersection.empty())
break;
1846 if (use_weight && clique_weight + intersection_weight <= 1.0) {
1853 if (
i + 1 == clique.size()) {
1856 double max_lp = 0.0;
1857 for (
int j = 0; j < intersection.size(); ++j) {
1860 use_weight ? 1.0 - expanded_lp_values[intersection[j]] +
1861 absl::Uniform<double>(*random_, 0.0, 1e-4)
1862 : can_be_included.size() - intersection[j].value();
1863 if (
index == -1 || lp > max_lp) {
1869 clique.push_back(
Literal(intersection[
index]).Negated());
1870 std::swap(intersection.back(), intersection[
index]);
1871 intersection.pop_back();
1873 clique_weight += expanded_lp_values[clique.back()];
1882template std::vector<Literal>
1884 const absl::Span<const Literal> at_most_one,
1887template std::vector<Literal>
1889 const absl::Span<const Literal> at_most_one,
1893const std::vector<std::vector<Literal>>&
1895 const std::vector<Literal>& literals,
1896 const std::vector<double>& lp_values) {
1898 const int num_literals = implications_.size();
1903 const int size = literals.size();
1904 for (
int i = 0;
i <
size; ++
i) {
1906 can_be_included[l] =
true;
1909 const double value = lp_values[
i];
1910 expanded_lp_values[l] =
value;
1919 bool operator<(
const Candidate& other)
const {
return sum > other.sum; }
1921 std::vector<Candidate> candidates;
1929 for (
int i = 0;
i <
size; ++
i) {
1930 Literal current_literal = literals[
i];
1931 double current_value = lp_values[
i];
1933 if (is_redundant_[current_literal])
continue;
1935 if (current_value < 0.5) {
1936 current_literal = current_literal.
Negated();
1937 current_value = 1.0 - current_value;
1942 double best_value = 0.0;
1943 for (
const Literal l : implications_[current_literal]) {
1944 if (!can_be_included[l])
continue;
1945 const double activity =
1946 current_value + expanded_lp_values[l.NegatedIndex()];
1947 if (activity <= 1.01)
continue;
1948 const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
1951 best = l.NegatedIndex();
1955 const double activity = current_value + expanded_lp_values[best];
1956 candidates.push_back({current_literal,
Literal(best), activity});
1961 const int kMaxNumberOfCutPerCall = 10;
1962 std::sort(candidates.begin(), candidates.end());
1963 if (candidates.size() > kMaxNumberOfCutPerCall) {
1964 candidates.resize(kMaxNumberOfCutPerCall);
1970 std::vector<Literal> at_most_one;
1971 for (
const Candidate& candidate : candidates) {
1973 {candidate.a, candidate.b}, can_be_included, expanded_lp_values);
1974 if (!at_most_one.empty()) tmp_cuts_.push_back(at_most_one);
1982std::vector<absl::Span<const Literal>>
1984 std::vector<absl::Span<const Literal>> result;
1987 implications_.size(),
false);
1988 for (
const Literal l : *literals) to_consider[l] =
true;
1991 std::priority_queue<std::pair<int, int>> pq;
1997 absl::flat_hash_set<int> explored_amo;
1998 for (
const Literal l : *literals) {
1999 if (l.Index() >= at_most_ones_.size())
continue;
2000 for (
const int start : at_most_ones_[l]) {
2001 const auto [_, inserted] = explored_amo.insert(
start);
2002 if (!inserted)
continue;
2004 int intersection_size = 0;
2006 if (to_consider[l]) ++intersection_size;
2008 if (intersection_size > 1) {
2009 pq.push({intersection_size,
start});
2013 if (intersection_size == literals->size())
break;
2018 int num_processed = 0;
2019 while (!pq.empty()) {
2020 const auto [old_size,
start] = pq.top();
2024 int intersection_size = 0;
2026 if (to_consider[l]) ++intersection_size;
2028 if (intersection_size != old_size) {
2030 if (intersection_size > 1) {
2031 pq.push({intersection_size,
start});
2038 to_consider[l] =
false;
2043 const int span_start = num_processed;
2044 for (
int i = num_processed;
i < literals->size(); ++
i) {
2045 if (to_consider[(*literals)[
i]])
continue;
2046 std::swap((*literals)[num_processed], (*literals)[
i]);
2049 result.push_back(absl::MakeSpan(literals->data() + span_start,
2050 num_processed - span_start));
2055void BinaryImplicationGraph::MarkDescendants(
Literal root) {
2056 bfs_stack_.resize(implications_.size());
2057 auto*
const stack = bfs_stack_.data();
2058 const int amo_size =
static_cast<int>(at_most_ones_.size());
2060 auto is_redundant = is_redundant_.
const_view();
2061 if (is_redundant[root])
return;
2065 is_marked_.
Set(root);
2066 for (
int j = 0; j < stack_size; ++j) {
2067 const Literal current = stack[j];
2068 if (!implies_something_[current])
continue;
2070 for (
const Literal l : implications_[current]) {
2071 if (!is_marked[l] && !is_redundant[l]) {
2073 stack[stack_size++] = l;
2077 if (current.Index() >= amo_size)
continue;
2078 for (
const int start : at_most_ones_[current]) {
2079 for (
const Literal l : AtMostOne(
start)) {
2080 if (l == current)
continue;
2081 if (!is_marked[l.NegatedIndex()] && !is_redundant[l.NegatedIndex()]) {
2083 stack[stack_size++] = l.Negated();
2088 work_done_in_mark_descendants_ += stack_size;
2091std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
2092 const absl::Span<const Literal> at_most_one,
2093 int64_t max_num_explored_nodes) {
2094 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
2097 for (
int i = 0;
i < clique.size(); ++
i) {
2098 if (implications_[clique[
i]].empty() || is_redundant_[clique[
i]]) {
2106 std::vector<LiteralIndex> intersection;
2107 for (
int i = 0;
i < clique.size(); ++
i) {
2108 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
2110 MarkDescendants(clique[
i]);
2114 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
2118 is_marked_.
Clear(clique[
i].NegatedIndex());
2119 for (
const LiteralIndex
index : intersection) {
2120 if (is_marked_[
index]) intersection[new_size++] =
index;
2122 intersection.resize(new_size);
2123 if (intersection.empty())
break;
2130 if (
i + 1 == clique.size()) {
2131 clique.push_back(Literal(intersection.back()).Negated());
2132 intersection.pop_back();
2142 DCHECK(!is_removed_[
literal]);
2145 for (
const Literal l : direct_implications_) {
2146 in_direct_implications_[l] =
false;
2148 direct_implications_.clear();
2156 if (!is_removed_[l] && !in_direct_implications_[l]) {
2157 in_direct_implications_[l] =
true;
2161 if (
literal.Index() < at_most_ones_.size()) {
2163 DCHECK(at_most_ones_[
literal].empty());
2169 if (!is_removed_[l] && !in_direct_implications_[l.NegatedIndex()]) {
2170 in_direct_implications_[l.NegatedIndex()] =
true;
2171 direct_implications_.
push_back(l.Negated());
2176 estimated_sizes_[
literal] = direct_implications_.size();
2177 return direct_implications_;
2180absl::Span<const Literal> BinaryImplicationGraph::AtMostOne(
int start)
const {
2181 const int size = at_most_one_buffer_[
start].Index().value();
2182 return absl::MakeSpan(&at_most_one_buffer_[
start + 1],
size);
2186 const int size1 = implications_[lhs].size();
2188 lhs.
Index() < at_most_ones_.size() ? at_most_ones_[lhs].size() : 0;
2191 const int choice = absl::Uniform<int>(*random_, 0, size1 + size2);
2192 if (choice < size1) {
2193 return implications_[lhs][choice].Index();
2196 const absl::Span<const Literal> amo =
2197 AtMostOne(at_most_ones_[lhs][choice - size1]);
2198 CHECK_GE(amo.size(), 2);
2199 const int first_choice = absl::Uniform<int>(*random_, 0, amo.size());
2204 int next_choice = absl::Uniform<int>(*random_, 0, amo.size() - 1);
2205 if (next_choice >= first_choice) {
2208 CHECK_NE(amo[next_choice], lhs);
2209 return amo[next_choice].NegatedIndex();
2221 direct_implications_of_negated_literal_ =
2224 for (
const Literal l : direct_implications_of_negated_literal_) {
2225 if (in_direct_implications_[l]) {
2227 if (!FixLiteral(l)) {
2238 BooleanVariable
var) {
2241 direct_implications_of_negated_literal_ =
2244 for (
const Literal l : direct_implications_of_negated_literal_) {
2248 DCHECK(!in_direct_implications_[l]);
2251 if (in_direct_implications_[l.NegatedIndex()]) result--;
2258 BooleanVariable
var, std::deque<std::vector<Literal>>* postsolve_clauses) {
2260 DCHECK(!is_removed_[
literal.Index()]);
2261 DCHECK(!is_redundant_[
literal.Index()]);
2263 direct_implications_of_negated_literal_ =
2266 if (is_removed_[
b])
continue;
2267 DCHECK(!is_redundant_[
b]);
2268 estimated_sizes_[
b.NegatedIndex()]--;
2269 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
2270 if (a_negated.Negated() ==
b)
continue;
2271 if (is_removed_[a_negated])
continue;
2275 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
2276 if (is_removed_[a_negated])
continue;
2277 DCHECK(!is_redundant_[a_negated]);
2278 estimated_sizes_[a_negated.NegatedIndex()]--;
2283 for (
const Literal b : direct_implications_) {
2284 if (drat_proof_handler_ !=
nullptr) {
2287 postsolve_clauses->push_back({
Literal(
var,
false),
b});
2289 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
2290 if (drat_proof_handler_ !=
nullptr) {
2293 postsolve_clauses->push_back({
Literal(
var,
true), a_negated});
2299 is_removed_[
index] =
true;
2300 implications_[
index].clear();
2301 if (!is_redundant_[
index]) {
2302 ++num_redundant_literals_;
2309 std::deque<std::vector<Literal>>* postsolve_clauses) {
2310 for (LiteralIndex
a(0);
a < implications_.size(); ++
a) {
2311 if (is_redundant_[
a] && !is_removed_[
a]) {
2312 postsolve_clauses->push_back(
2314 is_removed_[
a] =
true;
2320 auto& implication = implications_[
a];
2321 for (
const Literal l : implication) {
2322 if (!is_redundant_[l]) {
2323 implication[new_size++] = l;
2326 implication.resize(new_size);
2332 for (LiteralIndex
a(0);
a < implications_.size(); ++
a) {
2347 auto& implication = implications_[
a];
2348 for (
const Literal l : implication) {
2350 implication[new_size++] = l;
2353 implication.resize(new_size);
2357 at_most_ones_.clear();
2358 CleanUpAndAddAtMostOnes(0);
2361 DCHECK(InvariantsAreOk());
2364bool BinaryImplicationGraph::InvariantsAreOk() {
2366 absl::flat_hash_set<std::pair<LiteralIndex, LiteralIndex>> seen;
2367 int num_redundant = 0;
2369 for (LiteralIndex a_index(0); a_index < implications_.size(); ++a_index) {
2372 if (!implications_[a_index].empty()) {
2373 LOG(ERROR) <<
"Fixed literal has non-cleared implications";
2378 if (is_removed_[a_index]) {
2379 if (!implications_[a_index].empty()) {
2380 LOG(ERROR) <<
"Removed literal has non-cleared implications";
2385 if (is_redundant_[a_index]) {
2387 if (implications_[a_index].
size() != 1) {
2389 <<
"Redundant literal should only point to its representative "
2390 << Literal(a_index) <<
" => " << implications_[a_index];
2394 for (
const Literal
b : implications_[a_index]) {
2395 seen.insert({a_index,
b.Index()});
2402 lit_to_order.
assign(implications_.size(), -1);
2403 for (
int i = 0;
i < reverse_topological_order_.size(); ++
i) {
2404 lit_to_order[reverse_topological_order_[
i]] =
i;
2408 VLOG(2) <<
"num_redundant " << num_redundant;
2409 VLOG(2) <<
"num_fixed " << num_fixed;
2410 for (LiteralIndex a_index(0); a_index < implications_.size(); ++a_index) {
2411 const LiteralIndex not_a_index = Literal(a_index).NegatedIndex();
2412 for (
const Literal
b : implications_[a_index]) {
2413 if (is_removed_[
b]) {
2414 LOG(ERROR) <<
"A removed literal still appear! " << Literal(a_index)
2419 if (!seen.contains({b.NegatedIndex(), not_a_index})) {
2420 LOG(ERROR) <<
"We have " << Literal(a_index) <<
" => " <<
b
2421 <<
" but not the reverse implication!";
2422 LOG(ERROR) <<
"redundant[a]: " << is_redundant_[a_index]
2425 <<
" removed[a]: " << is_removed_[a_index]
2426 <<
" redundant[b]: " << is_redundant_[
b] <<
" assigned[b]: "
2428 <<
" removed[b]: " << is_removed_[
b];
2434 if (is_dag_ && !is_redundant_[
b] && !is_redundant_[a_index]) {
2435 DCHECK_NE(lit_to_order[
b], -1);
2436 DCHECK_LE(lit_to_order[
b], lit_to_order[a_index]);
2442 absl::flat_hash_set<std::pair<LiteralIndex, int>> lit_to_start;
2443 for (LiteralIndex
i(0);
i < at_most_ones_.size(); ++
i) {
2444 for (
const int start : at_most_ones_[
i]) {
2445 lit_to_start.insert({
i,
start});
2449 for (
int start = 0;
start < at_most_one_buffer_.size();) {
2450 const absl::Span<const Literal> amo = AtMostOne(
start);
2451 for (
const Literal l : amo) {
2452 if (is_removed_[l]) {
2453 LOG(ERROR) <<
"A removed literal still appear in an amo " << l;
2456 if (!lit_to_start.contains({l, start})) {
2460 start += amo.size() + 1;
2467 if (at_most_one_iterator_ >= at_most_one_buffer_.size()) {
2468 return absl::Span<const Literal>();
2471 const absl::Span<const Literal> result = AtMostOne(at_most_one_iterator_);
2472 at_most_one_iterator_ += result.size() + 1;
2480 DCHECK_GE(literals.size(), 2);
2483 clause->size_ = literals.size();
2484 for (
int i = 0;
i < literals.size(); ++
i) {
2485 clause->literals_[
i] = literals[
i];
2504 for (
int i = j;
i < size_; ++
i) {
2508 std::swap(literals_[j], literals_[
i]);
2526 if (!result.empty()) result.append(
" ");
2527 result.append(
literal.DebugString());
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
bool NodeIsInCurrentDfsPath(NodeIndex node) const
void Start()
When Start() is called multiple times, only the most recent is used.
void resize(int size)
Resizes the Bitset64 to the given number of bits. New bits are sets to 0.
ConstView const_view() const
void Set(IndexType i)
Sets the bit at position i to 1.
void ClearAndResize(IntegerType size)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Clear(IntegerType index)
Bitset64< IntegerType >::ConstView const_view() const
void Set(IntegerType index)
void SetUnsafe(IntegerType index)
void Resize(IntegerType size)
std::string StatString() const
void AdvanceDeterministicTime(double deterministic_duration)
bool ComputeTransitiveReduction(bool log_info=false)
Literal RepresentativeOf(Literal l) const
void Resize(int num_variables)
Resizes the data structure.
bool Propagate(Trail *trail) final
SatPropagator interface.
void RemoveDuplicates()
Clean up implications list that might have duplicates.
void MinimizeConflictWithReachability(std::vector< Literal > *c)
std::vector< Literal > ExpandAtMostOneWithWeight(absl::Span< const Literal > at_most_one, const util_intops::StrongVector< LiteralIndex, bool > &can_be_included, const util_intops::StrongVector< LiteralIndex, double > &expanded_lp_values)
Same as ExpandAtMostOne() but try to maximize the weight in the clique.
bool TransformIntoMaxCliques(std::vector< std::vector< Literal > > *at_most_ones, int64_t max_num_explored_nodes=1e8)
void CleanupAllRemovedAndFixedVariables()
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
bool IsEmpty() const final
Returns true if there is no constraints in this class.
bool AddBinaryClause(Literal a, Literal b)
void RemoveFixedVariables()
absl::Span< const Literal > NextAtMostOne()
Returns the next at_most_one, or a span of size 0 when finished.
bool AddImplication(Literal a, Literal b)
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
For all possible a => var => b, add a => b.
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, absl::BitGenRef random)
bool DetectEquivalences(bool log_info=false)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
const std::vector< Literal > & DirectImplications(Literal literal)
const std::vector< std::vector< Literal > > & GenerateAtMostOnesWithLargeWeight(const std::vector< Literal > &literals, const std::vector< double > &lp_values)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
void RemoveAllRedundantVariables(std::deque< std::vector< Literal > > *postsolve_clauses)
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
LiteralIndex RandomImpliedLiteral(Literal lhs)
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
std::vector< absl::Span< const Literal > > HeuristicAmoPartition(std::vector< Literal > *literals)
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
void LazyDetach(SatClause *clause)
void InprocessingRemoveClause(SatClause *clause)
These must only be called between [Detach/Attach]AllClauses() calls.
void Detach(SatClause *clause)
~ClauseManager() override
void DeleteRemovedClauses()
absl::Span< const Literal > Reason(const Trail &trail, int trail_index, int64_t conflict_id) const final
bool AddClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
Adds a new clause and perform initial propagation for this clause only.
ClauseManager(Model *model)
--— ClauseManager --—
SatClause * ReasonClause(int trail_index) const
SatClause * AddRemovableClause(absl::Span< const Literal > literals, Trail *trail, int lbd)
void Resize(int num_variables)
Must be called before adding clauses referring to such variables.
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
This one do not need the clause to be detached.
void Attach(SatClause *clause, Trail *trail)
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
bool Propagate(Trail *trail) final
SatPropagator API.
int AddLiterals(const std::vector< L > &wrapped_values)
size_t size() const
Size of the "key" space, always in [0, size()).
void reserve(int size)
Reserve memory if it is already known or tightly estimated.
void AddClause(absl::Span< const Literal > clause)
void DeleteClause(absl::Span< const Literal > clause)
void AddPotentialSubset(int index)
void DetectInclusions(const std::function< void(int subset, int superset)> &process)
void SetWorkLimit(uint64_t work_limit)
void AddPotentialSuperset(int index)
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
--— SatClause --—
absl::Span< const Literal > AsSpan() const
Returns a Span<> representation of the clause.
const Literal * begin() const
Allows for range based iteration: for (Literal literal : clause) {}.
bool IsSatisfied(const VariablesAssignment &assignment) const
int size() const
Number of literals in the clause.
std::string DebugString() const
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
Literal SecondLiteral() const
Base class for all the SAT constraints.
int propagation_trail_index_
SccGraph(SccFinder *finder, Implications *graph, AtMostOnes *at_most_ones, std::vector< Literal > *at_most_one_buffer)
int64_t work_done_
For the deterministic time.
std::vector< Literal > to_fix_
All these literals where detected to be true during the SCC computation.
const std::vector< int32_t > & operator[](int32_t node) const
void Enqueue(Literal true_literal, int propagator_id)
void EnqueueWithUnitReason(Literal true_literal)
Specific Enqueue() version for a fixed variable.
int CurrentDecisionLevel() const
const AssignmentInfo & Info(BooleanVariable var) const
void RegisterPropagator(SatPropagator *propagator)
void SetCurrentPropagatorId(int propagator_id)
const VariablesAssignment & Assignment() const
bool VariableIsAssigned(BooleanVariable var) const
Returns true iff the given variable is assigned.
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void assign(size_type n, const value_type &val)
void push_back(const value_type &val)
void resize(size_type new_size)
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
void STLDeleteElements(T *container)
void STLClearObject(T *obj)
const LiteralIndex kNoLiteralIndex(-1)
In SWIG mode, we don't want anything besides these top-level includes.
std::optional< int64_t > end
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)