29template<
typename SetType>
30class SimpleCandidateFinder
40 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
43 void MarkDone(SetType select)
noexcept { m_todo -= select; }
46 bool AllDone() const noexcept {
return m_todo.None(); }
54 std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations)
const noexcept
56 uint64_t iterations_left = max_iterations;
60 std::vector<std::pair<SetType, SetType>> queue;
62 queue.emplace_back(SetType{}, m_todo);
64 SetInfo best(m_depgraph, m_todo);
66 while (!queue.empty() && iterations_left) {
69 auto [inc, und] = queue.back();
72 bool inc_none = inc.None();
73 for (
auto split : und) {
80 queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
84 if (new_inc.feerate > best.feerate) best = new_inc;
89 return {std::move(best), max_iterations - iterations_left};
99template<
typename SetType>
100class ExhaustiveCandidateFinder
110 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
113 void MarkDone(SetType select)
noexcept { m_todo -= select; }
116 bool AllDone() const noexcept {
return m_todo.None(); }
127 uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
129 for (uint64_t x = 1; x < limit; ++x) {
134 for (
auto i : m_todo) {
135 if (x_shifted & 1) txn |= m_depgraph.
Ancestors(i);
138 SetInfo cur(m_depgraph, txn & m_todo);
139 if (cur.feerate > best.feerate) best = cur;
151template<
typename SetType>
152std::pair<std::vector<ClusterIndex>,
bool> SimpleLinearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations)
154 std::vector<ClusterIndex> linearization;
155 SimpleCandidateFinder finder(depgraph);
159 auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
160 if (iterations_done == max_iterations) optimal =
false;
161 depgraph.
AppendTopo(linearization, candidate.transactions);
162 todo -= candidate.transactions;
163 finder.MarkDone(candidate.transactions);
164 max_iterations -= iterations_done;
166 return {std::move(linearization), optimal};
180 depgraph.
AddDependencies(BS::Singleton(comp.Last()), nextcomp.First());
187template<
typename SetType>
193 }
catch(
const std::ios_base::failure&) {}
195 for (
auto i : todo) {
208 std::vector<ClusterIndex> linearization;
213 TestBitSet potential_next;
214 for (
auto j : todo) {
215 if ((depgraph.
Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
216 potential_next.Set(j);
220 assert(potential_next.Any());
225 }
catch (
const std::ios_base::failure&) {}
226 idx %= potential_next.Count();
228 for (
auto j : potential_next) {
231 linearization.push_back(j);
239 return linearization;
254 std::array<std::optional<std::pair<FeeFrac, TestBitSet>>, TestBitSet::Size()> sim;
259 auto idx_fn = [&]() {
262 if (!sim[i].has_value())
continue;
263 if (offset == 0)
return i;
271 auto subset_fn = [&]() {
272 auto range = (uint64_t{1} << num_tx_sim) - 1;
274 auto mask_shifted = mask;
277 if (!sim[i].has_value())
continue;
278 if (mask_shifted & 1) {
283 assert(mask_shifted == 0);
288 auto set_fn = [&]() {
289 auto range = (uint64_t{1} << sim.size()) - 1;
293 if ((mask >> i) & 1) {
301 auto anc_update_fn = [&]() {
305 if (!sim[chl].has_value())
continue;
306 for (
auto par : sim[chl]->second) {
307 if (!sim[chl]->second.IsSupersetOf(sim[par]->second)) {
308 sim[chl]->second |= sim[par]->second;
321 if (sim[i].has_value()) {
332 if (num_tx_sim == 0 || ((
command % 3) <= 0 && num_tx_sim < TestBitSet::Size())) {
340 assert(!sim[idx].has_value());
342 if (!sim[i].has_value()) {
348 sim[idx] = {feerate, TestBitSet::Singleton(idx)};
352 if ((
command % 3) <= 1 && num_tx_sim > 0) {
355 auto parents = subset_fn();
359 sim[child]->second |= parents;
362 if (num_tx_sim > 0) {
369 for (
auto i : del) check_fn(i);
374 if (sim[i].has_value()) {
377 sim[i] = std::nullopt;
379 sim[i]->second -= del;
391 for (
ClusterIndex i = 0; i < sim.size(); ++i) check_fn(i);
405 reader >> Using<DepGraphFormatter>(depgraph);
406 }
catch (
const std::ios_base::failure&) {}
407 SanityCheck(depgraph);
410 assert(IsAcyclic(depgraph));
421 reader >> Using<DepGraphFormatter>(depgraph);
422 }
catch (
const std::ios_base::failure&) {}
430 assert(component.IsSubsetOf(todo));
443 for (
auto i : component) {
449 for (
auto i : component) {
451 TestBitSet reachable = TestBitSet::Singleton(i);
454 TestBitSet new_reachable = reachable;
455 for (
auto j : new_reachable) {
456 new_reachable |= depgraph.
Ancestors(j) & todo;
459 if (new_reachable == reachable)
break;
460 reachable = new_reachable;
463 assert(component == reachable);
467 uint64_t subset_bits{0};
469 reader >>
VARINT(subset_bits);
470 }
catch (
const std::ios_base::failure&) {}
474 if (subset_bits & 1) subset.Set(i);
479 if (subset.None()) subset = TestBitSet::Singleton(todo.First());
495 reader >> Using<DepGraphFormatter>(depgraph);
496 }
catch (
const std::ios_base::failure&) {}
497 MakeConnected(depgraph);
498 SanityCheck(depgraph);
510 reader >> Using<DepGraphFormatter>(depgraph);
511 }
catch (
const std::ios_base::failure&) {}
514 auto linearization = ReadLinearization(depgraph, reader);
520 for (
size_t i = 1; i < chunking.size(); ++i) {
521 assert(!(chunking[i] >> chunking[i - 1]));
526 for (
const auto& chunk_feerate : chunking) {
531 accumulator.
Set(depgraph, idx);
552 reader >> Using<DepGraphFormatter>(depgraph);
553 }
catch (
const std::ios_base::failure&) {}
563 assert(best_anc.transactions.Any());
564 assert(best_anc.transactions.IsSubsetOf(todo));
565 assert(depgraph.
FeeRate(best_anc.transactions) == best_anc.feerate);
568 for (
auto i : best_anc.transactions) {
569 assert((depgraph.
Ancestors(i) & todo).IsSubsetOf(best_anc.transactions));
573 std::optional<SetInfo<TestBitSet>> real_best_anc;
574 for (
auto i : todo) {
576 if (!real_best_anc.has_value() || info.
feerate > real_best_anc->feerate) {
577 real_best_anc = info;
581 assert(real_best_anc.has_value());
582 assert(*real_best_anc == best_anc);
585 auto del_set = ReadTopologicalSet(depgraph, todo, reader);
587 if (del_set.None()) del_set = best_anc.transactions;
606 uint64_t rng_seed{0};
607 uint8_t make_connected{1};
609 reader >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected;
610 }
catch (
const std::ios_base::failure&) {}
613 if (make_connected) MakeConnected(depgraph);
617 SimpleCandidateFinder smp_finder(depgraph);
618 ExhaustiveCandidateFinder exh_finder(depgraph);
624 assert(!smp_finder.AllDone());
625 assert(!exh_finder.AllDone());
630 uint64_t max_iterations = 1;
632 reader >>
VARINT(max_iterations);
633 }
catch (
const std::ios_base::failure&) {}
634 max_iterations &= 0xfffff;
637 SetInfo init_best(depgraph, ReadTopologicalSet(depgraph, todo, reader));
640 auto [found, iterations_done] = src_finder.
FindCandidateSet(max_iterations, init_best);
643 assert(iterations_done <= max_iterations);
644 assert(found.transactions.Any());
645 assert(found.transactions.IsSubsetOf(todo));
646 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
649 for (
auto i : found.transactions) {
657 assert(iterations_done <= (uint64_t{1} << (todo.Count() - 1)));
661 if (iterations_done >= 1 && todo.Count() <= 63) {
662 Assume((iterations_done - 1) * (iterations_done - 1) <= uint64_t{1} << todo.Count());
666 if (iterations_done < max_iterations) {
672 assert(found.feerate >= simple.feerate);
674 assert(found.feerate == simple.feerate);
679 assert(found.feerate >= anc.feerate);
683 if (todo.Count() <= 12) {
684 auto exhaustive = exh_finder.FindCandidateSet();
685 assert(exhaustive.feerate == found.feerate);
688 assert(exhaustive.feerate >= simple.feerate);
690 assert(exhaustive.feerate == simple.feerate);
696 auto del_set = ReadTopologicalSet(depgraph, todo, reader);
698 if (del_set.None()) del_set = found.transactions;
701 smp_finder.MarkDone(del_set);
702 exh_finder.MarkDone(del_set);
707 assert(smp_finder.AllDone());
708 assert(exh_finder.AllDone());
721 reader >> Using<DepGraphFormatter>(depgraph);
722 }
catch (
const std::ios_base::failure&) {}
726 auto subset =
SetInfo(depgraph, ReadTopologicalSet(depgraph, todo, reader));
729 auto linearization = ReadLinearization(depgraph, reader);
740 std::vector<ClusterIndex> linearization_left;
741 for (
auto i : linearization) {
742 if (todo[i]) linearization_left.push_back(i);
757 const auto& chunk_info = chunking.
GetChunk(i);
759 assert(chunk_info.transactions.Any());
761 if (i > 0)
assert(!(chunk_info.feerate >> chunking.
GetChunk(i - 1).feerate));
763 assert(chunk_info.transactions.IsSubsetOf(todo));
765 assert(depgraph.
FeeRate(chunk_info.transactions) == chunk_info.feerate);
768 for (
auto j : linearization) {
769 if (todo[j] && !combined[j]) {
770 accumulator.
Set(depgraph, j);
779 assert(!chunk_info.transactions.Overlaps(combined));
780 combined |= chunk_info.transactions;
782 for (
auto idx : chunk_info.transactions) {
793 TestBitSet intersect_anc;
794 for (
auto idx : intersect.transactions) {
795 intersect_anc |= (depgraph.
Ancestors(idx) & todo);
797 assert(intersect.transactions == intersect_anc);
799 assert(intersect.feerate == depgraph.
FeeRate(intersect.transactions));
801 assert(intersect.transactions.Any() == subset.transactions.Any());
803 assert(intersect.feerate >= subset.feerate);
809 auto reintersect =
SetInfo(depgraph,
prefix & intersect.transactions);
810 if (!reintersect.feerate.IsEmpty()) {
811 assert(reintersect.feerate <= intersect.feerate);
816 auto done = ReadTopologicalSet(depgraph, todo, reader);
820 done = depgraph.
Ancestors(todo.First()) & todo;
824 subset =
SetInfo(depgraph, subset.transactions - done);
838 uint64_t rng_seed{0};
839 uint64_t iter_count{0};
840 uint8_t make_connected{1};
842 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected;
843 }
catch (
const std::ios_base::failure&) {}
846 if (make_connected) MakeConnected(depgraph);
849 std::vector<ClusterIndex> old_linearization;
851 uint8_t have_old_linearization{0};
853 reader >> have_old_linearization;
854 }
catch(
const std::ios_base::failure&) {}
855 if (have_old_linearization & 1) {
856 old_linearization = ReadLinearization(depgraph, reader);
857 SanityCheck(depgraph, old_linearization);
862 iter_count &= 0x7ffff;
863 auto [linearization, optimal] =
Linearize(depgraph, iter_count, rng_seed, old_linearization);
864 SanityCheck(depgraph, linearization);
868 if (!old_linearization.empty()) {
877 const uint64_t n = depgraph.
TxCount();
878 if (n <= 19 && iter_count > (uint64_t{1} << n)) {
885 static constexpr uint64_t MAX_OPTIMAL_ITERS[] = {
886 0, 4, 8, 12, 18, 26, 37, 51, 70, 97, 133, 182, 251, 346, 480, 666, 927, 1296, 1815, 2545,
887 3576, 5031, 7087, 9991, 14094, 19895, 28096, 39690, 56083, 79263, 112041, 158391, 223936,
890 if (n < std::size(MAX_OPTIMAL_ITERS) && iter_count >= MAX_OPTIMAL_ITERS[n]) {
898 SanityCheck(depgraph, simple_linearization);
904 if (simple_optimal)
assert(cmp == 0);
908 std::vector<ClusterIndex> perm_linearization;
913 TestBitSet perm_done;
914 bool perm_is_topo{
true};
915 for (
auto i : perm_linearization) {
917 if (!depgraph.
Ancestors(i).IsSubsetOf(perm_done)) {
918 perm_is_topo =
false;
928 }
while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
941 reader >> Using<DepGraphFormatter>(depgraph);
942 }
catch (
const std::ios_base::failure&) {}
945 std::vector<ClusterIndex> linearization;
946 linearization = ReadLinearization(depgraph, reader);
947 SanityCheck(depgraph, linearization);
950 auto post_linearization = linearization;
952 SanityCheck(depgraph, post_linearization);
961 auto post_post_linearization = post_linearization;
963 SanityCheck(depgraph, post_post_linearization);
983 uint64_t rng_seed{0};
985 uint8_t direction{0};
987 reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
988 }
catch (
const std::ios_base::failure&) {}
1004 if (direction & 1) {
1007 if (children.Any()) {
1008 depgraph_tree.
AddDependencies(TestBitSet::Singleton(i), children.First());
1014 if (parents.Any()) {
1015 depgraph_tree.
AddDependencies(TestBitSet::Singleton(parents.First()), i);
1021 std::vector<ClusterIndex> linearization;
1022 linearization = ReadLinearization(depgraph_tree, reader);
1023 SanityCheck(depgraph_tree, linearization);
1026 auto post_linearization = linearization;
1028 SanityCheck(depgraph_tree, post_linearization);
1038 auto post_post_linearization = post_linearization;
1040 SanityCheck(depgraph_tree, post_linearization);
1041 auto post_post_chunking =
ChunkLinearization(depgraph_tree, post_post_linearization);
1042 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
1047 auto [opt_linearization, _optimal] =
Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
1066 uint64_t fee_inc_code;
1067 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(fee_inc_code);
1068 fee_inc = fee_inc_code & 0x3ffff;
1069 }
catch (
const std::ios_base::failure&) {}
1070 if (depgraph.
TxCount() == 0)
return;
1073 auto lin = ReadLinearization(depgraph, reader);
1074 auto lin_leaf = ReadLinearization(depgraph, reader);
1078 std::vector<ClusterIndex> lin_moved;
1079 for (
auto i : lin) {
1080 if (i != lin_leaf.back()) lin_moved.push_back(i);
1082 lin_moved.push_back(lin_leaf.back());
1086 SanityCheck(depgraph, lin_moved);
1090 depgraph.
FeeRate(lin_leaf.back()).
fee += fee_inc;
1102 reader >> Using<DepGraphFormatter>(depgraph);
1103 }
catch (
const std::ios_base::failure&) {}
1106 auto lin1 = ReadLinearization(depgraph, reader);
1107 auto lin2 = ReadLinearization(depgraph, reader);
#define Assume(val)
Assume is the identity function.
T ConsumeIntegralInRange(T min, T max)
Minimal stream for reading from an existing byte array by Span.
Class encapsulating the state needed to find the best remaining ancestor set.
void MarkDone(SetType select) noexcept
Remove a set of transactions from the set of to-be-linearized ones.
ClusterIndex NumRemaining() const noexcept
Count the number of remaining unlinearized transactions.
SetInfo< SetType > FindCandidateSet() const noexcept
Find the best (highest-feerate, smallest among those in case of a tie) ancestor set among the remaini...
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors,...
void AppendTopo(std::vector< ClusterIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
SetType GetReducedParents(ClusterIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
void AddDependencies(const SetType &parents, ClusterIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
ClusterIndex PositionRange() const noexcept
Get the range of positions in this DepGraph.
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 & Positions() const noexcept
Get the set of transactions positions in use.
SetType GetReducedChildren(ClusterIndex i) const noexcept
Compute the (reduced) set of children of node i in this graph.
const SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (in the first available position),...
Data structure encapsulating the chunking of a linearization, permitting removal of subsets.
ClusterIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
SetInfo< SetType > IntersectPrefixes(const SetInfo< SetType > &subset) const noexcept
Find the shortest intersection between subset and the prefixes of remaining chunks of the linearizati...
const SetInfo< SetType > & GetChunk(ClusterIndex n) const noexcept
Access a chunk.
void MarkDone(SetType subset) noexcept
Remove some subset of transactions from the linearization.
Class encapsulating the state needed to perform search for good candidate sets.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
void MarkDone(const SetType &done) noexcept
Remove a subset of transactions from the cluster being linearized.
std::pair< SetInfo< SetType >, uint64_t > FindCandidateSet(uint64_t max_iterations, SetInfo< SetType > best) noexcept
Find a high-feerate topologically-valid subset of what remains of the cluster.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
std::pair< std::vector< ClusterIndex >, bool > Linearize(const DepGraph< SetType > &depgraph, uint64_t max_iterations, uint64_t rng_seed, Span< const ClusterIndex > old_linearization={}) noexcept
Find or improve a linearization for a cluster.
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
void PostLinearize(const DepGraph< SetType > &depgraph, Span< ClusterIndex > linearization)
Improve a given linearization.
std::vector< ClusterIndex > MergeLinearizations(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > lin1, Span< const ClusterIndex > lin2)
Merge two linearizations for the same cluster into one that is as good as both.
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).
A set of transactions together with their aggregate feerate.
FeeFrac feerate
Their combined fee and size.
SetType transactions
The transactions in the set.
void Set(const DepGraph< SetType > &depgraph, ClusterIndex pos) noexcept
Add a transaction to this SetInfo (which must not yet be in it).
FUZZ_TARGET(clusterlin_depgraph_sim)
static constexpr auto MAX_SIMPLE_ITERATIONS
std::partial_ordering CompareChunks(Span< const FeeFrac > chunks0, Span< const FeeFrac > chunks1)
Compare the feerate diagrams implied by the provided sorted chunks data.