5#ifndef BITCOIN_CLUSTER_LINEARIZE_H
6#define BITCOIN_CLUSTER_LINEARIZE_H
27template<
typename SetType>
28using Cluster = std::vector<std::pair<FeeFrac, SetType>>;
35template<
typename SetType>
77 Assume(ntx <= SetType::Size());
80 entries[i].ancestors = SetType::Singleton(i);
81 entries[i].descendants = SetType::Singleton(i);
93 entries[i].feerate = cluster[i].first;
95 entries[i].ancestors = cluster[i].second;
105 SetType to_merge =
entries[i].ancestors;
108 entries[j].ancestors |= to_merge;
115 for (
auto j :
entries[i].ancestors) {
141 entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
152 if (
entries[child].ancestors[parent])
return;
154 const auto& chl_des =
entries[child].descendants;
155 for (
auto anc_of_par :
Ancestors(parent)) {
156 entries[anc_of_par].descendants |= chl_des;
159 const auto& par_anc =
entries[parent].ancestors;
161 entries[dec_of_chl].ancestors |= par_anc;
172 for (
auto pos : elems)
ret +=
entries[pos].feerate;
190 if (todo.None())
return todo;
191 auto to_add = SetType::Singleton(todo.First());
195 for (
auto add : to_add) {
201 }
while (to_add.Any());
224 void AppendTopo(std::vector<ClusterIndex>& list,
const SetType& select)
const noexcept
227 for (
auto i : select) list.push_back(i);
229 const auto a_anc_count = entries[a].ancestors.Count();
230 const auto b_anc_count = entries[b].ancestors.Count();
231 if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
238template<
typename SetType>
279 swap(a.transactions, b.transactions);
280 swap(a.feerate, b.feerate);
288template<
typename SetType>
291 std::vector<FeeFrac>
ret;
294 auto new_chunk = depgraph.FeeRate(i);
296 while (!
ret.empty() && new_chunk >>
ret.back()) {
297 new_chunk +=
ret.back();
301 ret.push_back(std::move(new_chunk));
307template<
typename SetType>
340 if (!
m_todo[idx])
continue;
361 m_chunks.reserve(depgraph.TxCount());
381 if (
GetChunk(0).transactions == subset) {
414 const SetType to_add =
GetChunk(i).transactions & subset.transactions;
419 if (accumulator.
transactions == subset.transactions)
break;
427 if (!(accumulator.
feerate << subset.feerate))
return accumulator;
443template<
typename SetType>
460 m_todo{SetType::Fill(depgraph.TxCount())},
478 anc_feerate +=
m_depgraph.FeeRate(anc_to_add);
495 for (
auto i : select) {
517 std::optional<ClusterIndex> best;
519 if (best.has_value()) {
539template<
typename SetType>
560 m_todo(SetType::Fill(depgraph.TxCount())) {}
602 inc(std::move(i)), und(std::move(u)) {}
605 void Swap(WorkItem& other)
noexcept
607 swap(inc, other.inc);
608 swap(und, other.und);
623 uint64_t iterations_left = max_iterations;
634 if (inc.
feerate > best.feerate) {
642 if (und.None())
return;
654 auto split_fn = [&](WorkItem&& elem)
noexcept {
661 Assume(!elem.inc.transactions.Overlaps(elem.und));
667 const auto& desc =
m_depgraph.Descendants(split);
692 while (!queue.
empty()) {
695 queue[0].Swap(queue[1]);
703 if (!iterations_left)
break;
704 auto elem = queue.
back();
706 split_fn(std::move(elem));
710 if (!iterations_left)
break;
711 auto elem = queue.
front();
713 split_fn(std::move(elem));
717 return {std::move(best), max_iterations - iterations_left};
749template<
typename SetType>
752 Assume(old_linearization.empty() || old_linearization.size() == depgraph.
TxCount());
753 if (depgraph.
TxCount() == 0)
return {{},
true};
755 uint64_t iterations_left = max_iterations;
756 std::vector<ClusterIndex> linearization;
758 AncestorCandidateFinder anc_finder(depgraph);
759 SearchCandidateFinder src_finder(depgraph, rng_seed);
760 linearization.reserve(depgraph.
TxCount());
764 LinearizationChunking old_chunking(depgraph, old_linearization);
768 SetInfo<SetType> best_prefix;
769 if (old_chunking.NumChunksLeft()) best_prefix = old_chunking.GetChunk(0);
772 auto best = anc_finder.FindCandidateSet();
773 if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
777 uint64_t max_iterations_now = (iterations_left + 1) / 2;
778 uint64_t iterations_done_now = 0;
779 std::tie(best, iterations_done_now) = src_finder.FindCandidateSet(max_iterations_now, best);
780 iterations_left -= iterations_done_now;
782 if (iterations_done_now == max_iterations_now) {
787 if (old_chunking.NumChunksLeft() > 0) {
788 best = old_chunking.IntersectPrefixes(best);
793 depgraph.
AppendTopo(linearization, best.transactions);
796 anc_finder.MarkDone(best.transactions);
797 if (anc_finder.AllDone())
break;
798 src_finder.MarkDone(best.transactions);
799 if (old_chunking.NumChunksLeft() > 0) {
800 old_chunking.MarkDone(best.transactions);
804 return {std::move(linearization), optimal};
823template<
typename SetType>
914 std::vector<TxEntry> entries(linearization.
size() + 1);
917 for (
int pass = 0; pass < 2; ++pass) {
918 int rev = !(pass & 1);
920 entries[SENTINEL].prev_group = SENTINEL;
930 entries[cur_group].group = SetType::Singleton(idx);
932 entries[cur_group].feerate = depgraph.
FeeRate(idx);
933 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
934 entries[cur_group].prev_tx = NO_PREV_TX;
935 entries[cur_group].first_tx = cur_group;
937 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
938 entries[SENTINEL].prev_group = cur_group;
942 ClusterIndex prev_group = entries[cur_group].prev_group;
944 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
947 Assume(cur_group == entries[next_group].prev_group);
948 Assume(prev_group == entries[cur_group].prev_group);
952 Assume(cur_group != SENTINEL);
953 Assume(prev_group != SENTINEL);
954 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
958 entries[cur_group].group |= entries[prev_group].group;
959 entries[cur_group].deps |= entries[prev_group].deps;
960 entries[cur_group].feerate += entries[prev_group].feerate;
962 entries[entries[cur_group].first_tx].prev_tx = prev_group;
964 entries[cur_group].first_tx = entries[prev_group].first_tx;
966 prev_group = entries[prev_group].prev_group;
967 entries[cur_group].prev_group = prev_group;
970 ClusterIndex preprev_group = entries[prev_group].prev_group;
973 entries[next_group].prev_group = prev_group;
974 entries[prev_group].prev_group = cur_group;
975 entries[cur_group].prev_group = preprev_group;
978 next_group = prev_group;
979 prev_group = preprev_group;
987 while (cur_group != SENTINEL) {
993 *(linearization.
begin() + (done++)) = cur_tx - 1;
994 cur_tx = entries[cur_tx].prev_tx;
995 }
while (cur_tx != NO_PREV_TX);
998 *(linearization.
end() - (++done)) = cur_tx - 1;
999 cur_tx = entries[cur_tx].prev_tx;
1000 }
while (cur_tx != NO_PREV_TX);
1002 cur_group = entries[cur_group].prev_group;
1012template<
typename SetType>
1021 std::vector<ClusterIndex>
ret;
1027 Assume(chunking1.NumChunksLeft() > 0);
1032 const auto& lin1_firstchunk = chunking1.GetChunk(0);
1033 const auto& lin2_firstchunk = chunking2.
GetChunk(0);
1034 if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1035 best = chunking1.IntersectPrefixes(lin2_firstchunk);
1042 if (chunking1.NumChunksLeft() == 0)
break;
#define Assume(val)
Assume is the identity function.
bool randbool() noexcept
Generate a random boolean.
A Span is an object that can refer to a contiguous sequence of objects.
constexpr std::size_t size() const noexcept
CONSTEXPR_IF_NOT_DEBUG Span< C > subspan(std::size_t offset) const noexcept
constexpr C * begin() const noexcept
constexpr bool empty() const noexcept
constexpr C * end() const noexcept
CONSTEXPR_IF_NOT_DEBUG C & front() const noexcept
Data structure largely mimicking std::deque, but using single preallocated ring buffer.
bool empty() const noexcept
Test whether the contents of this deque is empty.
void pop_front()
Remove the first element of the deque.
size_t size() const noexcept
Get the number of elements in this deque.
void pop_back()
Remove the last element of the deque.
void emplace_back(Args &&... args)
Construct a new element at the end of the deque.
T & front() noexcept
Get a mutable reference to the first element of the deque.
void reserve(size_t capacity)
Increase the capacity to capacity.
size_t capacity() const noexcept
Get the capacity of this deque (maximum size it can have without reallocating).
T & back() noexcept
Get a mutable reference to the last element of the deque.
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...
const DepGraph< SetType > & m_depgraph
Internal dependency graph.
AncestorCandidateFinder(const DepGraph< SetType > &depgraph LIFETIMEBOUND) noexcept
Construct an AncestorCandidateFinder for a given cluster.
std::vector< FeeFrac > m_ancestor_set_feerates
Precomputed ancestor-set feerates (only kept up-to-date for indices in m_todo).
SetType m_todo
Which transaction are left to include.
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.
friend bool operator==(const DepGraph &, const DepGraph &) noexcept=default
Equality operator (primarily for testing purposes).
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
bool IsConnected() const noexcept
Determine if this entire graph is connected.
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.
std::vector< Entry > entries
Data for each transaction, in the same order as the Cluster it was constructed from.
DepGraph() noexcept=default
DepGraph(const Cluster< SetType > &cluster) noexcept
Construct a DepGraph object given a cluster.
FeeFrac FeeRate(const SetType &elems) const noexcept
Compute the aggregate feerate of a set of nodes in this graph.
FeeFrac & FeeRate(ClusterIndex i) noexcept
Get the mutable feerate of a given transaction i.
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.
SetType m_todo
Which transactions 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...
ClusterIndex m_chunks_skip
How large a prefix of m_chunks corresponds to removed transactions.
Span< const ClusterIndex > m_linearization
The linearization we started from, possibly with removed prefix stripped.
LinearizationChunking(const DepGraph< SetType > &depgraph LIFETIMEBOUND, Span< const ClusterIndex > lin LIFETIMEBOUND) noexcept
Initialize a LinearizationSubset object for a given length of linearization.
const SetInfo< SetType > & GetChunk(ClusterIndex n) const noexcept
Access a chunk.
void BuildChunks() noexcept
Fill the m_chunks variable, and remove the done prefix of m_linearization.
void MarkDone(SetType subset) noexcept
Remove some subset of transactions from the linearization.
const DepGraph< SetType > & m_depgraph
The depgraph this linearization is for.
std::vector< SetInfo< SetType > > m_chunks
Chunk sets and their feerates, of what remains of 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.
InsecureRandomContext m_rng
Internal RNG.
SetType m_todo
Which transactions are left to do (sorted indices).
const DepGraph< SetType > & m_depgraph
Internal dependency graph for the cluster.
SearchCandidateFinder(const DepGraph< SetType > &depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept
Construct a candidate finder for a graph.
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.
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.
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).
Information about a single transaction.
SetType descendants
All descendants of the transaction (including itself).
friend bool operator==(const Entry &, const Entry &) noexcept=default
Equality operator (primarily for for testing purposes).
Entry() noexcept=default
Construct an empty entry.
FeeFrac feerate
Fee and size of transaction itself.
SetType ancestors
All ancestors of the transaction (including itself).
A set of transactions together with their aggregate feerate.
FeeFrac feerate
Their combined fee and size.
SetInfo() noexcept=default
Construct a SetInfo for the empty set.
SetInfo(const DepGraph< SetType > &depgraph, ClusterIndex pos) noexcept
Construct a SetInfo for a given transaction in a depgraph.
SetType transactions
The transactions in the set.
friend void swap(SetInfo &a, SetInfo &b) noexcept
Swap two SetInfo objects.
SetInfo(const DepGraph< SetType > &depgraph, const SetType &txn) noexcept
Construct a SetInfo for a set of transactions in a depgraph.
SetInfo Add(const DepGraph< SetType > &depgraph, const SetType &txn) const noexcept
Construct a new SetInfo equal to this, with more transactions added (which may overlap with the exist...
SetInfo & operator|=(const SetInfo &other) noexcept
Add the transactions of other to this SetInfo (no overlap allowed).
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.