5 #ifndef BITCOIN_CLUSTER_LINEARIZE_H 6 #define BITCOIN_CLUSTER_LINEARIZE_H 29 template<
typename SetType>
46 Entry() noexcept = default;
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;
360 template<
typename SetType>
387 feerate += depgraph.FeeRate(pos);
418 swap(a.transactions, b.transactions);
419 swap(a.feerate, b.feerate);
427 template<
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));
447 template<
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));
466 template<
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;
721 template<
typename SetType,
typename CostModel = SFLDefaultCostModel>
732 using SetIdx = std::conditional_t<(SetType::Size() <= 0xff),
734 std::conditional_t<(SetType::Size() <= 0xffff),
793 for (
auto tx_idx : tx_idxs) {
794 if (pos == 0)
return tx_idx;
804 std::pair<SetType, SetType>
GetReachable(
const SetType& tx_idxs)
const noexcept
806 SetType parents, children;
807 for (
auto tx_idx : tx_idxs) {
809 parents |= tx_data.parents;
810 children |= tx_data.children;
812 return {parents - tx_idxs, children - tx_idxs};
821 auto& parent_data =
m_tx_data[parent_idx];
823 Assume(parent_data.children[child_idx]);
824 Assume(!parent_data.active_children[child_idx]);
828 auto parent_chunk_idx = parent_data.chunk_idx;
829 auto child_chunk_idx = child_data.chunk_idx;
830 Assume(parent_chunk_idx != child_chunk_idx);
833 auto& top_info =
m_set_info[parent_chunk_idx];
834 auto& bottom_info =
m_set_info[child_chunk_idx];
854 for (
auto tx_idx : top_info.transactions) {
856 tx_data.chunk_idx = child_chunk_idx;
857 for (
auto dep_child_idx : tx_data.active_children) {
858 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
859 if (dep_top_info.transactions[parent_idx]) dep_top_info |= bottom_info;
864 for (
auto tx_idx : bottom_info.transactions) {
866 for (
auto dep_child_idx : tx_data.active_children) {
867 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
868 if (dep_top_info.transactions[child_idx]) dep_top_info |= top_info;
872 bottom_info |= top_info;
877 m_reachable[child_chunk_idx].first -= bottom_info.transactions;
878 m_reachable[child_chunk_idx].second -= bottom_info.transactions;
880 parent_data.dep_top_idx[child_idx] = parent_chunk_idx;
881 parent_data.active_children.Set(child_idx);
884 m_cost.ActivateEnd(bottom_info.transactions.Count() - 1);
885 return child_chunk_idx;
894 auto& parent_data =
m_tx_data[parent_idx];
895 Assume(parent_data.children[child_idx]);
896 Assume(parent_data.active_children[child_idx]);
899 auto parent_chunk_idx = parent_data.dep_top_idx[child_idx];
900 auto child_chunk_idx = parent_data.chunk_idx;
901 Assume(parent_chunk_idx != child_chunk_idx);
904 auto& top_info =
m_set_info[parent_chunk_idx];
905 auto& bottom_info =
m_set_info[child_chunk_idx];
908 parent_data.active_children.Reset(child_idx);
910 auto ntx = bottom_info.transactions.Count();
912 bottom_info -= top_info;
915 SetType top_parents, top_children;
916 for (
auto tx_idx : top_info.transactions) {
918 tx_data.chunk_idx = parent_chunk_idx;
919 top_parents |= tx_data.parents;
920 top_children |= tx_data.children;
921 for (
auto dep_child_idx : tx_data.active_children) {
922 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
923 if (dep_top_info.transactions[parent_idx]) dep_top_info -= bottom_info;
926 SetType bottom_parents, bottom_children;
927 for (
auto tx_idx : bottom_info.transactions) {
929 bottom_parents |= tx_data.parents;
930 bottom_children |= tx_data.children;
931 for (
auto dep_child_idx : tx_data.active_children) {
932 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[dep_child_idx]];
933 if (dep_top_info.transactions[child_idx]) dep_top_info -= top_info;
938 m_reachable[parent_chunk_idx].first = top_parents - top_info.transactions;
939 m_reachable[parent_chunk_idx].second = top_children - top_info.transactions;
940 m_reachable[child_chunk_idx].first = bottom_parents - bottom_info.transactions;
941 m_reachable[child_chunk_idx].second = bottom_children - bottom_info.transactions;
943 m_cost.DeactivateEnd(ntx - 1);
944 return {parent_chunk_idx, child_chunk_idx};
951 m_cost.MergeChunksBegin();
955 auto& bottom_chunk_info =
m_set_info[bottom_idx];
957 unsigned num_deps{0};
958 for (
auto tx_idx : top_chunk_info.transactions) {
960 num_deps += (tx_data.children & bottom_chunk_info.transactions).Count();
962 m_cost.MergeChunksMid(top_chunk_info.transactions.Count());
966 unsigned num_steps = 0;
967 for (
auto tx_idx : top_chunk_info.transactions) {
970 auto intersect = tx_data.children & bottom_chunk_info.transactions;
971 auto count = intersect.Count();
973 for (
auto child_idx : intersect) {
975 m_cost.MergeChunksEnd(num_steps);
991 template<
bool DownWard>
994 if constexpr (DownWard) {
1002 template<
bool DownWard>
1005 m_cost.PickMergeCandidateBegin();
1017 FeeFrac best_other_chunk_feerate = chunk_info.feerate;
1022 uint64_t best_other_chunk_tiebreak{0};
1027 while (todo.Any()) {
1030 auto reached_chunk_idx =
m_tx_data[todo.First()].chunk_idx;
1031 auto& reached_chunk_info =
m_set_info[reached_chunk_idx];
1032 todo -= reached_chunk_info.transactions;
1034 auto cmp = DownWard ? FeeRateCompare(best_other_chunk_feerate, reached_chunk_info.feerate)
1035 : FeeRateCompare(reached_chunk_info.feerate, best_other_chunk_feerate);
1036 if (cmp > 0)
continue;
1038 if (cmp < 0 || tiebreak >= best_other_chunk_tiebreak) {
1039 best_other_chunk_feerate = reached_chunk_info.feerate;
1040 best_other_chunk_idx = reached_chunk_idx;
1041 best_other_chunk_tiebreak = tiebreak;
1046 m_cost.PickMergeCandidateEnd(steps);
1047 return best_other_chunk_idx;
1052 template<
bool DownWard>
1055 auto merge_chunk_idx = PickMergeCandidate<DownWard>(chunk_idx);
1057 chunk_idx = MergeChunksDirected<DownWard>(chunk_idx, merge_chunk_idx);
1063 template<
bool DownWard>
1068 auto merged_chunk_idx = MergeStep<DownWard>(chunk_idx);
1070 chunk_idx = merged_chunk_idx;
1085 auto [parent_chunk_idx, child_chunk_idx] =
Deactivate(parent_idx, child_idx);
1091 const auto& parent_reachable =
m_reachable[parent_chunk_idx].first;
1092 const auto& child_chunk_txn =
m_set_info[child_chunk_idx].transactions;
1093 if (parent_reachable.Overlaps(child_chunk_txn)) {
1102 auto merged_chunk_idx =
MergeChunks(child_chunk_idx, parent_chunk_idx);
1109 MergeSequence<false>(parent_chunk_idx);
1111 MergeSequence<true>(child_chunk_idx);
1118 m_cost.PickChunkToOptimizeBegin();
1128 m_cost.PickChunkToOptimizeEnd(steps);
1135 m_cost.PickChunkToOptimizeEnd(steps);
1142 m_cost.PickDependencyToSplitBegin();
1147 std::pair<TxIdx, TxIdx> candidate_dep = {
TxIdx(-1),
TxIdx(-1)};
1148 uint64_t candidate_tiebreak = 0;
1150 for (
auto tx_idx : chunk_info.transactions) {
1151 const auto& tx_data =
m_tx_data[tx_idx];
1153 for (
auto child_idx : tx_data.active_children) {
1154 auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[child_idx]];
1157 auto cmp = FeeRateCompare(dep_top_info.feerate, chunk_info.feerate);
1158 if (cmp <= 0)
continue;
1163 if (tiebreak < candidate_tiebreak)
continue;
1165 candidate_dep = {tx_idx, child_idx};
1166 candidate_tiebreak = tiebreak;
1169 m_cost.PickDependencyToSplitEnd(chunk_info.transactions.Count());
1170 return candidate_dep;
1179 m_cost.InitializeBegin();
1182 m_tx_data.resize(depgraph.PositionRange());
1185 size_t num_chunks = 0;
1186 size_t num_deps = 0;
1190 tx_data.parents = depgraph.GetReducedParents(tx_idx);
1191 for (
auto parent_idx : tx_data.parents) {
1192 m_tx_data[parent_idx].children.Set(tx_idx);
1194 num_deps += tx_data.parents.Count();
1196 tx_data.chunk_idx = num_chunks;
1197 m_set_info[num_chunks++] = SetInfo(depgraph, tx_idx);
1200 for (
SetIdx chunk_idx = 0; chunk_idx < num_transactions; ++chunk_idx) {
1205 Assume(num_chunks == num_transactions);
1208 m_cost.InitializeEnd(num_chunks, num_deps);
1218 auto chunk_idx =
m_tx_data[tx_idx].chunk_idx;
1221 chunk_idx = MergeStep<false>(chunk_idx);
1230 m_cost.MakeTopologicalBegin();
1240 SetType merged_chunks;
1264 unsigned direction = merged_chunks[chunk_idx] ? 3 : init_dir + 1;
1266 for (
int i = 0; i < 2; ++i) {
1268 if (!(direction & 1))
continue;
1270 auto result_up = MergeStep<false>(chunk_idx);
1276 merged_chunks.Set(result_up);
1280 if (!(direction & 2))
continue;
1282 auto result_down = MergeStep<true>(chunk_idx);
1288 merged_chunks.Set(result_down);
1294 m_cost.MakeTopologicalEnd(chunks, steps);
1300 m_cost.StartOptimizingBegin();
1324 if (parent_idx ==
TxIdx(-1)) {
1330 Improve(parent_idx, child_idx);
1338 m_cost.StartMinimizingBegin();
1360 m_cost.MinimizeStepBegin();
1366 bool move_pivot_down =
flags & 1;
1368 bool second_stage =
flags & 2;
1372 std::pair<TxIdx, TxIdx> candidate_dep;
1373 uint64_t candidate_tiebreak{0};
1374 bool have_any =
false;
1376 for (
auto tx_idx : chunk_info.transactions) {
1377 const auto& tx_data =
m_tx_data[tx_idx];
1379 for (
auto child_idx : tx_data.active_children) {
1380 const auto& dep_top_info =
m_set_info[tx_data.dep_top_idx[child_idx]];
1384 if (dep_top_info.feerate << chunk_info.feerate)
continue;
1387 if (move_pivot_down == dep_top_info.transactions[pivot_idx])
continue;
1390 if (tiebreak > candidate_tiebreak) {
1391 candidate_tiebreak = tiebreak;
1392 candidate_dep = {tx_idx, child_idx};
1396 m_cost.MinimizeStepMid(chunk_info.transactions.Count());
1398 if (!have_any)
return true;
1401 if (candidate_tiebreak == 0) {
1409 auto [parent_chunk_idx, child_chunk_idx] =
Deactivate(candidate_dep.first, candidate_dep.second);
1412 auto& parent_reachable =
m_reachable[parent_chunk_idx].first;
1413 auto& child_chunk_txn =
m_set_info[child_chunk_idx].transactions;
1414 if (parent_reachable.Overlaps(child_chunk_txn)) {
1418 auto merged_chunk_idx =
MergeChunks(child_chunk_idx, parent_chunk_idx);
1422 m_cost.MinimizeStepEnd(
false);
1431 if (move_pivot_down) {
1443 m_cost.MinimizeStepEnd(
true);
1464 std::vector<DepGraphIndex>
GetLinearization(
const StrongComparator<DepGraphIndex>
auto& fallback_order) noexcept
1466 m_cost.GetLinearizationBegin();
1468 std::vector<DepGraphIndex>
ret;
1473 std::vector<std::pair<SetIdx, TxIdx>> ready_chunks;
1476 std::vector<TxIdx> chunk_deps(
m_set_info.size(), 0);
1479 std::vector<TxIdx> tx_deps(
m_tx_data.size(), 0);
1482 std::vector<TxIdx> ready_tx;
1484 unsigned num_deps{0};
1486 const auto& chl_data =
m_tx_data[chl_idx];
1487 tx_deps[chl_idx] = chl_data.parents.Count();
1488 num_deps += tx_deps[chl_idx];
1489 auto chl_chunk_idx = chl_data.chunk_idx;
1490 auto& chl_chunk_info =
m_set_info[chl_chunk_idx];
1491 chunk_deps[chl_chunk_idx] += (chl_data.parents - chl_chunk_info.transactions).Count();
1494 auto max_fallback_fn = [&](
SetIdx chunk_idx) noexcept {
1495 auto& chunk =
m_set_info[chunk_idx].transactions;
1496 auto it = chunk.begin();
1499 while (it != chunk.end()) {
1500 if (fallback_order(*it,
ret) > 0)
ret = *it;
1507 auto tx_cmp_fn = [&](
const auto& a,
const auto& b) noexcept {
1509 if (a == b)
return false;
1513 auto feerate_cmp = FeeRateCompare(a_feerate, b_feerate);
1514 if (feerate_cmp != 0)
return feerate_cmp < 0;
1516 if (a_feerate.size != b_feerate.size) {
1517 return a_feerate.size > b_feerate.size;
1520 auto fallback_cmp = fallback_order(a, b);
1521 if (fallback_cmp != 0)
return fallback_cmp > 0;
1529 auto chunk_cmp_fn = [&](
const auto& a,
const auto& b) noexcept {
1531 if (a.first == b.first)
return false;
1533 auto& chunk_feerate_a =
m_set_info[a.first].feerate;
1534 auto& chunk_feerate_b =
m_set_info[b.first].feerate;
1535 auto feerate_cmp = FeeRateCompare(chunk_feerate_a, chunk_feerate_b);
1536 if (feerate_cmp != 0)
return feerate_cmp < 0;
1538 if (chunk_feerate_a.size != chunk_feerate_b.size) {
1539 return chunk_feerate_a.size > chunk_feerate_b.size;
1542 auto fallback_cmp = fallback_order(a.second, b.second);
1543 if (fallback_cmp != 0)
return fallback_cmp > 0;
1546 return a.second < b.second;
1550 if (chunk_deps[chunk_idx] == 0) {
1551 ready_chunks.emplace_back(chunk_idx, max_fallback_fn(chunk_idx));
1554 std::make_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1556 while (!ready_chunks.empty()) {
1557 auto [chunk_idx, _rnd] = ready_chunks.front();
1558 std::pop_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1559 ready_chunks.pop_back();
1560 Assume(chunk_deps[chunk_idx] == 0);
1561 const auto& chunk_txn =
m_set_info[chunk_idx].transactions;
1563 Assume(ready_tx.empty());
1564 for (
TxIdx tx_idx : chunk_txn) {
1565 if (tx_deps[tx_idx] == 0) ready_tx.push_back(tx_idx);
1567 Assume(!ready_tx.empty());
1568 std::make_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
1571 while (!ready_tx.empty()) {
1573 auto tx_idx = ready_tx.front();
1574 std::pop_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
1575 ready_tx.pop_back();
1577 ret.push_back(tx_idx);
1580 for (
TxIdx chl_idx : tx_data.children) {
1583 Assume(tx_deps[chl_idx] > 0);
1584 if (--tx_deps[chl_idx] == 0 && chunk_txn[chl_idx]) {
1586 ready_tx.push_back(chl_idx);
1587 std::push_heap(ready_tx.begin(), ready_tx.end(), tx_cmp_fn);
1590 if (chl_data.chunk_idx != chunk_idx) {
1591 Assume(chunk_deps[chl_data.chunk_idx] > 0);
1592 if (--chunk_deps[chl_data.chunk_idx] == 0) {
1594 ready_chunks.emplace_back(chl_data.chunk_idx, max_fallback_fn(chl_data.chunk_idx));
1595 std::push_heap(ready_chunks.begin(), ready_chunks.end(), chunk_cmp_fn);
1621 std::vector<FeeFrac>
ret;
1625 std::sort(
ret.begin(),
ret.end(), std::greater{});
1638 std::vector<std::pair<TxIdx, TxIdx>> expected_dependencies;
1639 std::vector<std::pair<TxIdx, TxIdx>> all_dependencies;
1640 std::vector<std::pair<TxIdx, TxIdx>> active_dependencies;
1641 for (
auto parent_idx :
m_depgraph.Positions()) {
1642 for (
auto child_idx :
m_depgraph.GetReducedChildren(parent_idx)) {
1643 expected_dependencies.emplace_back(parent_idx, child_idx);
1647 for (
auto child_idx :
m_tx_data[tx_idx].children) {
1648 all_dependencies.emplace_back(tx_idx, child_idx);
1649 if (
m_tx_data[tx_idx].active_children[child_idx]) {
1650 active_dependencies.emplace_back(tx_idx, child_idx);
1654 std::sort(expected_dependencies.begin(), expected_dependencies.end());
1655 std::sort(all_dependencies.begin(), all_dependencies.end());
1656 assert(expected_dependencies == all_dependencies);
1661 SetType chunk_cover;
1663 const auto& chunk_info =
m_set_info[chunk_idx];
1666 for (
auto tx_idx : chunk_info.transactions) {
1669 assert(!chunk_cover.Overlaps(chunk_info.transactions));
1670 chunk_cover |= chunk_info.transactions;
1675 assert(chunk_info.transactions.Any());
1676 SetType expected_chunk = SetType::Singleton(chunk_info.transactions.First());
1678 auto old = expected_chunk;
1679 size_t active_dep_count{0};
1680 for (
const auto& [par, chl] : active_dependencies) {
1681 if (expected_chunk[par] || expected_chunk[chl]) {
1682 expected_chunk.Set(par);
1683 expected_chunk.Set(chl);
1687 if (old == expected_chunk) {
1688 assert(expected_chunk.Count() == active_dep_count + 1);
1692 assert(chunk_info.transactions == expected_chunk);
1709 const auto& tx_data =
m_tx_data[tx_idx];
1717 assert(tx_data.active_children.IsSubsetOf(tx_data.children));
1719 for (
auto child_idx : tx_data.active_children) {
1728 for (
const auto& [par_idx, chl_idx] : active_dependencies) {
1733 SetType expected_top = SetType::Singleton(par_idx);
1735 auto old = expected_top;
1736 size_t active_dep_count{0};
1737 for (
const auto& [par2_idx, chl2_idx] : active_dependencies) {
1738 if (par_idx == par2_idx && chl_idx == chl2_idx)
continue;
1739 if (expected_top[par2_idx] || expected_top[chl2_idx]) {
1740 expected_top.Set(par2_idx);
1741 expected_top.Set(chl2_idx);
1745 if (old == expected_top) {
1746 assert(expected_top.Count() == active_dep_count + 1);
1750 assert(!expected_top[chl_idx]);
1752 assert(dep_top_info.transactions == expected_top);
1754 assert(dep_top_info.feerate ==
m_depgraph.FeeRate(dep_top_info.transactions));
1760 SetType suboptimal_idxs;
1763 assert(!suboptimal_idxs[chunk_idx]);
1764 suboptimal_idxs.Set(chunk_idx);
1771 SetType nonminimal_idxs;
1775 assert(!nonminimal_idxs[chunk_idx]);
1776 nonminimal_idxs.Set(chunk_idx);
1802 template<
typename SetType>
1803 std::tuple<std::vector<DepGraphIndex>, bool, uint64_t>
Linearize(
1807 const StrongComparator<DepGraphIndex>
auto& fallback_order,
1808 std::span<const DepGraphIndex> old_linearization = {},
1809 bool is_topological =
true) noexcept
1812 SpanningForestState forest(depgraph, rng_seed);
1813 if (!old_linearization.empty()) {
1814 forest.LoadLinearization(old_linearization);
1815 if (!is_topological) forest.MakeTopological();
1817 forest.MakeTopological();
1821 if (forest.GetCost() < max_cost) {
1822 forest.StartOptimizing();
1824 if (!forest.OptimizeStep())
break;
1825 }
while (forest.GetCost() < max_cost);
1829 bool optimal =
false;
1830 if (forest.GetCost() < max_cost) {
1831 forest.StartMinimizing();
1833 if (!forest.MinimizeStep()) {
1837 }
while (forest.GetCost() < max_cost);
1839 return {forest.GetLinearization(fallback_order), optimal, forest.GetCost()};
1858 template<
typename SetType>
1952 for (
int pass = 0; pass < 2; ++pass) {
1953 int rev = !(pass & 1);
1955 entries[SENTINEL].prev_group = SENTINEL;
1961 DepGraphIndex idx = linearization[rev ? linearization.size() - 1 - i : i];
1965 entries[cur_group].group = SetType::Singleton(idx);
1967 entries[cur_group].feerate = depgraph.
FeeRate(idx);
1968 if (rev) entries[cur_group].feerate.
fee = -entries[cur_group].feerate.fee;
1969 entries[cur_group].prev_tx = NO_PREV_TX;
1970 entries[cur_group].first_tx = cur_group;
1972 entries[cur_group].prev_group = entries[SENTINEL].prev_group;
1973 entries[SENTINEL].prev_group = cur_group;
1979 while (entries[cur_group].feerate >> entries[prev_group].feerate) {
1982 Assume(cur_group == entries[next_group].prev_group);
1983 Assume(prev_group == entries[cur_group].prev_group);
1987 Assume(cur_group != SENTINEL);
1988 Assume(prev_group != SENTINEL);
1989 if (entries[cur_group].deps.Overlaps(entries[prev_group].group)) {
1993 entries[cur_group].group |= entries[prev_group].group;
1994 entries[cur_group].deps |= entries[prev_group].deps;
1995 entries[cur_group].feerate += entries[prev_group].feerate;
1997 entries[entries[cur_group].first_tx].prev_tx = prev_group;
1999 entries[cur_group].first_tx = entries[prev_group].first_tx;
2001 prev_group = entries[prev_group].prev_group;
2002 entries[cur_group].prev_group = prev_group;
2005 DepGraphIndex preprev_group = entries[prev_group].prev_group;
2008 entries[next_group].prev_group = prev_group;
2009 entries[prev_group].prev_group = cur_group;
2010 entries[cur_group].prev_group = preprev_group;
2013 next_group = prev_group;
2014 prev_group = preprev_group;
2022 while (cur_group != SENTINEL) {
2028 *(linearization.begin() + (done++)) = cur_tx - 1;
2029 cur_tx = entries[cur_tx].prev_tx;
2030 }
while (cur_tx != NO_PREV_TX);
2033 *(linearization.end() - (++done)) = cur_tx - 1;
2034 cur_tx = entries[cur_tx].prev_tx;
2035 }
while (cur_tx != NO_PREV_TX);
2037 cur_group = entries[cur_group].prev_group;
2039 Assume(done == linearization.size());
2045 #endif // BITCOIN_CLUSTER_LINEARIZE_H void StartMinimizingEnd(int num_chunks) noexcept
void AppendTopo(std::vector< DepGraphIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
std::vector< DepGraphIndex > GetLinearization(const StrongComparator< DepGraphIndex > auto &fallback_order) noexcept
Construct a topologically-valid linearization from the current forest state.
A set of transactions together with their aggregate feerate.
SetType descendants
All descendants of the transaction (including itself).
std::pair< TxIdx, TxIdx > PickDependencyToSplit(SetIdx chunk_idx) noexcept
Find a (parent, child) dependency to deactivate in chunk_idx, or (-1, -1) if none.
size_t size() const noexcept
Get the number of elements in this deque.
void PickChunkToOptimizeEnd(int num_steps) noexcept
bool randbool() noexcept
Generate a random boolean.
void MakeTopologicalEnd(int num_chunks, int num_steps) noexcept
friend bool operator==(const SetInfo &, const SetInfo &) noexcept=default
Permit equality testing.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
std::vector< SetInfo< SetType > > m_set_info
Information about each set (chunk, or active dependency top set).
void MergeChunksBegin() noexcept
void PickChunkToOptimizeBegin() noexcept
void Set(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Add a transaction to this SetInfo (which must not yet be in it).
void SanityCheck() const
Verify internal consistency of the data structure.
constexpr uint64_t rand64() noexcept
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
FeeFrac & FeeRate(DepGraphIndex i) noexcept
Get the mutable feerate of a given transaction i.
void StartMinimizingBegin() noexcept
FeeFrac feerate
Their combined fee and size.
std::array< SetIdx, SetType::Size()> dep_top_idx
The top set for every active child dependency this transaction has, indexed by child TxIdx...
concept StrongComparator
Concept for function objects that return std::strong_ordering when invoked with two Args...
TxIdx PickRandomTx(const SetType &tx_idxs) noexcept
Pick a random transaction within a set (which must be non-empty).
VecDeque< SetIdx > m_suboptimal_chunks
A FIFO of chunk SetIdxs for chunks that may be improved still.
static size_t DynamicUsage(const int8_t &v)
Dynamic memory usage for built-in types is zero.
T & front() noexcept
Get a mutable reference to the first element of the deque.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
friend bool operator==(const Entry &, const Entry &) noexcept=default
Equality operator (primarily for testing purposes).
FeeFrac feerate
Fee and size of transaction itself.
FeeFrac FeeRate(const SetType &elems) const noexcept
Compute the aggregate feerate of a set of nodes in this graph.
SetInfo(const DepGraph< SetType > &depgraph, const SetType &txn) noexcept
Construct a SetInfo for a set of transactions in a depgraph.
T & back() noexcept
Get a mutable reference to the last element of the deque.
friend void swap(SetInfo &a, SetInfo &b) noexcept
Swap two SetInfo objects.
std::vector< TxData > m_tx_data
Information about each transaction (and chunks).
void MinimizeStepMid(int num_txns) noexcept
void DeactivateEnd(int num_deps) noexcept
std::vector< std::pair< SetType, SetType > > m_reachable
For each chunk, indexed by SetIdx, the set of out-of-chunk reachable transactions, in the upwards (.first) and downwards (.second) direction.
void InitializeBegin() noexcept
Entry() noexcept=default
Construct an empty entry.
SetIdx chunk_idx
Which chunk this transaction belongs to.
void MergeSequence(SetIdx chunk_idx) noexcept
Perform an upward or downward merge sequence on the specified chunk.
bool empty() const noexcept
Test whether the contents of this deque is empty.
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.
size_t DynamicMemoryUsage() const noexcept
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
SetType m_chunk_idxs
The set of all chunk SetIdx's.
void PickMergeCandidateBegin() noexcept
SetType active_children
The set of child transactions reachable through an active dependency.
void PostLinearize(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization)
Improve a given linearization.
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)...
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: ...
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...
void MinimizeStepBegin() noexcept
SetType m_used
Which positions are used.
void AddDependencies(const SetType &parents, DepGraphIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
DepGraphIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (in the first available position)...
void DeactivateBegin() noexcept
void emplace_back(Args &&... args)
Construct a new element at the end of the deque.
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.
Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
SetIdx MergeChunks(SetIdx top_idx, SetIdx bottom_idx) noexcept
Activate a dependency from the bottom set to the top set, which must exist.
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...
bool OptimizeStep() noexcept
Try to improve the forest.
bool IsAcyclic() const noexcept
Check if this graph is acyclic.
void reserve(size_t capacity)
Increase the capacity to capacity.
std::vector< Entry > entries
Data for each transaction.
void ActivateBegin() noexcept
bool IsConnected() const noexcept
Determine if this entire graph is connected.
SetInfo & operator-=(const SetInfo &other) noexcept
Remove the transactions of other from this SetInfo (which must be a subset).
std::compare_three_way IndexTxOrder
Simple default transaction ordering function for SpanningForestState::GetLinearization() and Lineariz...
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
uint64_t GetCost() const noexcept
Determine how much work was performed so far.
static constexpr SetIdx INVALID_SET_IDX
An invalid SetIdx.
#define Assume(val)
Assume is the identity function.
std::pair< SetIdx, SetIdx > Deactivate(TxIdx parent_idx, TxIdx child_idx) noexcept
Make a specified active dependency inactive.
SetType transactions
The transactions in the set.
SetType parents
The set of parent transactions of this transaction.
Structure with information about a single transaction.
void pop_front()
Remove the first element of the deque.
void GetLinearizationBegin() noexcept
SetIdx PickChunkToOptimize() noexcept
Determine the next chunk to optimize, or INVALID_SET_IDX if none.
void MakeTopologicalBegin() noexcept
std::vector< FeeFrac > GetDiagram() const noexcept
Get the diagram for the current state, which must be topological.
Data structure storing a fee and size, ordered by increasing fee/size.
SetType ancestors
All ancestors of the transaction (including itself).
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...
void Compact() noexcept
Reduce memory usage if possible.
void LoadLinearization(std::span< const DepGraphIndex > old_linearization) noexcept
Load an existing linearization.
void clear() noexcept
Resize the deque to be size 0.
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
uint64_t GetCost() const noexcept
void InitializeEnd(int num_txns, int num_deps) noexcept
bool MinimizeStep() noexcept
Try to reduce a chunk's size.
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors, descendants).
SetIdx PickMergeCandidate(SetIdx chunk_idx) noexcept
Determine which chunk to merge chunk_idx with, or INVALID_SET_IDX if none.
void StartOptimizingBegin() noexcept
DepGraph() noexcept=default
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
void PickDependencyToSplitEnd(int num_txns) noexcept
unsigned CountDependencies() const noexcept
DepGraphIndex PositionRange() const noexcept
Get the range of positions in this DepGraph.
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \)
void GetLinearizationEnd(int num_txns, int num_deps) noexcept
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...
DepGraphIndex TxIdx
Data type to represent indexing into m_tx_data.
const SetType & Ancestors(DepGraphIndex i) const noexcept
Get the ancestors of a given transaction i.
A default cost model for SFL for SetType=BitSet<64>, based on benchmarks.
CostModel m_cost
Accounting for the cost of this computation.
void PickMergeCandidateEnd(int num_steps) noexcept
InsecureRandomContext m_rng
Internal RNG.
void StartOptimizing() noexcept
Initialize the data structure for optimization.
SetInfo(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Construct a SetInfo for a given transaction in a depgraph.
SetType m_transaction_idxs
The set of all TxIdx's of transactions in the cluster indexing into m_tx_data.
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...
SetType children
The set of child transactions of this transaction.
void MinimizeStepEnd(bool split) noexcept
void PickDependencyToSplitBegin() noexcept
void StartMinimizing() noexcept
Initialize data structure for minimizing the chunks.
Information about a single transaction.
void ActivateEnd(int num_deps) noexcept
SetInfo() noexcept=default
Construct a SetInfo for the empty set.
SetInfo & operator|=(const SetInfo &other) noexcept
Add the transactions of other to this SetInfo (no overlap allowed).
SetType m_suboptimal_idxs
The set of all SetIdx's that appear in m_suboptimal_chunks.
std::vector< SetInfo< SetType > > ChunkLinearizationInfo(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the chunks of linearization as SetInfos.
SetType GetReducedChildren(DepGraphIndex i) const noexcept
Compute the (reduced) set of children of node i in this graph.
void MakeTopological() noexcept
Make state topological.
void push_back(T &&elem)
Move-construct a new element at the end of the deque.
void MergeChunksEnd(int num_steps) noexcept
SetType GetReducedParents(DepGraphIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
void RemoveTransactions(const SetType &del) noexcept
Remove the specified positions from this DepGraph.
void StartOptimizingEnd(int num_chunks) noexcept
const DepGraph< SetType > & m_depgraph
The DepGraph we are trying to linearize.
void MergeChunksMid(int num_txns) noexcept
auto TxCount() const noexcept
Get the number of transactions in the graph.
SetIdx MergeStep(SetIdx chunk_idx) noexcept
Perform an upward or downward merge step, on the specified chunk.
friend bool operator==(const DepGraph &a, const DepGraph &b) noexcept
Equality operator (primarily for testing purposes).
SetInfo operator-(const SetInfo &other) const noexcept
Compute the difference between this and other SetInfo (which must be a subset).
const FeeFrac & FeeRate(DepGraphIndex i) const noexcept
Get the feerate of a given transaction i.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.