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 if (todo.None())
return todo;
268 auto to_add = SetType::Singleton(todo.First());
272 for (
auto add : to_add) {
278 }
while (to_add.Any());
301 void AppendTopo(std::vector<DepGraphIndex>& list,
const SetType& select)
const noexcept
304 for (
auto i : select) list.push_back(i);
306 const auto a_anc_count = entries[a].ancestors.Count();
307 const auto b_anc_count = entries[b].ancestors.Count();
308 if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
326template<
typename SetType>
353 feerate += depgraph.FeeRate(pos);
375 swap(a.transactions, b.transactions);
376 swap(a.feerate, b.feerate);
384template<
typename SetType>
387 std::vector<FeeFrac>
ret;
390 auto new_chunk = depgraph.FeeRate(i);
392 while (!
ret.empty() && new_chunk >>
ret.back()) {
393 new_chunk +=
ret.back();
397 ret.push_back(std::move(new_chunk));
403template<
typename SetType>
436 if (!
m_todo[idx])
continue;
457 m_chunks.reserve(depgraph.TxCount());
477 if (
GetChunk(0).transactions == subset) {
510 const SetType to_add =
GetChunk(i).transactions & subset.transactions;
515 if (accumulator.
transactions == subset.transactions)
break;
523 if (!(accumulator.
feerate << subset.feerate))
return accumulator;
539template<
typename SetType>
556 m_todo{depgraph.Positions()},
574 anc_feerate +=
m_depgraph.FeeRate(anc_to_add);
591 for (
auto i : select) {
619 std::optional<DepGraphIndex> best;
621 if (best.has_value()) {
641template<
typename SetType>
688 for (
auto i : depgraph.Positions()) {
692 auto feerate_cmp = depgraph.FeeRate(a) <=> depgraph.FeeRate(b);
693 if (feerate_cmp == 0) return a < b;
694 return feerate_cmp > 0;
755 inc(std::move(i)), und(std::move(u)), pot_feerate(std::move(p_f))
761 void Swap(WorkItem& other)
noexcept
763 swap(inc, other.inc);
764 swap(und, other.und);
765 swap(pot_feerate, other.pot_feerate);
780 to_cover -= component;
786 std::move(component),
788 }
while (to_cover.Any());
791 uint64_t iterations_left = max_iterations;
817 for (
auto pos : imp & und) {
842 if (anc_todo.IsSubsetOf(pot.transactions)) inc.
transactions |= anc_todo;
849 if (inc.
feerate > best.feerate) {
868 if (und.None())
return;
877 std::move(pot.feerate));
883 auto split_fn = [&](WorkItem&& elem)
noexcept {
890 Assume(!elem.inc.transactions.Overlaps(elem.und));
892 Assume(elem.inc.feerate.IsEmpty() == elem.pot_feerate.IsEmpty());
895 if (!elem.inc.feerate.IsEmpty()) {
898 if (!elem.und.Overlaps(imp))
return;
901 if (elem.pot_feerate <= best.feerate)
return;
923 std::optional<std::pair<DepGraphIndex, DepGraphIndex>> split_counts;
924 for (
auto t : select) {
930 std::pair<DepGraphIndex, DepGraphIndex> counts{
933 if (counts.first < counts.second) std::swap(counts.first, counts.second);
935 if (!split_counts.has_value() || counts < *split_counts) {
937 split_counts = counts;
941 Assume(split_counts.has_value());
969 while (!queue.
empty()) {
972 queue[0].Swap(queue[1]);
980 if (!iterations_left)
break;
981 auto elem = queue.
back();
983 split_fn(std::move(elem));
987 if (!iterations_left)
break;
988 auto elem = queue.
front();
990 split_fn(std::move(elem));
996 return {std::move(best), max_iterations - iterations_left};
1006 Assume(done_sorted.Any());
1029template<
typename SetType>
1030std::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
1032 Assume(old_linearization.empty() || old_linearization.size() == depgraph.
TxCount());
1033 if (depgraph.
TxCount() == 0)
return {{},
true};
1035 uint64_t iterations_left = max_iterations;
1036 std::vector<DepGraphIndex> linearization;
1038 AncestorCandidateFinder anc_finder(depgraph);
1039 std::optional<SearchCandidateFinder<SetType>> src_finder;
1040 linearization.reserve(depgraph.
TxCount());
1041 bool optimal =
true;
1047 uint64_t start_iterations = (uint64_t{depgraph.
TxCount()} * depgraph.
TxCount() + 63) / 64;
1048 if (iterations_left > start_iterations) {
1049 iterations_left -= start_iterations;
1050 src_finder.emplace(depgraph, rng_seed);
1054 LinearizationChunking old_chunking(depgraph, old_linearization);
1058 SetInfo<SetType> best_prefix;
1059 if (old_chunking.NumChunksLeft()) best_prefix = old_chunking.GetChunk(0);
1062 auto best = anc_finder.FindCandidateSet();
1063 if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
1065 uint64_t iterations_done_now = 0;
1066 uint64_t max_iterations_now = 0;
1071 uint64_t base_iterations = (anc_finder.NumRemaining() + 3) / 4;
1072 if (iterations_left > base_iterations) {
1075 iterations_left -= base_iterations;
1076 max_iterations_now = (iterations_left + 1) / 2;
1077 std::tie(best, iterations_done_now) = src_finder->FindCandidateSet(max_iterations_now, best);
1078 iterations_left -= iterations_done_now;
1082 if (iterations_done_now == max_iterations_now) {
1087 if (old_chunking.NumChunksLeft() > 0) {
1088 best = old_chunking.IntersectPrefixes(best);
1093 depgraph.
AppendTopo(linearization, best.transactions);
1096 anc_finder.MarkDone(best.transactions);
1097 if (anc_finder.AllDone())
break;
1098 if (src_finder) src_finder->MarkDone(best.transactions);
1099 if (old_chunking.NumChunksLeft() > 0) {
1100 old_chunking.MarkDone(best.transactions);
1104 return {std::move(linearization), optimal};
1123template<
typename SetType>
1217 for (
int pass = 0; pass < 2; ++pass) {
1218 int rev = !(pass & 1);
1220 entries[SENTINEL].prev_group = SENTINEL;
1226 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1230 entries[cur_group].group = SetType::Singleton(idx);
1232 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1233 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1234 entries[cur_group].prev_tx = NO_PREV_TX;
1235 entries[cur_group].first_tx = cur_group;
1237 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1238 entries[SENTINEL].prev_group = cur_group;
1244 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1247 Assume(cur_group == entries[next_group].prev_group);
1248 Assume(prev_group == entries[cur_group].prev_group);
1252 Assume(cur_group != SENTINEL);
1253 Assume(prev_group != SENTINEL);
1254 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1258 entries[cur_group].group |= entries[prev_group].group;
1259 entries[cur_group].deps |= entries[prev_group].deps;
1260 entries[cur_group].feerate += entries[prev_group].feerate;
1262 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1264 entries[cur_group].first_tx = entries[prev_group].first_tx;
1266 prev_group = entries[prev_group].prev_group;
1267 entries[cur_group].prev_group = prev_group;
1270 DepGraphIndex preprev_group = entries[prev_group].prev_group;
1273 entries[next_group].prev_group = prev_group;
1274 entries[prev_group].prev_group = cur_group;
1275 entries[cur_group].prev_group = preprev_group;
1278 next_group = prev_group;
1279 prev_group = preprev_group;
1287 while (cur_group != SENTINEL) {
1293 *(linearization.begin() + (done++)) = cur_tx - 1;
1294 cur_tx = entries[cur_tx].prev_tx;
1295 }
while (cur_tx != NO_PREV_TX);
1298 *(linearization.end() - (++done)) = cur_tx - 1;
1299 cur_tx = entries[cur_tx].prev_tx;
1300 }
while (cur_tx != NO_PREV_TX);
1302 cur_group = entries[cur_group].prev_group;
1304 Assume(done == linearization.size());
1312template<
typename SetType>
1321 std::vector<DepGraphIndex>
ret;
1327 Assume(chunking1.NumChunksLeft() > 0);
1332 const auto& lin1_firstchunk = chunking1.GetChunk(0);
1333 const auto& lin2_firstchunk = chunking2.
GetChunk(0);
1334 if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1335 best = chunking1.IntersectPrefixes(lin2_firstchunk);
1342 if (chunking1.NumChunksLeft() == 0)
break;
1351template<
typename SetType>
1357 const auto len = linearization.size();
1365 SetType place_before = done & depgraph.
Ancestors(elem);
1368 while (place_before.Any()) {
1372 auto to_swap = linearization[len - 1 - (j - 1)];
1373 place_before.Reset(to_swap);
1374 linearization[len - 1 - (j--)] = to_swap;
1377 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.
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.