5#ifndef BITCOIN_CLUSTER_LINEARIZE_H
6#define BITCOIN_CLUSTER_LINEARIZE_H
30template<
typename SetType>
62 if (a.m_used != b.m_used)
return false;
64 for (
auto idx : a.m_used) {
65 if (a.entries[idx] != b.entries[idx])
return false;
94 Assume(mapping.size() == depgraph.PositionRange());
95 Assume((pos_range == 0) == (depgraph.TxCount() == 0));
97 auto new_idx = mapping[i];
98 Assume(new_idx < pos_range);
100 entries[new_idx].ancestors = SetType::Singleton(new_idx);
101 entries[new_idx].descendants = SetType::Singleton(new_idx);
104 entries[new_idx].feerate = depgraph.entries[i].feerate;
109 for (
auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]);
138 static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size());
139 auto available = ALL_POSITIONS -
m_used;
142 if (new_idx ==
entries.size()) {
143 entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
145 entries[new_idx] =
Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
171 entry.ancestors &=
m_used;
172 entry.descendants &=
m_used;
186 for (
auto par : parents -
Ancestors(child)) {
191 if (par_anc.None())
return;
193 const auto& chl_des =
entries[child].descendants;
194 for (
auto anc_of_par : par_anc) {
195 entries[anc_of_par].descendants |= chl_des;
199 entries[dec_of_chl].ancestors |= par_anc;
215 for (
auto parent : parents) {
216 if (parents[parent]) {
236 for (
auto child : children) {
237 if (children[child]) {
252 for (
auto pos : elems)
ret +=
entries[pos].feerate;
270 auto to_add = SetType::Singleton(tx);
274 for (
auto add : to_add) {
280 }
while (to_add.Any());
293 if (todo.None())
return todo;
316 void AppendTopo(std::vector<DepGraphIndex>& list,
const SetType& select)
const noexcept
319 for (
auto i : select) list.push_back(i);
321 const auto a_anc_count =
entries[a].ancestors.Count();
322 const auto b_anc_count =
entries[b].ancestors.Count();
323 if (a_anc_count != b_anc_count)
return a_anc_count < b_anc_count;
361template<
typename SetType>
388 feerate += depgraph.FeeRate(pos);
419 swap(a.transactions, b.transactions);
420 swap(a.feerate, b.feerate);
428template<
typename SetType>
431 std::vector<SetInfo<SetType>>
ret;
436 while (!
ret.empty() &&
ByRatio{new_chunk.feerate} >
ByRatio{ret.back().feerate}) {
437 new_chunk |=
ret.back();
441 ret.emplace_back(std::move(new_chunk));
448template<
typename SetType>
451 std::vector<FeeFrac>
ret;
454 auto new_chunk = depgraph.FeeRate(i);
457 new_chunk +=
ret.back();
461 ret.push_back(std::move(new_chunk));
467template<
typename F,
typename Arg>
469 std::regular_invocable<F, Arg, Arg> &&
470 std::is_same_v<std::invoke_result_t<F, Arg, Arg>, std::strong_ordering>;
508 m_cost += 48 * num_txns + 4 * num_deps;
521 m_cost += 20 * num_chunks + 28 * num_steps;
722template<
typename SetType,
typename CostModel = SFLDefaultCostModel>
733 using SetIdx = std::conditional_t<(SetType::Size() <= 0xff),
735 std::conditional_t<(SetType::Size() <= 0xffff),
794 for (
auto tx_idx : tx_idxs) {
795 if (pos == 0)
return tx_idx;
805 std::pair<SetType, SetType>
GetReachable(
const SetType& tx_idxs)
const noexcept
807 SetType parents, children;
808 for (
auto tx_idx : tx_idxs) {
810 parents |= tx_data.parents;
811 children |= tx_data.children;
813 return {parents - tx_idxs, children - tx_idxs};
822 auto& parent_data =
m_tx_data[parent_idx];
824 Assume(parent_data.children[child_idx]);
825 Assume(!parent_data.active_children[child_idx]);
829 auto parent_chunk_idx = parent_data.chunk_idx;
830 auto child_chunk_idx = child_data.chunk_idx;
831 Assume(parent_chunk_idx != child_chunk_idx);
834 auto& top_info =
m_set_info[parent_chunk_idx];
835 auto& bottom_info =
m_set_info[child_chunk_idx];
855 for (
auto tx_idx : top_info.transactions) {
857 tx_data.chunk_idx = child_chunk_idx;
858 for (
auto dep_child_idx : tx_data.active_children) {
859 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
860 if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info;
865 for (
auto tx_idx : bottom_info.transactions) {
867 for (
auto dep_child_idx : tx_data.active_children) {
868 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
869 if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info;
873 bottom_info |= top_info;
878 m_reachable[child_chunk_idx].first -= bottom_info.transactions;
879 m_reachable[child_chunk_idx].second -= bottom_info.transactions;
881 parent_data.dep_top_idx[child_idx] = parent_chunk_idx;
882 parent_data.active_children.Set(child_idx);
885 m_cost.ActivateEnd(bottom_info.transactions.Count() - 1);
886 return child_chunk_idx;
895 auto& parent_data =
m_tx_data[parent_idx];
896 Assume(parent_data.children[child_idx]);
897 Assume(parent_data.active_children[child_idx]);
900 auto parent_chunk_idx = parent_data.dep_top_idx[child_idx];
901 auto child_chunk_idx = parent_data.chunk_idx;
902 Assume(parent_chunk_idx != child_chunk_idx);
905 auto& top_info =
m_set_info[parent_chunk_idx];
906 auto& bottom_info =
m_set_info[child_chunk_idx];
909 parent_data.active_children.Reset(child_idx);
911 auto ntx = bottom_info.transactions.Count();
913 bottom_info -= top_info;
916 SetType top_parents, top_children;
917 for (
auto tx_idx : top_info.transactions) {
919 tx_data.chunk_idx = parent_chunk_idx;
920 top_parents |= tx_data.parents;
921 top_children |= tx_data.children;
922 for (
auto dep_child_idx : tx_data.active_children) {
923 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
924 if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info;
927 SetType bottom_parents, bottom_children;
928 for (
auto tx_idx : bottom_info.transactions) {
930 bottom_parents |= tx_data.parents;
931 bottom_children |= tx_data.children;
932 for (
auto dep_child_idx : tx_data.active_children) {
933 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
934 if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info;
939 m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions;
940 m_reachable[parent_chunk_idx].second = top_children - top_info.transactions;
941 m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions;
942 m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions;
944 m_cost.DeactivateEnd(ntx - 1);
945 return {parent_chunk_idx, child_chunk_idx};
952 m_cost.MergeChunksBegin();
956 auto& bottom_chunk_info =
m_set_info[bottom_idx];
960 unsigned num_deps{0};
961 std::array<
SetIdx, SetType::Size()> counts;
962 for (
auto tx_idx : top_chunk_info.transactions) {
964 auto count = (tx_data.children & bottom_chunk_info.transactions).Count();
965 counts[tx_idx] =
count;
968 m_cost.MergeChunksMid(top_chunk_info.transactions.Count());
972 unsigned num_steps = 0;
973 for (
auto tx_idx : top_chunk_info.transactions) {
975 auto count = counts[tx_idx];
978 auto intersect = tx_data.children & bottom_chunk_info.transactions;
979 for (
auto child_idx : intersect) {
981 m_cost.MergeChunksEnd(num_steps);
997 template<
bool DownWard>
1000 if constexpr (DownWard) {
1008 template<
bool DownWard>
1011 m_cost.PickMergeCandidateBegin();
1023 FeeFrac best_other_chunk_feerate = chunk_info.feerate;
1028 uint64_t best_other_chunk_tiebreak{0};
1033 while (todo.Any()) {
1036 auto reached_chunk_idx =
m_tx_data[todo.First()].chunk_idx;
1037 auto& reached_chunk_info =
m_set_info[reached_chunk_idx];
1038 todo -= reached_chunk_info.transactions;
1040 auto cmp = DownWard ?
ByRatio{best_other_chunk_feerate} <=>
ByRatio{reached_chunk_info.feerate}
1041 :
ByRatio{reached_chunk_info.feerate} <=>
ByRatio{best_other_chunk_feerate};
1042 if (cmp > 0)
continue;
1044 if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) {
1045 best_other_chunk_feerate = reached_chunk_info.feerate;
1046 best_other_chunk_idx = reached_chunk_idx;
1047 best_other_chunk_tiebreak = tiebreak;
1052 m_cost.PickMergeCandidateEnd(steps);
1053 return best_other_chunk_idx;
1058 template<
bool DownWard>
1061 auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx);
1063 chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx);
1069 template<
bool DownWard>
1074 auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx);
1076 chunk_idx = merged_chunk_idx;
1091 auto [parent_chunk_idx, child_chunk_idx] =
Deactivate(parent_idx, child_idx);
1097 const auto& parent_reachable =
m_reachable[parent_chunk_idx].first;
1098 const auto& child_chunk_txn =
m_set_info[child_chunk_idx].transactions;
1099 if (parent_reachable.Overlaps(child_chunk_txn)) {
1108 auto merged_chunk_idx =
MergeChunks(child_chunk_idx, parent_chunk_idx);
1115 MergeSequence<false>(parent_chunk_idx);
1117 MergeSequence<true>(child_chunk_idx);
1124 m_cost.PickChunkToOptimizeBegin();
1134 m_cost.PickChunkToOptimizeEnd(steps);
1141 m_cost.PickChunkToOptimizeEnd(steps);
1148 m_cost.PickDependencyToSplitBegin();
1153 std::pair<TxIdx, TxIdx> candidate_dep = {
TxIdx(-1),
TxIdx(-1)};
1154 uint64_t candidate_tiebreak = 0;
1156 for (
auto tx_idx : chunk_info.transactions) {
1157 const auto& tx_data =
m_tx_data[tx_idx];
1159 for (
auto child_idx : tx_data.active_children) {
1160 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[child_idx]];
1163 auto cmp =
ByRatio{dep_top_info.feerate} <=>
ByRatio{chunk_info.feerate};
1164 if (cmp <= 0)
continue;
1169 if (tiebreak < candidate_tiebreak)
continue;
1171 candidate_dep = {tx_idx, child_idx};
1172 candidate_tiebreak = tiebreak;
1175 m_cost.PickDependencyToSplitEnd(chunk_info.transactions.Count());
1176 return candidate_dep;
1185 m_cost.InitializeBegin();
1188 m_tx_data.resize(depgraph.PositionRange());
1192 size_t num_chunks = 0;
1193 size_t num_deps = 0;
1197 tx_data.parents = depgraph.GetReducedParents(tx_idx);
1198 for (
auto parent_idx : tx_data.parents) {
1199 m_tx_data[parent_idx].children.Set(tx_idx);
1201 num_deps += tx_data.parents.Count();
1203 tx_data.chunk_idx = num_chunks;
1204 m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx);
1207 for (
SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) {
1212 Assume(num_chunks == num_transactions);
1215 m_cost.InitializeEnd(num_chunks, num_deps);
1225 auto chunk_idx =
m_tx_data[tx_idx].chunk_idx;
1228 chunk_idx = MergeStep<false>(chunk_idx);
1237 m_cost.MakeTopologicalBegin();
1247 SetType merged_chunks;
1271 unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1;
1273 for (
int i = 0; i < 2; ++i) {
1275 if (!(direction & 1))
continue;
1277 auto result_up = MergeStep<false>(chunk_idx);
1283 merged_chunks.Set(result_up);
1287 if (!(direction & 2))
continue;
1289 auto result_down = MergeStep<true>(chunk_idx);
1295 merged_chunks.Set(result_down);
1301 m_cost.MakeTopologicalEnd(chunks, steps);
1307 m_cost.StartOptimizingBegin();
1331 if (parent_idx ==
TxIdx(-1)) {
1337 Improve(parent_idx, child_idx);
1345 m_cost.StartMinimizingBegin();
1367 m_cost.MinimizeStepBegin();
1373 bool move_pivot_down =
flags & 1;
1375 bool second_stage =
flags & 2;
1379 std::pair<TxIdx, TxIdx> candidate_dep;
1380 uint64_t candidate_tiebreak{0};
1381 bool have_any =
false;
1383 for (
auto tx_idx : chunk_info.transactions) {
1384 const auto& tx_data =
m_tx_data[tx_idx];
1386 for (
auto child_idx : tx_data.active_children) {
1387 const auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[child_idx]];
1391 if (
ByRatio{dep_top_info.feerate} <
ByRatio{chunk_info.feerate})
continue;
1394 if (move_pivot_down == dep_top_info.transactions[pivot_idx])
continue;
1397 if (tiebreak > candidate_tiebreak) {
1398 candidate_tiebreak = tiebreak;
1399 candidate_dep = {tx_idx, child_idx};
1403 m_cost.MinimizeStepMid(chunk_info.transactions.Count());
1405 if (!have_any)
return true;
1408 if (candidate_tiebreak == 0) {
1416 auto [parent_chunk_idx, child_chunk_idx] =
Deactivate(candidate_dep.first, candidate_dep.second);
1419 auto& parent_reachable =
m_reachable[parent_chunk_idx].first;
1420 auto& child_chunk_txn =
m_set_info[child_chunk_idx].transactions;
1421 if (parent_reachable.Overlaps(child_chunk_txn)) {
1425 auto merged_chunk_idx =
MergeChunks(child_chunk_idx, parent_chunk_idx);
1429 m_cost.MinimizeStepEnd(
false);
1438 if (move_pivot_down) {
1450 m_cost.MinimizeStepEnd(
true);
1473 m_cost.GetLinearizationBegin();
1475 std::vector<DepGraphIndex>
ret;
1480 std::array<std::pair<SetIdx, TxIdx>, SetType::Size()> ready_chunks;
1482 unsigned num_ready_chunks{0};
1485 std::array<
TxIdx, SetType::Size()> chunk_deps;
1489 std::array<
TxIdx, SetType::Size()> tx_deps;
1493 std::array<
TxIdx, SetType::Size()> ready_tx;
1495 unsigned num_ready_tx{0};
1497 unsigned num_deps{0};
1499 const auto& chl_data =
m_tx_data[chl_idx];
1500 tx_deps[chl_idx] = chl_data.parents.Count();
1501 num_deps += tx_deps[chl_idx];
1502 auto chl_chunk_idx = chl_data.chunk_idx;
1503 auto& chl_chunk_info =
m_set_info[chl_chunk_idx];
1504 chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count();
1507 auto max_fallback_fn = [&](
SetIdx chunk_idx)
noexcept {
1508 auto& chunk =
m_set_info[chunk_idx].transactions;
1509 auto it = chunk.begin();
1512 while (it != chunk.end()) {
1513 if (fallback_order(*it,
ret) > 0)
ret = *it;
1520 auto tx_cmp_fn = [&](
const auto& a,
const auto& b)
noexcept {
1522 if (a == b)
return false;
1527 if (feerate_cmp != 0)
return feerate_cmp < 0;
1529 if (a_feerate.size != b_feerate.size) {
1530 return a_feerate.size > b_feerate.size;
1533 auto fallback_cmp = fallback_order(a, b);
1534 if (fallback_cmp != 0)
return fallback_cmp > 0;
1542 auto chunk_cmp_fn = [&](
const auto& a,
const auto& b)
noexcept {
1544 if (a.first == b.first)
return false;
1546 auto& chunk_feerate_a =
m_set_info[a.first].feerate;
1547 auto& chunk_feerate_b =
m_set_info[b.first].feerate;
1548 auto feerate_cmp =
ByRatio{chunk_feerate_a} <=>
ByRatio{chunk_feerate_b};
1549 if (feerate_cmp != 0)
return feerate_cmp < 0;
1551 if (chunk_feerate_a.size != chunk_feerate_b.size) {
1552 return chunk_feerate_a.size > chunk_feerate_b.size;
1555 auto fallback_cmp = fallback_order(a.second, b.second);
1556 if (fallback_cmp != 0)
return fallback_cmp > 0;
1559 return a.second < b.second;
1563 if (chunk_deps[chunk_idx] == 0) {
1564 ready_chunks[num_ready_chunks++] = {chunk_idx, max_fallback_fn(chunk_idx)};
1567 std::make_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn);
1569 while (num_ready_chunks > 0) {
1570 auto [chunk_idx, _rnd] = ready_chunks.front();
1571 std::pop_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn);
1573 Assume(chunk_deps[chunk_idx] == 0);
1574 const auto& chunk_txn =
m_set_info[chunk_idx].transactions;
1576 Assume(num_ready_tx == 0);
1577 for (
TxIdx tx_idx : chunk_txn) {
1578 if (tx_deps[tx_idx] == 0) ready_tx[num_ready_tx++] = tx_idx;
1580 Assume(num_ready_tx > 0);
1581 std::make_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn);
1584 while (num_ready_tx > 0) {
1586 auto tx_idx = ready_tx.front();
1587 std::pop_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn);
1590 ret.push_back(tx_idx);
1593 for (
TxIdx chl_idx : tx_data.children) {
1596 Assume(tx_deps[chl_idx] > 0);
1597 if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) {
1599 ready_tx[num_ready_tx++] = chl_idx;
1600 std::push_heap(ready_tx.begin(), ready_tx.begin() + num_ready_tx, tx_cmp_fn);
1603 if (chl_data.chunk_idx != chunk_idx) {
1604 Assume(chunk_deps[chl_data.chunk_idx] > 0);
1605 if (--chunk_deps[chl_data.chunk_idx] == 0) {
1607 ready_chunks[num_ready_chunks++] = {chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx)};
1608 std::push_heap(ready_chunks.begin(), ready_chunks.begin() + num_ready_chunks, chunk_cmp_fn);
1634 std::vector<FeeFrac>
ret;
1651 std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies;
1652 std::vector<std::pair<TxIdx, TxIdx>> all_dependencies;
1653 std::vector<std::pair<TxIdx, TxIdx>> active_dependencies;
1654 for (
auto parent_idx :
m_depgraph.Positions()) {
1655 for (
auto child_idx :
m_depgraph.GetReducedChildren(parent_idx)) {
1656 expected_dependencies.emplace_back(parent_idx, child_idx);
1660 for (
auto child_idx :
m_tx_data[tx_idx].children) {
1661 all_dependencies.emplace_back(tx_idx, child_idx);
1662 if (
m_tx_data[tx_idx].active_children[child_idx]) {
1663 active_dependencies.emplace_back(tx_idx, child_idx);
1667 std::ranges::sort(expected_dependencies);
1668 std::ranges::sort(all_dependencies);
1669 assert(expected_dependencies == all_dependencies);
1674 SetType chunk_cover;
1676 const auto& chunk_info =
m_set_info[chunk_idx];
1679 for (
auto tx_idx : chunk_info.transactions) {
1682 assert(!chunk_cover.Overlaps(chunk_info.transactions));
1683 chunk_cover |= chunk_info.transactions;
1688 assert(chunk_info.transactions.Any());
1689 SetType expected_chunk = SetType::Singleton(chunk_info.transactions.First());
1691 auto old = expected_chunk;
1692 size_t active_dep_count{0};
1693 for (
const auto& [par, chl] : active_dependencies) {
1694 if (expected_chunk[par] || expected_chunk[chl]) {
1695 expected_chunk.Set(par);
1696 expected_chunk.Set(chl);
1700 if (old == expected_chunk) {
1701 assert(expected_chunk.Count() == active_dep_count + 1);
1705 assert(chunk_info.transactions == expected_chunk);
1722 const auto& tx_data =
m_tx_data[tx_idx];
1730 assert(tx_data.active_children.IsSubsetOf(tx_data.children));
1732 for (
auto child_idx : tx_data.active_children) {
1741 for (
const auto& [par_idx, chl_idx] : active_dependencies) {
1746 SetType expected_top = SetType::Singleton(par_idx);
1748 auto old = expected_top;
1749 size_t active_dep_count{0};
1750 for (
const auto& [par2_idx, chl2_idx] : active_dependencies) {
1751 if (par_idx == par2_idx && chl_idx == chl2_idx)
continue;
1752 if (expected_top[par2_idx] || expected_top[chl2_idx]) {
1753 expected_top.Set(par2_idx);
1754 expected_top.Set(chl2_idx);
1758 if (old == expected_top) {
1759 assert(expected_top.Count() == active_dep_count + 1);
1763 assert(!expected_top[chl_idx]);
1765 assert(dep_top_info.transactions == expected_top);
1767 assert(dep_top_info.feerate ==
m_depgraph.FeeRate(dep_top_info.transactions));
1773 SetType suboptimal_idxs;
1776 assert(!suboptimal_idxs[chunk_idx]);
1777 suboptimal_idxs.Set(chunk_idx);
1784 SetType nonminimal_idxs;
1788 assert(!nonminimal_idxs[chunk_idx]);
1789 nonminimal_idxs.Set(chunk_idx);
1815template<
typename SetType>
1816std::tuple<std::vector<DepGraphIndex>, bool, uint64_t>
Linearize(
1821 std::span<const DepGraphIndex> old_linearization = {},
1822 bool is_topological =
true)
noexcept
1825 SpanningForestState forest(depgraph, rng_seed);
1826 if (!old_linearization.empty()) {
1827 forest.LoadLinearization(old_linearization);
1828 if (!is_topological) forest.MakeTopological();
1830 forest.MakeTopological();
1834 if (forest.GetCost() < max_cost) {
1835 forest.StartOptimizing();
1837 if (!forest.OptimizeStep())
break;
1838 }
while (forest.GetCost() < max_cost);
1842 bool optimal =
false;
1843 if (forest.GetCost() < max_cost) {
1844 forest.StartMinimizing();
1846 if (!forest.MinimizeStep()) {
1850 }
while (forest.GetCost() < max_cost);
1852 return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()};
1871template<
typename SetType>
1965 for (
int pass = 0; pass < 2; ++pass) {
1966 int rev = !(pass & 1);
1968 entries[SENTINEL].prev_group = SENTINEL;
1974 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1978 entries[cur_group].group = SetType::Singleton(idx);
1980 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1981 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1982 entries[cur_group].prev_tx = NO_PREV_TX;
1983 entries[cur_group].first_tx = cur_group;
1985 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1986 entries[SENTINEL].prev_group = cur_group;
1992 while (
ByRatio{entries[cur_group].feerate} >
ByRatio{entries[prev_group].feerate}) {
1995 Assume(cur_group == entries[next_group].prev_group);
1996 Assume(prev_group == entries[cur_group].prev_group);
2000 Assume(cur_group != SENTINEL);
2001 Assume(prev_group != SENTINEL);
2002 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
2006 entries[cur_group].group |= entries[prev_group].group;
2007 entries[cur_group].deps |= entries[prev_group].deps;
2008 entries[cur_group].feerate += entries[prev_group].feerate;
2010 entries[entries[cur_group].first_tx].prev_tx = prev_group;
2012 entries[cur_group].first_tx = entries[prev_group].first_tx;
2014 prev_group = entries[prev_group].prev_group;
2015 entries[cur_group].prev_group = prev_group;
2018 DepGraphIndex preprev_group = entries[prev_group].prev_group;
2021 entries[next_group].prev_group = prev_group;
2022 entries[prev_group].prev_group = cur_group;
2023 entries[cur_group].prev_group = preprev_group;
2026 next_group = prev_group;
2027 prev_group = preprev_group;
2035 while (cur_group != SENTINEL) {
2041 *(linearization.begin() + (done++)) = cur_tx - 1;
2042 cur_tx = entries[cur_tx].prev_tx;
2043 }
while (cur_tx != NO_PREV_TX);
2046 *(linearization.end() - (++done)) = cur_tx - 1;
2047 cur_tx = entries[cur_tx].prev_tx;
2048 }
while (cur_tx != NO_PREV_TX);
2050 cur_group = entries[cur_group].prev_group;
2052 Assume(done == linearization.size());
#define Assume(val)
Assume is the identity function.
Wrapper around FeeFrac & derived types, which adds a feerate-based ordering which treats equal-feerat...
Wrapper around FeeFrac & derived types, which adds a total ordering which first sorts by feerate and ...
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.
A default cost model for SFL for SetType=BitSet<64>, based on benchmarks.
void InitializeBegin() noexcept
void PickChunkToOptimizeEnd(int num_steps) noexcept
void PickMergeCandidateEnd(int num_steps) noexcept
void MakeTopologicalEnd(int num_chunks, int num_steps) noexcept
void MinimizeStepBegin() noexcept
void StartOptimizingEnd(int num_chunks) noexcept
void MakeTopologicalBegin() noexcept
void MergeChunksBegin() noexcept
void MergeChunksMid(int num_txns) noexcept
void PickChunkToOptimizeBegin() noexcept
void StartOptimizingBegin() noexcept
void PickDependencyToSplitBegin() noexcept
void StartMinimizingEnd(int num_chunks) noexcept
void StartMinimizingBegin() noexcept
void MergeChunksEnd(int num_steps) noexcept
void InitializeEnd(int num_txns, int num_deps) noexcept
void PickDependencyToSplitEnd(int num_txns) noexcept
void GetLinearizationEnd(int num_txns, int num_deps) noexcept
void MinimizeStepMid(int num_txns) noexcept
void DeactivateEnd(int num_deps) noexcept
uint64_t GetCost() const noexcept
void ActivateEnd(int num_deps) noexcept
void DeactivateBegin() noexcept
void MinimizeStepEnd(bool split) noexcept
void PickMergeCandidateBegin() noexcept
void GetLinearizationBegin() noexcept
void ActivateBegin() noexcept
Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.
std::pair< TxIdx, TxIdx > PickDependencyToSplit(SetIdx chunk_idx) noexcept
Find a (parent, child) dependency to deactivate in chunk_idx, or (-1, -1) if none.
void StartMinimizing() noexcept
Initialize data structure for minimizing the chunks.
SetIdx PickChunkToOptimize() noexcept
Determine the next chunk to optimize, or INVALID_SET_IDX if none.
std::vector< DepGraphIndex > GetLinearization(const StrongComparator< DepGraphIndex > auto &fallback_order) noexcept
Construct a topologically-valid linearization from the current forest state.
SetIdx MergeChunksDirected(SetIdx chunk_idx, SetIdx merge_chunk_idx) noexcept
Activate a dependency from chunk_idx to merge_chunk_idx (if !DownWard), or a dependency from merge_ch...
SpanningForestState(const DepGraph< SetType > &depgraph LIFETIMEBOUND, uint64_t rng_seed, const CostModel &cost=CostModel{}) noexcept
Construct a spanning forest for the given DepGraph, with every transaction in its own chunk (not topo...
static constexpr SetIdx INVALID_SET_IDX
An invalid SetIdx.
bool MinimizeStep() noexcept
Try to reduce a chunk's size.
std::vector< TxData > m_tx_data
Information about each transaction (and chunks).
std::vector< FeeFrac > GetDiagram() const noexcept
Get the diagram for the current state, which must be topological.
CostModel m_cost
Accounting for the cost of this computation.
SetIdx MergeChunks(SetIdx top_idx, SetIdx bottom_idx) noexcept
Activate a dependency from the bottom set to the top set, which must exist.
void LoadLinearization(std::span< const DepGraphIndex > old_linearization) noexcept
Load an existing linearization.
SetIdx MergeStep(SetIdx chunk_idx) noexcept
Perform an upward or downward merge step, on the specified chunk.
std::vector< SetInfo< SetType > > m_set_info
Information about each set (chunk, or active dependency top set).
SetIdx PickMergeCandidate(SetIdx chunk_idx) noexcept
Determine which chunk to merge chunk_idx with, or INVALID_SET_IDX if none.
SetIdx Activate(TxIdx parent_idx, TxIdx child_idx) noexcept
Make the inactive dependency from child to parent, which must not be in the same chunk already,...
TxIdx PickRandomTx(const SetType &tx_idxs) noexcept
Pick a random transaction within a set (which must be non-empty).
bool OptimizeStep() noexcept
Try to improve the forest.
void StartOptimizing() noexcept
Initialize the data structure for optimization.
std::pair< SetType, SetType > GetReachable(const SetType &tx_idxs) const noexcept
Find the set of out-of-chunk transactions reachable from tx_idxs, both in upwards and downwards direc...
std::vector< std::pair< SetType, SetType > > m_reachable
For each chunk, indexed by SetIdx, the set of out-of-chunk reachable transactions,...
void Improve(TxIdx parent_idx, TxIdx child_idx) noexcept
Split a chunk, and then merge the resulting two chunks to make the graph topological again.
SetType m_transaction_idxs
The set of all TxIdx's of transactions in the cluster indexing into m_tx_data.
InsecureRandomContext m_rng
Internal RNG.
DepGraphIndex TxIdx
Data type to represent indexing into m_tx_data.
const DepGraph< SetType > & m_depgraph
The DepGraph we are trying to linearize.
VecDeque< std::tuple< SetIdx, TxIdx, unsigned > > m_nonminimal_chunks
A FIFO of chunk indexes with a pivot transaction in them, and a flag to indicate their status:
std::conditional_t<(SetType::Size()<=0xff), uint8_t, std::conditional_t<(SetType::Size()<=0xffff), uint16_t, uint32_t > > SetIdx
Data type to represent indexing into m_set_info.
void MergeSequence(SetIdx chunk_idx) noexcept
Perform an upward or downward merge sequence on the specified chunk.
void MakeTopological() noexcept
Make state topological.
std::pair< SetIdx, SetIdx > Deactivate(TxIdx parent_idx, TxIdx child_idx) noexcept
Make a specified active dependency inactive.
uint64_t GetCost() const noexcept
Determine how much work was performed so far.
SetType m_chunk_idxs
The set of all chunk SetIdx's.
VecDeque< SetIdx > m_suboptimal_chunks
A FIFO of chunk SetIdxs for chunks that may be improved still.
SetType m_suboptimal_idxs
The set of all SetIdx's that appear in m_suboptimal_chunks.
void SanityCheck() const
Verify internal consistency of the data structure.
Concept for function objects that return std::strong_ordering when invoked with two Args.
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_cost, uint64_t rng_seed, const StrongComparator< DepGraphIndex > auto &fallback_order, std::span< const DepGraphIndex > old_linearization={}, bool is_topological=true) noexcept
Find or improve a linearization for a cluster.
std::compare_three_way IndexTxOrder
Simple default transaction ordering function for SpanningForestState::GetLinearization() and Lineariz...
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.
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \t")
Data structure storing a fee and 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 transaction.
SetType active_children
The set of child transactions reachable through an active dependency.
SetType children
The set of child transactions of this transaction.
SetIdx chunk_idx
Which chunk this transaction belongs to.
std::array< SetIdx, SetType::Size()> dep_top_idx
The top set for every active child dependency this transaction has, indexed by child TxIdx.
SetType parents
The set of parent transactions of this transaction.