5 #ifndef BITCOIN_CLUSTER_LINEARIZE_H 6 #define BITCOIN_CLUSTER_LINEARIZE_H 27 template<
typename SetType>
28 using Cluster = std::vector<std::pair<FeeFrac, SetType>>;
35 template<
typename SetType>
52 Entry() noexcept = default;
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;
238 template<
typename SetType>
279 swap(a.transactions, b.transactions);
280 swap(a.feerate, b.feerate);
288 template<
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));
307 template<
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;
418 accumulator.transactions |= to_add;
419 if (accumulator.transactions == subset.transactions)
break;
421 accumulator.feerate +=
m_depgraph.FeeRate(to_add);
427 if (!(accumulator.feerate << subset.feerate))
return accumulator;
443 template<
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()) {
539 template<
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));
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};
749 template<
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};
823 template<
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;
1012 template<
typename SetType>
1021 std::vector<ClusterIndex>
ret;
1027 Assume(chunking1.NumChunksLeft() > 0);
1028 Assume(chunking2.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);
1037 best = chunking2.IntersectPrefixes(lin1_firstchunk);
1041 chunking1.MarkDone(best.transactions);
1042 if (chunking1.NumChunksLeft() == 0)
break;
1043 chunking2.MarkDone(best.transactions);
1052 #endif // BITCOIN_CLUSTER_LINEARIZE_H const DepGraph< SetType > & m_depgraph
Internal dependency graph.
CONSTEXPR_IF_NOT_DEBUG Span< C > subspan(std::size_t offset) const noexcept
A set of transactions together with their aggregate feerate.
SetType descendants
All descendants of the transaction (including itself).
void BuildChunks() noexcept
Fill the m_chunks variable, and remove the done prefix of m_linearization.
size_t size() const noexcept
Get the number of elements in this deque.
bool randbool() noexcept
Generate a random boolean.
const DepGraph< SetType > & m_depgraph
Internal dependency graph for the cluster.
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets...
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (at the end), and return its ClusterIndex...
constexpr C * end() const noexcept
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
Data structure largely mimicking std::deque, but using single preallocated ring buffer.
FeeFrac feerate
Their combined fee and size.
T & front() noexcept
Get a mutable reference to the first element of the deque.
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
friend bool operator==(const Entry &, const Entry &) noexcept=default
Equality operator (primarily for for testing purposes).
FeeFrac feerate
Fee and size of transaction itself.
FeeFrac FeeRate(const SetType &elems) const noexcept
Compute the aggregate feerate of a set of nodes in this graph.
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 SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
const SetInfo< SetType > & GetChunk(ClusterIndex n) const noexcept
Access a chunk.
constexpr std::size_t size() const noexcept
void pop_back()
Remove the last element of the deque.
SetInfo(const DepGraph< SetType > &depgraph, const SetType &txn) noexcept
Construct a SetInfo for a set of transactions in a depgraph.
T & back() noexcept
Get a mutable reference to the last element of the deque.
friend void swap(SetInfo &a, SetInfo &b) noexcept
Swap two SetInfo objects.
InsecureRandomContext m_rng
Internal RNG.
Entry() noexcept=default
Construct an empty entry.
bool empty() const noexcept
Test whether the contents of this deque is empty.
SetType m_todo
Which transaction are left to include.
void MarkDone(SetType select) noexcept
Remove a set of transactions from the set of to-be-linearized ones.
LinearizationChunking(const DepGraph< SetType > &depgraph LIFETIMEBOUND, Span< const ClusterIndex > lin LIFETIMEBOUND) noexcept
Initialize a LinearizationSubset object for a given length of linearization.
std::vector< SetInfo< SetType > > m_chunks
Chunk sets and their feerates, of what remains of the linearization.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
void emplace_back(Args &&... args)
Construct a new element at the end of the deque.
void MarkDone(SetType subset) noexcept
Remove some subset of transactions from the linearization.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
const DepGraph< SetType > & m_depgraph
The depgraph this linearization is for.
DepGraph(const Cluster< SetType > &cluster) noexcept
Construct a DepGraph object given a cluster.
void reserve(size_t capacity)
Increase the capacity to capacity.
std::vector< Entry > entries
Data for each transaction, in the same order as the Cluster it was constructed from.
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...
Class encapsulating the state needed to find the best remaining ancestor set.
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.
bool IsConnected() const noexcept
Determine if this entire graph is connected.
friend bool operator==(const DepGraph &, const DepGraph &) noexcept=default
Equality operator (primarily for testing purposes).
CONSTEXPR_IF_NOT_DEBUG C & front() const noexcept
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
#define Assume(val)
Assume is the identity function.
SetType transactions
The transactions in the set.
FeeFrac & FeeRate(ClusterIndex i) noexcept
Get the mutable feerate of a given transaction i.
SetInfo(const DepGraph< SetType > &depgraph, ClusterIndex pos) noexcept
Construct a SetInfo for a given transaction in a depgraph.
void pop_front()
Remove the first element of the deque.
constexpr C * begin() const noexcept
ClusterIndex m_chunks_skip
How large a prefix of m_chunks corresponds to removed transactions.
Data structure storing a fee and size, ordered by increasing fee/size.
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
Modify this transaction graph, adding a dependency between a specified parent and child...
SetType ancestors
All ancestors of the transaction (including itself).
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors, descendants).
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.
constexpr bool empty() const noexcept
SetInfo< SetType > FindCandidateSet() const noexcept
Find the best (highest-feerate, smallest among those in case of a tie) ancestor set among the remaini...
DepGraph() noexcept=default
SetType m_todo
Which transactions are left to do (sorted indices).
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \)
ClusterIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
SetType m_todo
Which transactions remain in the 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.
size_t capacity() const noexcept
Get the capacity of this deque (maximum size it can have without reallocating).
void PostLinearize(const DepGraph< SetType > &depgraph, Span< ClusterIndex > linearization)
Improve a given linearization.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
Information about a single transaction.
std::vector< std::pair< FeeFrac, SetType > > Cluster
Data type to represent cluster input.
SetInfo() noexcept=default
Construct a SetInfo for the empty set.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
SetInfo & operator|=(const SetInfo &other) noexcept
Add the transactions of other to this SetInfo (no overlap allowed).
SearchCandidateFinder(const DepGraph< SetType > &depgraph LIFETIMEBOUND, uint64_t rng_seed) noexcept
Construct a candidate finder for a graph.
AncestorCandidateFinder(const DepGraph< SetType > &depgraph LIFETIMEBOUND) noexcept
Construct an AncestorCandidateFinder for a given cluster.
Span< const ClusterIndex > m_linearization
The linearization we started from, possibly with removed prefix stripped.
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
auto TxCount() const noexcept
Get the number of transactions in the graph.
void MarkDone(const SetType &done) noexcept
Remove a subset of transactions from the cluster being linearized.
Class encapsulating the state needed to perform search for good candidate sets.
std::vector< FeeFrac > m_ancestor_set_feerates
Precomputed ancestor-set feerates (only kept up-to-date for indices in m_todo).
void AppendTopo(std::vector< ClusterIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.