73template<
typename SetType>
74class SimpleCandidateFinder
84 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
87 void MarkDone(SetType select)
noexcept { m_todo -= select; }
90 bool AllDone() const noexcept {
return m_todo.None(); }
100 std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations)
const noexcept
102 uint64_t iterations_left = max_iterations;
106 std::vector<std::pair<SetType, SetType>> queue;
108 queue.emplace_back(SetType{}, m_todo);
113 while (!queue.empty() && iterations_left) {
115 auto [inc, und] = queue.back();
118 bool inc_none = inc.None();
119 for (
auto split : und) {
127 queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
131 if (new_inc.feerate > best.feerate) best = new_inc;
136 return {std::move(best), max_iterations - iterations_left};
146template<
typename SetType>
147class ExhaustiveCandidateFinder
157 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
160 void MarkDone(SetType select)
noexcept { m_todo -= select; }
163 bool AllDone() const noexcept {
return m_todo.None(); }
174 uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
176 for (uint64_t x = 1; x < limit; ++x) {
181 for (
auto i : m_todo) {
182 if (x_shifted & 1) txn |= m_depgraph.
Ancestors(i);
185 SetInfo cur(m_depgraph, txn & m_todo);
186 if (cur.feerate > best.feerate) best = cur;
198template<
typename SetType>
199std::pair<std::vector<DepGraphIndex>,
bool> SimpleLinearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations)
201 std::vector<DepGraphIndex> linearization;
202 SimpleCandidateFinder finder(depgraph);
206 auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
207 if (iterations_done == max_iterations) optimal =
false;
208 depgraph.
AppendTopo(linearization, candidate.transactions);
209 todo -= candidate.transactions;
210 finder.MarkDone(candidate.transactions);
211 max_iterations -= iterations_done;
213 return {std::move(linearization), optimal};
223template<
typename SetType>
227 std::vector<DepGraphIndex> linearization;
228 std::vector<FeeFrac> chunking;
230 std::vector<DepGraphIndex> perm_linearization;
237 TestBitSet perm_done;
238 while (topo_length < perm_linearization.size()) {
239 auto i = perm_linearization[topo_length];
241 if (!depgraph.
Ancestors(i).IsSubsetOf(perm_done))
break;
244 if (topo_length == perm_linearization.size()) {
251 if (linearization.empty() || cmp > 0 || (cmp == 0 && perm_chunking.size() > chunking.size())) {
252 linearization = perm_linearization;
253 chunking = perm_chunking;
258 auto first_non_topo = perm_linearization.begin() + topo_length;
259 assert(std::is_sorted(first_non_topo + 1, perm_linearization.end()));
260 std::reverse(first_non_topo + 1, perm_linearization.end());
262 }
while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
264 return linearization;
279 depgraph.
AddDependencies(BS::Singleton(comp.Last()), nextcomp.First());
286template<
typename SetType>
294 }
catch(
const std::ios_base::failure&) {}
295 if (mask != uint64_t(-1)) mask += non_empty;
298 for (
auto i : todo) {
309 if (non_empty &&
ret.None()) {
319std::vector<DepGraphIndex> ReadLinearization(
const DepGraph<BS>& depgraph,
SpanReader& reader,
bool topological=
true)
321 std::vector<DepGraphIndex> linearization;
326 TestBitSet potential_next;
329 for (
auto j : todo) {
330 if ((depgraph.
Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
331 potential_next.Set(j);
336 potential_next = todo;
339 assert(potential_next.Any());
344 }
catch (
const std::ios_base::failure&) {}
345 idx %= potential_next.Count();
347 for (
auto j : potential_next) {
350 linearization.push_back(j);
358 return linearization;
384 if (children.Any()) {
397 return depgraph_tree;
412 std::array<std::optional<std::pair<FeeFrac, TestBitSet>>, TestBitSet::Size()> sim;
417 auto idx_fn = [&]() {
420 if (!sim[i].has_value())
continue;
421 if (offset == 0)
return i;
429 auto subset_fn = [&]() {
430 auto range = (uint64_t{1} << num_tx_sim) - 1;
432 auto mask_shifted = mask;
435 if (!sim[i].has_value())
continue;
436 if (mask_shifted & 1) {
441 assert(mask_shifted == 0);
446 auto set_fn = [&]() {
447 auto range = (uint64_t{1} << sim.size()) - 1;
451 if ((mask >> i) & 1) {
459 auto anc_update_fn = [&]() {
463 if (!sim[chl].has_value())
continue;
464 for (
auto par : sim[chl]->second) {
465 if (!sim[chl]->second.IsSupersetOf(sim[par]->second)) {
466 sim[chl]->second |= sim[par]->second;
479 if (sim[i].has_value()) {
494 if (num_tx_sim < TestBitSet::Size() &&
command-- == 0) {
502 assert(!sim[idx].has_value());
504 if (!sim[i].has_value()) {
510 sim[idx] = {feerate, TestBitSet::Singleton(idx)};
513 }
else if (num_tx_sim > 0 &&
command-- == 0) {
516 auto parents = subset_fn();
520 sim[child]->second |= parents;
522 }
else if (num_tx_sim > 0 &&
command-- == 0) {
529 for (
auto i : del) check_fn(i);
534 if (sim[i].has_value()) {
537 sim[i] = std::nullopt;
539 sim[i]->second -= del;
549 assert(real.
PositionRange() < last_compaction_pos ? mem_after < mem_before : mem_after <= mem_before);
573 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(par_code) >>
VARINT(chl_code);
574 }
catch (
const std::ios_base::failure&) {}
575 SanityCheck(depgraph);
581 if (depgraph.
TxCount() < 2)
return;
584 par_code %= depgraph.
TxCount();
593 auto ancestors = depgraph.
Ancestors(par) - TestBitSet::Singleton(par);
594 if (ancestors.None())
return;
595 chl_code %= ancestors.Count();
596 for (
auto i : ancestors) {
616 std::vector<DepGraphIndex> linearization;
618 reader >> Using<DepGraphFormatter>(depgraph);
619 }
catch (
const std::ios_base::failure&) {}
624 std::optional<DepGraphIndex> picked;
626 uint64_t picked_num{0};
628 reader >>
VARINT(picked_num);
629 }
catch (
const std::ios_base::failure&) {}
630 if (picked_num < todo.Size() && todo[picked_num]) {
640 assert(component.IsSubsetOf(todo));
644 if (picked)
assert(component[*picked]);
656 for (
auto i : component) {
662 for (
auto i : component) {
664 TestBitSet reachable = TestBitSet::Singleton(i);
667 TestBitSet new_reachable = reachable;
668 for (
auto j : new_reachable) {
669 new_reachable |= depgraph.
Ancestors(j) & todo;
672 if (new_reachable == reachable)
break;
673 reachable = new_reachable;
676 assert(component == reachable);
680 uint64_t subset_bits{0};
682 reader >>
VARINT(subset_bits);
683 }
catch (
const std::ios_base::failure&) {}
687 if (subset_bits & 1) subset.Set(i);
692 if (subset.None()) subset = TestBitSet::Singleton(todo.First());
708 reader >> Using<DepGraphFormatter>(depgraph);
709 }
catch (
const std::ios_base::failure&) {}
710 MakeConnected(depgraph);
711 SanityCheck(depgraph);
723 reader >> Using<DepGraphFormatter>(depgraph);
724 }
catch (
const std::ios_base::failure&) {}
727 auto linearization = ReadLinearization(depgraph, reader);
734 assert(chunking.size() == chunking_info.size());
735 for (
size_t i = 0; i < chunking.size(); ++i) {
736 assert(chunking[i] == chunking_info[i].feerate);
737 assert(
SetInfo(depgraph, chunking_info[i].transactions) == chunking_info[i]);
741 for (
size_t i = 1; i < chunking.size(); ++i) {
742 assert(!(chunking[i] >> chunking[i - 1]));
747 for (
const auto& [chunk_set, chunk_feerate] : chunking_info) {
752 accumulator.
Set(depgraph, idx);
782 reader >> Using<DepGraphFormatter>(depgraph);
783 }
catch (
const std::ios_base::failure&) {}
787 SimpleCandidateFinder smp_finder(depgraph);
788 ExhaustiveCandidateFinder exh_finder(depgraph);
792 assert(!smp_finder.AllDone());
793 assert(!exh_finder.AllDone());
801 assert(found.transactions.Any());
802 assert(found.transactions.IsSubsetOf(todo));
803 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
805 for (
auto i : found.transactions) {
812 assert(iterations_done <= (uint64_t{1} << (todo.Count() - 1)));
820 if (todo.Count() <= 12) {
823 auto exhaustive = exh_finder.FindCandidateSet();
824 assert(exhaustive.feerate == found.feerate);
829 auto read_topo = ReadTopologicalSet(depgraph, todo, reader,
true);
836 auto del_set = ReadTopologicalSet(depgraph, todo, reader,
true);
838 smp_finder.MarkDone(del_set);
839 exh_finder.MarkDone(del_set);
842 assert(smp_finder.AllDone());
843 assert(exh_finder.AllDone());
854 uint64_t iter_count{0};
857 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph);
858 }
catch (
const std::ios_base::failure&) {}
862 auto [linearization, optimal] = SimpleLinearize(depgraph, iter_count);
863 SanityCheck(depgraph, linearization);
869 const uint64_t n = depgraph.
TxCount();
870 if (n <= 63 && (iter_count >> n)) {
876 if (optimal && depgraph.
TxCount() <= 8) {
877 auto exh_linearization = ExhaustiveLinearize(depgraph);
881 assert(simple_chunking.size() == exh_chunking.size());
886 auto read = ReadLinearization(depgraph, reader);
900 uint64_t rng_seed{0};
902 reader >> rng_seed >>
flags >> Using<DepGraphFormatter>(depgraph);
903 }
catch (
const std::ios_base::failure&) {}
904 if (depgraph.
TxCount() <= 1)
return;
907 const bool make_connected =
flags & 1;
909 const bool load_linearization =
flags & 2;
911 const bool load_topological = load_linearization && (
flags & 4);
914 if (make_connected) MakeConnected(depgraph);
918 std::vector<FeeFrac> last_diagram;
919 auto test_fn = [&](
bool is_optimal =
false) {
934 if (is_optimal)
assert(cmp_lin == 0);
937 if (!last_diagram.empty()) {
941 last_diagram = std::move(diagram);
944 if (load_linearization) {
945 auto input_lin = ReadLinearization(depgraph, reader, load_topological);
947 if (load_topological) {
977 auto simple_cmp =
CompareChunks(last_diagram, simple_diagram);
979 if (simple_optimal)
assert(simple_cmp == 0);
983 for (
int i = 0; i < 10; ++i) {
984 auto read_lin = ReadLinearization(depgraph, reader);
999 uint64_t rng_seed{0};
1000 uint64_t iter_count{0};
1001 uint8_t make_connected{1};
1003 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected;
1004 }
catch (
const std::ios_base::failure&) {}
1007 if (make_connected) MakeConnected(depgraph);
1010 std::vector<DepGraphIndex> old_linearization;
1012 uint8_t have_old_linearization{0};
1014 reader >> have_old_linearization;
1015 }
catch(
const std::ios_base::failure&) {}
1016 if (have_old_linearization & 1) {
1017 old_linearization = ReadLinearization(depgraph, reader);
1018 SanityCheck(depgraph, old_linearization);
1023 iter_count &= 0x7ffff;
1024 auto [linearization, optimal, cost] =
Linearize(depgraph, iter_count, rng_seed, old_linearization);
1025 SanityCheck(depgraph, linearization);
1029 if (!old_linearization.empty()) {
1036 if (iter_count > MaxOptimalLinearizationIters(depgraph.
TxCount())) {
1044 SanityCheck(depgraph, simple_linearization);
1050 if (simple_optimal)
assert(cmp == 0);
1060 auto read = ReadLinearization(depgraph, reader);
1075 reader >> Using<DepGraphFormatter>(depgraph);
1076 }
catch (
const std::ios_base::failure&) {}
1079 std::vector<DepGraphIndex> linearization;
1080 linearization = ReadLinearization(depgraph, reader);
1081 SanityCheck(depgraph, linearization);
1084 auto post_linearization = linearization;
1086 SanityCheck(depgraph, post_linearization);
1095 auto post_post_linearization = post_linearization;
1097 SanityCheck(depgraph, post_post_linearization);
1104 for (
const auto& [chunk_set, _chunk_feerate] : linchunking) {
1116 uint64_t rng_seed{0};
1118 uint8_t direction{0};
1120 reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
1121 }
catch (
const std::ios_base::failure&) {}
1123 auto depgraph_tree = BuildTreeGraph(depgraph_gen, direction);
1126 std::vector<DepGraphIndex> linearization;
1127 linearization = ReadLinearization(depgraph_tree, reader);
1128 SanityCheck(depgraph_tree, linearization);
1131 auto post_linearization = linearization;
1133 SanityCheck(depgraph_tree, post_linearization);
1143 auto post_post_linearization = post_linearization;
1145 SanityCheck(depgraph_tree, post_post_linearization);
1146 auto post_post_chunking =
ChunkLinearization(depgraph_tree, post_post_linearization);
1147 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
1152 auto [opt_linearization, _optimal, _cost] =
Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
1171 uint64_t fee_inc_code;
1172 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(fee_inc_code);
1173 fee_inc = fee_inc_code & 0x3ffff;
1174 }
catch (
const std::ios_base::failure&) {}
1175 if (depgraph.
TxCount() == 0)
return;
1178 auto lin = ReadLinearization(depgraph, reader);
1179 auto lin_leaf = ReadLinearization(depgraph, reader);
1183 std::vector<DepGraphIndex> lin_moved;
1184 for (
auto i : lin) {
1185 if (i != lin_leaf.back()) lin_moved.push_back(i);
1187 lin_moved.push_back(lin_leaf.back());
1191 SanityCheck(depgraph, lin_moved);
1195 depgraph.
FeeRate(lin_leaf.back()).
fee += fee_inc;
1209 reader >> Using<DepGraphFormatter>(depgraph);
1210 }
catch (
const std::ios_base::failure&) {}
1213 std::vector<DepGraphIndex> linearization = ReadLinearization(depgraph, reader,
false);
1219 size_t topo_prefix = 0;
1221 while (topo_prefix < linearization.size()) {
1224 if (todo.Overlaps(depgraph.
Ancestors(idx)))
break;
1229 auto linearization_fixed = linearization;
1232 SanityCheck(depgraph, linearization_fixed);
1235 assert(std::equal(linearization.begin(), linearization.begin() + topo_prefix,
1236 linearization_fixed.begin()));
1239 if (topo_prefix == linearization.size()) {
1240 assert(linearization == linearization_fixed);
#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 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.
#define LIMITED_WHILE(condition, limit)
Can be used to limit a theoretically unbounded loop.
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.
void FixLinearization(const DepGraph< SetType > &depgraph, std::span< DepGraphIndex > linearization) noexcept
Make linearization topological, retaining its ordering where possible.
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.