25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/container/inlined_vector.h"
28#include "absl/random/bit_gen_ref.h"
29#include "absl/random/distributions.h"
30#include "absl/types/span.h"
52template <
typename Watcher>
53bool WatcherListContains(
const std::vector<Watcher>& list,
54 const SatClause& candidate) {
55 for (
const Watcher& watcher : list) {
56 if (watcher.clause == &candidate)
return true;
62template <
typename Container,
typename Predicate>
63void RemoveIf(Container c, Predicate p) {
64 c->erase(std::remove_if(c->begin(), c->end(), p), c->end());
75 num_inspected_clauses_(0),
76 num_inspected_clause_literals_(0),
77 num_watched_clauses_(0),
78 stats_(
"LiteralWatchers") {
89 watchers_on_false_.resize(num_variables << 1);
90 reasons_.resize(num_variables);
91 needs_cleaning_.
Resize(LiteralIndex(num_variables << 1));
100 DCHECK(!WatcherListContains(watchers_on_false_[
literal.Index()], *clause));
101 watchers_on_false_[
literal.Index()].push_back(
102 Watcher(clause, blocking_literal));
105bool LiteralWatchers::PropagateOnFalse(Literal false_literal,
Trail* trail) {
108 std::vector<Watcher>& watchers = watchers_on_false_[false_literal.Index()];
109 const VariablesAssignment& assignment = 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;
157 if (i >=
start) i = size;
163 literals[0] = other_watched_literal;
164 literals[1] = literals[i];
165 literals[i] = false_literal;
166 watchers_on_false_[literals[1].Index()].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;
211 int trail_index)
const {
212 return reasons_[trail_index]->PropagationReason();
216 return reasons_[trail_index];
226 clauses_.push_back(clause);
227 return AttachAndPropagate(clause, trail);
231 const std::vector<Literal>& literals,
Trail* trail) {
233 clauses_.push_back(clause);
234 CHECK(AttachAndPropagate(clause, trail));
243bool LiteralWatchers::AttachAndPropagate(
SatClause* clause,
Trail* trail) {
246 const int size = clause->
size();
247 Literal* literals = clause->literals();
251 int num_literal_not_false = 0;
252 for (
int i = 0; i < size; ++i) {
254 std::swap(literals[i], literals[num_literal_not_false]);
255 ++num_literal_not_false;
256 if (num_literal_not_false == 2) {
265 if (num_literal_not_false == 0)
return false;
267 if (num_literal_not_false == 1) {
270 int max_level = trail->
Info(literals[1].Variable()).
level;
271 for (
int i = 2; i < size; ++i) {
272 const int level = trail->
Info(literals[i].Variable()).
level;
273 if (level > max_level) {
281 reasons_[trail->
Index()] = clause;
286 ++num_watched_clauses_;
287 AttachOnFalse(literals[0], literals[1], clause);
288 AttachOnFalse(literals[1], literals[0], clause);
293 Literal* literals = clause->literals();
297 ++num_watched_clauses_;
298 AttachOnFalse(literals[0], literals[1], clause);
299 AttachOnFalse(literals[1], literals[0], clause);
302void LiteralWatchers::InternalDetach(
SatClause* clause) {
303 --num_watched_clauses_;
304 const size_t size = clause->
size();
305 if (drat_proof_handler_ !=
nullptr && size > 2) {
308 clauses_info_.erase(clause);
313 InternalDetach(clause);
320 InternalDetach(clause);
322 needs_cleaning_.
Clear(l.Index());
323 RemoveIf(&(watchers_on_false_[l.Index()]), [](
const Watcher& watcher) {
324 return !watcher.clause->IsAttached();
330 if (!all_clauses_are_attached_)
return;
331 all_clauses_are_attached_ =
false;
336 num_watched_clauses_ = 0;
337 watchers_on_false_.clear();
341 if (all_clauses_are_attached_)
return;
342 all_clauses_are_attached_ =
true;
345 watchers_on_false_.resize(needs_cleaning_.
size().value());
349 ++num_watched_clauses_;
359 if (drat_proof_handler_ !=
nullptr) {
360 drat_proof_handler_->
AddClause({true_literal});
369 return implication_graph_->
Propagate(trail_);
377 CHECK(!all_clauses_are_attached_);
378 if (drat_proof_handler_ !=
nullptr) {
381 clauses_info_.erase(clause);
386 SatClause* clause, absl::Span<const Literal> new_clause) {
387 if (new_clause.empty())
return false;
390 for (
const Literal l : new_clause) {
395 if (new_clause.size() == 1) {
401 if (new_clause.size() == 2) {
407 if (drat_proof_handler_ !=
nullptr) {
409 drat_proof_handler_->
AddClause(new_clause);
413 if (all_clauses_are_attached_) {
416 --num_watched_clauses_;
419 needs_cleaning_.
Clear(l.Index());
420 RemoveIf(&(watchers_on_false_[l.Index()]), [](
const Watcher& watcher) {
421 return !watcher.clause->IsAttached();
426 clause->Rewrite(new_clause);
429 if (all_clauses_are_attached_)
Attach(clause, trail_);
434 absl::Span<const Literal> new_clause) {
435 CHECK(!new_clause.empty());
436 CHECK(!all_clauses_are_attached_);
438 for (
const Literal l : new_clause) {
443 if (new_clause.size() == 1) {
449 if (new_clause.size() == 2) {
455 clauses_.push_back(clause);
463 RemoveIf(&(watchers_on_false_[
index]), [](
const Watcher& watcher) {
476 if (to_minimize_index_ >= clauses_.size()) {
477 to_minimize_index_ = clauses_.size();
480 std::stable_partition(clauses_.begin(),
481 clauses_.begin() + to_minimize_index_,
482 [](
SatClause*
a) { return a->IsAttached(); }) -
486 std::vector<SatClause*>::iterator iter =
487 std::stable_partition(clauses_.begin(), clauses_.end(),
488 [](
SatClause*
a) { return a->IsAttached(); });
490 clauses_.erase(iter, clauses_.end());
497 implications_.resize(num_variables << 1);
498 is_redundant_.
resize(implications_.size(),
false);
499 is_removed_.
resize(implications_.size(),
false);
500 estimated_sizes_.
resize(implications_.size(), 0);
501 in_direct_implications_.
resize(implications_.size(),
false);
502 reasons_.resize(num_variables);
511 if (drat_proof_handler_ !=
nullptr) {
518 estimated_sizes_[
a.NegatedIndex()]++;
519 estimated_sizes_[
b.NegatedIndex()]++;
520 implications_[
a.NegatedIndex()].push_back(
b);
521 implications_[
b.NegatedIndex()].push_back(
a);
523 num_implications_ += 2;
535 const auto& assignment = trail_->
Assignment();
536 if (assignment.LiteralIsFalse(
a)) {
537 if (assignment.LiteralIsAssigned(
b)) {
538 if (assignment.LiteralIsFalse(
b))
return false;
540 reasons_[trail_->
Index()] =
a;
543 }
else if (assignment.LiteralIsFalse(
b)) {
544 if (!assignment.LiteralIsAssigned(
a)) {
545 reasons_[trail_->
Index()] =
b;
554 absl::Span<const Literal> at_most_one) {
556 if (at_most_one.size() <= 1)
return true;
561 const int base_index = at_most_one_buffer_.size();
562 at_most_one_buffer_.insert(at_most_one_buffer_.end(), at_most_one.begin(),
567 return CleanUpAndAddAtMostOnes(base_index);
572bool BinaryImplicationGraph::FixLiteral(
Literal true_literal) {
576 if (drat_proof_handler_ !=
nullptr) {
577 drat_proof_handler_->
AddClause({true_literal});
587bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(
const int base_index) {
588 const VariablesAssignment& assignment = trail_->
Assignment();
589 int local_end = base_index;
590 const int buffer_size = at_most_one_buffer_.size();
591 for (
int i = base_index; i < buffer_size; ++i) {
596 const int local_start = local_end;
597 bool set_all_left_to_false =
false;
599 const Literal l = at_most_one_buffer_[i];
601 if (assignment.LiteralIsFalse(l))
continue;
602 if (is_removed_[l.Index()])
continue;
603 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
604 set_all_left_to_false =
true;
612 bool some_duplicates =
false;
613 if (!set_all_left_to_false) {
614 int new_local_end = local_start;
615 std::sort(&at_most_one_buffer_[local_start],
616 &at_most_one_buffer_[local_end]);
618 bool remove_previous =
false;
619 for (
int j = local_start; j < local_end; ++j) {
620 const Literal l = at_most_one_buffer_[j];
621 if (l.Index() == previous) {
622 if (assignment.LiteralIsTrue(l))
return false;
623 if (!assignment.LiteralIsFalse(l)) {
624 if (!FixLiteral(l.Negated()))
return false;
626 remove_previous =
true;
627 some_duplicates =
true;
633 if (remove_previous) {
635 remove_previous =
false;
637 previous = l.Index();
638 at_most_one_buffer_[new_local_end++] = l;
640 if (remove_previous) --new_local_end;
641 local_end = new_local_end;
646 if (some_duplicates) {
647 int new_local_end = local_start;
648 for (
int j = local_start; j < local_end; ++j) {
649 const Literal l = at_most_one_buffer_[j];
650 if (assignment.LiteralIsFalse(l))
continue;
651 if (!set_all_left_to_false && assignment.LiteralIsTrue(l)) {
652 set_all_left_to_false =
true;
655 at_most_one_buffer_[new_local_end++] = l;
657 local_end = new_local_end;
661 if (set_all_left_to_false) {
662 for (
int j = local_start; j < local_end; ++j) {
663 const Literal l = at_most_one_buffer_[j];
664 if (assignment.LiteralIsFalse(l))
continue;
665 if (assignment.LiteralIsTrue(l))
return false;
666 if (!FixLiteral(l.Negated()))
return false;
668 local_end = local_start;
673 const absl::Span<const Literal> at_most_one(
674 &at_most_one_buffer_[local_start], local_end - local_start);
678 if (at_most_one.size() < 10) {
680 for (
const Literal
a : at_most_one) {
681 for (
const Literal
b : at_most_one) {
682 if (
a ==
b)
continue;
683 implications_[
a.Index()].push_back(
b.Negated());
686 num_implications_ += at_most_one.size() * (at_most_one.size() - 1);
689 local_end = local_start;
694 for (
const Literal l : at_most_one) {
695 if (l.Index() >= at_most_ones_.
size()) {
696 at_most_ones_.
resize(l.Index().value() + 1);
698 CHECK(!is_redundant_[l.Index()]);
699 at_most_ones_[l.Index()].
push_back(local_start);
706 at_most_one_buffer_.resize(local_end);
710bool BinaryImplicationGraph::PropagateOnTrue(Literal true_literal,
714 const VariablesAssignment& assignment = trail->Assignment();
715 DCHECK(assignment.LiteralIsTrue(true_literal));
720 num_inspections_ += implications_[true_literal.Index()].size();
722 for (Literal
literal : implications_[true_literal.Index()]) {
723 if (assignment.LiteralIsTrue(
literal)) {
733 if (assignment.LiteralIsFalse(
literal)) {
735 *(trail->MutableConflict()) = {true_literal.Negated(),
literal};
739 reasons_[trail->Index()] = true_literal.Negated();
745 if (true_literal.Index() < at_most_ones_.
size()) {
746 for (
const int start : at_most_ones_[true_literal.Index()]) {
748 for (
int i =
start;; ++i) {
749 const Literal
literal = at_most_one_buffer_[i];
760 if (assignment.LiteralIsFalse(
literal))
continue;
763 if (assignment.LiteralIsTrue(
literal)) {
765 *(trail->MutableConflict()) = {true_literal.Negated(),
770 reasons_[trail->Index()] = true_literal.Negated();
785 while (propagation_trail_index_ < trail->
Index()) {
787 if (!PropagateOnTrue(
literal, trail))
return false;
793 const Trail& trail,
int trail_index)
const {
794 return {&reasons_[trail_index], 1};
805 std::vector<Literal>* conflict) {
811 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
813 is_marked_.
Set(root_literal_index);
822 const bool also_prune_direct_implication_list =
false;
826 auto& direct_implications = implications_[root_literal_index];
827 for (
const Literal l : direct_implications) {
828 if (is_marked_[l.Index()])
continue;
829 dfs_stack_.push_back(l);
830 while (!dfs_stack_.empty()) {
831 const LiteralIndex
index = dfs_stack_.back().Index();
832 dfs_stack_.pop_back();
833 if (!is_marked_[
index]) {
836 if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
852 if (also_prune_direct_implication_list) {
853 is_marked_.
Clear(l.Index());
859 if (also_prune_direct_implication_list) {
861 for (
const Literal l : direct_implications) {
862 if (!is_marked_[l.Index()]) {
863 is_marked_.
Set(l.Index());
864 direct_implications[new_size] = l;
868 if (new_size < direct_implications.size()) {
869 num_redundant_implications_ += direct_implications.size() - new_size;
870 direct_implications.resize(new_size);
874 RemoveRedundantLiterals(conflict);
882 const Trail& trail, std::vector<Literal>* conflict,
885 CHECK(!conflict->empty());
887 MarkDescendants(conflict->front().Negated());
893 RemoveRedundantLiterals(conflict);
900 const Trail& trail, std::vector<Literal>* conflict,
903 const LiteralIndex root_literal_index = conflict->front().NegatedIndex();
905 is_marked_.
Set(root_literal_index);
908 auto& direct_implications = implications_[root_literal_index];
914 std::shuffle(direct_implications.begin(), direct_implications.end(), random);
916 for (
const Literal l : direct_implications) {
917 if (is_marked_[l.Index()]) {
923 direct_implications[new_size++] = l;
924 dfs_stack_.push_back(l);
925 while (!dfs_stack_.empty()) {
926 const LiteralIndex
index = dfs_stack_.back().Index();
927 dfs_stack_.pop_back();
928 if (!is_marked_[
index]) {
931 if (!is_marked_[implied.Index()]) dfs_stack_.push_back(implied);
936 if (new_size < direct_implications.size()) {
937 num_redundant_implications_ += direct_implications.size() - new_size;
938 direct_implications.resize(new_size);
940 RemoveRedundantLiterals(conflict);
943void BinaryImplicationGraph::RemoveRedundantLiterals(
944 std::vector<Literal>* conflict) {
947 for (
int i = 1; i < conflict->size(); ++i) {
948 if (!is_marked_[(*conflict)[i].NegatedIndex()]) {
949 (*conflict)[new_index] = (*conflict)[i];
953 if (new_index < conflict->size()) {
955 num_literals_removed_ += conflict->size() - new_index;
956 conflict->resize(new_index);
962 const Trail& trail, std::vector<Literal>* conflict) {
965 is_simplified_.
ClearAndResize(LiteralIndex(implications_.size()));
966 for (
Literal lit : *conflict) {
967 is_marked_.
Set(lit.Index());
983 for (
int i = 1; i < conflict->size(); ++i) {
984 const Literal lit = (*conflict)[i];
986 bool keep_literal =
true;
988 if (is_marked_[implied.Index()]) {
990 if (lit_level == trail.
Info(implied.Variable()).
level &&
991 is_simplified_[implied.Index()]) {
994 keep_literal =
false;
999 (*conflict)[
index] = lit;
1005 if (index < conflict->size()) {
1006 ++num_minimization_;
1007 num_literals_removed_ += conflict->size() -
index;
1008 conflict->erase(conflict->begin() +
index, conflict->end());
1018 const int new_num_fixed = trail_->
Index();
1020 if (num_processed_fixed_variables_ == new_num_fixed)
return;
1024 for (; num_processed_fixed_variables_ < new_num_fixed;
1025 ++num_processed_fixed_variables_) {
1026 const Literal true_literal = (*trail_)[num_processed_fixed_variables_];
1030 for (
const Literal lit : implications_[true_literal.
Index()]) {
1044 is_marked_.
Set(lit.NegatedIndex());
1049 if (true_literal.
Index() < at_most_ones_.
size()) {
1057 RemoveIf(&implications_[i], [&assignment](
const Literal& lit) {
1064 at_most_ones_.
clear();
1065 CleanUpAndAddAtMostOnes(0);
1076 std::vector<std::vector<int32_t>>>;
1080 std::vector<Literal>* at_most_one_buffer)
1082 implications_(*graph),
1083 at_most_ones_(*at_most_ones),
1084 at_most_one_buffer_(*at_most_one_buffer) {}
1088 for (
const Literal l : implications_[LiteralIndex(node)]) {
1089 tmp_.push_back(l.Index().value());
1094 if (node < at_most_ones_.
size()) {
1095 for (
const int start : at_most_ones_[LiteralIndex(node)]) {
1096 if (
start >= at_most_one_already_explored_.size()) {
1097 at_most_one_already_explored_.resize(
start + 1,
false);
1098 previous_node_to_explore_at_most_one_.resize(
start + 1);
1108 if (at_most_one_already_explored_[
start]) {
1110 const int first_node = previous_node_to_explore_at_most_one_[
start];
1129 previous_node_to_explore_at_most_one_[
start] = node;
1134 Literal(LiteralIndex(first_node)).NegatedIndex().
value());
1138 at_most_one_already_explored_[
start] =
true;
1139 previous_node_to_explore_at_most_one_[
start] = node;
1142 for (
int i =
start;; ++i) {
1143 const Literal l = at_most_one_buffer_[i];
1145 if (l.
Index() == node)
continue;
1167 const std::vector<Literal>& at_most_one_buffer_;
1169 mutable std::vector<int32_t> tmp_;
1172 mutable std::vector<bool> at_most_one_already_explored_;
1173 mutable std::vector<int> previous_node_to_explore_at_most_one_;
1179 if (is_dag_)
return true;
1190 int num_fixed_during_scc = 0;
1191 const int32_t size(implications_.size());
1192 std::vector<std::vector<int32_t>> scc;
1196 SccGraph graph(&finder, &implications_, &at_most_ones_,
1197 &at_most_one_buffer_);
1204 ++num_fixed_during_scc;
1205 if (!FixLiteral(l))
return false;
1211 is_redundant_.
resize(size,
false);
1213 int num_equivalences = 0;
1214 reverse_topological_order_.clear();
1215 for (std::vector<int32_t>& component : scc) {
1223 bool all_fixed =
false;
1224 bool all_true =
false;
1225 for (
const int32_t i : component) {
1234 for (
const int32_t i : component) {
1236 if (!is_redundant_[l.
Index()]) {
1237 ++num_redundant_literals_;
1238 is_redundant_[l.
Index()] =
true;
1243 ++num_fixed_during_scc;
1244 if (!FixLiteral(l))
return false;
1253 if (component.size() == 1 && is_removed_[LiteralIndex(component[0])]) {
1262 std::sort(component.begin(), component.end());
1266 if (component.size() == 1) {
1269 if (num_equivalences > 0) {
1271 for (
Literal& ref : representative_list) {
1272 const LiteralIndex rep = representative_of_[ref.Index()];
1283 for (
int i = 1; i < component.size(); ++i) {
1285 if (!is_redundant_[
literal.Index()]) {
1286 ++num_redundant_literals_;
1287 is_redundant_[
literal.Index()] =
true;
1293 if (
Literal(LiteralIndex(component[i - 1])).Negated() ==
literal) {
1294 LOG_IF(
INFO, log_info) <<
"Trivially UNSAT in DetectEquivalences()";
1303 for (
const Literal l : representative_list) {
1306 representative_list[new_size++] = rep;
1308 representative_list.resize(new_size);
1309 for (
int i = 1; i < component.size(); ++i) {
1311 auto& ref = implications_[
literal.Index()];
1322 representative_list.push_back(
literal);
1327 num_equivalences += component.size() - 1;
1331 if (num_equivalences != 0) {
1335 at_most_ones_.
clear();
1336 CleanUpAndAddAtMostOnes(0);
1338 num_implications_ = 0;
1339 for (LiteralIndex i(0); i < size; ++i) {
1340 num_implications_ += implications_[i].size();
1342 dtime += 2e-8 * num_implications_;
1346 LOG_IF(
INFO, log_info) <<
"SCC. " << num_equivalences
1347 <<
" redundant equivalent literals. "
1348 << num_fixed_during_scc <<
" fixed. "
1349 << num_implications_ <<
" implications left. "
1350 << implications_.size() <<
" literals."
1351 <<
" size of at_most_one buffer = "
1352 << at_most_one_buffer_.size() <<
"."
1353 <<
" dtime: " << dtime
1377 int64_t num_fixed = 0;
1378 int64_t num_new_redundant_implications = 0;
1379 bool aborted =
false;
1380 work_done_in_mark_descendants_ = 0;
1381 int marked_index = 0;
1399 const LiteralIndex size(implications_.size());
1401 for (
const LiteralIndex root : reverse_topological_order_) {
1406 if (is_redundant_[root])
continue;
1409 auto& direct_implications = implications_[root];
1410 if (direct_implications.empty())
continue;
1419 bool clear_previous_reachability =
true;
1420 for (
const Literal direct_child : direct_implications) {
1421 if (direct_child.Index() == previous) {
1422 clear_previous_reachability =
false;
1423 is_marked_.
Clear(previous);
1427 if (clear_previous_reachability) {
1433 for (
const Literal direct_child : direct_implications) {
1434 if (is_redundant_[direct_child.Index()])
continue;
1435 if (is_marked_[direct_child.Index()])
continue;
1439 if (direct_child.Index() == root)
continue;
1443 if (direct_child.NegatedIndex() == root) {
1444 is_marked_.
Set(direct_child.Index());
1448 MarkDescendants(direct_child);
1451 is_marked_.
Clear(direct_child.Index());
1453 CHECK(!is_marked_[root])
1454 <<
"DetectEquivalences() should have removed cycles!";
1455 is_marked_.
Set(root);
1461 for (; marked_index < marked_positions.size(); ++marked_index) {
1462 const LiteralIndex i = marked_positions[marked_index];
1463 if (is_marked_[
Literal(i).NegatedIndex()]) {
1471 if (!FixLiteral(
Literal(root).Negated()))
return false;
1485 for (
const Literal l : direct_implications) {
1486 if (!is_marked_[l.Index()]) {
1487 direct_implications[new_size++] = l;
1489 CHECK(!is_redundant_[l.Index()]);
1492 const int diff = direct_implications.size() - new_size;
1493 direct_implications.resize(new_size);
1494 direct_implications.shrink_to_fit();
1495 num_new_redundant_implications += diff;
1496 num_implications_ -= diff;
1499 if (work_done_in_mark_descendants_ > 1e8) {
1507 const double dtime = 1e-8 * work_done_in_mark_descendants_;
1509 num_redundant_implications_ += num_new_redundant_implications;
1510 LOG_IF(
INFO, log_info) <<
"Transitive reduction removed "
1511 << num_new_redundant_implications <<
" literals. "
1512 << num_fixed <<
" fixed. " << num_implications_
1513 <<
" implications left. " << implications_.size()
1515 <<
" dtime: " << dtime
1517 << (aborted ?
" Aborted." :
"");
1523bool IntersectionIsEmpty(
const std::vector<int>&
a,
const std::vector<int>&
b) {
1524 DCHECK(std::is_sorted(
a.begin(),
a.end()));
1525 DCHECK(std::is_sorted(
b.begin(),
b.end()));
1528 for (; i <
a.size() && j <
b.size();) {
1529 if (
a[i] ==
b[j])
return false;
1541 std::size_t operator()(
const std::vector<Literal>& at_most_one)
const {
1543 for (Literal
literal : at_most_one) {
1553 std::vector<std::vector<Literal>>* at_most_ones,
1554 int64_t max_num_explored_nodes) {
1557 work_done_in_mark_descendants_ = 0;
1559 int num_extended = 0;
1560 int num_removed = 0;
1563 absl::flat_hash_set<std::vector<Literal>, VectorHash> max_cliques;
1565 implications_.size());
1569 std::vector<std::pair<int, int>> index_size_vector;
1570 index_size_vector.reserve(at_most_ones->size());
1571 for (
int i = 0; i < at_most_ones->size(); ++i) {
1572 index_size_vector.push_back({i, (*at_most_ones)[i].size()});
1575 index_size_vector.begin(), index_size_vector.end(),
1576 [](
const std::pair<int, int>
a,
const std::pair<int, int>&
b) {
1577 return a.second > b.second;
1579 for (
const auto& [
index, old_size] : index_size_vector) {
1580 std::vector<Literal>& clique = (*at_most_ones)[
index];
1591 const LiteralIndex rep = representative_of_[ref.Index()];
1600 if (old_size == 2 && clique[0] != clique[1]) {
1601 if (!IntersectionIsEmpty(max_cliques_containing[clique[0].
Index()],
1602 max_cliques_containing[clique[1].
Index()])) {
1610 if (work_done_in_mark_descendants_ < max_num_explored_nodes) {
1611 clique = ExpandAtMostOne(clique, max_num_explored_nodes);
1613 std::sort(clique.begin(), clique.end());
1620 const int clique_index = max_cliques.size();
1621 for (
const Literal l : clique) {
1622 max_cliques_containing[l.Index()].
push_back(clique_index);
1624 if (clique.size() > old_size) ++num_extended;
1628 if (num_extended > 0 || num_removed > 0 || num_added > 0) {
1629 VLOG(1) <<
"Clique Extended: " << num_extended
1630 <<
" Removed: " << num_removed <<
" Added: " << num_added
1631 << (work_done_in_mark_descendants_ > max_num_explored_nodes
1638template <
bool use_weight>
1640 const absl::Span<const Literal> at_most_one,
1643 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1644 std::vector<LiteralIndex> intersection;
1645 double clique_weight = 0.0;
1646 const int64_t old_work = work_done_in_mark_descendants_;
1648 for (
const Literal l : clique) {
1649 clique_weight += expanded_lp_values[l.Index()];
1652 for (
int i = 0; i < clique.size(); ++i) {
1654 if (work_done_in_mark_descendants_ - old_work > 1e8)
break;
1657 MarkDescendants(clique[i]);
1661 intersection.push_back(
index);
1664 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1668 double intersection_weight = 0.0;
1670 is_marked_.
Clear(clique[i].NegatedIndex());
1671 for (
const LiteralIndex
index : intersection) {
1672 if (!is_marked_[
index])
continue;
1673 intersection[new_size++] =
index;
1675 intersection_weight += expanded_lp_values[
index];
1678 intersection.
resize(new_size);
1679 if (intersection.empty())
break;
1683 if (use_weight && clique_weight + intersection_weight <= 1.0) {
1690 if (i + 1 == clique.size()) {
1693 double max_lp = 0.0;
1694 for (
int j = 0; j < intersection.size(); ++j) {
1697 use_weight ? 1.0 - expanded_lp_values[intersection[j]] +
1698 absl::Uniform<double>(*random_, 0.0, 1e-4)
1699 : can_be_included.
size() - intersection[j].value();
1700 if (
index == -1 || lp > max_lp) {
1706 clique.push_back(
Literal(intersection[
index]).Negated());
1708 intersection.pop_back();
1710 clique_weight += expanded_lp_values[clique.back().Index()];
1720 true>(
const absl::Span<const Literal> at_most_one,
1724 false>(
const absl::Span<const Literal> at_most_one,
1728const std::vector<std::vector<Literal>>&
1730 const std::vector<Literal>& literals,
1731 const std::vector<double>& lp_values) {
1733 const int num_literals = implications_.size();
1737 const int size = literals.size();
1738 for (
int i = 0; i < size; ++i) {
1739 const Literal l = literals[i];
1740 can_be_included[l.
Index()] =
true;
1743 const double value = lp_values[i];
1753 bool operator<(
const Candidate& other)
const {
return sum > other.sum; }
1755 std::vector<Candidate> candidates;
1763 for (
int i = 0; i < size; ++i) {
1764 Literal current_literal = literals[i];
1765 double current_value = lp_values[i];
1767 if (is_redundant_[current_literal.
Index()])
continue;
1769 if (current_value < 0.5) {
1770 current_literal = current_literal.
Negated();
1771 current_value = 1.0 - current_value;
1776 double best_value = 0.0;
1777 for (
const Literal l : implications_[current_literal.
Index()]) {
1778 if (!can_be_included[l.Index()])
continue;
1779 const double activity =
1780 current_value + expanded_lp_values[l.NegatedIndex()];
1781 if (activity <= 1.01)
continue;
1782 const double v = activity + absl::Uniform<double>(*random_, 0.0, 1e-4);
1785 best = l.NegatedIndex();
1789 const double activity = current_value + expanded_lp_values[best];
1795 const int kMaxNumberOfCutPerCall = 50;
1796 std::sort(candidates.begin(), candidates.end());
1797 if (candidates.size() > kMaxNumberOfCutPerCall) {
1798 candidates.resize(kMaxNumberOfCutPerCall);
1804 std::vector<Literal> at_most_one;
1805 for (
const Candidate& candidate : candidates) {
1807 {candidate.a, candidate.b}, can_be_included, expanded_lp_values);
1808 if (!at_most_one.empty()) tmp_cuts_.push_back(at_most_one);
1814void BinaryImplicationGraph::MarkDescendants(
Literal root) {
1815 dfs_stack_ = {root};
1817 if (is_redundant_[root.
Index()])
return;
1818 for (
int j = 0; j < dfs_stack_.size(); ++j) {
1819 const Literal current = dfs_stack_[j];
1820 for (
const Literal l : implications_[current.Index()]) {
1821 if (!is_marked_[l.Index()] && !is_redundant_[l.Index()]) {
1822 dfs_stack_.push_back(l);
1823 is_marked_.
Set(l.Index());
1827 if (current.Index() >= at_most_ones_.
size())
continue;
1828 for (
const int start : at_most_ones_[current.Index()]) {
1829 for (
int i =
start;; ++i) {
1830 const Literal l = at_most_one_buffer_[i];
1832 if (l == current)
continue;
1833 if (!is_marked_[l.NegatedIndex()] && !is_redundant_[l.NegatedIndex()]) {
1834 dfs_stack_.push_back(l.Negated());
1835 is_marked_.
Set(l.NegatedIndex());
1840 work_done_in_mark_descendants_ += dfs_stack_.size();
1843std::vector<Literal> BinaryImplicationGraph::ExpandAtMostOne(
1844 const absl::Span<const Literal> at_most_one,
1845 int64_t max_num_explored_nodes) {
1846 std::vector<Literal> clique(at_most_one.begin(), at_most_one.end());
1849 for (
int i = 0; i < clique.size(); ++i) {
1850 if (implications_[clique[i].
Index()].empty() ||
1851 is_redundant_[clique[i].
Index()]) {
1856 std::vector<LiteralIndex> intersection;
1857 for (
int i = 0; i < clique.size(); ++i) {
1858 if (work_done_in_mark_descendants_ > max_num_explored_nodes)
break;
1860 MarkDescendants(clique[i]);
1864 for (
const Literal l : clique) is_marked_.
Clear(l.NegatedIndex());
1868 is_marked_.
Clear(clique[i].NegatedIndex());
1869 for (
const LiteralIndex
index : intersection) {
1870 if (is_marked_[
index]) intersection[new_size++] =
index;
1872 intersection.resize(new_size);
1873 if (intersection.empty())
break;
1876 if (i + 1 == clique.size()) {
1877 clique.push_back(Literal(intersection.back()).Negated());
1878 intersection.pop_back();
1891 for (
const Literal l : direct_implications_) {
1892 in_direct_implications_[l.Index()] =
false;
1894 direct_implications_.
clear();
1902 if (!is_removed_[l.Index()] && !in_direct_implications_[l.Index()]) {
1903 in_direct_implications_[l.Index()] =
true;
1908 if (is_redundant_[
literal.Index()]) {
1911 for (
const int start : at_most_ones_[
literal.Index()]) {
1912 for (
int i =
start;; ++i) {
1913 const Literal l = at_most_one_buffer_[i];
1917 if (!is_removed_[l.
Index()] &&
1925 estimated_sizes_[
literal.Index()] = direct_implications_.
size();
1926 return direct_implications_;
1938 direct_implications_of_negated_literal_ =
1941 for (
const Literal l : direct_implications_of_negated_literal_) {
1942 if (in_direct_implications_[l.Index()]) {
1944 if (!FixLiteral(l)) {
1955 BooleanVariable
var) {
1958 direct_implications_of_negated_literal_ =
1961 for (
const Literal l : direct_implications_of_negated_literal_) {
1965 CHECK(!in_direct_implications_[l.Index()]);
1968 if (in_direct_implications_[l.NegatedIndex()]) result--;
1975 BooleanVariable
var, std::deque<std::vector<Literal>>* postsolve_clauses) {
1977 direct_implications_of_negated_literal_ =
1980 estimated_sizes_[
b.NegatedIndex()]--;
1981 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1982 if (a_negated.Negated() ==
b)
continue;
1986 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1987 estimated_sizes_[a_negated.NegatedIndex()]--;
1992 for (
const Literal b : direct_implications_) {
1993 if (drat_proof_handler_ !=
nullptr) {
1996 postsolve_clauses->push_back({
Literal(
var,
false),
b});
1998 for (
const Literal a_negated : direct_implications_of_negated_literal_) {
1999 if (drat_proof_handler_ !=
nullptr) {
2002 postsolve_clauses->push_back({
Literal(
var,
true), a_negated});
2008 is_removed_[
index] =
true;
2009 if (!is_redundant_[
index]) {
2010 ++num_redundant_literals_;
2011 is_redundant_[
index] =
true;
2013 implications_[
index].clear();
2018 for (
auto& implication : implications_) {
2020 for (
const Literal l : implication) {
2021 if (!is_removed_[l.Index()]) implication[new_size++] = l;
2023 implication.resize(new_size);
2027 at_most_ones_.
clear();
2028 CleanUpAndAddAtMostOnes(0);
2038 clause->size_ = literals.size();
2039 for (
int i = 0; i < literals.size(); ++i) {
2040 clause->literals_[i] = literals[i];
2059 for (
int i = j; i < size_; ++i) {
2081 if (!result.empty()) result.append(
" ");
2082 result.append(
literal.DebugString());
#define LOG_IF(severity, condition)
#define DCHECK_LE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define DCHECK_GE(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK_LT(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
#define VLOG(verboselevel)
bool NodeIsInCurrentDfsPath(NodeIndex node) const
void FindStronglyConnectedComponents(const NodeIndex num_nodes, const Graph &graph, SccOutput *components)
void resize(size_type new_size)
void push_back(const value_type &x)
void Set(IntegerType index)
const std::vector< IntegerType > & PositionsSetAtLeastOnce() const
void Clear(IntegerType index)
void Resize(IntegerType size)
void ClearAndResize(IntegerType size)
std::string StatString() const
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void AdvanceDeterministicTime(double deterministic_duration)
Advances the deterministic time.
int64_t NumImplicationOnVariableRemoval(BooleanVariable var)
bool Propagate(Trail *trail) final
void AddBinaryClause(Literal a, Literal b)
bool ComputeTransitiveReduction(bool log_info=false)
void MinimizeConflictWithReachability(std::vector< Literal > *c)
bool AddBinaryClauseDuringSearch(Literal a, Literal b)
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) const final
void RemoveBooleanVariable(BooleanVariable var, std::deque< std::vector< Literal > > *postsolve_clauses)
void MinimizeConflictFirstWithTransitiveReduction(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked, absl::BitGenRef random)
Literal RepresentativeOf(Literal l) const
bool TransformIntoMaxCliques(std::vector< std::vector< Literal > > *at_most_ones, int64_t max_num_explored_nodes=1e8)
const std::vector< Literal > & DirectImplications(Literal literal)
void AddImplication(Literal a, Literal b)
void MinimizeConflictFirst(const Trail &trail, std::vector< Literal > *c, SparseBitset< BooleanVariable > *marked)
bool DetectEquivalences(bool log_info=false)
void CleanupAllRemovedVariables()
bool FindFailedLiteralAroundVar(BooleanVariable var, bool *is_unsat)
void RemoveFixedVariables()
bool IsEmpty() const final
std::vector< Literal > ExpandAtMostOneWithWeight(const absl::Span< const Literal > at_most_one, const absl::StrongVector< LiteralIndex, bool > &can_be_included, const absl::StrongVector< LiteralIndex, double > &expanded_lp_values)
ABSL_MUST_USE_RESULT bool AddAtMostOne(absl::Span< const Literal > at_most_one)
void MinimizeConflictExperimental(const Trail &trail, std::vector< Literal > *c)
void Resize(int num_variables)
void DeleteClause(absl::Span< const Literal > clause)
void AddClause(absl::Span< const Literal > clause)
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
ABSL_MUST_USE_RESULT bool InprocessingFixLiteral(Literal true_literal)
LiteralWatchers(Model *model)
bool Propagate(Trail *trail) final
void InprocessingRemoveClause(SatClause *clause)
absl::Span< const Literal > Reason(const Trail &trail, int trail_index) const final
SatClause * AddRemovableClause(const std::vector< Literal > &literals, Trail *trail)
SatClause * InprocessingAddClause(absl::Span< const Literal > new_clause)
void Attach(SatClause *clause, Trail *trail)
bool AddClause(absl::Span< const Literal > literals, Trail *trail)
SatClause * ReasonClause(int trail_index) const
ABSL_MUST_USE_RESULT bool InprocessingRewriteClause(SatClause *clause, absl::Span< const Literal > new_clause)
void LazyDetach(SatClause *clause)
~LiteralWatchers() override
void DeleteRemovedClauses()
void Detach(SatClause *clause)
void Resize(int num_variables)
Class that owns everything related to a particular optimization model.
absl::Span< const Literal > AsSpan() const
const Literal *const begin() const
Literal SecondLiteral() const
bool IsSatisfied(const VariablesAssignment &assignment) const
bool RemoveFixedLiteralsAndTestIfTrue(const VariablesAssignment &assignment)
std::string DebugString() const
Literal FirstLiteral() const
static SatClause * Create(absl::Span< const Literal > literals)
int propagation_trail_index_
SccGraph(SccFinder *finder, Implication *graph, AtMostOne *at_most_ones, std::vector< Literal > *at_most_one_buffer)
std::vector< Literal > to_fix_
const std::vector< int32_t > & operator[](int32_t node) const
void RegisterPropagator(SatPropagator *propagator)
void Enqueue(Literal true_literal, int propagator_id)
const VariablesAssignment & Assignment() const
const AssignmentInfo & Info(BooleanVariable var) const
int CurrentDecisionLevel() const
void EnqueueWithUnitReason(Literal true_literal)
bool LiteralIsAssigned(Literal literal) const
bool VariableIsAssigned(BooleanVariable var) const
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
void STLSortAndRemoveDuplicates(T *v, const LessFunc &less_func)
bool InsertIfNotPresent(Collection *const collection, const typename Collection::value_type &value)
void STLDeleteElements(T *container)
void STLDeleteContainerPointers(ForwardIterator begin, ForwardIterator end)
void STLClearObject(T *obj)
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
const LiteralIndex kNoLiteralIndex(-1)
Collection of objects used to extend the Constraint Solver library.
uint64_t Hash(uint64_t num, uint64_t c)
std::optional< int64_t > end
#define IF_STATS_ENABLED(instructions)
#define SCOPED_TIME_STAT(stats)
#define VLOG_IS_ON(verboselevel)