@@ -168,6 +168,58 @@ std::pair<std::vector<DepGraphIndex>, bool> SimpleLinearize(const DepGraph<SetTy
168168 return {std::move (linearization), optimal};
169169}
170170
171+ /* * An even simpler linearization algorithm that tries all permutations.
172+ *
173+ * This roughly matches SimpleLinearize() (and Linearize) in interface and behavior, but always
174+ * tries all topologically-valid transaction orderings, has no way to bound how much work it does,
175+ * and always finds the optimal. With an O(n!) complexity, it should only be used for small
176+ * clusters.
177+ */
178+ template <typename SetType>
179+ std::vector<DepGraphIndex> ExhaustiveLinearize (const DepGraph<SetType>& depgraph)
180+ {
181+ // The best linearization so far, and its chunking.
182+ std::vector<DepGraphIndex> linearization;
183+ std::vector<FeeFrac> chunking;
184+
185+ std::vector<DepGraphIndex> perm_linearization;
186+ // Initialize with the lexicographically-first linearization.
187+ for (DepGraphIndex i : depgraph.Positions ()) perm_linearization.push_back (i);
188+ // Iterate over all valid permutations.
189+ do {
190+ /* * What prefix of perm_linearization is topological. */
191+ DepGraphIndex topo_length{0 };
192+ TestBitSet perm_done;
193+ while (topo_length < perm_linearization.size ()) {
194+ auto i = perm_linearization[topo_length];
195+ perm_done.Set (i);
196+ if (!depgraph.Ancestors (i).IsSubsetOf (perm_done)) break ;
197+ ++topo_length;
198+ }
199+ if (topo_length == perm_linearization.size ()) {
200+ // If all of perm_linearization is topological, check if it is perhaps our best
201+ // linearization so far.
202+ auto perm_chunking = ChunkLinearization (depgraph, perm_linearization);
203+ auto cmp = CompareChunks (perm_chunking, chunking);
204+ // If the diagram is better, or if it is equal but with more chunks (because we
205+ // prefer minimal chunks), consider this better.
206+ if (linearization.empty () || cmp > 0 || (cmp == 0 && perm_chunking.size () > chunking.size ())) {
207+ linearization = perm_linearization;
208+ chunking = perm_chunking;
209+ }
210+ } else {
211+ // Otherwise, fast forward to the last permutation with the same non-topological
212+ // prefix.
213+ auto first_non_topo = perm_linearization.begin () + topo_length;
214+ assert (std::is_sorted (first_non_topo + 1 , perm_linearization.end ()));
215+ std::reverse (first_non_topo + 1 , perm_linearization.end ());
216+ }
217+ } while (std::next_permutation (perm_linearization.begin (), perm_linearization.end ()));
218+
219+ return linearization;
220+ }
221+
222+
171223/* * Stitch connected components together in a DepGraph, guaranteeing its corresponding cluster is connected. */
172224template <typename BS>
173225void MakeConnected (DepGraph<BS>& depgraph)
@@ -996,38 +1048,11 @@ FUZZ_TARGET(clusterlin_simple_linearize)
9961048 // If SimpleLinearize claims optimal result, and the cluster is sufficiently small (there are
9971049 // n! linearizations), test that the result is as good as every valid linearization.
9981050 if (optimal && depgraph.TxCount () <= 8 ) {
999- std::vector<DepGraphIndex> perm_linearization;
1000- // Initialize with the lexicographically-first linearization.
1001- for (DepGraphIndex i : depgraph.Positions ()) perm_linearization.push_back (i);
1002- // Iterate over all valid permutations.
1003- do {
1004- /* * What prefix of perm_linearization is topological. */
1005- DepGraphIndex topo_length{0 };
1006- TestBitSet perm_done;
1007- while (topo_length < perm_linearization.size ()) {
1008- auto i = perm_linearization[topo_length];
1009- perm_done.Set (i);
1010- if (!depgraph.Ancestors (i).IsSubsetOf (perm_done)) break ;
1011- ++topo_length;
1012- }
1013- if (topo_length == perm_linearization.size ()) {
1014- // If all of perm_linearization is topological, verify that the obtained
1015- // linearization is no worse than it.
1016- auto perm_chunking = ChunkLinearization (depgraph, perm_linearization);
1017- auto cmp = CompareChunks (simple_chunking, perm_chunking);
1018- assert (cmp >= 0 );
1019- // If perm_chunking is diagram-optimal, it cannot have more chunks than
1020- // simple_chunking (as simple_chunking claims to be optimal, which implies minimal
1021- // chunks.
1022- if (cmp == 0 ) assert (simple_chunking.size () >= perm_chunking.size ());
1023- } else {
1024- // Otherwise, fast forward to the last permutation with the same non-topological
1025- // prefix.
1026- auto first_non_topo = perm_linearization.begin () + topo_length;
1027- assert (std::is_sorted (first_non_topo + 1 , perm_linearization.end ()));
1028- std::reverse (first_non_topo + 1 , perm_linearization.end ());
1029- }
1030- } while (std::next_permutation (perm_linearization.begin (), perm_linearization.end ()));
1051+ auto exh_linearization = ExhaustiveLinearize (depgraph);
1052+ auto exh_chunking = ChunkLinearization (depgraph, exh_linearization);
1053+ auto cmp = CompareChunks (simple_chunking, exh_chunking);
1054+ assert (cmp == 0 );
1055+ assert (simple_chunking.size () == exh_chunking.size ());
10311056 }
10321057
10331058 if (optimal) {
0 commit comments