40 static constexpr auto MISSING = Pos(-1);
48 std::array<std::shared_ptr<TxGraph::Ref>, MAX_TRANSACTIONS> simmap;
50 std::map<const TxGraph::Ref*, Pos> simrevmap;
52 std::vector<std::shared_ptr<TxGraph::Ref>> removed;
54 std::optional<bool> oversized;
61 uint64_t max_cluster_size;
63 bool real_is_optimal{
false};
66 explicit SimTxGraph(
DepGraphIndex cluster_count, uint64_t cluster_size) :
67 max_cluster_count(cluster_count), max_cluster_size(cluster_size) {}
70 SimTxGraph(
const SimTxGraph&)
noexcept =
default;
71 SimTxGraph& operator=(
const SimTxGraph&)
noexcept =
default;
72 SimTxGraph(SimTxGraph&&) noexcept = default;
73 SimTxGraph& operator=(SimTxGraph&&) noexcept = default;
76 std::vector<SetType> GetComponents()
79 std::vector<SetType>
ret;
83 ret.push_back(component);
93 if (!oversized.has_value()) {
96 for (
auto component : GetComponents()) {
97 if (component.Count() > max_cluster_count) oversized =
true;
98 uint64_t component_size{0};
99 for (
auto i : component) component_size += graph.
FeeRate(i).
size;
100 if (component_size > max_cluster_size) oversized =
true;
127 auto it = simrevmap.find(ref);
128 if (it != simrevmap.end())
return it->second;
137 return simmap[pos].get();
145 real_is_optimal =
false;
146 MakeModified(simpos);
148 simmap[simpos] = std::make_shared<TxGraph::Ref>(std::move(ref));
149 auto ptr = simmap[simpos].get();
150 simrevmap[ptr] = simpos;
152 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
158 auto par_pos =
Find(parent);
159 if (par_pos ==
MISSING)
return;
160 auto chl_pos =
Find(child);
161 if (chl_pos ==
MISSING)
return;
163 MakeModified(par_pos);
164 real_is_optimal =
false;
166 if (oversized.has_value() && !*oversized) oversized = std::nullopt;
172 auto pos =
Find(ref);
175 real_is_optimal =
false;
182 auto pos =
Find(ref);
185 real_is_optimal =
false;
187 simrevmap.erase(simmap[pos].get());
190 removed.push_back(std::move(simmap[pos]));
193 if (oversized.has_value() && *oversized) oversized = std::nullopt;
200 void DestroyTransaction(
TxGraph::Ref* ref,
bool reset_oversize)
202 auto pos =
Find(ref);
207 auto remove = std::partition(removed.begin(), removed.end(), [&](
auto& arg) { return arg.get() != ref; });
208 removed.erase(remove, removed.end());
212 real_is_optimal =
false;
213 simrevmap.erase(simmap[pos].get());
216 if (reset_oversize && oversized.has_value() && *oversized) {
217 oversized = std::nullopt;
224 SetType MakeSet(std::span<TxGraph::Ref* const> arg)
228 auto pos =
Find(ptr);
238 auto pos =
Find(arg);
245 void IncludeAncDesc(std::vector<TxGraph::Ref*>& arg,
bool desc)
247 std::vector<TxGraph::Ref*>
ret;
248 for (
auto ptr : arg) {
249 auto simpos =
Find(ptr);
252 ret.push_back(simmap[i].get());
261 for (
auto ptr :
ret) {
262 if (std::find(arg.begin(), arg.end(), ptr) == arg.end()) {
271 bool MatchesOversizedClusters(
const SetType& set)
273 if (set.Any() && !IsOversized())
return false;
276 if (!set.IsSubsetOf(todo))
return false;
282 bool is_oversized = component.Count() > max_cluster_count;
283 uint64_t component_size{0};
284 for (
auto i : component) component_size += graph.
FeeRate(i).
size;
285 is_oversized |= component_size > max_cluster_size;
287 if (is_oversized != set.Overlaps(component))
return false;
323 auto real =
MakeTxGraph(max_cluster_count, max_cluster_size, acceptable_iters);
324 std::vector<SimTxGraph> sims;
326 sims.emplace_back(max_cluster_count, max_cluster_size);
329 struct BlockBuilderData
332 std::unique_ptr<TxGraph::BlockBuilder> builder;
334 SimTxGraph::SetType included;
336 SimTxGraph::SetType done;
340 BlockBuilderData(std::unique_ptr<TxGraph::BlockBuilder> builder_in) : builder(std::move(builder_in)) {}
344 std::vector<BlockBuilderData> block_builders;
349 size_t tx_count[2] = {sims[0].GetTransactionCount(), 0};
351 size_t choices = tx_count[0] + sims[0].removed.size() + 1;
352 if (sims.size() == 2) {
353 tx_count[1] = sims[1].GetTransactionCount();
354 choices += tx_count[1] + sims[1].removed.size();
359 for (
size_t level = 0; level < sims.size(); ++level) {
360 auto& sim = sims[level];
361 if (choice < tx_count[level]) {
363 for (
auto i : sim.graph.Positions()) {
364 if (choice == 0)
return sim.GetRef(i);
369 choice -= tx_count[level];
371 if (choice < sim.removed.size()) {
373 return sim.removed[choice].get();
375 choice -= sim.removed.size();
385 auto get_diagram_fn = [&](
TxGraph::Level level_select) -> std::vector<FeeFrac> {
387 auto& sim = sims[level];
389 std::set<std::vector<TxGraph::Ref*>> clusters;
390 for (
auto i : sim.graph.Positions()) {
391 auto ref = sim.GetRef(i);
392 clusters.insert(real->GetCluster(*ref, level_select));
396 std::vector<FeeFrac> chunk_feerates;
397 for (
const auto& cluster : clusters) {
398 num_tx += cluster.size();
399 std::vector<SimTxGraph::Pos> linearization;
400 linearization.reserve(cluster.size());
401 for (
auto refptr : cluster) linearization.push_back(sim.Find(refptr));
403 chunk_feerates.push_back(chunk_feerate);
409 assert(num_tx == sim.GetTransactionCount());
412 std::sort(chunk_feerates.begin(), chunk_feerates.end(), std::greater{});
413 return chunk_feerates;
431 int builder_idx = block_builders.empty() ? -1 : int((orig_command & 3) % block_builders.size());
436 auto& top_sim = sims.back();
438 auto& main_sim = sims[0];
443 if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() < SimTxGraph::MAX_TRANSACTIONS &&
command-- == 0) {
459 auto ref = real->AddTransaction(feerate);
461 top_sim.AddTransaction(std::move(ref), feerate);
463 }
else if ((block_builders.empty() || sims.size() > 1) && top_sim.GetTransactionCount() + top_sim.removed.size() > 1 &&
command-- == 0) {
465 auto par = pick_fn();
466 auto chl = pick_fn();
467 auto pos_par = top_sim.Find(par);
468 auto pos_chl = top_sim.Find(chl);
472 if (top_sim.graph.Ancestors(pos_par)[pos_chl])
break;
474 top_sim.AddDependency(par, chl);
475 top_sim.real_is_optimal =
false;
476 real->AddDependency(*par, *chl);
478 }
else if ((block_builders.empty() || sims.size() > 1) && top_sim.removed.size() < 100 &&
command-- == 0) {
482 std::vector<TxGraph::Ref*> to_remove;
483 to_remove.push_back(pick_fn());
484 top_sim.IncludeAncDesc(to_remove, alt);
487 std::shuffle(to_remove.begin(), to_remove.end(), rng);
489 real->RemoveTransaction(*ptr);
490 top_sim.RemoveTransaction(ptr);
493 }
else if (sel_sim.removed.size() > 0 &&
command-- == 0) {
503 if (removed_pos != sel_sim.removed.size() - 1) {
504 std::swap(sel_sim.removed[removed_pos], sel_sim.removed.back());
506 sel_sim.removed.pop_back();
508 }
else if (block_builders.empty() &&
command-- == 0) {
510 std::vector<TxGraph::Ref*> to_destroy;
511 to_destroy.push_back(pick_fn());
517 auto old_size = to_destroy.size();
518 for (
auto& sim : sims) sim.IncludeAncDesc(to_destroy, alt);
519 if (to_destroy.size() == old_size)
break;
523 std::shuffle(to_destroy.begin(), to_destroy.end(), rng);
525 for (
size_t level = 0; level < sims.size(); ++level) {
526 sims[level].DestroyTransaction(ptr, level == sims.size() - 1);
530 }
else if (block_builders.empty() &&
command-- == 0) {
538 auto ref = pick_fn();
539 real->SetTransactionFee(*ref,
fee);
540 for (
auto& sim : sims) {
541 sim.SetTransactionFee(ref,
fee);
546 assert(real->GetTransactionCount(level_select) == sel_sim.GetTransactionCount());
550 auto ref = pick_fn();
551 bool exists = real->Exists(*ref, level_select);
553 assert(exists == should_exist);
557 assert(sel_sim.IsOversized() == real->IsOversized(level_select));
561 auto ref = pick_fn();
562 auto feerate = real->GetIndividualFeerate(*ref);
564 for (
auto& sim : sims) {
565 auto simpos = sim.Find(ref);
568 assert(feerate == sim.graph.FeeRate(simpos));
573 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
575 auto ref = pick_fn();
576 auto feerate = real->GetMainChunkFeerate(*ref);
577 auto simpos = main_sim.Find(ref);
583 assert(feerate.
size >= main_sim.graph.FeeRate(simpos).size);
584 assert(feerate.
size <= main_sim.SumAll().size);
587 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
589 auto ref = pick_fn();
590 auto result = alt ? real->GetDescendants(*ref, level_select)
591 : real->GetAncestors(*ref, level_select);
592 assert(result.size() <= max_cluster_count);
593 auto result_set = sel_sim.MakeSet(result);
594 assert(result.size() == result_set.Count());
595 auto expect_set = sel_sim.GetAncDesc(ref, alt);
596 assert(result_set == expect_set);
598 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
600 std::vector<TxGraph::Ref*> refs;
604 for (
size_t i = 0; i <
count; ++i) {
608 std::shuffle(refs.begin(), refs.end(), rng);
610 auto result = alt ? real->GetDescendantsUnion(refs, level_select)
611 : real->GetAncestorsUnion(refs, level_select);
612 auto result_set = sel_sim.MakeSet(result);
613 assert(result.size() == result_set.Count());
615 SimTxGraph::SetType expect_set;
616 for (
TxGraph::Ref* ref : refs) expect_set |= sel_sim.GetAncDesc(ref, alt);
618 assert(result_set == expect_set);
620 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
622 auto ref = pick_fn();
623 auto result = real->GetCluster(*ref, level_select);
625 assert(result.size() <= max_cluster_count);
627 auto left = sel_sim.graph.Positions();
628 uint64_t total_size{0};
629 for (
auto refptr : result) {
630 auto simpos = sel_sim.Find(refptr);
631 total_size += sel_sim.graph.FeeRate(simpos).size;
635 assert(!sel_sim.graph.Ancestors(simpos).Overlaps(left));
638 assert(total_size <= max_cluster_size);
640 auto result_set = sel_sim.MakeSet(result);
641 assert(sel_sim.graph.IsConnected(result_set));
643 auto simpos = sel_sim.Find(ref);
645 assert(result_set[simpos]);
647 assert(result_set.None());
650 for (
auto i : result_set) {
651 assert(sel_sim.graph.Ancestors(i).IsSubsetOf(result_set));
652 assert(sel_sim.graph.Descendants(i).IsSubsetOf(result_set));
657 assert((sims.size() == 2) == real->HaveStaging());
659 }
else if (sims.size() < 2 &&
command-- == 0) {
661 sims.emplace_back(sims.back());
662 sims.back().modified = SimTxGraph::SetType{};
663 real->StartStaging();
665 }
else if (block_builders.empty() && sims.size() > 1 &&
command-- == 0) {
667 real->CommitStaging();
669 const bool main_optimal = std::all_of(sims.cbegin(), sims.cend(), [](
const auto &sim) { return sim.real_is_optimal; });
670 sims.erase(sims.begin());
671 sims.front().real_is_optimal = main_optimal;
673 }
else if (sims.size() > 1 &&
command-- == 0) {
675 real->AbortStaging();
680 sims.back().oversized = std::nullopt;
682 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
684 auto ref_a = pick_fn();
685 auto ref_b = pick_fn();
686 auto sim_a = main_sim.Find(ref_a);
687 auto sim_b = main_sim.Find(ref_b);
690 auto cmp = real->CompareMainOrder(*ref_a, *ref_b);
692 if (sim_a != sim_b)
assert(cmp != 0);
694 if (main_sim.graph.Ancestors(sim_a)[sim_b])
assert(cmp >= 0);
695 if (main_sim.graph.Descendants(sim_a)[sim_b])
assert(cmp <= 0);
700 }
else if (!sel_sim.IsOversized() &&
command-- == 0) {
702 std::vector<TxGraph::Ref*> refs;
706 for (
size_t i = 0; i <
count; ++i) {
710 std::shuffle(refs.begin(), refs.end(), rng);
712 auto result = real->CountDistinctClusters(refs, level_select);
716 SimTxGraph::SetType sim_reps;
717 for (
auto ref : refs) {
719 auto simpos = sel_sim.Find(ref);
722 auto component = sel_sim.graph.GetConnectedComponent(sel_sim.graph.Positions(), simpos);
725 sim_reps.Set(component.First());
729 assert(result == sim_reps.Count());
734 bool ret = real->DoWork(iters);
735 uint64_t iters_for_optimal{0};
736 for (
unsigned level = 0; level < sims.size(); ++level) {
741 if (sims[level].IsOversized())
continue;
742 if (level == 0 && !block_builders.empty())
continue;
746 sims[level].real_is_optimal =
true;
749 for (
auto component : sims[level].GetComponents()) {
750 auto iters_opt_this_cluster = MaxOptimalLinearizationIters(component.Count());
751 if (iters_opt_this_cluster > acceptable_iters) {
755 iters_for_optimal += iters_opt_this_cluster + acceptable_iters;
757 iters_for_optimal += iters_opt_this_cluster;
765 assert(iters <= iters_for_optimal);
768 }
else if (sims.size() == 2 && !sims[0].IsOversized() && !sims[1].IsOversized() &&
command-- == 0) {
770 auto [real_main_diagram, real_staged_diagram] = real->GetMainStagingDiagrams();
771 auto real_sum_main = std::accumulate(real_main_diagram.begin(), real_main_diagram.end(),
FeeFrac{});
772 auto real_sum_staged = std::accumulate(real_staged_diagram.begin(), real_staged_diagram.end(),
FeeFrac{});
773 auto real_gain = real_sum_staged - real_sum_main;
774 auto sim_gain = sims[1].SumAll() - sims[0].SumAll();
778 assert(sim_gain == real_gain);
780 for (
size_t i = 1; i < real_main_diagram.size(); ++i) {
781 assert(FeeRateCompare(real_main_diagram[i], real_main_diagram[i - 1]) <= 0);
783 for (
size_t i = 1; i < real_staged_diagram.size(); ++i) {
784 assert(FeeRateCompare(real_staged_diagram[i], real_staged_diagram[i - 1]) <= 0);
787 }
else if (block_builders.size() < 4 && !main_sim.IsOversized() &&
command-- == 0) {
789 block_builders.emplace_back(real->GetBlockBuilder());
791 }
else if (!block_builders.empty() &&
command-- == 0) {
793 block_builders.erase(block_builders.begin() + builder_idx);
795 }
else if (!block_builders.empty() &&
command-- == 0) {
797 auto& builder_data = block_builders[builder_idx];
798 auto new_included = builder_data.included;
799 auto new_done = builder_data.done;
800 auto chunk = builder_data.builder->GetCurrentChunk();
803 if (!builder_data.last_feerate.IsEmpty()) {
804 assert(!(chunk->second >> builder_data.last_feerate));
806 builder_data.last_feerate = chunk->second;
811 auto simpos = main_sim.Find(ref);
814 sum_feerate += main_sim.graph.FeeRate(simpos);
816 assert(!new_done[simpos]);
817 new_done.Set(simpos);
819 new_included.Set(simpos);
820 assert(main_sim.graph.Ancestors(simpos).IsSubsetOf(new_included));
822 assert(sum_feerate == chunk->second);
826 if (builder_data.done == builder_data.included) {
827 assert(builder_data.done.Count() == main_sim.GetTransactionCount());
831 if ((orig_command % 7) >= 5) {
832 auto chunk2 = builder_data.builder->GetCurrentChunk();
836 if ((orig_command % 5) >= 3) {
838 builder_data.builder->Skip();
841 builder_data.builder->Include();
842 builder_data.included = new_included;
844 builder_data.done = new_done;
846 }
else if (!main_sim.IsOversized() &&
command-- == 0) {
848 auto [worst_chunk, worst_chunk_feerate] = real->GetWorstMainChunk();
851 if (main_sim.GetTransactionCount() == 0) {
852 assert(worst_chunk.empty());
853 assert(worst_chunk_feerate.IsEmpty());
855 assert(!worst_chunk.empty());
856 SimTxGraph::SetType done;
860 auto simpos = main_sim.Find(ref);
862 sum += main_sim.graph.FeeRate(simpos);
867 assert(main_sim.graph.Descendants(simpos).IsSubsetOf(done));
872 }
else if ((block_builders.empty() || sims.size() > 1) &&
command-- == 0) {
874 bool was_oversized = top_sim.IsOversized();
875 auto removed = real->Trim();
877 assert(was_oversized == !removed.empty());
878 if (!was_oversized)
break;
879 auto removed_set = top_sim.MakeSet(removed);
881 for (
auto simpos : removed_set) {
882 assert(top_sim.graph.Descendants(simpos).IsSubsetOf(removed_set));
886 assert(top_sim.MatchesOversizedClusters(removed_set));
891 for (
auto simpos : removed_set) {
892 top_sim.RemoveTransaction(top_sim.GetRef(simpos));
894 assert(!top_sim.IsOversized());
896 }
else if ((block_builders.empty() || sims.size() > 1) &&
897 top_sim.GetTransactionCount() > max_cluster_count && !top_sim.IsOversized() &&
command-- == 0) {
908 auto clusters = top_sim.GetComponents();
911 bool made_oversized =
false;
912 auto merges_left = clusters.size() - 1;
913 while (merges_left > 0) {
916 auto par_cl = rng.
randrange(clusters.size());
917 auto chl_cl = rng.
randrange(clusters.size() - 1);
918 chl_cl += (chl_cl >= par_cl);
925 for (
int i = 0; i < num_deps; ++i) {
927 auto par_idx = rng.
randrange(clusters[par_cl].Count());
928 SimTxGraph::Pos par_pos = 0;
929 for (
auto j : clusters[par_cl]) {
937 auto chl_idx = rng.
randrange(clusters[chl_cl].Count());
938 SimTxGraph::Pos chl_pos = 0;
939 for (
auto j : clusters[chl_cl]) {
947 auto par_ref = top_sim.GetRef(par_pos);
948 auto chl_ref = top_sim.GetRef(chl_pos);
949 top_sim.AddDependency(par_ref, chl_ref);
950 real->AddDependency(*par_ref, *chl_ref);
953 auto par_cluster = clusters[par_cl];
954 auto chl_cluster = clusters[chl_cl];
955 auto new_cluster = par_cluster | chl_cluster;
957 std::erase_if(clusters, [&](
const auto& cl)
noexcept {
return cl == par_cluster || cl == chl_cluster; });
959 clusters.push_back(new_cluster);
962 if (!made_oversized) {
963 made_oversized = new_cluster.Count() > max_cluster_count;
964 if (!made_oversized) {
966 for (
auto i : new_cluster) total += top_sim.graph.FeeRate(i);
967 if (uint32_t(total.
size) > max_cluster_size) made_oversized =
true;
969 if (made_oversized) merges_left = rng.
randrange(clusters.size());
974 uint32_t max_removed = 0;
975 for (
auto& cluster : clusters) {
977 std::vector<uint32_t> sizes;
978 for (
auto i : cluster) sizes.push_back(top_sim.graph.FeeRate(i).size);
979 auto sum_sizes = std::accumulate(sizes.begin(), sizes.end(), uint64_t{0});
981 std::sort(sizes.begin(), sizes.end(), std::greater{});
983 while (sizes.size() > max_cluster_count || sum_sizes > max_cluster_size) {
984 sum_sizes -= sizes.back();
991 auto removed = real->Trim();
993 assert(removed.size() >= 1);
994 assert(removed.size() <= max_removed);
996 auto removed_set = top_sim.MakeSet(removed);
997 for (
auto simpos : removed_set) {
998 assert(top_sim.graph.Descendants(simpos).IsSubsetOf(removed_set));
1002 assert(top_sim.MatchesOversizedClusters(removed_set));
1007 for (
auto simpos : removed_set) {
1008 top_sim.RemoveTransaction(top_sim.GetRef(simpos));
1010 assert(!top_sim.IsOversized());
1014 auto usage = real->GetMainMemoryUsage();
1017 auto usage2 = real->GetMainMemoryUsage();
1021 if (main_sim.GetTransactionCount() == 0) {
1033 real->SanityCheck();
1035 if (!sims[0].IsOversized()) {
1039 std::vector<SimTxGraph::Pos> vec1;
1040 for (
auto i : sims[0].graph.
Positions()) vec1.push_back(i);
1041 std::shuffle(vec1.begin(), vec1.end(), rng);
1043 std::shuffle(vec2.begin(), vec2.end(), rng);
1044 if (vec1 == vec2) std::next_permutation(vec2.begin(), vec2.end());
1047 auto cmp = [&](SimTxGraph::Pos a, SimTxGraph::Pos b)
noexcept {
1048 return real->CompareMainOrder(*sims[0].GetRef(a), *sims[0].GetRef(b)) < 0;
1050 std::sort(vec1.begin(), vec1.end(), cmp);
1051 std::sort(vec2.begin(), vec2.end(), cmp);
1058 auto todo = sims[0].graph.Positions();
1059 for (
auto i : vec1) {
1067 if (sims[0].real_is_optimal) {
1069 auto [sim_lin, _optimal, _cost] =
Linearize(sims[0].graph, 300000, rng.
rand64(), vec1);
1077 for (
size_t pos = 0; pos < vec1.size(); ++pos) {
1078 auto pos_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[pos]));
1080 size_t before = rng.
randrange<
size_t>(pos);
1081 auto before_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[before]));
1082 assert(FeeRateCompare(before_feerate, pos_feerate) >= 0);
1084 if (pos + 1 < vec1.size()) {
1085 size_t after = pos + 1 + rng.
randrange<
size_t>(vec1.size() - 1 - pos);
1086 auto after_feerate = real->GetMainChunkFeerate(*sims[0].GetRef(vec1[after]));
1087 assert(FeeRateCompare(after_feerate, pos_feerate) <= 0);
1093 auto builder = real->GetBlockBuilder();
1094 std::vector<SimTxGraph::Pos> vec_builder;
1095 std::vector<TxGraph::Ref*> last_chunk;
1097 while (
auto chunk = builder->GetCurrentChunk()) {
1102 assert(real->GetMainChunkFeerate(*ref) == chunk->second);
1104 sum += real->GetIndividualFeerate(*ref);
1106 auto simpos = sims[0].Find(ref);
1108 vec_builder.push_back(simpos);
1111 last_chunk = std::move(chunk->first);
1112 last_chunk_feerate = chunk->second;
1115 assert(vec_builder == vec1);
1118 std::reverse(last_chunk.begin(), last_chunk.end());
1119 auto [worst_chunk, worst_chunk_feerate] = real->GetWorstMainChunk();
1120 assert(last_chunk == worst_chunk);
1121 assert(last_chunk_feerate == worst_chunk_feerate);
1129 if (sims.size() >= 2 && !sims[1].IsOversized()) {
1132 auto [main_cmp_diagram, stage_cmp_diagram] = real->GetMainStagingDiagrams();
1134 for (
size_t i = 1; i < main_cmp_diagram.size(); ++i) {
1135 assert(FeeRateCompare(main_cmp_diagram[i], main_cmp_diagram[i - 1]) <= 0);
1137 for (
size_t i = 1; i < stage_cmp_diagram.size(); ++i) {
1138 assert(FeeRateCompare(stage_cmp_diagram[i], stage_cmp_diagram[i - 1]) <= 0);
1144 std::sort(main_cmp_diagram.begin(), main_cmp_diagram.end(), std::greater{});
1145 std::sort(stage_cmp_diagram.begin(), stage_cmp_diagram.end(), std::greater{});
1149 std::vector<FeeFrac> missing_main_cmp;
1150 std::set_difference(main_real_diagram.begin(), main_real_diagram.end(),
1151 main_cmp_diagram.begin(), main_cmp_diagram.end(),
1152 std::inserter(missing_main_cmp, missing_main_cmp.end()),
1154 assert(main_cmp_diagram.size() + missing_main_cmp.size() == main_real_diagram.size());
1157 std::vector<FeeFrac> missing_stage_cmp;
1158 std::set_difference(stage_real_diagram.begin(), stage_real_diagram.end(),
1159 stage_cmp_diagram.begin(), stage_cmp_diagram.end(),
1160 std::inserter(missing_stage_cmp, missing_stage_cmp.end()),
1162 assert(stage_cmp_diagram.size() + missing_stage_cmp.size() == stage_real_diagram.size());
1165 assert(missing_main_cmp == missing_stage_cmp);
1172 for (
const auto& feerate : missing_main_cmp) missing_real += feerate;
1173 FeeFrac missing_expected = sims[1].graph.FeeRate(sims[1].graph.Positions() - sims[1].modified);
1180 assert(real->HaveStaging() == (sims.size() > 1));
1187 assert(real->IsOversized(level) == sim.IsOversized());
1188 assert(real->GetTransactionCount(level) == sim.GetTransactionCount());
1190 if (!sim.IsOversized()) {
1191 auto todo = sim.graph.Positions();
1194 while (todo.Any()) {
1195 auto component = sim.graph.FindConnectedComponent(todo);
1198 for (
auto i : component) {
1200 assert(sim.graph.FeeRate(i) == real->GetIndividualFeerate(*sim.GetRef(i)));
1202 auto expect_anc = sim.graph.Ancestors(i);
1203 auto anc = sim.MakeSet(real->GetAncestors(*sim.GetRef(i), level));
1204 assert(anc.Count() <= max_cluster_count);
1205 assert(anc == expect_anc);
1207 auto expect_desc = sim.graph.Descendants(i);
1208 auto desc = sim.MakeSet(real->GetDescendants(*sim.GetRef(i), level));
1209 assert(desc.Count() <= max_cluster_count);
1210 assert(desc == expect_desc);
1212 auto cluster = real->GetCluster(*sim.GetRef(i), level);
1213 assert(cluster.size() <= max_cluster_count);
1214 assert(sim.MakeSet(cluster) == component);
1217 std::vector<DepGraphIndex> simlin;
1218 SimTxGraph::SetType done;
1219 uint64_t total_size{0};
1221 auto simpos = sim.Find(ptr);
1222 assert(sim.graph.Descendants(simpos).IsSubsetOf(component - done));
1224 assert(sim.graph.Ancestors(simpos).IsSubsetOf(done));
1225 simlin.push_back(simpos);
1226 total_size += sim.graph.FeeRate(simpos).size;
1229 assert(total_size <= max_cluster_size);
1235 for (
unsigned chunknum = 0; chunknum < simlinchunk.
NumChunksLeft(); ++chunknum) {
1236 auto chunk = simlinchunk.
GetChunk(chunknum);
1239 assert(sim.graph.IsConnected(chunk.transactions));
1241 while (chunk.transactions.Any()) {
1242 assert(chunk.transactions[simlin[idx]]);
1243 chunk.transactions.Reset(simlin[idx]);
1244 assert(chunk.feerate == real->GetMainChunkFeerate(*cluster[idx]));
1255 real->SanityCheck();
1258 block_builders.clear();
std::conditional_t<(BITS<=32), bitset_detail::IntBitSet< uint32_t >, std::conditional_t<(BITS<=std::numeric_limits< size_t >::digits), bitset_detail::IntBitSet< size_t >, bitset_detail::MultiIntBitSet< size_t,(BITS+std::numeric_limits< size_t >::digits - 1)/std::numeric_limits< size_t >::digits > > > BitSet
#define Assume(val)
Assume is the identity function.
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t rand64() noexcept
I randrange(I range) noexcept
Generate a random integer in the range [0..range), with range > 0.
@ MAIN
Always refers to the main graph, whether staging is present or not.
@ TOP
Refers to staging if it exists, main otherwise.
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
const SetType & Ancestors(DepGraphIndex i) const noexcept
Get the ancestors of a given transaction i.
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).
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.
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
void AddDependencies(const SetType &parents, DepGraphIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets.
DepGraphIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
const SetInfo< SetType > & GetChunk(DepGraphIndex n) const noexcept
Access a chunk.
constexpr MaybeCoin MISSING
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
Value Find(const std::map< Key, Value > &map, const Key &key)
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_iterations, uint64_t rng_seed, std::span< const DepGraphIndex > old_linearization={}) noexcept
Find or improve a linearization for a cluster.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.
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).
Tagged wrapper around FeeFrac to avoid unit confusion.
void SeedRandomStateForTest(SeedRand seedtype)
Seed the global RNG state for testing and log the seed value.
@ ZEROS
Seed with a compile time constant of zeros.
std::unique_ptr< TxGraph > MakeTxGraph(unsigned max_cluster_count, uint64_t max_cluster_size, uint64_t acceptable_iters) noexcept
Construct a new TxGraph with the specified limit on the number of transactions within a cluster,...
static constexpr unsigned MAX_CLUSTER_COUNT_LIMIT
std::partial_ordering CompareChunks(std::span< const FeeFrac > chunks0, std::span< const FeeFrac > chunks1)
Compare the feerate diagrams implied by the provided sorted chunks data.