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<ClusterIndex>& 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;
315template<
typename SetType>
342 feerate += depgraph.FeeRate(pos);
364 swap(a.transactions, b.transactions);
365 swap(a.feerate, b.feerate);
373template<
typename SetType>
376 std::vector<FeeFrac>
ret;
379 auto new_chunk = depgraph.FeeRate(i);
381 while (!
ret.empty() && new_chunk >>
ret.back()) {
382 new_chunk +=
ret.back();
386 ret.push_back(std::move(new_chunk));
392template<
typename SetType>
425 if (!
m_todo[idx])
continue;
446 m_chunks.reserve(depgraph.TxCount());
466 if (
GetChunk(0).transactions == subset) {
499 const SetType to_add =
GetChunk(i).transactions & subset.transactions;
504 if (accumulator.
transactions == subset.transactions)
break;
512 if (!(accumulator.
feerate << subset.feerate))
return accumulator;
528template<
typename SetType>
545 m_todo{depgraph.Positions()},
563 anc_feerate +=
m_depgraph.FeeRate(anc_to_add);
580 for (
auto i : select) {
608 std::optional<ClusterIndex> best;
610 if (best.has_value()) {
630template<
typename SetType>
677 for (
auto i : depgraph.Positions()) {
681 auto feerate_cmp = depgraph.FeeRate(a) <=> depgraph.FeeRate(b);
682 if (feerate_cmp == 0) return a < b;
683 return feerate_cmp > 0;
744 inc(std::move(i)), und(std::move(u)), pot_feerate(std::move(p_f))
750 void Swap(WorkItem& other)
noexcept
752 swap(inc, other.inc);
753 swap(und, other.und);
754 swap(pot_feerate, other.pot_feerate);
769 to_cover -= component;
775 std::move(component),
777 }
while (to_cover.Any());
780 uint64_t iterations_left = max_iterations;
806 for (
auto pos : imp & und) {
831 if (anc_todo.IsSubsetOf(pot.transactions)) inc.
transactions |= anc_todo;
838 if (inc.
feerate > best.feerate) {
857 if (und.None())
return;
866 std::move(pot.feerate));
872 auto split_fn = [&](WorkItem&& elem)
noexcept {
879 Assume(!elem.inc.transactions.Overlaps(elem.und));
881 Assume(elem.inc.feerate.IsEmpty() == elem.pot_feerate.IsEmpty());
884 if (!elem.inc.feerate.IsEmpty()) {
887 if (!elem.und.Overlaps(imp))
return;
890 if (elem.pot_feerate <= best.feerate)
return;
912 std::optional<std::pair<ClusterIndex, ClusterIndex>> split_counts;
913 for (
auto t : select) {
919 std::pair<ClusterIndex, ClusterIndex> counts{
922 if (counts.first < counts.second) std::swap(counts.first, counts.second);
924 if (!split_counts.has_value() || counts < *split_counts) {
926 split_counts = counts;
930 Assume(split_counts.has_value());
958 while (!queue.
empty()) {
961 queue[0].Swap(queue[1]);
969 if (!iterations_left)
break;
970 auto elem = queue.
back();
972 split_fn(std::move(elem));
976 if (!iterations_left)
break;
977 auto elem = queue.
front();
979 split_fn(std::move(elem));
985 return {std::move(best), max_iterations - iterations_left};
995 Assume(done_sorted.Any());
1018template<
typename SetType>
1021 Assume(old_linearization.empty() || old_linearization.size() == depgraph.
TxCount());
1022 if (depgraph.
TxCount() == 0)
return {{},
true};
1024 uint64_t iterations_left = max_iterations;
1025 std::vector<ClusterIndex> linearization;
1027 AncestorCandidateFinder anc_finder(depgraph);
1028 std::optional<SearchCandidateFinder<SetType>> src_finder;
1029 linearization.reserve(depgraph.
TxCount());
1030 bool optimal =
true;
1036 uint64_t start_iterations = (uint64_t{depgraph.
TxCount()} * depgraph.
TxCount() + 63) / 64;
1037 if (iterations_left > start_iterations) {
1038 iterations_left -= start_iterations;
1039 src_finder.emplace(depgraph, rng_seed);
1043 LinearizationChunking old_chunking(depgraph, old_linearization);
1047 SetInfo<SetType> best_prefix;
1048 if (old_chunking.NumChunksLeft()) best_prefix = old_chunking.GetChunk(0);
1051 auto best = anc_finder.FindCandidateSet();
1052 if (!best_prefix.feerate.IsEmpty() && best_prefix.feerate >= best.feerate) best = best_prefix;
1054 uint64_t iterations_done_now = 0;
1055 uint64_t max_iterations_now = 0;
1060 uint64_t base_iterations = (anc_finder.NumRemaining() + 3) / 4;
1061 if (iterations_left > base_iterations) {
1064 iterations_left -= base_iterations;
1065 max_iterations_now = (iterations_left + 1) / 2;
1066 std::tie(best, iterations_done_now) = src_finder->FindCandidateSet(max_iterations_now, best);
1067 iterations_left -= iterations_done_now;
1071 if (iterations_done_now == max_iterations_now) {
1076 if (old_chunking.NumChunksLeft() > 0) {
1077 best = old_chunking.IntersectPrefixes(best);
1082 depgraph.
AppendTopo(linearization, best.transactions);
1085 anc_finder.MarkDone(best.transactions);
1086 if (anc_finder.AllDone())
break;
1087 if (src_finder) src_finder->MarkDone(best.transactions);
1088 if (old_chunking.NumChunksLeft() > 0) {
1089 old_chunking.MarkDone(best.transactions);
1093 return {std::move(linearization), optimal};
1112template<
typename SetType>
1206 for (
int pass = 0; pass < 2; ++pass) {
1207 int rev = !(pass & 1);
1209 entries[SENTINEL].prev_group = SENTINEL;
1219 entries[cur_group].group = SetType::Singleton(idx);
1221 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1222 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1223 entries[cur_group].prev_tx = NO_PREV_TX;
1224 entries[cur_group].first_tx = cur_group;
1226 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1227 entries[SENTINEL].prev_group = cur_group;
1231 ClusterIndex prev_group = entries[cur_group].prev_group;
1233 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1236 Assume(cur_group == entries[next_group].prev_group);
1237 Assume(prev_group == entries[cur_group].prev_group);
1241 Assume(cur_group != SENTINEL);
1242 Assume(prev_group != SENTINEL);
1243 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1247 entries[cur_group].group |= entries[prev_group].group;
1248 entries[cur_group].deps |= entries[prev_group].deps;
1249 entries[cur_group].feerate += entries[prev_group].feerate;
1251 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1253 entries[cur_group].first_tx = entries[prev_group].first_tx;
1255 prev_group = entries[prev_group].prev_group;
1256 entries[cur_group].prev_group = prev_group;
1259 ClusterIndex preprev_group = entries[prev_group].prev_group;
1262 entries[next_group].prev_group = prev_group;
1263 entries[prev_group].prev_group = cur_group;
1264 entries[cur_group].prev_group = preprev_group;
1267 next_group = prev_group;
1268 prev_group = preprev_group;
1276 while (cur_group != SENTINEL) {
1282 *(linearization.
begin() + (done++)) = cur_tx - 1;
1283 cur_tx = entries[cur_tx].prev_tx;
1284 }
while (cur_tx != NO_PREV_TX);
1287 *(linearization.
end() - (++done)) = cur_tx - 1;
1288 cur_tx = entries[cur_tx].prev_tx;
1289 }
while (cur_tx != NO_PREV_TX);
1291 cur_group = entries[cur_group].prev_group;
1301template<
typename SetType>
1310 std::vector<ClusterIndex>
ret;
1316 Assume(chunking1.NumChunksLeft() > 0);
1321 const auto& lin1_firstchunk = chunking1.GetChunk(0);
1322 const auto& lin2_firstchunk = chunking2.
GetChunk(0);
1323 if (lin2_firstchunk.feerate >> lin1_firstchunk.feerate) {
1324 best = chunking1.IntersectPrefixes(lin2_firstchunk);
1331 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.
ClusterIndex 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,...
void AppendTopo(std::vector< ClusterIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
SetType GetReducedParents(ClusterIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
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.
void AddDependencies(const SetType &parents, ClusterIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
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.
ClusterIndex PositionRange() const noexcept
Get the range of positions in this DepGraph.
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.
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.
FeeFrac & FeeRate(ClusterIndex i) noexcept
Get the mutable feerate of a given transaction i.
SetType GetReducedChildren(ClusterIndex i) const noexcept
Compute the (reduced) set of children of node i in this graph.
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 (in the first available position),...
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 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.
std::vector< ClusterIndex > m_sorted_to_original
m_sorted_to_original[i] is the original position that sorted transaction position i had.
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< ClusterIndex > m_original_to_sorted
m_original_to_sorted[i] is the sorted position original transaction position i has.
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.
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.
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.
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).
void Set(const DepGraph< SetType > &depgraph, ClusterIndex pos) noexcept
Add a transaction to this SetInfo (which must not yet be in it).
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.