5#ifndef BITCOIN_CLUSTER_LINEARIZE_H
6#define BITCOIN_CLUSTER_LINEARIZE_H
27template<
typename SetType>
59 if (a.m_used != b.m_used)
return false;
61 for (
auto idx : a.m_used) {
62 if (a.entries[idx] != b.entries[idx])
return false;
91 Assume(mapping.size() == depgraph.PositionRange());
92 Assume((pos_range == 0) == (depgraph.TxCount() == 0));
94 auto new_idx = mapping[i];
95 Assume(new_idx < pos_range);
97 entries[new_idx].ancestors = SetType::Singleton(new_idx);
98 entries[new_idx].descendants = SetType::Singleton(new_idx);
101 entries[new_idx].feerate = depgraph.entries[i].feerate;
106 for (
auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]);
135 static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size());
136 auto available = ALL_POSITIONS -
m_used;
139 if (new_idx ==
entries.size()) {
140 entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
142 entries[new_idx] =
Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
168 entry.ancestors &=
m_used;
169 entry.descendants &=
m_used;
183 for (
auto par : parents -
Ancestors(child)) {
188 if (par_anc.None())
return;
190 const auto& chl_des =
entries[child].descendants;
191 for (
auto anc_of_par : par_anc) {
192 entries[anc_of_par].descendants |= chl_des;
196 entries[dec_of_chl].ancestors |= par_anc;
212 for (
auto parent : parents) {
213 if (parents[parent]) {
233 for (
auto child : children) {
234 if (children[child]) {
249 for (
auto pos : elems)
ret +=
entries[pos].feerate;
267 auto to_add = SetType::Singleton(tx);
271 for (
auto add : to_add) {
277 }
while (to_add.Any());
290 if (todo.None())
return todo;
313 void AppendTopo(std::vector<DepGraphIndex>& list,
const SetType& select)
const noexcept
316 for (
auto i : select) list.push_back(i);
318 const auto a_anc_count = entries[a].ancestors.Count();
319 const auto b_anc_count = entries[b].ancestors.Count();
320 if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
338template<
typename SetType>
365 feerate += depgraph.FeeRate(pos);
387 swap(a.transactions, b.transactions);
388 swap(a.feerate, b.feerate);
396template<
typename SetType>
399 std::vector<FeeFrac>
ret;
402 auto new_chunk = depgraph.FeeRate(i);
404 while (!
ret.empty() && new_chunk >>
ret.back()) {
405 new_chunk +=
ret.back();
409 ret.push_back(std::move(new_chunk));
415template<
typename SetType>
448 if (!
m_todo[idx])
continue;
469 m_chunks.reserve(depgraph.TxCount());
489 if (
GetChunk(0).transactions == subset) {
522 const SetType to_add =
GetChunk(i).transactions & subset.transactions;
527 if (accumulator.
transactions == subset.transactions)
break;
535 if (!(accumulator.
feerate << subset.feerate))
return accumulator;
551template<
typename SetType>
568 m_todo{depgraph.Positions()},
586 anc_feerate +=
m_depgraph.FeeRate(anc_to_add);
603 for (
auto i : select) {
631 std::optional<DepGraphIndex> best;
633 if (best.has_value()) {
653template<
typename SetType>
700 for (
auto i : depgraph.Positions()) {
704 auto feerate_cmp = depgraph.FeeRate(a) <=> depgraph.FeeRate(b);
705 if (feerate_cmp == 0) return a < b;
706 return feerate_cmp > 0;
767 inc(std::move(i)), und(std::move(u)), pot_feerate(std::move(p_f))
773 void Swap(WorkItem& other)
noexcept
775 swap(inc, other.inc);
776 swap(und, other.und);
777 swap(pot_feerate, other.pot_feerate);
792 to_cover -= component;
798 std::move(component),
800 }
while (to_cover.Any());
803 uint64_t iterations_left = max_iterations;
829 for (
auto pos : imp & und) {
854 if (anc_todo.IsSubsetOf(pot.transactions)) inc.
transactions |= anc_todo;
861 if (inc.
feerate > best.feerate) {
880 if (und.None())
return;
889 std::move(pot.feerate));
895 auto split_fn = [&](WorkItem&& elem)
noexcept {
902 Assume(!elem.inc.transactions.Overlaps(elem.und));
904 Assume(elem.inc.feerate.IsEmpty() == elem.pot_feerate.IsEmpty());
907 if (!elem.inc.feerate.IsEmpty()) {
910 if (!elem.und.Overlaps(imp))
return;
913 if (elem.pot_feerate <= best.feerate)
return;
935 std::optional<std::pair<DepGraphIndex, DepGraphIndex>> split_counts;
936 for (
auto t : select) {
942 std::pair<DepGraphIndex, DepGraphIndex> counts{
945 if (counts.first < counts.second) std::swap(counts.first, counts.second);
947 if (!split_counts.has_value() || counts < *split_counts) {
949 split_counts = counts;
953 Assume(split_counts.has_value());
981 while (!queue.
empty()) {
984 queue[0].Swap(queue[1]);
992 if (!iterations_left)
break;
993 auto elem = queue.
back();
995 split_fn(std::move(elem));
999 if (!iterations_left)
break;
1000 auto elem = queue.
front();
1002 split_fn(std::move(elem));
1008 return {std::move(best), max_iterations - iterations_left};
1018 Assume(done_sorted.Any());
1041template<
typename SetType>
1042std::pair<std::vector<DepGraphIndex>,
bool>
Linearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations, uint64_t rng_seed, std::span<const DepGraphIndex> old_linearization = {})
noexcept
1044 Assume(old_linearization.empty() || old_linearization.size() == depgraph.
TxCount());
1045 if (depgraph.
TxCount() == 0)
return {{},
true};
1047 uint64_t iterations_left = max_iterations;
1048 std::vector<DepGraphIndex> linearization;
1050 AncestorCandidateFinder anc_finder(depgraph);
1051 std::optional<SearchCandidateFinder<SetType>> src_finder;
1052 linearization.reserve(depgraph.
TxCount());
1053 bool optimal =
true;
1059 uint64_t start_iterations = (uint64_t{depgraph.
TxCount()} * depgraph.
TxCount() + 63) / 64;
1060 if (iterations_left > start_iterations) {
1061 iterations_left -= start_iterations;
1062 src_finder.emplace(depgraph, rng_seed);
1066 LinearizationChunking old_chunking(depgraph, old_linearization);
1070 SetInfo<SetType> best_prefix;
1071 if (old_chunking.NumChunksLeft()) best_prefix = old_chunking.GetChunk(0);
1074 auto best = anc_finder.FindCandidateSet();
1075 if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
1077 uint64_t iterations_done_now = 0;
1078 uint64_t max_iterations_now = 0;
1083 uint64_t base_iterations = (anc_finder.NumRemaining() + 3) / 4;
1084 if (iterations_left > base_iterations) {
1087 iterations_left -= base_iterations;
1088 max_iterations_now = (iterations_left + 1) / 2;
1089 std::tie(best, iterations_done_now) = src_finder->FindCandidateSet(max_iterations_now, best);
1090 iterations_left -= iterations_done_now;
1094 if (iterations_done_now == max_iterations_now) {
1099 if (old_chunking.NumChunksLeft() > 0) {
1100 best = old_chunking.IntersectPrefixes(best);
1105 depgraph.
AppendTopo(linearization, best.transactions);
1108 anc_finder.MarkDone(best.transactions);
1109 if (anc_finder.AllDone())
break;
1110 if (src_finder) src_finder->MarkDone(best.transactions);
1111 if (old_chunking.NumChunksLeft() > 0) {
1112 old_chunking.MarkDone(best.transactions);
1116 return {std::move(linearization), optimal};
1135template<
typename SetType>
1229 for (
int pass = 0; pass < 2; ++pass) {
1230 int rev = !(pass & 1);
1232 entries[SENTINEL].prev_group = SENTINEL;
1238 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1242 entries[cur_group].group = SetType::Singleton(idx);
1244 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1245 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1246 entries[cur_group].prev_tx = NO_PREV_TX;
1247 entries[cur_group].first_tx = cur_group;
1249 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1250 entries[SENTINEL].prev_group = cur_group;
1256 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1259 Assume(cur_group == entries[next_group].prev_group);
1260 Assume(prev_group == entries[cur_group].prev_group);
1264 Assume(cur_group != SENTINEL);
1265 Assume(prev_group != SENTINEL);
1266 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1270 entries[cur_group].group |= entries[prev_group].group;
1271 entries[cur_group].deps |= entries[prev_group].deps;
1272 entries[cur_group].feerate += entries[prev_group].feerate;
1274 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1276 entries[cur_group].first_tx = entries[prev_group].first_tx;
1278 prev_group = entries[prev_group].prev_group;
1279 entries[cur_group].prev_group = prev_group;
1282 DepGraphIndex preprev_group = entries[prev_group].prev_group;
1285 entries[next_group].prev_group = prev_group;
1286 entries[prev_group].prev_group = cur_group;
1287 entries[cur_group].prev_group = preprev_group;
1290 next_group = prev_group;
1291 prev_group = preprev_group;
1299 while (cur_group != SENTINEL) {
1305 *(linearization.begin() + (done++)) = cur_tx - 1;
1306 cur_tx = entries[cur_tx].prev_tx;
1307 }
while (cur_tx != NO_PREV_TX);
1310 *(linearization.end() - (++done)) = cur_tx - 1;
1311 cur_tx = entries[cur_tx].prev_tx;
1312 }
while (cur_tx != NO_PREV_TX);
1314 cur_group = entries[cur_group].prev_group;
1316 Assume(done == linearization.size());
1324template<
typename SetType>
1333 std::vector<DepGraphIndex>
ret;
1339 Assume(chunking1.NumChunksLeft() > 0);
1344 const auto& lin1_firstchunk = chunking1.GetChunk(0);
1345 const auto& lin2_firstchunk = chunking2.
GetChunk(0);
1346 if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1347 best = chunking1.IntersectPrefixes(lin2_firstchunk);
1354 if (chunking1.NumChunksLeft() == 0)
break;
1363template<
typename SetType>
1369 const auto len = linearization.size();
1377 SetType place_before = done & depgraph.
Ancestors(elem);
1380 while (place_before.Any()) {
1384 auto to_swap = linearization[len - 1 - (j - 1)];
1385 place_before.Reset(to_swap);
1386 linearization[len - 1 - (j--)] = to_swap;
1389 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).
DepGraph() noexcept=default
SetType m_used
Which positions are used.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
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::pair< std::vector< DepGraphIndex >, bool > 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 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.