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;
359template<
typename SetType>
386 feerate += depgraph.FeeRate(pos);
417 swap(a.transactions, b.transactions);
418 swap(a.feerate, b.feerate);
426template<
typename SetType>
429 std::vector<SetInfo<SetType>>
ret;
434 while (!
ret.empty() && new_chunk.
feerate >>
ret.back().feerate) {
435 new_chunk |=
ret.back();
439 ret.emplace_back(std::move(new_chunk));
446template<
typename SetType>
449 std::vector<FeeFrac>
ret;
452 auto new_chunk = depgraph.FeeRate(i);
454 while (!
ret.empty() && new_chunk >>
ret.back()) {
455 new_chunk +=
ret.back();
459 ret.push_back(std::move(new_chunk));
630template<
typename SetType>
695 for (
auto tx_idx : tx_idxs) {
696 if (pos == 0)
return tx_idx;
708 template<
bool Subtract>
712 for (
auto tx_idx : chunk) {
715 tx_data.chunk_rep = chunk_rep;
718 auto child_deps = std::span{tx_data.child_deps};
719 for (
auto dep_idx : child_deps) {
721 Assume(dep_entry.parent == tx_idx);
723 if (!dep_entry.active)
continue;
726 if (dep_entry.top_setinfo.transactions[query]) {
727 if constexpr (Subtract) {
728 dep_entry.top_setinfo -= dep_change;
730 dep_entry.top_setinfo |= dep_change;
742 auto& child_tx_data =
m_tx_data[dep_data.child];
743 auto& parent_tx_data =
m_tx_data[dep_data.parent];
746 Assume(parent_tx_data.chunk_rep != child_tx_data.chunk_rep);
747 auto& par_chunk_data =
m_tx_data[parent_tx_data.chunk_rep];
748 auto& chl_chunk_data =
m_tx_data[child_tx_data.chunk_rep];
749 TxIdx top_rep = parent_tx_data.chunk_rep;
750 auto top_part = par_chunk_data.chunk_setinfo;
751 auto bottom_part = chl_chunk_data.chunk_setinfo;
753 par_chunk_data.chunk_setinfo |= bottom_part;
754 m_cost += par_chunk_data.chunk_setinfo.transactions.Count();
775 UpdateChunk<false>(top_part.transactions, dep_data.parent,
776 top_rep, bottom_part);
781 UpdateChunk<false>(bottom_part.transactions, dep_data.child,
784 dep_data.active =
true;
785 dep_data.top_setinfo = top_part;
794 auto& parent_tx_data =
m_tx_data[dep_data.parent];
796 dep_data.active =
false;
798 auto& chunk_data =
m_tx_data[parent_tx_data.chunk_rep];
799 m_cost += chunk_data.chunk_setinfo.transactions.Count();
800 auto top_part = dep_data.top_setinfo;
801 auto bottom_part = chunk_data.chunk_setinfo - top_part;
802 TxIdx bottom_rep = dep_data.child;
803 auto& bottom_chunk_data =
m_tx_data[bottom_rep];
804 bottom_chunk_data.chunk_setinfo = bottom_part;
805 TxIdx top_rep = dep_data.parent;
806 auto& top_chunk_data =
m_tx_data[top_rep];
807 top_chunk_data.chunk_setinfo = top_part;
815 UpdateChunk<true>(top_part.transactions, dep_data.parent,
816 top_rep, bottom_part);
820 UpdateChunk<true>(bottom_part.transactions, dep_data.child,
821 bottom_rep, top_part);
830 Assume(top_chunk.chunk_rep == top_rep);
831 auto& bottom_chunk =
m_tx_data[bottom_rep];
832 Assume(bottom_chunk.chunk_rep == bottom_rep);
835 for (
auto tx : top_chunk.chunk_setinfo.transactions) {
837 num_deps += (tx_data.children & bottom_chunk.chunk_setinfo.transactions).Count();
839 if (num_deps == 0)
return TxIdx(-1);
842 for (
auto tx : top_chunk.chunk_setinfo.transactions) {
844 auto intersect = tx_data.children & bottom_chunk.chunk_setinfo.transactions;
845 auto count = intersect.Count();
847 for (
auto dep : tx_data.child_deps) {
849 if (bottom_chunk.chunk_setinfo.transactions[dep_data.child]) {
850 if (pick == 0)
return Activate(dep);
864 template<
bool DownWard>
869 SetType chunk_txn = chunk_data.chunk_setinfo.transactions;
878 SetType explored = chunk_txn;
882 FeeFrac best_other_chunk_feerate = chunk_data.chunk_setinfo.feerate;
887 uint64_t best_other_chunk_tiebreak{0};
888 for (
auto tx : chunk_txn) {
892 auto newly_reached = (DownWard ? tx_data.children : tx_data.parents) - explored;
893 explored |= newly_reached;
894 while (newly_reached.Any()) {
896 auto reached_chunk_rep =
m_tx_data[newly_reached.First()].chunk_rep;
897 auto& reached_chunk =
m_tx_data[reached_chunk_rep].chunk_setinfo;
898 newly_reached -= reached_chunk.transactions;
900 auto cmp = DownWard ? FeeRateCompare(best_other_chunk_feerate, reached_chunk.feerate)
901 : FeeRateCompare(reached_chunk.feerate, best_other_chunk_feerate);
902 if (cmp > 0)
continue;
904 if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) {
905 best_other_chunk_feerate = reached_chunk.feerate;
906 best_other_chunk_rep = reached_chunk_rep;
907 best_other_chunk_tiebreak = tiebreak;
912 if (best_other_chunk_rep ==
TxIdx(-1))
return TxIdx(-1);
913 if constexpr (DownWard) {
914 chunk_rep =
MergeChunks(chunk_rep, best_other_chunk_rep);
916 chunk_rep =
MergeChunks(best_other_chunk_rep, chunk_rep);
924 template<
bool DownWard>
927 auto chunk_rep =
m_tx_data[tx_idx].chunk_rep;
929 auto merged_rep = MergeStep<DownWard>(chunk_rep);
930 if (merged_rep ==
TxIdx(-1))
break;
931 chunk_rep = merged_rep;
954 MergeSequence<false>(dep_data.parent);
956 MergeSequence<true>(dep_data.child);
966 m_tx_data.resize(depgraph.PositionRange());
974 m_dep_data.reserve((num_transactions * num_transactions) / 4);
978 tx_data.chunk_rep = tx;
979 tx_data.chunk_setinfo.transactions = SetType::Singleton(tx);
980 tx_data.chunk_setinfo.feerate = depgraph.FeeRate(tx);
982 SetType parents = depgraph.GetReducedParents(tx);
983 for (
auto par : parents) {
992 tx_data.parents.Set(par);
994 par_tx_data.child_deps.push_back(dep_idx);
995 par_tx_data.children.Set(tx);
1007 auto chunk_rep =
m_tx_data[tx].chunk_rep;
1010 chunk_rep = MergeStep<false>(chunk_rep);
1011 if (chunk_rep ==
TxIdx(-1))
break;
1021 if (tx_data.chunk_rep == tx) {
1037 if (chunk_data.chunk_rep != chunk)
continue;
1039 for (
int i = 0; i < 2; ++i) {
1042 auto result_up = MergeStep<false>(chunk);
1043 if (result_up !=
TxIdx(-1)) {
1049 auto result_down = MergeStep<true>(chunk);
1050 if (result_down !=
TxIdx(-1)) {
1065 if (tx_data.chunk_rep == tx) {
1087 if (chunk_data.chunk_rep != chunk)
continue;
1090 uint64_t candidate_tiebreak = 0;
1092 for (
auto tx : chunk_data.chunk_setinfo.transactions) {
1095 const auto children = std::span{tx_data.child_deps};
1096 for (
DepIdx dep_idx : children) {
1098 if (!dep_data.active)
continue;
1101 auto cmp = FeeRateCompare(dep_data.top_setinfo.feerate, chunk_data.chunk_setinfo.feerate);
1102 if (cmp <= 0)
continue;
1107 if (tiebreak < candidate_tiebreak)
continue;
1109 candidate_dep = dep_idx;
1110 candidate_tiebreak = tiebreak;
1134 if (tx_data.chunk_rep == tx) {
1154 auto& chunk_data =
m_tx_data[chunk_rep];
1155 Assume(chunk_data.chunk_rep == chunk_rep);
1157 bool move_pivot_down =
flags & 1;
1159 bool second_stage =
flags & 2;
1164 uint64_t candidate_tiebreak{0};
1165 bool have_any =
false;
1167 for (
auto tx_idx : chunk_data.chunk_setinfo.transactions) {
1168 const auto& tx_data =
m_tx_data[tx_idx];
1170 for (
auto dep_idx : tx_data.child_deps) {
1173 if (!dep_data.active)
continue;
1177 if (dep_data.top_setinfo.feerate << chunk_data.chunk_setinfo.feerate)
continue;
1180 if (move_pivot_down == dep_data.top_setinfo.transactions[pivot_idx])
continue;
1183 if (tiebreak > candidate_tiebreak) {
1184 candidate_tiebreak = tiebreak;
1185 candidate_dep = dep_idx;
1190 if (!have_any)
return true;
1193 if (candidate_tiebreak == 0) {
1203 auto parent_chunk_rep =
m_tx_data[dep_data.parent].chunk_rep;
1204 auto child_chunk_rep =
m_tx_data[dep_data.child].chunk_rep;
1207 auto merged_chunk_rep =
MergeChunks(child_chunk_rep, parent_chunk_rep);
1208 if (merged_chunk_rep !=
TxIdx(-1)) {
1221 if (move_pivot_down) {
1242 std::vector<DepGraphIndex>
ret;
1246 std::vector<std::pair<TxIdx, uint64_t>> ready_chunks;
1253 std::vector<std::pair<TxIdx, TxIdx>> chunk_deps(
m_tx_data.size(), {0, 0});
1257 std::vector<TxIdx> ready_tx;
1261 const auto& chl_data =
m_tx_data[chl_idx];
1262 chunk_deps[chl_idx].second = chl_data.parents.Count();
1263 auto chl_chunk_rep = chl_data.chunk_rep;
1264 chunk_reps.Set(chl_chunk_rep);
1265 for (
auto par_idx : chl_data.parents) {
1266 auto par_chunk_rep =
m_tx_data[par_idx].chunk_rep;
1267 chunk_deps[chl_chunk_rep].first += (par_chunk_rep != chl_chunk_rep);
1272 auto chunk_cmp_fn = [&](
const std::pair<TxIdx, uint64_t>& a,
const std::pair<TxIdx, uint64_t>& b)
noexcept {
1275 Assume(chunk_a.chunk_rep == a.first);
1276 Assume(chunk_b.chunk_rep == b.first);
1278 if (chunk_a.chunk_setinfo.feerate != chunk_b.chunk_setinfo.feerate) {
1279 return chunk_a.chunk_setinfo.feerate < chunk_b.chunk_setinfo.feerate;
1282 if (a.second != b.second)
return a.second < b.second;
1284 return a.first < b.first;
1286 for (
TxIdx chunk_rep : chunk_reps) {
1287 if (chunk_deps[chunk_rep].first == 0) ready_chunks.emplace_back(chunk_rep,
m_rng.
rand64());
1289 std::make_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1291 while (!ready_chunks.empty()) {
1292 auto [chunk_rep, _rnd] = ready_chunks.front();
1293 std::pop_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1294 ready_chunks.pop_back();
1296 Assume(chunk_deps[chunk_rep].first == 0);
1297 const auto& chunk_txn =
m_tx_data[chunk_rep].chunk_setinfo.transactions;
1299 for (
TxIdx tx_idx : chunk_txn) {
1300 if (chunk_deps[tx_idx].second == 0) {
1301 ready_tx.push_back(tx_idx);
1304 Assume(!ready_tx.empty());
1307 while (!ready_tx.empty()) {
1310 if (pos != ready_tx.size() - 1) std::swap(ready_tx.back(), ready_tx[pos]);
1312 auto tx_idx = ready_tx.back();
1313 Assume(chunk_txn[tx_idx]);
1314 ready_tx.pop_back();
1316 ret.push_back(tx_idx);
1319 for (
TxIdx chl_idx : tx_data.children) {
1322 Assume(chunk_deps[chl_idx].second > 0);
1323 if (--chunk_deps[chl_idx].second == 0 && chunk_txn[chl_idx]) {
1325 ready_tx.push_back(chl_idx);
1328 if (chl_data.chunk_rep != chunk_rep) {
1329 Assume(chunk_deps[chl_data.chunk_rep].first > 0);
1330 if (--chunk_deps[chl_data.chunk_rep].first == 0) {
1332 ready_chunks.emplace_back(chl_data.chunk_rep,
m_rng.
rand64());
1333 std::push_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1358 std::vector<FeeFrac>
ret;
1364 std::sort(
ret.begin(),
ret.end(), std::greater{});
1377 std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies;
1378 std::vector<std::tuple<TxIdx, TxIdx, DepIdx>> all_dependencies;
1379 std::vector<std::tuple<TxIdx, TxIdx, DepIdx>> active_dependencies;
1380 for (
auto parent_idx : depgraph.
Positions()) {
1382 expected_dependencies.emplace_back(parent_idx, child_idx);
1387 all_dependencies.emplace_back(dep_data.parent, dep_data.child, dep_idx);
1390 active_dependencies.emplace_back(dep_data.parent, dep_data.child, dep_idx);
1393 std::sort(expected_dependencies.begin(), expected_dependencies.end());
1394 std::sort(all_dependencies.begin(), all_dependencies.end());
1395 assert(expected_dependencies.size() == all_dependencies.size());
1396 for (
size_t i = 0; i < expected_dependencies.size(); ++i) {
1397 assert(expected_dependencies[i] ==
1398 std::make_pair(std::get<0>(all_dependencies[i]),
1399 std::get<1>(all_dependencies[i])));
1405 for (
auto tx_idx: depgraph.
Positions()) {
1407 if (
m_tx_data[tx_idx].chunk_rep == tx_idx) {
1408 const auto& chunk_data =
m_tx_data[tx_idx];
1411 for (
auto chunk_tx : chunk_data.chunk_setinfo.transactions) {
1418 SetType expected_chunk = SetType::Singleton(tx_idx);
1420 auto old = expected_chunk;
1421 size_t active_dep_count{0};
1422 for (
const auto& [par, chl, _dep] : active_dependencies) {
1423 if (expected_chunk[par] || expected_chunk[chl]) {
1424 expected_chunk.Set(par);
1425 expected_chunk.Set(chl);
1429 if (old == expected_chunk) {
1430 assert(expected_chunk.Count() == active_dep_count + 1);
1434 assert(chunk_data.chunk_setinfo.transactions == expected_chunk);
1436 assert(chunk_data.chunk_setinfo.feerate ==
1437 depgraph.
FeeRate(chunk_data.chunk_setinfo.transactions));
1446 const auto& tx_data =
m_tx_data[tx_idx];
1450 assert(
m_tx_data[tx_data.chunk_rep].chunk_setinfo.transactions[tx_idx]);
1455 std::vector<DepIdx> expected_child_deps;
1456 for (
const auto& [par_idx, chl_idx, dep_idx] : all_dependencies) {
1457 if (tx_idx == par_idx) {
1458 assert(tx_data.children[chl_idx]);
1459 expected_child_deps.push_back(dep_idx);
1462 std::sort(expected_child_deps.begin(), expected_child_deps.end());
1463 auto child_deps_copy = tx_data.child_deps;
1464 std::sort(child_deps_copy.begin(), child_deps_copy.end());
1465 assert(expected_child_deps == child_deps_copy);
1471 for (
const auto& [par_idx, chl_idx, dep_idx] : active_dependencies) {
1476 SetType expected_top = SetType::Singleton(par_idx);
1478 auto old = expected_top;
1479 for (
const auto& [par2_idx, chl2_idx, dep2_idx] : active_dependencies) {
1480 if (dep2_idx != dep_idx && (expected_top[par2_idx] || expected_top[chl2_idx])) {
1481 expected_top.Set(par2_idx);
1482 expected_top.Set(chl2_idx);
1485 if (old == expected_top)
break;
1487 assert(!expected_top[chl_idx]);
1488 assert(dep_data.top_setinfo.transactions == expected_top);
1490 assert(dep_data.top_setinfo.feerate ==
1491 depgraph.
FeeRate(dep_data.top_setinfo.transactions));
1505 SetType nonminimal_reps;
1510 assert(!nonminimal_reps[chunk_rep]);
1511 nonminimal_reps.Set(chunk_rep);
1534template<
typename SetType>
1535std::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 = {},
bool is_topological =
true)
noexcept
1538 SpanningForestState forest(depgraph, rng_seed);
1539 if (!old_linearization.empty()) {
1540 forest.LoadLinearization(old_linearization);
1541 if (!is_topological) forest.MakeTopological();
1543 forest.MakeTopological();
1547 if (forest.GetCost() < max_iterations) {
1548 forest.StartOptimizing();
1550 if (!forest.OptimizeStep())
break;
1551 }
while (forest.GetCost() < max_iterations);
1555 bool optimal =
false;
1556 if (forest.GetCost() < max_iterations) {
1557 forest.StartMinimizing();
1559 if (!forest.MinimizeStep()) {
1563 }
while (forest.GetCost() < max_iterations);
1565 return {forest.GetLinearization(), optimal, forest.GetCost()};
1584template<
typename SetType>
1678 for (
int pass = 0; pass < 2; ++pass) {
1679 int rev = !(pass & 1);
1681 entries[SENTINEL].prev_group = SENTINEL;
1687 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1691 entries[cur_group].group = SetType::Singleton(idx);
1693 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1694 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1695 entries[cur_group].prev_tx = NO_PREV_TX;
1696 entries[cur_group].first_tx = cur_group;
1698 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1699 entries[SENTINEL].prev_group = cur_group;
1705 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1708 Assume(cur_group == entries[next_group].prev_group);
1709 Assume(prev_group == entries[cur_group].prev_group);
1713 Assume(cur_group != SENTINEL);
1714 Assume(prev_group != SENTINEL);
1715 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1719 entries[cur_group].group |= entries[prev_group].group;
1720 entries[cur_group].deps |= entries[prev_group].deps;
1721 entries[cur_group].feerate += entries[prev_group].feerate;
1723 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1725 entries[cur_group].first_tx = entries[prev_group].first_tx;
1727 prev_group = entries[prev_group].prev_group;
1728 entries[cur_group].prev_group = prev_group;
1731 DepGraphIndex preprev_group = entries[prev_group].prev_group;
1734 entries[next_group].prev_group = prev_group;
1735 entries[prev_group].prev_group = cur_group;
1736 entries[cur_group].prev_group = preprev_group;
1739 next_group = prev_group;
1740 prev_group = preprev_group;
1748 while (cur_group != SENTINEL) {
1754 *(linearization.begin() + (done++)) = cur_tx - 1;
1755 cur_tx = entries[cur_tx].prev_tx;
1756 }
while (cur_tx != NO_PREV_TX);
1759 *(linearization.end() - (++done)) = cur_tx - 1;
1760 cur_tx = entries[cur_tx].prev_tx;
1761 }
while (cur_tx != NO_PREV_TX);
1763 cur_group = entries[cur_group].prev_group;
1765 Assume(done == linearization.size());
#define Assume(val)
Assume is the identity function.
constexpr uint64_t rand64() noexcept
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
bool randbool() noexcept
Generate a random boolean.
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
bool empty() const noexcept
Test whether the contents of this deque is empty.
void clear() noexcept
Resize the deque to be size 0.
void push_back(T &&elem)
Move-construct a new element at the end of the deque.
void pop_front()
Remove the first element of the deque.
size_t size() const noexcept
Get the number of elements in this 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.
T & back() noexcept
Get a mutable reference to the last element of the deque.
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
unsigned CountDependencies() const noexcept
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.
Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.
void UpdateChunk(const SetType &chunk, TxIdx query, TxIdx chunk_rep, const SetInfo< SetType > &dep_change) noexcept
Update a chunk:
TxIdx PickRandomTx(const SetType &tx_idxs) noexcept
Pick a random transaction within a set (which must be non-empty).
std::vector< TxData > m_tx_data
Information about each transaction (and chunks).
InsecureRandomContext m_rng
Internal RNG.
bool OptimizeStep() noexcept
Try to improve the forest.
uint64_t GetCost() const noexcept
Determine how much work was performed so far.
TxIdx Activate(DepIdx dep_idx) noexcept
Make a specified inactive dependency active.
void Improve(DepIdx dep_idx) noexcept
Split a chunk, and then merge the resulting two chunks to make the graph topological again.
std::vector< FeeFrac > GetDiagram() const noexcept
Get the diagram for the current state, which must be topological.
std::vector< DepData > m_dep_data
Information about each dependency.
void MakeTopological() noexcept
Make state topological.
TxIdx MergeChunks(TxIdx top_rep, TxIdx bottom_rep) noexcept
Activate a dependency from the chunk represented by bottom_idx to the chunk represented by top_idx.
void StartMinimizing() noexcept
Initialize data structure for minimizing the chunks.
VecDeque< std::tuple< TxIdx, TxIdx, unsigned > > m_nonminimal_chunks
A FIFO of chunk representatives with a pivot transaction in them, and a flag to indicate their status...
uint32_t TxIdx
Data type to represent indexing into m_tx_data.
TxIdx MergeStep(TxIdx chunk_rep) noexcept
Perform an upward or downward merge step, on the specified chunk representative.
void SanityCheck(const DepGraph< SetType > &depgraph) const
Verify internal consistency of the data structure.
SpanningForestState(const DepGraph< SetType > &depgraph, uint64_t rng_seed) noexcept
Construct a spanning forest for the given DepGraph, with every transaction in its own chunk (not topo...
void StartOptimizing() noexcept
Initialize the data structure for optimization.
VecDeque< TxIdx > m_suboptimal_chunks
A FIFO of chunk representatives of chunks that may be improved still.
void LoadLinearization(std::span< const DepGraphIndex > old_linearization) noexcept
Load an existing linearization.
std::vector< DepGraphIndex > GetLinearization() noexcept
Construct a topologically-valid linearization from the current forest state.
bool MinimizeStep() noexcept
Try to reduce a chunk's size.
uint32_t DepIdx
Data type to represent indexing into m_dep_data.
SetType m_transaction_idxs
The set of all TxIdx's of transactions in the cluster indexing into m_tx_data.
void MergeSequence(TxIdx tx_idx) noexcept
Perform an upward or downward merge sequence on the specified transaction.
uint64_t m_cost
The number of updated transactions in activations/deactivations.
void Deactivate(DepIdx dep_idx) noexcept
Make a specified active dependency inactive.
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={}, bool is_topological=true) noexcept
Find or improve a linearization for a cluster.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
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.
std::vector< SetInfo< SetType > > ChunkLinearizationInfo(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the chunks of linearization as SetInfos.
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
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 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.
SetInfo operator-(const SetInfo &other) const noexcept
Compute the difference between this and other SetInfo (which must be a subset).
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 & operator|=(const SetInfo &other) noexcept
Add the transactions of other to this SetInfo (no overlap allowed).
SetInfo & operator-=(const SetInfo &other) noexcept
Remove the transactions of other from this SetInfo (which must be a subset).
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.
Structure with information about a single dependency.
bool active
Whether this dependency is active.
SetInfo< SetType > top_setinfo
(Only if this dependency is active) the would-be top chunk and its feerate that would be formed if th...
TxIdx parent
What the parent and child transactions are.
Structure with information about a single transaction.
std::vector< DepIdx > child_deps
The dependencies to children of this transaction.
SetType parents
The set of parent transactions of this transaction.
SetType children
The set of child transactions of this transaction.
TxIdx chunk_rep
Which transaction holds the chunk_setinfo for the chunk this transaction is in (the representative fo...
SetInfo< SetType > chunk_setinfo
(Only if this transaction is the representative for the chunk it is in) the total chunk set and feera...