28 template<
typename SetType>
29 class SimpleCandidateFinder
39 m_depgraph(depgraph), m_todo{SetType::Fill(depgraph.TxCount())} {}
42 void MarkDone(SetType select) noexcept { m_todo -= select; }
45 bool AllDone() const noexcept {
return m_todo.None(); }
53 std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations)
const noexcept
55 uint64_t iterations_left = max_iterations;
59 std::vector<std::pair<SetType, SetType>> queue;
61 queue.emplace_back(SetType{}, m_todo);
63 SetInfo best(m_depgraph, m_todo);
65 while (!queue.empty() && iterations_left) {
68 auto [inc, und] = queue.back();
71 bool inc_none = inc.None();
72 for (
auto split : und) {
79 queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
83 if (new_inc.feerate > best.feerate) best = new_inc;
88 return {std::move(best), max_iterations - iterations_left};
98 template<
typename SetType>
99 class ExhaustiveCandidateFinder
109 m_depgraph(depgraph), m_todo{SetType::Fill(depgraph.TxCount())} {}
112 void MarkDone(SetType select) noexcept { m_todo -= select; }
115 bool AllDone() const noexcept {
return m_todo.None(); }
126 uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
128 for (uint64_t x = 1; x < limit; ++x) {
133 for (
auto i : m_todo) {
134 if (x_shifted & 1) txn |= m_depgraph.
Ancestors(i);
137 SetInfo cur(m_depgraph, txn & m_todo);
138 if (cur.feerate > best.feerate) best = cur;
150 template<
typename SetType>
151 std::pair<std::vector<ClusterIndex>,
bool> SimpleLinearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations)
153 std::vector<ClusterIndex> linearization;
154 SimpleCandidateFinder finder(depgraph);
155 SetType todo = SetType::Fill(depgraph.
TxCount());
158 auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
159 if (iterations_done == max_iterations) optimal =
false;
160 depgraph.
AppendTopo(linearization, candidate.transactions);
161 todo -= candidate.transactions;
162 finder.MarkDone(candidate.transactions);
163 max_iterations -= iterations_done;
165 return {std::move(linearization), optimal};
169 template<
typename SetType>
175 }
catch(
const std::ios_base::failure&) {}
177 for (
auto i : todo) {
187 template<
typename BS>
190 std::vector<ClusterIndex> linearization;
191 TestBitSet todo = TestBitSet::Fill(depgraph.
TxCount());
195 TestBitSet potential_next;
196 for (
auto j : todo) {
197 if ((depgraph.
Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
198 potential_next.Set(j);
202 assert(potential_next.Any());
207 }
catch (
const std::ios_base::failure&) {}
208 idx %= potential_next.Count();
210 for (
auto j : potential_next) {
213 linearization.push_back(j);
221 return linearization;
237 SanityCheck(depgraph);
239 LIMITED_WHILE(provider.remaining_bytes() > 0, TestBitSet::Size() * TestBitSet::Size()) {
240 auto parent = provider.ConsumeIntegralInRange<
ClusterIndex>(0, num_tx - 1);
241 auto child = provider.ConsumeIntegralInRange<
ClusterIndex>(0, num_tx - 2);
242 child += (child >= parent);
243 cluster[child].second.Set(parent);
244 depgraph.AddDependency(parent, child);
245 assert(depgraph.Ancestors(child)[parent]);
246 assert(depgraph.Descendants(parent)[child]);
249 SanityCheck(depgraph);
265 cluster.resize(num_tx);
267 cluster[i].first.size = provider.ConsumeIntegralInRange<int32_t>(1, 0x3fffff);
268 cluster[i].first.fee = provider.ConsumeIntegralInRange<int64_t>(-0x8000000000000, 0x7ffffffffffff);
270 if (i == j)
continue;
271 if (provider.ConsumeBool()) cluster[i].second.Set(j);
278 VerifyDepGraphFromCluster(cluster, depgraph);
289 reader >> Using<DepGraphFormatter>(depgraph);
290 }
catch (
const std::ios_base::failure&) {}
291 SanityCheck(depgraph);
294 assert(IsAcyclic(depgraph));
305 reader >> Using<DepGraphFormatter>(depgraph);
306 }
catch (
const std::ios_base::failure&) {}
308 TestBitSet todo = TestBitSet::Fill(depgraph.
TxCount());
314 assert(component.IsSubsetOf(todo));
319 if (todo == TestBitSet::Fill(depgraph.
TxCount())) {
327 for (
auto i : component) {
333 for (
auto i : component) {
335 TestBitSet reachable = TestBitSet::Singleton(i);
338 TestBitSet new_reachable = reachable;
339 for (
auto j : new_reachable) {
340 new_reachable |= depgraph.
Ancestors(j) & todo;
343 if (new_reachable == reachable)
break;
344 reachable = new_reachable;
347 assert(component == reachable);
351 uint64_t subset_bits{0};
353 reader >>
VARINT(subset_bits);
354 }
catch (
const std::ios_base::failure&) {}
358 if (subset_bits & 1) subset.Set(i);
363 if (subset.None()) subset = TestBitSet::Singleton(todo.First());
380 reader >> Using<DepGraphFormatter>(depgraph);
381 }
catch (
const std::ios_base::failure&) {}
384 auto linearization = ReadLinearization(depgraph, reader);
390 for (
size_t i = 1; i < chunking.size(); ++i) {
391 assert(!(chunking[i] >> chunking[i - 1]));
395 auto todo = TestBitSet::Fill(depgraph.
TxCount());
396 for (
const auto& chunk_feerate : chunking) {
401 accumulator |=
SetInfo(depgraph, idx);
402 if (best.feerate.IsEmpty() || accumulator.feerate >> best.feerate) {
407 assert(chunk_feerate == best.feerate);
408 assert(best.transactions.IsSubsetOf(todo));
409 todo -= best.transactions;
422 reader >> Using<DepGraphFormatter>(depgraph);
423 }
catch (
const std::ios_base::failure&) {}
426 auto todo = TestBitSet::Fill(depgraph.
TxCount());
432 assert(best_anc.transactions.Any());
433 assert(best_anc.transactions.IsSubsetOf(todo));
434 assert(depgraph.
FeeRate(best_anc.transactions) == best_anc.feerate);
437 for (
auto i : best_anc.transactions) {
438 assert((depgraph.
Ancestors(i) & todo).IsSubsetOf(best_anc.transactions));
442 std::optional<SetInfo<TestBitSet>> real_best_anc;
443 for (
auto i : todo) {
445 if (!real_best_anc.has_value() || info.feerate > real_best_anc->feerate) {
446 real_best_anc = info;
450 assert(real_best_anc.has_value());
451 assert(*real_best_anc == best_anc);
454 auto del_set = ReadTopologicalSet(depgraph, todo, reader);
456 if (del_set.None()) del_set = best_anc.transactions;
474 uint64_t rng_seed{0};
476 reader >> Using<DepGraphFormatter>(depgraph) >> rng_seed;
477 }
catch (
const std::ios_base::failure&) {}
481 SimpleCandidateFinder smp_finder(depgraph);
482 ExhaustiveCandidateFinder exh_finder(depgraph);
485 auto todo = TestBitSet::Fill(depgraph.
TxCount());
488 assert(!smp_finder.AllDone());
489 assert(!exh_finder.AllDone());
493 uint64_t max_iterations = 1;
495 reader >>
VARINT(max_iterations);
496 }
catch (
const std::ios_base::failure&) {}
497 max_iterations &= 0xfffff;
500 SetInfo init_best(depgraph, ReadTopologicalSet(depgraph, todo, reader));
503 auto [found, iterations_done] = src_finder.
FindCandidateSet(max_iterations, init_best);
506 assert(iterations_done <= max_iterations);
507 assert(found.transactions.Any());
508 assert(found.transactions.IsSubsetOf(todo));
509 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
512 for (
auto i : found.transactions) {
518 assert(iterations_done <= ((uint64_t{1} << todo.Count()) - 1));
521 if (iterations_done < max_iterations) {
527 assert(found.feerate >= simple.feerate);
529 assert(found.feerate == simple.feerate);
534 assert(found.feerate >= anc.feerate);
538 if (todo.Count() <= 12) {
539 auto exhaustive = exh_finder.FindCandidateSet();
540 assert(exhaustive.feerate == found.feerate);
543 assert(exhaustive.feerate >= simple.feerate);
545 assert(exhaustive.feerate == simple.feerate);
551 auto del_set = ReadTopologicalSet(depgraph, todo, reader);
553 if (del_set.None()) del_set = found.transactions;
556 smp_finder.MarkDone(del_set);
557 exh_finder.MarkDone(del_set);
562 assert(smp_finder.AllDone());
563 assert(exh_finder.AllDone());
575 reader >> Using<DepGraphFormatter>(depgraph);
576 }
catch (
const std::ios_base::failure&) {}
579 auto todo = TestBitSet::Fill(depgraph.
TxCount());
580 auto subset =
SetInfo(depgraph, ReadTopologicalSet(depgraph, todo, reader));
583 auto linearization = ReadLinearization(depgraph, reader);
594 std::vector<ClusterIndex> linearization_left;
595 for (
auto i : linearization) {
596 if (todo[i]) linearization_left.push_back(i);
611 const auto& chunk_info = chunking.
GetChunk(i);
613 assert(chunk_info.transactions.Any());
615 if (i > 0)
assert(!(chunk_info.feerate >> chunking.
GetChunk(i - 1).feerate));
617 assert(chunk_info.transactions.IsSubsetOf(todo));
619 assert(depgraph.
FeeRate(chunk_info.transactions) == chunk_info.feerate);
622 for (
auto j : linearization) {
623 if (todo[j] && !combined[j]) {
624 accumulator |=
SetInfo(depgraph, j);
625 if (best.feerate.IsEmpty() || accumulator.feerate > best.feerate) {
630 assert(best.transactions == chunk_info.transactions);
631 assert(best.feerate == chunk_info.feerate);
633 assert(!chunk_info.transactions.Overlaps(combined));
634 combined |= chunk_info.transactions;
636 for (
auto idx : chunk_info.transactions) {
647 TestBitSet intersect_anc;
648 for (
auto idx : intersect.transactions) {
649 intersect_anc |= (depgraph.
Ancestors(idx) & todo);
651 assert(intersect.transactions == intersect_anc);
653 assert(intersect.feerate == depgraph.
FeeRate(intersect.transactions));
655 assert(intersect.transactions.Any() == subset.transactions.Any());
657 assert(intersect.feerate >= subset.feerate);
663 auto reintersect =
SetInfo(depgraph,
prefix & intersect.transactions);
664 if (!reintersect.feerate.IsEmpty()) {
665 assert(reintersect.feerate <= intersect.feerate);
670 auto done = ReadTopologicalSet(depgraph, todo, reader);
674 done = depgraph.
Ancestors(todo.First()) & todo;
678 subset =
SetInfo(depgraph, subset.transactions - done);
691 uint64_t rng_seed{0};
692 uint64_t iter_count{0};
694 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed;
695 }
catch (
const std::ios_base::failure&) {}
698 std::vector<ClusterIndex> old_linearization;
700 uint8_t have_old_linearization{0};
702 reader >> have_old_linearization;
703 }
catch(
const std::ios_base::failure&) {}
704 if (have_old_linearization & 1) {
705 old_linearization = ReadLinearization(depgraph, reader);
706 SanityCheck(depgraph, old_linearization);
711 iter_count &= 0x7ffff;
712 auto [linearization, optimal] =
Linearize(depgraph, iter_count, rng_seed, old_linearization);
713 SanityCheck(depgraph, linearization);
717 if (!old_linearization.empty()) {
726 const uint64_t n = depgraph.
TxCount();
727 if (n <= 18 && iter_count > 2U * ((uint64_t{1} << n) - 1U)) {
735 SanityCheck(depgraph, simple_linearization);
741 if (simple_optimal)
assert(cmp == 0);
745 std::vector<ClusterIndex> perm_linearization(depgraph.
TxCount());
750 TestBitSet perm_done;
751 bool perm_is_topo{
true};
752 for (
auto i : perm_linearization) {
754 if (!depgraph.
Ancestors(i).IsSubsetOf(perm_done)) {
755 perm_is_topo =
false;
765 }
while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
778 reader >> Using<DepGraphFormatter>(depgraph);
779 }
catch (
const std::ios_base::failure&) {}
782 std::vector<ClusterIndex> linearization;
783 linearization = ReadLinearization(depgraph, reader);
784 SanityCheck(depgraph, linearization);
787 auto post_linearization = linearization;
789 SanityCheck(depgraph, post_linearization);
798 auto post_post_linearization = post_linearization;
800 SanityCheck(depgraph, post_post_linearization);
820 uint64_t rng_seed{0};
822 uint8_t direction{0};
824 reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
825 }
catch (
const std::ios_base::failure&) {}
835 auto children = depgraph_gen.
Descendants(i) - TestBitSet::Singleton(i);
837 for (
auto j : children) {
838 if (!children[j])
continue;
842 if (children.Any()) depgraph_tree.
AddDependency(i, children.First());
846 auto parents = depgraph_gen.
Ancestors(i) - TestBitSet::Singleton(i);
848 for (
auto j : parents) {
849 if (!parents[j])
continue;
853 if (parents.Any()) depgraph_tree.
AddDependency(parents.First(), i);
858 std::vector<ClusterIndex> linearization;
859 linearization = ReadLinearization(depgraph_tree, reader);
860 SanityCheck(depgraph_tree, linearization);
863 auto post_linearization = linearization;
865 SanityCheck(depgraph_tree, post_linearization);
875 auto post_post_linearization = post_linearization;
877 SanityCheck(depgraph_tree, post_linearization);
879 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
884 auto [opt_linearization, _optimal] =
Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
903 uint64_t fee_inc_code;
904 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(fee_inc_code);
905 fee_inc = fee_inc_code & 0x3ffff;
906 }
catch (
const std::ios_base::failure&) {}
907 if (depgraph.
TxCount() == 0)
return;
910 auto lin = ReadLinearization(depgraph, reader);
911 auto lin_leaf = ReadLinearization(depgraph, reader);
915 std::vector<ClusterIndex> lin_moved;
917 if (i != lin_leaf.back()) lin_moved.push_back(i);
919 lin_moved.push_back(lin_leaf.back());
923 SanityCheck(depgraph, lin_moved);
927 depgraph.
FeeRate(lin_leaf.back()).fee += fee_inc;
939 reader >> Using<DepGraphFormatter>(depgraph);
940 }
catch (
const std::ios_base::failure&) {}
943 auto lin1 = ReadLinearization(depgraph, reader);
944 auto lin2 = ReadLinearization(depgraph, reader);
A set of transactions together with their aggregate feerate.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets...
ClusterIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (at the end), and return its ClusterIndex...
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
FeeFrac feerate
Their combined fee and size.
uint32_t ClusterIndex
Data type to represent transaction indices in clusters.
static constexpr auto MAX_SIMPLE_ITERATIONS
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 SetType & Descendants(ClusterIndex i) const noexcept
Get the descendants of a given transaction i.
const SetInfo< SetType > & GetChunk(ClusterIndex n) const noexcept
Access a chunk.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
void MarkDone(SetType select) noexcept
Remove a set of transactions from the set of to-be-linearized ones.
Minimal stream for reading from an existing byte array by Span.
bool AllDone() const noexcept
Check whether any unlinearized transactions remain.
void MarkDone(SetType subset) noexcept
Remove some subset of transactions from the linearization.
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
Class encapsulating the state needed to find the best remaining ancestor set.
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.
bool IsEmpty() const noexcept
Check if this is empty (size and fee are 0).
FUZZ_TARGET(clusterlin_add_dependency)
std::partial_ordering CompareChunks(Span< const FeeFrac > chunks0, Span< const FeeFrac > chunks1)
Compare the feerate diagrams implied by the provided sorted chunks data.
Data structure storing a fee and size, ordered by increasing fee/size.
void AddDependency(ClusterIndex parent, ClusterIndex child) noexcept
Modify this transaction graph, adding a dependency between a specified parent and child...
Data structure that holds a transaction graph's preprocessed data (fee, size, ancestors, descendants).
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.
SetInfo< SetType > FindCandidateSet() const noexcept
Find the best (highest-feerate, smallest among those in case of a tie) ancestor set among the remaini...
static std::vector< std::string > split(const std::string &str, const std::string &delims=" \)
ClusterIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the 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.
void PostLinearize(const DepGraph< SetType > &depgraph, Span< ClusterIndex > linearization)
Improve a given linearization.
const FeeFrac & FeeRate(ClusterIndex i) const noexcept
Get the feerate of a given transaction i.
T ConsumeIntegralInRange(T min, T max)
std::vector< std::pair< FeeFrac, SetType > > Cluster
Data type to represent cluster input.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, Span< const ClusterIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
const SetType & Ancestors(ClusterIndex i) const noexcept
Get the ancestors of a given transaction i.
auto TxCount() const noexcept
Get the number of transactions in the graph.
void MarkDone(const SetType &done) noexcept
Remove a subset of transactions from the cluster being linearized.
Class encapsulating the state needed to perform search for good candidate sets.
void AppendTopo(std::vector< ClusterIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.