5#ifndef BITCOIN_CLUSTER_LINEARIZE_H
6#define BITCOIN_CLUSTER_LINEARIZE_H
29template<
typename SetType>
61 if (a.m_used != b.m_used)
return false;
63 for (
auto idx : a.m_used) {
64 if (a.entries[idx] != b.entries[idx])
return false;
93 Assume(mapping.size() == depgraph.PositionRange());
94 Assume((pos_range == 0) == (depgraph.TxCount() == 0));
96 auto new_idx = mapping[i];
97 Assume(new_idx < pos_range);
99 entries[new_idx].ancestors = SetType::Singleton(new_idx);
100 entries[new_idx].descendants = SetType::Singleton(new_idx);
103 entries[new_idx].feerate = depgraph.entries[i].feerate;
108 for (
auto j : depgraph.GetReducedParents(i)) parents.Set(mapping[j]);
137 static constexpr auto ALL_POSITIONS = SetType::Fill(SetType::Size());
138 auto available = ALL_POSITIONS -
m_used;
141 if (new_idx ==
entries.size()) {
142 entries.emplace_back(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
144 entries[new_idx] =
Entry(feefrac, SetType::Singleton(new_idx), SetType::Singleton(new_idx));
170 entry.ancestors &=
m_used;
171 entry.descendants &=
m_used;
185 for (
auto par : parents -
Ancestors(child)) {
190 if (par_anc.None())
return;
192 const auto& chl_des =
entries[child].descendants;
193 for (
auto anc_of_par : par_anc) {
194 entries[anc_of_par].descendants |= chl_des;
198 entries[dec_of_chl].ancestors |= par_anc;
214 for (
auto parent : parents) {
215 if (parents[parent]) {
235 for (
auto child : children) {
236 if (children[child]) {
251 for (
auto pos : elems)
ret +=
entries[pos].feerate;
269 auto to_add = SetType::Singleton(tx);
273 for (
auto add : to_add) {
279 }
while (to_add.Any());
292 if (todo.None())
return todo;
315 void AppendTopo(std::vector<DepGraphIndex>& list,
const SetType& select)
const noexcept
318 for (
auto i : select) list.push_back(i);
320 const auto a_anc_count = entries[a].ancestors.Count();
321 const auto b_anc_count = entries[b].ancestors.Count();
322 if (a_anc_count != b_anc_count) return a_anc_count < b_anc_count;
360template<
typename SetType>
387 feerate += depgraph.FeeRate(pos);
418 swap(a.transactions, b.transactions);
419 swap(a.feerate, b.feerate);
427template<
typename SetType>
430 std::vector<SetInfo<SetType>>
ret;
435 while (!
ret.empty() && new_chunk.
feerate >>
ret.back().feerate) {
436 new_chunk |=
ret.back();
440 ret.emplace_back(std::move(new_chunk));
447template<
typename SetType>
450 std::vector<FeeFrac>
ret;
453 auto new_chunk = depgraph.FeeRate(i);
455 while (!
ret.empty() && new_chunk >>
ret.back()) {
456 new_chunk +=
ret.back();
460 ret.push_back(std::move(new_chunk));
466template<
typename F,
typename Arg>
468 std::regular_invocable<F, Arg, Arg> &&
469 std::is_same_v<std::invoke_result_t<F, Arg, Arg>, std::strong_ordering>;
507 m_cost += 48 * num_txns + 4 * num_deps;
520 m_cost += 20 * num_chunks + 28 * num_steps;
717template<
typename SetType,
typename CostModel = SFLDefaultCostModel>
728 using SetIdx = std::conditional_t<(SetType::Size() <= 0xff),
730 std::conditional_t<(SetType::Size() <= 0xffff),
789 for (
auto tx_idx : tx_idxs) {
790 if (pos == 0)
return tx_idx;
800 std::pair<SetType, SetType>
GetReachable(
const SetType& tx_idxs)
const noexcept
802 SetType parents, children;
803 for (
auto tx_idx : tx_idxs) {
805 parents |= tx_data.parents;
806 children |= tx_data.children;
808 return {parents - tx_idxs, children - tx_idxs};
817 auto& parent_data =
m_tx_data[parent_idx];
819 Assume(parent_data.children[child_idx]);
820 Assume(!parent_data.active_children[child_idx]);
824 auto parent_chunk_idx = parent_data.chunk_idx;
825 auto child_chunk_idx = child_data.chunk_idx;
826 Assume(parent_chunk_idx != child_chunk_idx);
829 auto& top_info =
m_set_info[parent_chunk_idx];
830 auto& bottom_info =
m_set_info[child_chunk_idx];
850 for (
auto tx_idx : top_info.transactions) {
852 tx_data.chunk_idx = child_chunk_idx;
853 for (
auto dep_child_idx : tx_data.active_children) {
854 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
855 if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info;
860 for (
auto tx_idx : bottom_info.transactions) {
862 for (
auto dep_child_idx : tx_data.active_children) {
863 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
864 if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info;
868 bottom_info |= top_info;
873 m_reachable[child_chunk_idx].first -= bottom_info.transactions;
874 m_reachable[child_chunk_idx].second -= bottom_info.transactions;
876 parent_data.dep_top_idx[child_idx] = parent_chunk_idx;
877 parent_data.active_children.Set(child_idx);
880 m_cost.ActivateEnd(bottom_info.transactions.Count() - 1);
881 return child_chunk_idx;
890 auto& parent_data =
m_tx_data[parent_idx];
891 Assume(parent_data.children[child_idx]);
892 Assume(parent_data.active_children[child_idx]);
895 auto parent_chunk_idx = parent_data.dep_top_idx[child_idx];
896 auto child_chunk_idx = parent_data.chunk_idx;
897 Assume(parent_chunk_idx != child_chunk_idx);
900 auto& top_info =
m_set_info[parent_chunk_idx];
901 auto& bottom_info =
m_set_info[child_chunk_idx];
904 parent_data.active_children.Reset(child_idx);
906 auto ntx = bottom_info.transactions.Count();
908 bottom_info -= top_info;
911 SetType top_parents, top_children;
912 for (
auto tx_idx : top_info.transactions) {
914 tx_data.chunk_idx = parent_chunk_idx;
915 top_parents |= tx_data.parents;
916 top_children |= tx_data.children;
917 for (
auto dep_child_idx : tx_data.active_children) {
918 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
919 if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info;
922 SetType bottom_parents, bottom_children;
923 for (
auto tx_idx : bottom_info.transactions) {
925 bottom_parents |= tx_data.parents;
926 bottom_children |= tx_data.children;
927 for (
auto dep_child_idx : tx_data.active_children) {
928 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
929 if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info;
934 m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions;
935 m_reachable[parent_chunk_idx].second = top_children - top_info.transactions;
936 m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions;
937 m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions;
939 m_cost.DeactivateEnd(ntx - 1);
940 return {parent_chunk_idx, child_chunk_idx};
947 m_cost.MergeChunksBegin();
951 auto& bottom_chunk_info =
m_set_info[bottom_idx];
953 unsigned num_deps{0};
954 for (
auto tx_idx : top_chunk_info.transactions) {
956 num_deps += (tx_data.children & bottom_chunk_info.transactions).Count();
958 m_cost.MergeChunksMid(top_chunk_info.transactions.Count());
962 unsigned num_steps = 0;
963 for (
auto tx_idx : top_chunk_info.transactions) {
966 auto intersect = tx_data.children & bottom_chunk_info.transactions;
967 auto count = intersect.Count();
969 for (
auto child_idx : intersect) {
971 m_cost.MergeChunksEnd(num_steps);
987 template<
bool DownWard>
990 if constexpr (DownWard) {
998 template<
bool DownWard>
1001 m_cost.PickMergeCandidateBegin();
1013 FeeFrac best_other_chunk_feerate = chunk_info.feerate;
1018 uint64_t best_other_chunk_tiebreak{0};
1023 while (todo.Any()) {
1026 auto reached_chunk_idx =
m_tx_data[todo.First()].chunk_idx;
1027 auto& reached_chunk_info =
m_set_info[reached_chunk_idx];
1028 todo -= reached_chunk_info.transactions;
1030 auto cmp = DownWard ? FeeRateCompare(best_other_chunk_feerate, reached_chunk_info.feerate)
1031 : FeeRateCompare(reached_chunk_info.feerate, best_other_chunk_feerate);
1032 if (cmp > 0)
continue;
1034 if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) {
1035 best_other_chunk_feerate = reached_chunk_info.feerate;
1036 best_other_chunk_idx = reached_chunk_idx;
1037 best_other_chunk_tiebreak = tiebreak;
1042 m_cost.PickMergeCandidateEnd(steps);
1043 return best_other_chunk_idx;
1048 template<
bool DownWard>
1051 auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx);
1053 chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx);
1059 template<
bool DownWard>
1064 auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx);
1066 chunk_idx = merged_chunk_idx;
1081 auto [parent_chunk_idx, child_chunk_idx] =
Deactivate(parent_idx, child_idx);
1087 const auto& parent_reachable =
m_reachable[parent_chunk_idx].first;
1088 const auto& child_chunk_txn =
m_set_info[child_chunk_idx].transactions;
1089 if (parent_reachable.Overlaps(child_chunk_txn)) {
1098 auto merged_chunk_idx =
MergeChunks(child_chunk_idx, parent_chunk_idx);
1105 MergeSequence<false>(parent_chunk_idx);
1107 MergeSequence<true>(child_chunk_idx);
1114 m_cost.PickChunkToOptimizeBegin();
1124 m_cost.PickChunkToOptimizeEnd(steps);
1131 m_cost.PickChunkToOptimizeEnd(steps);
1138 m_cost.PickDependencyToSplitBegin();
1143 std::pair<TxIdx, TxIdx> candidate_dep = {
TxIdx(-1),
TxIdx(-1)};
1144 uint64_t candidate_tiebreak = 0;
1146 for (
auto tx_idx : chunk_info.transactions) {
1147 const auto& tx_data =
m_tx_data[tx_idx];
1149 for (
auto child_idx : tx_data.active_children) {
1150 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[child_idx]];
1153 auto cmp = FeeRateCompare(dep_top_info.feerate, chunk_info.feerate);
1154 if (cmp <= 0)
continue;
1159 if (tiebreak < candidate_tiebreak)
continue;
1161 candidate_dep = {tx_idx, child_idx};
1162 candidate_tiebreak = tiebreak;
1165 m_cost.PickDependencyToSplitEnd(chunk_info.transactions.Count());
1166 return candidate_dep;
1175 m_cost.InitializeBegin();
1178 m_tx_data.resize(depgraph.PositionRange());
1181 size_t num_chunks = 0;
1182 size_t num_deps = 0;
1186 tx_data.parents = depgraph.GetReducedParents(tx_idx);
1187 for (
auto parent_idx : tx_data.parents) {
1188 m_tx_data[parent_idx].children.Set(tx_idx);
1190 num_deps += tx_data.parents.Count();
1192 tx_data.chunk_idx = num_chunks;
1193 m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx);
1196 for (
SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) {
1201 Assume(num_chunks == num_transactions);
1204 m_cost.InitializeEnd(num_chunks, num_deps);
1214 auto chunk_idx =
m_tx_data[tx_idx].chunk_idx;
1217 chunk_idx = MergeStep<false>(chunk_idx);
1226 m_cost.MakeTopologicalBegin();
1236 SetType merged_chunks;
1260 unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1;
1262 for (
int i = 0; i < 2; ++i) {
1264 if (!(direction & 1))
continue;
1266 auto result_up = MergeStep<false>(chunk_idx);
1272 merged_chunks.Set(result_up);
1276 if (!(direction & 2))
continue;
1278 auto result_down = MergeStep<true>(chunk_idx);
1284 merged_chunks.Set(result_down);
1290 m_cost.MakeTopologicalEnd(chunks, steps);
1296 m_cost.StartOptimizingBegin();
1320 if (parent_idx ==
TxIdx(-1)) {
1326 Improve(parent_idx, child_idx);
1334 m_cost.StartMinimizingBegin();
1356 m_cost.MinimizeStepBegin();
1362 bool move_pivot_down =
flags & 1;
1364 bool second_stage =
flags & 2;
1368 std::pair<TxIdx, TxIdx> candidate_dep;
1369 uint64_t candidate_tiebreak{0};
1370 bool have_any =
false;
1372 for (
auto tx_idx : chunk_info.transactions) {
1373 const auto& tx_data =
m_tx_data[tx_idx];
1375 for (
auto child_idx : tx_data.active_children) {
1376 const auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[child_idx]];
1380 if (dep_top_info.feerate << chunk_info.feerate)
continue;
1383 if (move_pivot_down == dep_top_info.transactions[pivot_idx])
continue;
1386 if (tiebreak > candidate_tiebreak) {
1387 candidate_tiebreak = tiebreak;
1388 candidate_dep = {tx_idx, child_idx};
1392 m_cost.MinimizeStepMid(chunk_info.transactions.Count());
1394 if (!have_any)
return true;
1397 if (candidate_tiebreak == 0) {
1405 auto [parent_chunk_idx, child_chunk_idx] =
Deactivate(candidate_dep.first, candidate_dep.second);
1408 auto& parent_reachable =
m_reachable[parent_chunk_idx].first;
1409 auto& child_chunk_txn =
m_set_info[child_chunk_idx].transactions;
1410 if (parent_reachable.Overlaps(child_chunk_txn)) {
1414 auto merged_chunk_idx =
MergeChunks(child_chunk_idx, parent_chunk_idx);
1418 m_cost.MinimizeStepEnd(
false);
1427 if (move_pivot_down) {
1439 m_cost.MinimizeStepEnd(
true);
1462 m_cost.GetLinearizationBegin();
1464 std::vector<DepGraphIndex>
ret;
1469 std::vector<std::pair<SetIdx, TxIdx>> ready_chunks;
1472 std::vector<TxIdx> chunk_deps(
m_set_info.size(), 0);
1475 std::vector<TxIdx> tx_deps(
m_tx_data.size(), 0);
1478 std::vector<TxIdx> ready_tx;
1480 unsigned num_deps{0};
1482 const auto& chl_data =
m_tx_data[chl_idx];
1483 tx_deps[chl_idx] = chl_data.parents.Count();
1484 num_deps += tx_deps[chl_idx];
1485 auto chl_chunk_idx = chl_data.chunk_idx;
1486 auto& chl_chunk_info =
m_set_info[chl_chunk_idx];
1487 chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count();
1490 auto max_fallback_fn = [&](
SetIdx chunk_idx)
noexcept {
1491 auto& chunk =
m_set_info[chunk_idx].transactions;
1492 auto it = chunk.begin();
1495 while (it != chunk.end()) {
1496 if (fallback_order(*it,
ret) > 0)
ret = *it;
1503 auto tx_cmp_fn = [&](
const auto& a,
const auto& b)
noexcept {
1505 if (a == b)
return false;
1509 auto feerate_cmp = FeeRateCompare(a_feerate, b_feerate);
1510 if (feerate_cmp != 0)
return feerate_cmp < 0;
1512 if (a_feerate.size != b_feerate.size) {
1513 return a_feerate.size > b_feerate.size;
1516 auto fallback_cmp = fallback_order(a, b);
1517 if (fallback_cmp != 0)
return fallback_cmp > 0;
1525 auto chunk_cmp_fn = [&](
const auto& a,
const auto& b)
noexcept {
1527 if (a.first == b.first)
return false;
1529 auto& chunk_feerate_a =
m_set_info[a.first].feerate;
1530 auto& chunk_feerate_b =
m_set_info[b.first].feerate;
1531 auto feerate_cmp = FeeRateCompare(chunk_feerate_a, chunk_feerate_b);
1532 if (feerate_cmp != 0)
return feerate_cmp < 0;
1534 if (chunk_feerate_a.size != chunk_feerate_b.size) {
1535 return chunk_feerate_a.size > chunk_feerate_b.size;
1538 auto fallback_cmp = fallback_order(a.second, b.second);
1539 if (fallback_cmp != 0)
return fallback_cmp > 0;
1542 return a.second < b.second;
1546 if (chunk_deps[chunk_idx] == 0) {
1547 ready_chunks.emplace_back(chunk_idx, max_fallback_fn(chunk_idx));
1550 std::make_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1552 while (!ready_chunks.empty()) {
1553 auto [chunk_idx, _rnd] = ready_chunks.front();
1554 std::pop_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1555 ready_chunks.pop_back();
1556 Assume(chunk_deps[chunk_idx] == 0);
1557 const auto& chunk_txn =
m_set_info[chunk_idx].transactions;
1559 Assume(ready_tx.empty());
1560 for (
TxIdx tx_idx : chunk_txn) {
1561 if (tx_deps[tx_idx] == 0) ready_tx.push_back(tx_idx);
1563 Assume(!ready_tx.empty());
1564 std::make_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
1567 while (!ready_tx.empty()) {
1569 auto tx_idx = ready_tx.front();
1570 std::pop_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
1571 ready_tx.pop_back();
1573 ret.push_back(tx_idx);
1576 for (
TxIdx chl_idx : tx_data.children) {
1579 Assume(tx_deps[chl_idx] > 0);
1580 if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) {
1582 ready_tx.push_back(chl_idx);
1583 std::push_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
1586 if (chl_data.chunk_idx != chunk_idx) {
1587 Assume(chunk_deps[chl_data.chunk_idx] > 0);
1588 if (--chunk_deps[chl_data.chunk_idx] == 0) {
1590 ready_chunks.emplace_back(chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx));
1591 std::push_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1617 std::vector<FeeFrac>
ret;
1621 std::sort(
ret.begin(),
ret.end(), std::greater{});
1634 std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies;
1635 std::vector<std::pair<TxIdx, TxIdx>> all_dependencies;
1636 std::vector<std::pair<TxIdx, TxIdx>> active_dependencies;
1637 for (
auto parent_idx :
m_depgraph.Positions()) {
1638 for (
auto child_idx :
m_depgraph.GetReducedChildren(parent_idx)) {
1639 expected_dependencies.emplace_back(parent_idx, child_idx);
1643 for (
auto child_idx :
m_tx_data[tx_idx].children) {
1644 all_dependencies.emplace_back(tx_idx, child_idx);
1645 if (
m_tx_data[tx_idx].active_children[child_idx]) {
1646 active_dependencies.emplace_back(tx_idx, child_idx);
1650 std::sort(expected_dependencies.begin(), expected_dependencies.end());
1651 std::sort(all_dependencies.begin(), all_dependencies.end());
1652 assert(expected_dependencies == all_dependencies);
1657 SetType chunk_cover;
1659 const auto& chunk_info =
m_set_info[chunk_idx];
1662 for (
auto tx_idx : chunk_info.transactions) {
1665 assert(!chunk_cover.Overlaps(chunk_info.transactions));
1666 chunk_cover |= chunk_info.transactions;
1671 assert(chunk_info.transactions.Any());
1672 SetType expected_chunk = SetType::Singleton(chunk_info.transactions.First());
1674 auto old = expected_chunk;
1675 size_t active_dep_count{0};
1676 for (
const auto& [par, chl] : active_dependencies) {
1677 if (expected_chunk[par] || expected_chunk[chl]) {
1678 expected_chunk.Set(par);
1679 expected_chunk.Set(chl);
1683 if (old == expected_chunk) {
1684 assert(expected_chunk.Count() == active_dep_count + 1);
1688 assert(chunk_info.transactions == expected_chunk);
1705 const auto& tx_data =
m_tx_data[tx_idx];
1713 assert(tx_data.active_children.IsSubsetOf(tx_data.children));
1715 for (
auto child_idx : tx_data.active_children) {
1724 for (
const auto& [par_idx, chl_idx] : active_dependencies) {
1729 SetType expected_top = SetType::Singleton(par_idx);
1731 auto old = expected_top;
1732 size_t active_dep_count{0};
1733 for (
const auto& [par2_idx, chl2_idx] : active_dependencies) {
1734 if (par_idx == par2_idx && chl_idx == chl2_idx)
continue;
1735 if (expected_top[par2_idx] || expected_top[chl2_idx]) {
1736 expected_top.Set(par2_idx);
1737 expected_top.Set(chl2_idx);
1741 if (old == expected_top) {
1742 assert(expected_top.Count() == active_dep_count + 1);
1746 assert(!expected_top[chl_idx]);
1748 assert(dep_top_info.transactions == expected_top);
1750 assert(dep_top_info.feerate ==
m_depgraph.FeeRate(dep_top_info.transactions));
1756 SetType suboptimal_idxs;
1759 assert(!suboptimal_idxs[chunk_idx]);
1760 suboptimal_idxs.Set(chunk_idx);
1767 SetType nonminimal_idxs;
1771 assert(!nonminimal_idxs[chunk_idx]);
1772 nonminimal_idxs.Set(chunk_idx);
1798template<
typename SetType>
1799std::tuple<std::vector<DepGraphIndex>, bool, uint64_t>
Linearize(
1804 std::span<const DepGraphIndex> old_linearization = {},
1805 bool is_topological =
true)
noexcept
1808 SpanningForestState forest(depgraph, rng_seed);
1809 if (!old_linearization.empty()) {
1810 forest.LoadLinearization(old_linearization);
1811 if (!is_topological) forest.MakeTopological();
1813 forest.MakeTopological();
1817 if (forest.GetCost() < max_cost) {
1818 forest.StartOptimizing();
1820 if (!forest.OptimizeStep())
break;
1821 }
while (forest.GetCost() < max_cost);
1825 bool optimal =
false;
1826 if (forest.GetCost() < max_cost) {
1827 forest.StartMinimizing();
1829 if (!forest.MinimizeStep()) {
1833 }
while (forest.GetCost() < max_cost);
1835 return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()};
1854template<
typename SetType>
1948 for (
int pass = 0; pass < 2; ++pass) {
1949 int rev = !(pass & 1);
1951 entries[SENTINEL].prev_group = SENTINEL;
1957 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1961 entries[cur_group].group = SetType::Singleton(idx);
1963 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1964 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1965 entries[cur_group].prev_tx = NO_PREV_TX;
1966 entries[cur_group].first_tx = cur_group;
1968 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1969 entries[SENTINEL].prev_group = cur_group;
1975 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1978 Assume(cur_group == entries[next_group].prev_group);
1979 Assume(prev_group == entries[cur_group].prev_group);
1983 Assume(cur_group != SENTINEL);
1984 Assume(prev_group != SENTINEL);
1985 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1989 entries[cur_group].group |= entries[prev_group].group;
1990 entries[cur_group].deps |= entries[prev_group].deps;
1991 entries[cur_group].feerate += entries[prev_group].feerate;
1993 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1995 entries[cur_group].first_tx = entries[prev_group].first_tx;
1997 prev_group = entries[prev_group].prev_group;
1998 entries[cur_group].prev_group = prev_group;
2001 DepGraphIndex preprev_group = entries[prev_group].prev_group;
2004 entries[next_group].prev_group = prev_group;
2005 entries[prev_group].prev_group = cur_group;
2006 entries[cur_group].prev_group = preprev_group;
2009 next_group = prev_group;
2010 prev_group = preprev_group;
2018 while (cur_group != SENTINEL) {
2024 *(linearization.begin() + (done++)) = cur_tx - 1;
2025 cur_tx = entries[cur_tx].prev_tx;
2026 }
while (cur_tx != NO_PREV_TX);
2029 *(linearization.end() - (++done)) = cur_tx - 1;
2030 cur_tx = entries[cur_tx].prev_tx;
2031 }
while (cur_tx != NO_PREV_TX);
2033 cur_group = entries[cur_group].prev_group;
2035 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.
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, 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 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.