5#ifndef BITCOIN_CLUSTER_LINEARIZE_H
6#define BITCOIN_CLUSTER_LINEARIZE_H
28template<
typename SetType>
60 if (a.m_used != b.m_used)
return false;
62 for (
auto idx : a.m_used) {
63 if (a.entries[idx] != b.entries[idx])
return false;
92 Assume(mapping.size() == depgraph.PositionRange());
93 Assume((pos_range == 0) == (depgraph.TxCount() == 0));
95 auto new_idx = mapping[i];
96 Assume(new_idx < pos_range);
98 entries[new_idx].ancestors = SetType::Singleton(new_idx);
99 entries[new_idx].descendants = SetType::Singleton(new_idx);
102 entries[new_idx].feerate = depgraph.entries[i].feerate;
107 for (
auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]);
136 static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size());
137 auto available = ALL_POSITIONS -
m_used;
140 if (new_idx ==
entries.size()) {
141 entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
143 entries[new_idx] =
Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
169 entry.ancestors &=
m_used;
170 entry.descendants &=
m_used;
184 for (
auto par : parents -
Ancestors(child)) {
189 if (par_anc.None())
return;
191 const auto& chl_des =
entries[child].descendants;
192 for (
auto anc_of_par : par_anc) {
193 entries[anc_of_par].descendants |= chl_des;
197 entries[dec_of_chl].ancestors |= par_anc;
213 for (
auto parent : parents) {
214 if (parents[parent]) {
234 for (
auto child : children) {
235 if (children[child]) {
250 for (
auto pos : elems)
ret +=
entries[pos].feerate;
268 auto to_add = SetType::Singleton(tx);
272 for (
auto add : to_add) {
278 }
while (to_add.Any());
291 if (todo.None())
return todo;
314 void AppendTopo(std::vector<DepGraphIndex>& list,
const SetType& select)
const noexcept
317 for (
auto i : select) list.push_back(i);
319 const auto a_anc_count = entries[a].ancestors.Count();
320 const auto b_anc_count = entries[b].ancestors.Count();
321 if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
350template<
typename SetType>
377 feerate += depgraph.FeeRate(pos);
399 swap(a.transactions, b.transactions);
400 swap(a.feerate, b.feerate);
408template<
typename SetType>
411 std::vector<FeeFrac>
ret;
414 auto new_chunk = depgraph.FeeRate(i);
416 while (!
ret.empty() && new_chunk >>
ret.back()) {
417 new_chunk +=
ret.back();
421 ret.push_back(std::move(new_chunk));
427template<
typename SetType>
460 if (!
m_todo[idx])
continue;
481 m_chunks.reserve(depgraph.TxCount());
501 if (
GetChunk(0).transactions == subset) {
534 const SetType to_add =
GetChunk(i).transactions & subset.transactions;
539 if (accumulator.
transactions == subset.transactions)
break;
547 if (!(accumulator.
feerate << subset.feerate))
return accumulator;
563template<
typename SetType>
580 m_todo{depgraph.Positions()},
598 anc_feerate +=
m_depgraph.FeeRate(anc_to_add);
615 for (
auto i : select) {
643 std::optional<DepGraphIndex> best;
645 if (best.has_value()) {
665template<
typename SetType>
712 for (
auto i : depgraph.Positions()) {
716 auto feerate_cmp = depgraph.FeeRate(a) <=> depgraph.FeeRate(b);
717 if (feerate_cmp == 0) return a < b;
718 return feerate_cmp > 0;
779 inc(std::move(i)), und(std::move(u)), pot_feerate(std::move(p_f))
785 void Swap(WorkItem& other)
noexcept
787 swap(inc, other.inc);
788 swap(und, other.und);
789 swap(pot_feerate, other.pot_feerate);
804 to_cover -= component;
810 std::move(component),
812 }
while (to_cover.Any());
815 uint64_t iterations_left = max_iterations;
841 for (
auto pos : imp & und) {
866 if (anc_todo.IsSubsetOf(pot.transactions)) inc.
transactions |= anc_todo;
873 if (inc.
feerate > best.feerate) {
892 if (und.None())
return;
901 std::move(pot.feerate));
907 auto split_fn = [&](WorkItem&& elem)
noexcept {
914 Assume(!elem.inc.transactions.Overlaps(elem.und));
916 Assume(elem.inc.feerate.IsEmpty() == elem.pot_feerate.IsEmpty());
919 if (!elem.inc.feerate.IsEmpty()) {
922 if (!elem.und.Overlaps(imp))
return;
925 if (elem.pot_feerate <= best.feerate)
return;
947 std::optional<std::pair<DepGraphIndex, DepGraphIndex>> split_counts;
948 for (
auto t : select) {
954 std::pair<DepGraphIndex, DepGraphIndex> counts{
957 if (counts.first < counts.second) std::swap(counts.first, counts.second);
959 if (!split_counts.has_value() || counts < *split_counts) {
961 split_counts = counts;
965 Assume(split_counts.has_value());
993 while (!queue.
empty()) {
996 queue[0].Swap(queue[1]);
1004 if (!iterations_left)
break;
1005 auto elem = queue.
back();
1007 split_fn(std::move(elem));
1011 if (!iterations_left)
break;
1012 auto elem = queue.
front();
1014 split_fn(std::move(elem));
1020 return {std::move(best), max_iterations - iterations_left};
1030 Assume(done_sorted.Any());
1054template<
typename SetType>
1055std::tuple<std::vector<DepGraphIndex>, bool, uint64_t>
Linearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed, std::span<const DepGraphIndex> old_linearization = {})
noexcept
1057 Assume(old_linearization.empty() || old_linearization.size() == depgraph.
TxCount());
1058 if (depgraph.
TxCount() == 0)
return {{},
true, 0};
1060 uint64_t iterations_left = max_iterations;
1061 std::vector<DepGraphIndex> linearization;
1063 AncestorCandidateFinder anc_finder(depgraph);
1064 std::optional<SearchCandidateFinder<SetType>> src_finder;
1065 linearization.reserve(depgraph.
TxCount());
1066 bool optimal =
true;
1072 uint64_t start_iterations = (uint64_t{depgraph.
TxCount()} * depgraph.
TxCount() + 63) / 64;
1073 if (iterations_left > start_iterations) {
1074 iterations_left -= start_iterations;
1075 src_finder.emplace(depgraph, rng_seed);
1079 LinearizationChunking old_chunking(depgraph, old_linearization);
1083 SetInfo<SetType> best_prefix;
1084 if (old_chunking.NumChunksLeft()) best_prefix = old_chunking.GetChunk(0);
1087 auto best = anc_finder.FindCandidateSet();
1088 if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
1090 uint64_t iterations_done_now = 0;
1091 uint64_t max_iterations_now = 0;
1096 uint64_t base_iterations = (anc_finder.NumRemaining() + 3) / 4;
1097 if (iterations_left > base_iterations) {
1100 iterations_left -= base_iterations;
1101 max_iterations_now = (iterations_left + 1) / 2;
1102 std::tie(best, iterations_done_now) = src_finder->FindCandidateSet(max_iterations_now, best);
1103 iterations_left -= iterations_done_now;
1107 if (iterations_done_now == max_iterations_now) {
1112 if (old_chunking.NumChunksLeft() > 0) {
1113 best = old_chunking.IntersectPrefixes(best);
1118 depgraph.
AppendTopo(linearization, best.transactions);
1121 anc_finder.MarkDone(best.transactions);
1122 if (anc_finder.AllDone())
break;
1123 if (src_finder) src_finder->MarkDone(best.transactions);
1124 if (old_chunking.NumChunksLeft() > 0) {
1125 old_chunking.MarkDone(best.transactions);
1129 return {std::move(linearization), optimal, max_iterations - iterations_left};
1148template<
typename SetType>
1242 for (
int pass = 0; pass < 2; ++pass) {
1243 int rev = !(pass & 1);
1245 entries[SENTINEL].prev_group = SENTINEL;
1251 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1255 entries[cur_group].group = SetType::Singleton(idx);
1257 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1258 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1259 entries[cur_group].prev_tx = NO_PREV_TX;
1260 entries[cur_group].first_tx = cur_group;
1262 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1263 entries[SENTINEL].prev_group = cur_group;
1269 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1272 Assume(cur_group == entries[next_group].prev_group);
1273 Assume(prev_group == entries[cur_group].prev_group);
1277 Assume(cur_group != SENTINEL);
1278 Assume(prev_group != SENTINEL);
1279 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1283 entries[cur_group].group |= entries[prev_group].group;
1284 entries[cur_group].deps |= entries[prev_group].deps;
1285 entries[cur_group].feerate += entries[prev_group].feerate;
1287 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1289 entries[cur_group].first_tx = entries[prev_group].first_tx;
1291 prev_group = entries[prev_group].prev_group;
1292 entries[cur_group].prev_group = prev_group;
1295 DepGraphIndex preprev_group = entries[prev_group].prev_group;
1298 entries[next_group].prev_group = prev_group;
1299 entries[prev_group].prev_group = cur_group;
1300 entries[cur_group].prev_group = preprev_group;
1303 next_group = prev_group;
1304 prev_group = preprev_group;
1312 while (cur_group != SENTINEL) {
1318 *(linearization.begin() + (done++)) = cur_tx - 1;
1319 cur_tx = entries[cur_tx].prev_tx;
1320 }
while (cur_tx != NO_PREV_TX);
1323 *(linearization.end() - (++done)) = cur_tx - 1;
1324 cur_tx = entries[cur_tx].prev_tx;
1325 }
while (cur_tx != NO_PREV_TX);
1327 cur_group = entries[cur_group].prev_group;
1329 Assume(done == linearization.size());
1337template<
typename SetType>
1346 std::vector<DepGraphIndex>
ret;
1352 Assume(chunking1.NumChunksLeft() > 0);
1357 const auto& lin1_firstchunk = chunking1.GetChunk(0);
1358 const auto& lin2_firstchunk = chunking2.
GetChunk(0);
1359 if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1360 best = chunking1.IntersectPrefixes(lin2_firstchunk);
1367 if (chunking1.NumChunksLeft() == 0)
break;
1376template<
typename SetType>
1382 const auto len = linearization.size();
1390 SetType place_before = done & depgraph.
Ancestors(elem);
1393 while (place_before.Any()) {
1397 auto to_swap = linearization[len - 1 - (j - 1)];
1398 place_before.Reset(to_swap);
1399 linearization[len - 1 - (j--)] = to_swap;
1402 linearization[len - 1 - j] = elem;
#define Assume(val)
Assume is the identity function.
bool randbool() noexcept
Generate a random boolean.
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.
DepGraphIndex NumRemaining() const noexcept
Count the number of remaining unlinearized transactions.
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,...
const SetType & Ancestors(DepGraphIndex i) const noexcept
Get the ancestors of a given transaction i.
FeeFrac & FeeRate(DepGraphIndex i) noexcept
Get the mutable feerate of a given transaction i.
SetType GetReducedChildren(DepGraphIndex i) const noexcept
Compute the (reduced) set of children of node i in this graph.
SetType GetReducedParents(DepGraphIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
void AppendTopo(std::vector< DepGraphIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
const FeeFrac & FeeRate(DepGraphIndex i) const noexcept
Get the feerate of a given transaction i.
SetType GetConnectedComponent(const SetType &todo, DepGraphIndex tx) const noexcept
Get the connected component within the subset "todo" that contains tx (which must be in todo).
bool IsConnected() const noexcept
Determine if this entire graph is connected.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
DepGraphIndex PositionRange() const noexcept
Get the range of positions in this DepGraph.
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
DepGraphIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (in the first available position),...
void RemoveTransactions(const SetType &del) noexcept
Remove the specified positions from this DepGraph.
auto TxCount() const noexcept
Get the number of transactions in the graph.
std::vector< Entry > entries
Data for each transaction.
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
friend bool operator==(const DepGraph &a, const DepGraph &b) noexcept
Equality operator (primarily for testing purposes).
size_t DynamicMemoryUsage() const noexcept
DepGraph() noexcept=default
SetType m_used
Which positions are used.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
void Compact() noexcept
Reduce memory usage if possible.
FeeFrac FeeRate(const SetType &elems) const noexcept
Compute the aggregate feerate of a set of nodes in this graph.
void AddDependencies(const SetType &parents, DepGraphIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
bool IsAcyclic() const noexcept
Check if this graph is acyclic.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets.
std::span< const DepGraphIndex > m_linearization
The linearization we started from, possibly with removed prefix stripped.
DepGraphIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
const SetInfo< SetType > & GetChunk(DepGraphIndex n) const noexcept
Access a chunk.
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...
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.
DepGraphIndex m_chunks_skip
How large a prefix of m_chunks corresponds to removed transactions.
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.
LinearizationChunking(const DepGraph< SetType > &depgraph LIFETIMEBOUND, std::span< const DepGraphIndex > lin LIFETIMEBOUND) noexcept
Initialize a LinearizationSubset object for a given length of linearization.
Class encapsulating the state needed to perform search for good candidate sets.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
std::vector< DepGraphIndex > m_sorted_to_original
m_sorted_to_original[i] is the original position that sorted transaction position i had.
std::vector< DepGraphIndex > m_original_to_sorted
m_original_to_sorted[i] is the sorted position original transaction position i has.
void MarkDone(const SetType &done) noexcept
Remove a subset of transactions from the cluster being linearized.
InsecureRandomContext m_rng
Internal RNG.
SetType OriginalToSorted(const SetType &arg) const noexcept
Given a set of transactions with original indices, get their sorted indices.
SetType m_todo
Which transactions are left to do (indices in m_sorted_depgraph's order).
SetType SortedToOriginal(const SetType &arg) const noexcept
Given a set of transactions with sorted indices, get their original indices.
SearchCandidateFinder(const DepGraph< SetType > &depgraph, uint64_t rng_seed) noexcept
Construct a candidate finder for a graph.
DepGraph< SetType > m_sorted_depgraph
Internal dependency graph for the cluster (with transactions in decreasing individual feerate order).
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::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
std::tuple< std::vector< DepGraphIndex >, bool, uint64_t > Linearize(const DepGraph< SetType > &depgraph, uint64_t max_iterations, uint64_t rng_seed, std::span< const DepGraphIndex > old_linearization={}) noexcept
Find or improve a linearization for a cluster.
void FixLinearization(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization) noexcept
Make linearization topological, retaining its ordering where possible.
std::vector< DepGraphIndex > MergeLinearizations(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > lin1, std::span< const DepGraphIndex > lin2)
Merge two linearizations for the same cluster into one that is as good as both.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.
void PostLinearize(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization)
Improve a given linearization.
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \t")
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.
SetInfo(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Construct a SetInfo for a given transaction in a depgraph.
FeeFrac feerate
Their combined fee and size.
SetInfo() noexcept=default
Construct a SetInfo for the empty set.
void Set(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Add a transaction to this SetInfo (which must not yet be in it).
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.