28template<
typename SetType>
29class SimpleCandidateFinder
39 m_depgraph(depgraph), m_todo{SetType::Fill(depgraph.TxCount())} {}
42 void MarkDone(SetType select)
noexcept { m_todo -= select; }
45 bool AllDone() const noexcept {
return m_todo.None(); }
53 std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations)
const noexcept
55 uint64_t iterations_left = max_iterations;
59 std::vector<std::pair<SetType, SetType>> queue;
61 queue.emplace_back(SetType{}, m_todo);
63 SetInfo best(m_depgraph, m_todo);
65 while (!queue.empty() && iterations_left) {
68 auto [inc, und] = queue.back();
71 bool inc_none = inc.None();
72 for (
auto split : und) {
76 if (inc_none || inc.Overlaps(m_depgraph.
Ancestors(split))) {
79 queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
81 queue.emplace_back(inc, und - m_depgraph.
Descendants(split));
83 if (new_inc.feerate > best.feerate) best = new_inc;
88 return {std::move(best), max_iterations - iterations_left};
98template<
typename SetType>
99class ExhaustiveCandidateFinder
109 m_depgraph(depgraph), m_todo{SetType::Fill(depgraph.TxCount())} {}
112 void MarkDone(SetType select)
noexcept { m_todo -= select; }
115 bool AllDone() const noexcept {
return m_todo.None(); }
126 uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
128 for (uint64_t x = 1; x < limit; ++x) {
133 for (
auto i : m_todo) {
134 if (x_shifted & 1) txn |= m_depgraph.
Ancestors(i);
137 SetInfo cur(m_depgraph, txn & m_todo);
138 if (cur.feerate > best.feerate) best = cur;
150template<
typename SetType>
151std::pair<std::vector<ClusterIndex>,
bool> SimpleLinearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations)
153 std::vector<ClusterIndex> linearization;
154 SimpleCandidateFinder finder(depgraph);
155 SetType todo = SetType::Fill(depgraph.
TxCount());
158 auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
159 if (iterations_done == max_iterations) optimal =
false;
160 depgraph.
AppendTopo(linearization, candidate.transactions);
161 todo -= candidate.transactions;
162 finder.MarkDone(candidate.transactions);
163 max_iterations -= iterations_done;
165 return {std::move(linearization), optimal};
169template<
typename SetType>
175 }
catch(
const std::ios_base::failure&) {}
177 for (
auto i : todo) {
190 std::vector<ClusterIndex> linearization;
191 TestBitSet todo = TestBitSet::Fill(depgraph.
TxCount());
195 TestBitSet potential_next;
196 for (
auto j : todo) {
197 if ((depgraph.
Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
198 potential_next.Set(j);
202 assert(potential_next.Any());
207 }
catch (
const std::ios_base::failure&) {}
208 idx %= potential_next.Count();
210 for (
auto j : potential_next) {
213 linearization.push_back(j);
221 return linearization;
237 SanityCheck(depgraph);
242 child += (child >= parent);
243 cluster[child].second.Set(parent);
244 depgraph.AddDependency(parent, child);
245 assert(depgraph.Ancestors(child)[parent]);
246 assert(depgraph.Descendants(parent)[child]);
249 SanityCheck(depgraph);
265 cluster.resize(num_tx);
270 if (i == j)
continue;
271 if (provider.
ConsumeBool()) cluster[i].second.Set(j);
278 VerifyDepGraphFromCluster(cluster, depgraph);
290 }
catch (
const std::ios_base::failure&) {}
291 SanityCheck(depgraph);
294 assert(IsAcyclic(depgraph));
306 }
catch (
const std::ios_base::failure&) {}
308 TestBitSet todo = TestBitSet::Fill(depgraph.
TxCount());
314 assert(component.IsSubsetOf(todo));
319 if (todo == TestBitSet::Fill(depgraph.
TxCount())) {
327 for (
auto i : component) {
333 for (
auto i : component) {
335 TestBitSet reachable = TestBitSet::Singleton(i);
338 TestBitSet new_reachable = reachable;
339 for (
auto j : new_reachable) {
340 new_reachable |= depgraph.
Ancestors(j) & todo;
343 if (new_reachable == reachable)
break;
344 reachable = new_reachable;
347 assert(component == reachable);
351 uint64_t subset_bits{0};
353 reader >>
VARINT(subset_bits);
354 }
catch (
const std::ios_base::failure&) {}
358 if (subset_bits & 1) subset.Set(i);
363 if (subset.None()) subset = TestBitSet::Singleton(todo.First());
381 }
catch (
const std::ios_base::failure&) {}
384 auto linearization = ReadLinearization(depgraph, reader);
390 for (
size_t i = 1; i < chunking.size(); ++i) {
391 assert(!(chunking[i] >> chunking[i - 1]));
395 auto todo = TestBitSet::Fill(depgraph.
TxCount());
396 for (
const auto& chunk_feerate : chunking) {
401 accumulator |=
SetInfo(depgraph, idx);
423 }
catch (
const std::ios_base::failure&) {}
426 auto todo = TestBitSet::Fill(depgraph.
TxCount());
432 assert(best_anc.transactions.Any());
433 assert(best_anc.transactions.IsSubsetOf(todo));
434 assert(depgraph.
FeeRate(best_anc.transactions) == best_anc.feerate);
437 for (
auto i : best_anc.transactions) {
438 assert((depgraph.
Ancestors(i) & todo).IsSubsetOf(best_anc.transactions));
442 std::optional<SetInfo<TestBitSet>> real_best_anc;
443 for (
auto i : todo) {
445 if (!real_best_anc.has_value() || info.
feerate > real_best_anc->feerate) {
446 real_best_anc = info;
450 assert(real_best_anc.has_value());
451 assert(*real_best_anc == best_anc);
454 auto del_set = ReadTopologicalSet(depgraph, todo, reader);
456 if (del_set.None()) del_set = best_anc.transactions;
474 uint64_t rng_seed{0};
477 }
catch (
const std::ios_base::failure&) {}
481 SimpleCandidateFinder smp_finder(depgraph);
482 ExhaustiveCandidateFinder exh_finder(depgraph);
485 auto todo = TestBitSet::Fill(depgraph.
TxCount());
488 assert(!smp_finder.AllDone());
489 assert(!exh_finder.AllDone());
493 uint64_t max_iterations = 1;
495 reader >>
VARINT(max_iterations);
496 }
catch (
const std::ios_base::failure&) {}
497 max_iterations &= 0xfffff;
500 SetInfo init_best(depgraph, ReadTopologicalSet(depgraph, todo, reader));
503 auto [found, iterations_done] = src_finder.
FindCandidateSet(max_iterations, init_best);
506 assert(iterations_done <= max_iterations);
507 assert(found.transactions.Any());
508 assert(found.transactions.IsSubsetOf(todo));
509 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
512 for (
auto i : found.transactions) {
518 assert(iterations_done <= ((uint64_t{1} << todo.Count()) - 1));
521 if (iterations_done < max_iterations) {
527 assert(found.feerate >= simple.feerate);
529 assert(found.feerate == simple.feerate);
534 assert(found.feerate >= anc.feerate);
538 if (todo.Count() <= 12) {
539 auto exhaustive = exh_finder.FindCandidateSet();
540 assert(exhaustive.feerate == found.feerate);
543 assert(exhaustive.feerate >= simple.feerate);
545 assert(exhaustive.feerate == simple.feerate);
551 auto del_set = ReadTopologicalSet(depgraph, todo, reader);
553 if (del_set.None()) del_set = found.transactions;
556 smp_finder.MarkDone(del_set);
557 exh_finder.MarkDone(del_set);
562 assert(smp_finder.AllDone());
563 assert(exh_finder.AllDone());
576 }
catch (
const std::ios_base::failure&) {}
579 auto todo = TestBitSet::Fill(depgraph.
TxCount());
580 auto subset =
SetInfo(depgraph, ReadTopologicalSet(depgraph, todo, reader));
583 auto linearization = ReadLinearization(depgraph, reader);
594 std::vector<ClusterIndex> linearization_left;
595 for (
auto i : linearization) {
596 if (todo[i]) linearization_left.push_back(i);
611 const auto& chunk_info = chunking.
GetChunk(i);
613 assert(chunk_info.transactions.Any());
615 if (i > 0)
assert(!(chunk_info.feerate >> chunking.
GetChunk(i - 1).feerate));
617 assert(chunk_info.transactions.IsSubsetOf(todo));
619 assert(depgraph.
FeeRate(chunk_info.transactions) == chunk_info.feerate);
622 for (
auto j : linearization) {
623 if (todo[j] && !combined[j]) {
624 accumulator |=
SetInfo(depgraph, j);
633 assert(!chunk_info.transactions.Overlaps(combined));
634 combined |= chunk_info.transactions;
636 for (
auto idx : chunk_info.transactions) {
647 TestBitSet intersect_anc;
648 for (
auto idx : intersect.transactions) {
649 intersect_anc |= (depgraph.
Ancestors(idx) & todo);
651 assert(intersect.transactions == intersect_anc);
653 assert(intersect.feerate == depgraph.
FeeRate(intersect.transactions));
655 assert(intersect.transactions.Any() == subset.transactions.Any());
657 assert(intersect.feerate >= subset.feerate);
663 auto reintersect =
SetInfo(depgraph,
prefix & intersect.transactions);
664 if (!reintersect.feerate.IsEmpty()) {
665 assert(reintersect.feerate <= intersect.feerate);
670 auto done = ReadTopologicalSet(depgraph, todo, reader);
674 done = depgraph.
Ancestors(todo.First()) & todo;
678 subset =
SetInfo(depgraph, subset.transactions - done);
691 uint64_t rng_seed{0};
692 uint64_t iter_count{0};
695 }
catch (
const std::ios_base::failure&) {}
698 std::vector<ClusterIndex> old_linearization;
700 uint8_t have_old_linearization{0};
702 reader >> have_old_linearization;
703 }
catch(
const std::ios_base::failure&) {}
704 if (have_old_linearization & 1) {
705 old_linearization = ReadLinearization(depgraph, reader);
706 SanityCheck(depgraph, old_linearization);
711 iter_count &= 0x7ffff;
712 auto [linearization, optimal] =
Linearize(depgraph, iter_count, rng_seed, old_linearization);
713 SanityCheck(depgraph, linearization);
717 if (!old_linearization.empty()) {
726 const uint64_t n = depgraph.
TxCount();
727 if (n <= 18 && iter_count > 2U * ((uint64_t{1} << n) - 1U)) {
735 SanityCheck(depgraph, simple_linearization);
741 if (simple_optimal)
assert(cmp == 0);
745 std::vector<ClusterIndex> perm_linearization(depgraph.
TxCount());
750 TestBitSet perm_done;
751 bool perm_is_topo{
true};
752 for (
auto i : perm_linearization) {
754 if (!depgraph.
Ancestors(i).IsSubsetOf(perm_done)) {
755 perm_is_topo =
false;
765 }
while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
779 }
catch (
const std::ios_base::failure&) {}
782 std::vector<ClusterIndex> linearization;
783 linearization = ReadLinearization(depgraph, reader);
784 SanityCheck(depgraph, linearization);
787 auto post_linearization = linearization;
789 SanityCheck(depgraph, post_linearization);
798 auto post_post_linearization = post_linearization;
800 SanityCheck(depgraph, post_post_linearization);
820 uint64_t rng_seed{0};
822 uint8_t direction{0};
825 }
catch (
const std::ios_base::failure&) {}
835 auto children = depgraph_gen.
Descendants(i) - TestBitSet::Singleton(i);
837 for (
auto j : children) {
838 if (!children[j])
continue;
842 if (children.Any()) depgraph_tree.
AddDependency(i, children.First());
846 auto parents = depgraph_gen.
Ancestors(i) - TestBitSet::Singleton(i);
848 for (
auto j : parents) {
849 if (!parents[j])
continue;
853 if (parents.Any()) depgraph_tree.
AddDependency(parents.First(), i);
858 std::vector<ClusterIndex> linearization;
859 linearization = ReadLinearization(depgraph_tree, reader);
860 SanityCheck(depgraph_tree, linearization);
863 auto post_linearization = linearization;
865 SanityCheck(depgraph_tree, post_linearization);
875 auto post_post_linearization = post_linearization;
877 SanityCheck(depgraph_tree, post_linearization);
879 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
884 auto [opt_linearization, _optimal] =
Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
903 uint64_t fee_inc_code;
905 fee_inc = fee_inc_code & 0x3ffff;
906 }
catch (
const std::ios_base::failure&) {}
907 if (depgraph.
TxCount() == 0)
return;
910 auto lin = ReadLinearization(depgraph, reader);
911 auto lin_leaf = ReadLinearization(depgraph, reader);
915 std::vector<ClusterIndex> lin_moved;
917 if (i != lin_leaf.back()) lin_moved.push_back(i);
919 lin_moved.push_back(lin_leaf.back());
923 SanityCheck(depgraph, lin_moved);
927 depgraph.
FeeRate(lin_leaf.back()).
fee += fee_inc;
940 }
catch (
const std::ios_base::failure&) {}
943 auto lin1 = ReadLinearization(depgraph, reader);
944 auto lin2 = ReadLinearization(depgraph, reader);
T ConsumeIntegralInRange(T min, T max)
Minimal stream for reading from an existing byte array by Span.
Class encapsulating the state needed to find the best remaining ancestor set.
void MarkDone(SetType select) noexcept
Remove a set of transactions from the set of to-be-linearized ones.
SetInfo< SetType > FindCandidateSet() const noexcept
Find the best (highest-feerate, smallest among those in case of a tie) ancestor set among the remaini...
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
void AppendTopo(std::vector< ClusterIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
Modify this transaction graph, adding a dependency between a specified parent and child.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
auto TxCount() const noexcept
Get the number of transactions in the graph.
const SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (at the end), and return its ClusterIndex...
Data structure encapsulating the chunking of a linearization, permitting removal of subsets.
ClusterIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
SetInfo< SetType > IntersectPrefixes(const SetInfo< SetType > &subset) const noexcept
Find the shortest intersection between subset and the prefixes of remaining chunks of the linearizati...
const SetInfo< SetType > & GetChunk(ClusterIndex n) const noexcept
Access a chunk.
void MarkDone(SetType subset) noexcept
Remove some subset of transactions from the linearization.
Class encapsulating the state needed to perform search for good candidate sets.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
void MarkDone(const SetType &done) noexcept
Remove a subset of transactions from the cluster being linearized.
std::pair< SetInfo< SetType >, uint64_t > FindCandidateSet(uint64_t max_iterations, SetInfo< SetType > best) noexcept
Find a high-feerate topologically-valid subset of what remains of the cluster.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
std::pair< std::vector< ClusterIndex >, bool > Linearize(const DepGraph< SetType > &depgraph, uint64_t max_iterations, uint64_t rng_seed, Span< const ClusterIndex > old_linearization={}) noexcept
Find or improve a linearization for a cluster.
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
std::vector< std::pair< FeeFrac, SetType > > Cluster
Data type to represent cluster input.
void PostLinearize(const DepGraph< SetType > &depgraph, Span< ClusterIndex > linearization)
Improve a given linearization.
std::vector< ClusterIndex > MergeLinearizations(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > lin1, Span< const ClusterIndex > lin2)
Merge two linearizations for the same cluster into one that is as good as both.
static Wrapper< Formatter, T & > Using(T &&t)
Cause serialization/deserialization of an object to be done using a specified formatter class.
Data structure storing a fee and size, ordered by increasing fee/size.
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
A set of transactions together with their aggregate feerate.
FeeFrac feerate
Their combined fee and size.
SetType transactions
The transactions in the set.
static constexpr auto MAX_SIMPLE_ITERATIONS
std::partial_ordering CompareChunks(Span< const FeeFrac > chunks0, Span< const FeeFrac > chunks1)
Compare the feerate diagrams implied by the provided sorted chunks data.