@@ -592,9 +592,9 @@ bool BinaryImplicationGraph::AddAtMostOne(
592592 // at_most_one_buffer_. It will be cleaned up and added by
593593 // CleanUpAndAddAtMostOnes().
594594 const int base_index = at_most_one_buffer_.size ();
595+ at_most_one_buffer_.push_back (Literal (LiteralIndex (at_most_one.size ())));
595596 at_most_one_buffer_.insert (at_most_one_buffer_.end (), at_most_one.begin (),
596597 at_most_one.end ());
597- at_most_one_buffer_.push_back (Literal (kNoLiteralIndex ));
598598
599599 is_dag_ = false ;
600600 return CleanUpAndAddAtMostOnes (base_index);
@@ -624,27 +624,27 @@ bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(int base_index) {
624624 const VariablesAssignment& assignment = trail_->Assignment ();
625625 int local_end = base_index;
626626 const int buffer_size = at_most_one_buffer_.size ();
627- for (int i = base_index; i < buffer_size; ++i) {
628- if (at_most_one_buffer_[i].Index () == kNoLiteralIndex ) continue ;
629-
627+ for (int i = base_index; i < buffer_size;) {
630628 // Process a new at most one.
631- // It will be copied into buffer[local_start, local_end].
629+ // It will be copied into buffer[local_start + 1, local_end].
630+ // With its size at buffer[local_start].
632631 const int local_start = local_end;
633632
634633 // Update the iterator now. Even if the current at_most_one is reduced away,
635634 // local_start will still point to the next one, or to the end of the
636635 // buffer.
637636 if (i == at_most_one_iterator_) {
638637 at_most_one_iterator_ = local_start;
639- DCHECK (at_most_one_iterator_ == 0 ||
640- at_most_one_buffer_[at_most_one_iterator_ - 1 ].Index () ==
641- kNoLiteralIndex );
642638 }
643639
640+ // We have an at_most_one starting at i, and we increment i to the next one.
641+ const absl::Span<const Literal> initial_amo = AtMostOne (i);
642+ i += initial_amo.size () + 1 ;
643+
644+ // Reserve space for size.
645+ local_end++;
644646 bool set_all_left_to_false = false ;
645- for (;; ++i) {
646- const Literal l = at_most_one_buffer_[i];
647- if (l.Index () == kNoLiteralIndex ) break ;
647+ for (const Literal l : initial_amo) {
648648 if (assignment.LiteralIsFalse (l)) continue ;
649649 if (is_removed_[l]) continue ;
650650 if (!set_all_left_to_false && assignment.LiteralIsTrue (l)) {
@@ -653,18 +653,22 @@ bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(int base_index) {
653653 }
654654 at_most_one_buffer_[local_end++] = RepresentativeOf (l);
655655 }
656+ at_most_one_buffer_[local_start] =
657+ Literal (LiteralIndex (local_end - local_start - 1 ));
656658
657659 // Deal with duplicates.
658660 // Any duplicate in an "at most one" must be false.
659661 bool some_duplicates = false ;
660662 if (!set_all_left_to_false) {
661- int new_local_end = local_start;
662- std::sort (&at_most_one_buffer_[local_start],
663- &at_most_one_buffer_[local_end]);
663+ // Sort the copied amo.
664+ // We only want to expose a const AtMostOne() so we sort directly here.
665+ Literal* pt = &at_most_one_buffer_[local_start + 1 ];
666+ std::sort (pt, pt + AtMostOne (local_start).size ());
667+
664668 LiteralIndex previous = kNoLiteralIndex ;
665669 bool remove_previous = false ;
666- for ( int j = local_start; j < local_end; ++j) {
667- const Literal l = at_most_one_buffer_[j];
670+ int new_local_end = local_start + 1 ;
671+ for ( const Literal l : AtMostOne (local_start)) {
668672 if (l.Index () == previous) {
669673 if (assignment.LiteralIsTrue (l)) return false ;
670674 if (!assignment.LiteralIsFalse (l)) {
@@ -685,40 +689,47 @@ bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(int base_index) {
685689 at_most_one_buffer_[new_local_end++] = l;
686690 }
687691 if (remove_previous) --new_local_end;
692+
693+ // Update local end and the amo size.
688694 local_end = new_local_end;
695+ at_most_one_buffer_[local_start] =
696+ Literal (LiteralIndex (local_end - local_start - 1 ));
689697 }
690698
691699 // If there was some duplicates, we need to rescan to see if a literal
692700 // didn't become true because its negation was appearing twice!
693701 if (some_duplicates) {
694- int new_local_end = local_start;
695- for (int j = local_start; j < local_end; ++j) {
696- const Literal l = at_most_one_buffer_[j];
702+ int new_local_end = local_start + 1 ;
703+ for (const Literal l : AtMostOne (local_start)) {
697704 if (assignment.LiteralIsFalse (l)) continue ;
698705 if (!set_all_left_to_false && assignment.LiteralIsTrue (l)) {
699706 set_all_left_to_false = true ;
700707 continue ;
701708 }
702709 at_most_one_buffer_[new_local_end++] = l;
703710 }
711+
712+ // Update local end and the amo size.
704713 local_end = new_local_end;
714+ at_most_one_buffer_[local_start] =
715+ Literal (LiteralIndex (local_end - local_start - 1 ));
705716 }
706717
707718 // Deal with all false.
708719 if (set_all_left_to_false) {
709- for (int j = local_start; j < local_end; ++j) {
710- const Literal l = at_most_one_buffer_[j];
720+ for (const Literal l : AtMostOne (local_start)) {
711721 if (assignment.LiteralIsFalse (l)) continue ;
712722 if (assignment.LiteralIsTrue (l)) return false ;
713723 if (!FixLiteral (l.Negated ())) return false ;
714724 }
725+
726+ // Erase this at_most_one.
715727 local_end = local_start;
716728 continue ;
717729 }
718730
719731 // Create a Span<> to simplify the code below.
720- const absl::Span<const Literal> at_most_one (
721- &at_most_one_buffer_[local_start], local_end - local_start);
732+ const absl::Span<const Literal> at_most_one = AtMostOne (local_start);
722733
723734 // We expand small sizes into implications.
724735 // TODO(user): Investigate what the best threshold is.
@@ -747,9 +758,6 @@ bool BinaryImplicationGraph::CleanUpAndAddAtMostOnes(int base_index) {
747758 at_most_ones_[l].push_back (local_start);
748759 implies_something_.Set (l);
749760 }
750-
751- // Add sentinel.
752- at_most_one_buffer_[local_end++] = Literal (kNoLiteralIndex );
753761 }
754762
755763 at_most_one_buffer_.resize (local_end);
@@ -794,10 +802,7 @@ bool BinaryImplicationGraph::PropagateOnTrue(Literal true_literal,
794802 if (true_literal.Index () < at_most_ones_.size ()) {
795803 for (const int start : at_most_ones_[true_literal]) {
796804 bool seen = false ;
797- for (int i = start;; ++i) {
798- const Literal literal = at_most_one_buffer_[i];
799- if (literal.Index () == kNoLiteralIndex ) break ;
800-
805+ for (const Literal literal : AtMostOne (start)) {
801806 ++num_inspections_;
802807 if (literal == true_literal) {
803808 if (DEBUG_MODE) {
@@ -1128,16 +1133,16 @@ void BinaryImplicationGraph::RemoveFixedVariables() {
11281133
11291134class SccGraph {
11301135 public:
1131- using Implication =
1136+ using Implications =
11321137 absl::StrongVector<LiteralIndex, absl::InlinedVector<Literal, 6 >>;
1133- using AtMostOne =
1138+ using AtMostOnes =
11341139 absl::StrongVector<LiteralIndex, absl::InlinedVector<int32_t , 6 >>;
11351140 using SccFinder =
11361141 StronglyConnectedComponentsFinder<int32_t , SccGraph,
11371142 CompactVectorVector<int32_t , int32_t >>;
11381143
1139- explicit SccGraph (SccFinder* finder, Implication * graph,
1140- AtMostOne * at_most_ones,
1144+ explicit SccGraph (SccFinder* finder, Implications * graph,
1145+ AtMostOnes * at_most_ones,
11411146 std::vector<Literal>* at_most_one_buffer)
11421147 : finder_(*finder),
11431148 implications_(*graph),
@@ -1200,9 +1205,10 @@ class SccGraph {
12001205 previous_node_to_explore_at_most_one_[start] = node;
12011206 }
12021207
1203- for (int i = start;; ++i) {
1204- const Literal l = at_most_one_buffer_[i];
1205- if (l.Index () == kNoLiteralIndex ) break ;
1208+ const absl::Span<const Literal> amo =
1209+ absl::MakeSpan (&at_most_one_buffer_[start + 1 ],
1210+ at_most_one_buffer_[start].Index ().value ());
1211+ for (const Literal l : amo) {
12061212 if (l.Index () == node) continue ;
12071213 tmp_.push_back (l.NegatedIndex ().value ());
12081214 if (finder_.NodeIsInCurrentDfsPath (l.Index ().value ())) {
@@ -1223,8 +1229,8 @@ class SccGraph {
12231229
12241230 private:
12251231 const SccFinder& finder_;
1226- const Implication & implications_;
1227- const AtMostOne & at_most_ones_;
1232+ const Implications & implications_;
1233+ const AtMostOnes & at_most_ones_;
12281234 const std::vector<Literal>& at_most_one_buffer_;
12291235
12301236 mutable std::vector<int32_t > tmp_;
@@ -1524,9 +1530,7 @@ bool BinaryImplicationGraph::ComputeTransitiveReduction(bool log_info) {
15241530 // Also mark all the ones reachable through the root AMOs.
15251531 if (root < at_most_ones_.size ()) {
15261532 for (const int start : at_most_ones_[root]) {
1527- for (int i = start;; ++i) {
1528- const Literal l = at_most_one_buffer_[i];
1529- if (l.Index () == kNoLiteralIndex ) break ;
1533+ for (const Literal l : AtMostOne (start)) {
15301534 if (l.Index () == root) continue ;
15311535 if (!is_marked_[l.Negated ()] && !is_redundant_[l.Negated ()]) {
15321536 is_marked_.SetUnsafe (l.Negated ());
@@ -1991,9 +1995,7 @@ BinaryImplicationGraph::HeuristicAmoPartition(std::vector<Literal>* literals) {
19911995 if (!inserted) continue ;
19921996
19931997 int intersection_size = 0 ;
1994- for (int i = start;; ++i) {
1995- const Literal l = at_most_one_buffer_[i];
1996- if (l.Index () == kNoLiteralIndex ) break ;
1998+ for (const Literal l : AtMostOne (start)) {
19971999 if (to_consider[l]) ++intersection_size;
19982000 }
19992001 if (intersection_size > 1 ) {
@@ -2013,9 +2015,7 @@ BinaryImplicationGraph::HeuristicAmoPartition(std::vector<Literal>* literals) {
20132015
20142016 // Recompute size.
20152017 int intersection_size = 0 ;
2016- for (int i = start;; ++i) {
2017- const Literal l = at_most_one_buffer_[i];
2018- if (l.Index () == kNoLiteralIndex ) break ;
2018+ for (const Literal l : AtMostOne (start)) {
20192019 if (to_consider[l]) ++intersection_size;
20202020 }
20212021 if (intersection_size != old_size) {
@@ -2027,9 +2027,7 @@ BinaryImplicationGraph::HeuristicAmoPartition(std::vector<Literal>* literals) {
20272027 }
20282028
20292029 // Mark the literal of that at most one at false.
2030- for (int i = start;; ++i) {
2031- const Literal l = at_most_one_buffer_[i];
2032- if (l.Index () == kNoLiteralIndex ) break ;
2030+ for (const Literal l : AtMostOne (start)) {
20332031 to_consider[l] = false ;
20342032 }
20352033
@@ -2050,7 +2048,6 @@ BinaryImplicationGraph::HeuristicAmoPartition(std::vector<Literal>* literals) {
20502048void BinaryImplicationGraph::MarkDescendants (Literal root) {
20512049 bfs_stack_.resize (implications_.size ());
20522050 auto * const stack = bfs_stack_.data ();
2053- auto * const amo_buffer = at_most_one_buffer_.data ();
20542051 const int amo_size = static_cast <int >(at_most_ones_.size ());
20552052 auto is_marked = is_marked_.const_view ();
20562053 auto is_redundant = is_redundant_.const_view ();
@@ -2072,9 +2069,7 @@ void BinaryImplicationGraph::MarkDescendants(Literal root) {
20722069
20732070 if (current.Index () >= amo_size) continue ;
20742071 for (const int start : at_most_ones_[current]) {
2075- for (int i = start;; ++i) {
2076- const Literal l = amo_buffer[i];
2077- if (l.Index () == kNoLiteralIndex ) break ;
2072+ for (const Literal l : AtMostOne (start)) {
20782073 if (l == current) continue ;
20792074 if (!is_marked[l.NegatedIndex ()] && !is_redundant[l.NegatedIndex ()]) {
20802075 is_marked_.SetUnsafe (l.NegatedIndex ());
@@ -2161,9 +2156,7 @@ const std::vector<Literal>& BinaryImplicationGraph::DirectImplications(
21612156 DCHECK (at_most_ones_[literal].empty ());
21622157 }
21632158 for (const int start : at_most_ones_[literal]) {
2164- for (int i = start;; ++i) {
2165- const Literal l = at_most_one_buffer_[i];
2166- if (l.Index () == kNoLiteralIndex ) break ;
2159+ for (const Literal l : AtMostOne (start)) {
21672160 if (l == literal) continue ;
21682161 if (assignment.LiteralIsAssigned (l)) continue ;
21692162 if (!is_removed_[l] && !in_direct_implications_[l.NegatedIndex ()]) {
@@ -2177,6 +2170,38 @@ const std::vector<Literal>& BinaryImplicationGraph::DirectImplications(
21772170 return direct_implications_;
21782171}
21792172
2173+ absl::Span<const Literal> BinaryImplicationGraph::AtMostOne (int start) const {
2174+ const int size = at_most_one_buffer_[start].Index ().value ();
2175+ return absl::MakeSpan (&at_most_one_buffer_[start + 1 ], size);
2176+ }
2177+
2178+ LiteralIndex BinaryImplicationGraph::RandomImpliedLiteral (Literal lhs) {
2179+ const int size1 = implications_[lhs].size ();
2180+ const int size2 =
2181+ lhs.Index () < at_most_ones_.size () ? at_most_ones_[lhs].size () : 0 ;
2182+ if (size1 + size2 == 0 ) return kNoLiteralIndex ;
2183+
2184+ const int choice = absl::Uniform<int >(*random_, 0 , size1 + size2);
2185+ if (choice < size1) {
2186+ return implications_[lhs][choice].Index ();
2187+ }
2188+
2189+ const absl::Span<const Literal> amo =
2190+ AtMostOne (at_most_ones_[lhs][choice - size1]);
2191+ CHECK_GE (amo.size (), 2 );
2192+ const int first_choice = absl::Uniform<int >(*random_, 0 , amo.size ());
2193+ const Literal lit = amo[first_choice];
2194+ if (lit != lhs) return lit.NegatedIndex ();
2195+
2196+ // We are unlucky and just picked the wrong literal: take a different one.
2197+ int next_choice = absl::Uniform<int >(*random_, 0 , amo.size () - 1 );
2198+ if (next_choice >= first_choice) {
2199+ next_choice += 1 ;
2200+ }
2201+ CHECK_NE (amo[next_choice], lhs);
2202+ return amo[next_choice].NegatedIndex ();
2203+ }
2204+
21802205bool BinaryImplicationGraph::FindFailedLiteralAroundVar (BooleanVariable var,
21812206 bool * is_unsat) {
21822207 const int saved_index = propagation_trail_index_;
@@ -2413,22 +2438,19 @@ bool BinaryImplicationGraph::InvariantsAreOk() {
24132438 lit_to_start.insert ({i, start});
24142439 }
24152440 }
2416- int index = 0 ;
2417- int start = 0 ;
2418- for (int i = 0 ; i < at_most_one_buffer_.size (); ++i) {
2419- if (at_most_one_buffer_[i].Index () == kNoLiteralIndex ) {
2420- ++index;
2421- start = i + 1 ;
2422- continue ;
2423- }
2424- if (is_removed_[at_most_one_buffer_[i]]) {
2425- LOG (ERROR) << " A removed literal still appear in an amo "
2426- << at_most_one_buffer_[i];
2427- return false ;
2428- }
2429- if (!lit_to_start.contains ({at_most_one_buffer_[i], start})) {
2430- return false ;
2441+
2442+ for (int start = 0 ; start < at_most_one_buffer_.size ();) {
2443+ const absl::Span<const Literal> amo = AtMostOne (start);
2444+ for (const Literal l : amo) {
2445+ if (is_removed_[l]) {
2446+ LOG (ERROR) << " A removed literal still appear in an amo " << l;
2447+ return false ;
2448+ }
2449+ if (!lit_to_start.contains ({l, start})) {
2450+ return false ;
2451+ }
24312452 }
2453+ start += amo.size () + 1 ;
24322454 }
24332455
24342456 return true ;
@@ -2438,19 +2460,10 @@ absl::Span<const Literal> BinaryImplicationGraph::NextAtMostOne() {
24382460 if (at_most_one_iterator_ >= at_most_one_buffer_.size ()) {
24392461 return absl::Span<const Literal>();
24402462 }
2441- const int local_start = at_most_one_iterator_;
2442- DCHECK_NE (at_most_one_buffer_[local_start].Index (), kNoLiteralIndex );
2443- DCHECK (at_most_one_iterator_ == 0 ||
2444- at_most_one_iterator_ >= at_most_one_buffer_.size () ||
2445- at_most_one_buffer_[at_most_one_iterator_ - 1 ].Index () ==
2446- kNoLiteralIndex );
2447- int local_end = at_most_one_iterator_ + 1 ;
2448- while (at_most_one_buffer_[local_end].Index () != kNoLiteralIndex ) {
2449- ++local_end;
2450- }
2451- at_most_one_iterator_ = local_end + 1 ;
2452- return absl::MakeSpan (at_most_one_buffer_.data () + local_start,
2453- local_end - local_start);
2463+
2464+ const absl::Span<const Literal> result = AtMostOne (at_most_one_iterator_);
2465+ at_most_one_iterator_ += result.size () + 1 ;
2466+ return result;
24542467}
24552468
24562469// ----- SatClause -----
0 commit comments