71template<
typename SetType>
72class SimpleCandidateFinder
82 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
85 void MarkDone(SetType select)
noexcept { m_todo -= select; }
88 bool AllDone() const noexcept {
return m_todo.None(); }
98 std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations)
const noexcept
100 uint64_t iterations_left = max_iterations;
104 std::vector<std::pair<SetType, SetType>> queue;
106 queue.emplace_back(SetType{}, m_todo);
111 while (!queue.empty() && iterations_left) {
113 auto [inc, und] = queue.back();
116 bool inc_none = inc.None();
117 for (
auto split : und) {
125 queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
129 if (new_inc.feerate > best.feerate) best = new_inc;
134 return {std::move(best), max_iterations - iterations_left};
144template<
typename SetType>
145class ExhaustiveCandidateFinder
155 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
158 void MarkDone(SetType select)
noexcept { m_todo -= select; }
161 bool AllDone() const noexcept {
return m_todo.None(); }
172 uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
174 for (uint64_t x = 1; x < limit; ++x) {
179 for (
auto i : m_todo) {
180 if (x_shifted & 1) txn |= m_depgraph.
Ancestors(i);
183 SetInfo cur(m_depgraph, txn & m_todo);
184 if (cur.feerate > best.feerate) best = cur;
196template<
typename SetType>
197std::pair<std::vector<DepGraphIndex>,
bool> SimpleLinearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations)
199 std::vector<DepGraphIndex> linearization;
200 SimpleCandidateFinder finder(depgraph);
204 auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
205 if (iterations_done == max_iterations) optimal =
false;
206 depgraph.
AppendTopo(linearization, candidate.transactions);
207 todo -= candidate.transactions;
208 finder.MarkDone(candidate.transactions);
209 max_iterations -= iterations_done;
211 return {std::move(linearization), optimal};
221template<
typename SetType>
225 std::vector<DepGraphIndex> linearization;
226 std::vector<FeeFrac> chunking;
228 std::vector<DepGraphIndex> perm_linearization;
235 TestBitSet perm_done;
236 while (topo_length < perm_linearization.size()) {
237 auto i = perm_linearization[topo_length];
239 if (!depgraph.
Ancestors(i).IsSubsetOf(perm_done))
break;
242 if (topo_length == perm_linearization.size()) {
249 if (linearization.empty() || cmp > 0 || (cmp == 0 && perm_chunking.size() > chunking.size())) {
250 linearization = perm_linearization;
251 chunking = perm_chunking;
256 auto first_non_topo = perm_linearization.begin() + topo_length;
257 assert(std::is_sorted(first_non_topo + 1, perm_linearization.end()));
258 std::reverse(first_non_topo + 1, perm_linearization.end());
260 }
while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
262 return linearization;
277 depgraph.
AddDependencies(BS::Singleton(comp.Last()), nextcomp.First());
284template<
typename SetType>
292 }
catch(
const std::ios_base::failure&) {}
293 if (mask != uint64_t(-1)) mask += non_empty;
296 for (
auto i : todo) {
307 if (non_empty &&
ret.None()) {
317std::vector<DepGraphIndex> ReadLinearization(
const DepGraph<BS>& depgraph,
SpanReader& reader,
bool topological=
true)
319 std::vector<DepGraphIndex> linearization;
324 TestBitSet potential_next;
327 for (
auto j : todo) {
328 if ((depgraph.
Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
329 potential_next.Set(j);
334 potential_next = todo;
337 assert(potential_next.Any());
342 }
catch (
const std::ios_base::failure&) {}
343 idx %= potential_next.Count();
345 for (
auto j : potential_next) {
348 linearization.push_back(j);
356 return linearization;
382 if (children.Any()) {
395 return depgraph_tree;
410 std::array<std::optional<std::pair<FeeFrac, TestBitSet>>, TestBitSet::Size()> sim;
415 auto idx_fn = [&]() {
418 if (!sim[i].has_value())
continue;
419 if (offset == 0)
return i;
427 auto subset_fn = [&]() {
428 auto range = (uint64_t{1} << num_tx_sim) - 1;
430 auto mask_shifted = mask;
433 if (!sim[i].has_value())
continue;
434 if (mask_shifted & 1) {
439 assert(mask_shifted == 0);
444 auto set_fn = [&]() {
445 auto range = (uint64_t{1} << sim.size()) - 1;
449 if ((mask >> i) & 1) {
457 auto anc_update_fn = [&]() {
461 if (!sim[chl].has_value())
continue;
462 for (
auto par : sim[chl]->second) {
463 if (!sim[chl]->second.IsSupersetOf(sim[par]->second)) {
464 sim[chl]->second |= sim[par]->second;
477 if (sim[i].has_value()) {
492 if (num_tx_sim < TestBitSet::Size() &&
command-- == 0) {
500 assert(!sim[idx].has_value());
502 if (!sim[i].has_value()) {
508 sim[idx] = {feerate, TestBitSet::Singleton(idx)};
511 }
else if (num_tx_sim > 0 &&
command-- == 0) {
514 auto parents = subset_fn();
518 sim[child]->second |= parents;
520 }
else if (num_tx_sim > 0 &&
command-- == 0) {
527 for (
auto i : del) check_fn(i);
532 if (sim[i].has_value()) {
535 sim[i] = std::nullopt;
537 sim[i]->second -= del;
547 assert(real.
PositionRange() < last_compaction_pos ? mem_after < mem_before : mem_after <= mem_before);
571 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(par_code) >>
VARINT(chl_code);
572 }
catch (
const std::ios_base::failure&) {}
573 SanityCheck(depgraph);
579 if (depgraph.
TxCount() < 2)
return;
582 par_code %= depgraph.
TxCount();
591 auto ancestors = depgraph.
Ancestors(par) - TestBitSet::Singleton(par);
592 if (ancestors.None())
return;
593 chl_code %= ancestors.Count();
594 for (
auto i : ancestors) {
614 std::vector<DepGraphIndex> linearization;
616 reader >> Using<DepGraphFormatter>(depgraph);
617 }
catch (
const std::ios_base::failure&) {}
622 std::optional<DepGraphIndex> picked;
624 uint64_t picked_num{0};
626 reader >>
VARINT(picked_num);
627 }
catch (
const std::ios_base::failure&) {}
628 if (picked_num < todo.Size() && todo[picked_num]) {
638 assert(component.IsSubsetOf(todo));
642 if (picked)
assert(component[*picked]);
654 for (
auto i : component) {
660 for (
auto i : component) {
662 TestBitSet reachable = TestBitSet::Singleton(i);
665 TestBitSet new_reachable = reachable;
666 for (
auto j : new_reachable) {
667 new_reachable |= depgraph.
Ancestors(j) & todo;
670 if (new_reachable == reachable)
break;
671 reachable = new_reachable;
674 assert(component == reachable);
678 uint64_t subset_bits{0};
680 reader >>
VARINT(subset_bits);
681 }
catch (
const std::ios_base::failure&) {}
685 if (subset_bits & 1) subset.Set(i);
690 if (subset.None()) subset = TestBitSet::Singleton(todo.First());
706 reader >> Using<DepGraphFormatter>(depgraph);
707 }
catch (
const std::ios_base::failure&) {}
708 MakeConnected(depgraph);
709 SanityCheck(depgraph);
721 reader >> Using<DepGraphFormatter>(depgraph);
722 }
catch (
const std::ios_base::failure&) {}
725 auto linearization = ReadLinearization(depgraph, reader);
732 assert(chunking.size() == chunking_info.size());
733 for (
size_t i = 0; i < chunking.size(); ++i) {
734 assert(chunking[i] == chunking_info[i].feerate);
735 assert(
SetInfo(depgraph, chunking_info[i].transactions) == chunking_info[i]);
739 for (
size_t i = 1; i < chunking.size(); ++i) {
740 assert(!(chunking[i] >> chunking[i - 1]));
745 for (
const auto& [chunk_set, chunk_feerate] : chunking_info) {
750 accumulator.
Set(depgraph, idx);
780 reader >> Using<DepGraphFormatter>(depgraph);
781 }
catch (
const std::ios_base::failure&) {}
785 SimpleCandidateFinder smp_finder(depgraph);
786 ExhaustiveCandidateFinder exh_finder(depgraph);
790 assert(!smp_finder.AllDone());
791 assert(!exh_finder.AllDone());
799 assert(found.transactions.Any());
800 assert(found.transactions.IsSubsetOf(todo));
801 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
803 for (
auto i : found.transactions) {
810 assert(iterations_done <= (uint64_t{1} << (todo.Count() - 1)));
818 if (todo.Count() <= 12) {
821 auto exhaustive = exh_finder.FindCandidateSet();
822 assert(exhaustive.feerate == found.feerate);
827 auto read_topo = ReadTopologicalSet(depgraph, todo, reader,
true);
834 auto del_set = ReadTopologicalSet(depgraph, todo, reader,
true);
836 smp_finder.MarkDone(del_set);
837 exh_finder.MarkDone(del_set);
840 assert(smp_finder.AllDone());
841 assert(exh_finder.AllDone());
852 uint64_t iter_count{0};
855 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph);
856 }
catch (
const std::ios_base::failure&) {}
860 auto [linearization, optimal] = SimpleLinearize(depgraph, iter_count);
861 SanityCheck(depgraph, linearization);
867 const uint64_t n = depgraph.
TxCount();
868 if (n <= 63 && (iter_count >> n)) {
874 if (optimal && depgraph.
TxCount() <= 8) {
875 auto exh_linearization = ExhaustiveLinearize(depgraph);
879 assert(simple_chunking.size() == exh_chunking.size());
884 auto read = ReadLinearization(depgraph, reader);
898 uint64_t rng_seed{0};
900 reader >> rng_seed >>
flags >> Using<DepGraphFormatter>(depgraph);
901 }
catch (
const std::ios_base::failure&) {}
902 if (depgraph.
TxCount() <= 1)
return;
905 const bool make_connected =
flags & 1;
907 const bool load_linearization =
flags & 2;
909 const bool load_topological = load_linearization && (
flags & 4);
912 if (make_connected) MakeConnected(depgraph);
916 std::vector<FeeFrac> last_diagram;
917 bool was_optimal{
false};
918 auto test_fn = [&](
bool is_optimal =
false,
bool is_minimal =
false) {
933 if (is_optimal)
assert(cmp_lin == 0);
936 if (is_minimal)
assert(diagram.size() == lin_diagram.size());
939 if (!last_diagram.empty()) {
943 if (was_optimal)
assert(cmp == 0);
945 if (was_optimal)
assert(diagram.size() >= last_diagram.size());
947 last_diagram = std::move(diagram);
948 was_optimal = is_optimal;
951 if (load_linearization) {
952 auto input_lin = ReadLinearization(depgraph, reader, load_topological);
954 if (load_topological) {
992 auto simple_cmp =
CompareChunks(last_diagram, simple_diagram);
994 if (simple_optimal)
assert(simple_cmp == 0);
997 if (simple_cmp == 0)
assert(last_diagram.size() >= simple_diagram.size());
1001 for (
int i = 0; i < 10; ++i) {
1002 auto read_lin = ReadLinearization(depgraph, reader);
1006 if (cmp == 0)
assert(last_diagram.size() >= read_diagram.size());
1018 uint64_t rng_seed{0};
1019 uint64_t iter_count{0};
1022 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed >>
flags;
1023 }
catch (
const std::ios_base::failure&) {}
1024 bool make_connected =
flags & 1;
1031 bool provide_input =
flags & 6;
1032 bool provide_topological_input =
flags & 4;
1033 bool claim_topological_input = (
flags & 6) == 6;
1036 if (make_connected) MakeConnected(depgraph);
1039 std::vector<DepGraphIndex> old_linearization;
1040 if (provide_input) {
1041 old_linearization = ReadLinearization(depgraph, reader, provide_topological_input);
1042 if (provide_topological_input) SanityCheck(depgraph, old_linearization);
1046 iter_count &= 0x7ffff;
1047 auto [linearization, optimal, cost] =
Linearize(depgraph, iter_count, rng_seed, old_linearization, claim_topological_input);
1048 SanityCheck(depgraph, linearization);
1053 if (provide_topological_input) {
1060 if (iter_count > MaxOptimalLinearizationIters(depgraph.
TxCount())) {
1068 SanityCheck(depgraph, simple_linearization);
1074 if (simple_optimal)
assert(cmp == 0);
1078 if (cmp == 0)
assert(chunking.size() >= simple_chunking.size());
1081 auto read = ReadLinearization(depgraph, reader);
1096 reader >> Using<DepGraphFormatter>(depgraph);
1097 }
catch (
const std::ios_base::failure&) {}
1100 std::vector<DepGraphIndex> linearization;
1101 linearization = ReadLinearization(depgraph, reader);
1102 SanityCheck(depgraph, linearization);
1105 auto post_linearization = linearization;
1107 SanityCheck(depgraph, post_linearization);
1116 auto post_post_linearization = post_linearization;
1118 SanityCheck(depgraph, post_post_linearization);
1125 for (
const auto& [chunk_set, _chunk_feerate] : linchunking) {
1137 uint64_t rng_seed{0};
1139 uint8_t direction{0};
1141 reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
1142 }
catch (
const std::ios_base::failure&) {}
1144 auto depgraph_tree = BuildTreeGraph(depgraph_gen, direction);
1147 std::vector<DepGraphIndex> linearization;
1148 linearization = ReadLinearization(depgraph_tree, reader);
1149 SanityCheck(depgraph_tree, linearization);
1152 auto post_linearization = linearization;
1154 SanityCheck(depgraph_tree, post_linearization);
1164 auto post_post_linearization = post_linearization;
1166 SanityCheck(depgraph_tree, post_post_linearization);
1167 auto post_post_chunking =
ChunkLinearization(depgraph_tree, post_post_linearization);
1168 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
1173 auto [opt_linearization, _optimal, _cost] =
Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
1192 uint64_t fee_inc_code;
1193 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(fee_inc_code);
1194 fee_inc = fee_inc_code & 0x3ffff;
1195 }
catch (
const std::ios_base::failure&) {}
1196 if (depgraph.
TxCount() == 0)
return;
1199 auto lin = ReadLinearization(depgraph, reader);
1200 auto lin_leaf = ReadLinearization(depgraph, reader);
1204 std::vector<DepGraphIndex> lin_moved;
1205 for (
auto i : lin) {
1206 if (i != lin_leaf.back()) lin_moved.push_back(i);
1208 lin_moved.push_back(lin_leaf.back());
1212 SanityCheck(depgraph, lin_moved);
1216 depgraph.
FeeRate(lin_leaf.back()).
fee += fee_inc;
#define Assume(val)
Assume is the identity function.
T ConsumeIntegralInRange(T min, T max)
constexpr uint64_t rand64() noexcept
uint64_t randbits(int bits) noexcept
Generate a random (bits)-bit integer.
Minimal stream for reading from an existing byte array by std::span.
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.
SetType GetReducedChildren(DepGraphIndex i) const noexcept
Compute the (reduced) set of children of node i in this graph.
SetType GetReducedParents(DepGraphIndex i) const noexcept
Compute the (reduced) set of parents of node i in this graph.
void AppendTopo(std::vector< DepGraphIndex > &list, const SetType &select) const noexcept
Append the entries of select to list in a topologically valid order.
const FeeFrac & FeeRate(DepGraphIndex i) const noexcept
Get the feerate of a given transaction i.
SetType GetConnectedComponent(const SetType &todo, DepGraphIndex tx) const noexcept
Get the connected component within the subset "todo" that contains tx (which must be in todo).
bool IsConnected(const SetType &subset) const noexcept
Determine if a subset is connected.
DepGraphIndex PositionRange() const noexcept
Get the range of positions in this DepGraph.
SetType FindConnectedComponent(const SetType &todo) const noexcept
Find some connected component within the subset "todo" of this graph.
DepGraphIndex AddTransaction(const FeeFrac &feefrac) noexcept
Add a new unconnected transaction to this transaction graph (in the first available position),...
void RemoveTransactions(const SetType &del) noexcept
Remove the specified positions from this DepGraph.
auto TxCount() const noexcept
Get the number of transactions in the graph.
const SetType & Descendants(DepGraphIndex i) const noexcept
Get the descendants of a given transaction i.
size_t DynamicMemoryUsage() const noexcept
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
void Compact() noexcept
Reduce memory usage if possible.
void AddDependencies(const SetType &parents, DepGraphIndex child) noexcept
Modify this transaction graph, adding multiple parents to a specified child.
bool IsAcyclic() const noexcept
Check if this graph is acyclic.
Class to represent the internal state of the spanning-forest linearization (SFL) algorithm.
bool OptimizeStep() noexcept
Try to improve the forest.
uint64_t GetCost() const noexcept
Determine how much work was performed so far.
std::vector< FeeFrac > GetDiagram() const noexcept
Get the diagram for the current state, which must be topological.
void MakeTopological() noexcept
Make state topological.
void StartMinimizing() noexcept
Initialize data structure for minimizing the chunks.
void SanityCheck(const DepGraph< SetType > &depgraph) const
Verify internal consistency of the data structure.
void StartOptimizing() noexcept
Initialize the data structure for optimization.
void LoadLinearization(std::span< const DepGraphIndex > old_linearization) noexcept
Load an existing linearization.
std::vector< DepGraphIndex > GetLinearization() noexcept
Construct a topologically-valid linearization from the current forest state.
bool MinimizeStep() noexcept
Try to reduce a chunk's size.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
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={}, bool is_topological=true) noexcept
Find or improve a linearization for a cluster.
std::vector< FeeFrac > ChunkLinearization(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the feerates of the chunks of linearization.
uint32_t DepGraphIndex
Data type to represent transaction indices in DepGraphs and the clusters they represent.
void PostLinearize(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization)
Improve a given linearization.
std::vector< SetInfo< SetType > > ChunkLinearizationInfo(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > linearization) noexcept
Compute the chunks of linearization as SetInfos.
static 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.
void Set(const DepGraph< SetType > &depgraph, DepGraphIndex pos) noexcept
Add a transaction to this SetInfo (which must not yet be in it).
SetType transactions
The transactions in the set.
FUZZ_TARGET(clusterlin_depgraph_sim)
static constexpr auto MAX_SIMPLE_ITERATIONS
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.