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 max_cost{0};
1022 reader >>
VARINT(max_cost) >> Using<DepGraphFormatter>(depgraph) >> rng_seed >>
flags;
1023 }
catch (
const std::ios_base::failure&) {}
1024 if (depgraph.
TxCount() <= 1)
return;
1025 bool make_connected =
flags & 1;
1032 bool provide_input =
flags & 6;
1033 bool provide_topological_input =
flags & 4;
1034 bool claim_topological_input = (
flags & 6) == 6;
1037 if (make_connected) MakeConnected(depgraph);
1040 std::vector<DepGraphIndex> old_linearization;
1041 if (provide_input) {
1042 old_linearization = ReadLinearization(depgraph, reader, provide_topological_input);
1043 if (provide_topological_input) SanityCheck(depgraph, old_linearization);
1047 max_cost &= 0x3fffff;
1048 auto [linearization, optimal, cost] =
Linearize(
1054 claim_topological_input);
1055 SanityCheck(depgraph, linearization);
1060 if (provide_topological_input) {
1067 if (max_cost > MaxOptimalLinearizationCost(depgraph.
TxCount())) {
1075 SanityCheck(depgraph, simple_linearization);
1081 if (simple_optimal)
assert(cmp == 0);
1085 if (cmp == 0)
assert(chunking.size() >= simple_chunking.size());
1088 auto read = ReadLinearization(depgraph, reader);
1102 for (
const auto& chunk : chunking_info) {
1103 auto chunk_start = pos;
1104 auto chunk_end = pos + chunk.transactions.Count() - 1;
1106 for (
unsigned pos1 = chunk_start; pos1 <= chunk_end; ++pos1) {
1107 auto tx1 = linearization[pos1];
1108 for (
unsigned pos2 = pos1 + 1; pos2 <= chunk_end; ++pos2) {
1109 auto tx2 = linearization[pos2];
1111 if ((depgraph.
Ancestors(tx2) - done).Count() == 1) {
1124 pos += chunk.transactions.Count();
1133 for (
unsigned chunk_num1 = 0; chunk_num1 < chunking_info.size(); ++chunk_num1) {
1134 const auto& chunk1 = chunking_info[chunk_num1];
1135 for (
unsigned chunk_num2 = chunk_num1 + 1; chunk_num2 < chunking_info.size(); ++chunk_num2) {
1136 const auto& chunk2 = chunking_info[chunk_num2];
1137 TestBitSet chunk2_ancestors;
1138 for (
auto tx : chunk2.transactions) chunk2_ancestors |= depgraph.
Ancestors(tx);
1140 if ((chunk2_ancestors - done).IsSubsetOf(chunk2.transactions)) {
1143 assert(chunk1.feerate >= chunk2.feerate);
1145 if (chunk1.feerate == chunk2.feerate) {
1146 assert(chunk1.transactions.Last() < chunk2.transactions.Last());
1150 done |= chunk1.transactions;
1155 auto [linearization2, optimal2, cost2] =
Linearize(depgraph, MaxOptimalLinearizationCost(depgraph.
TxCount()) + 1, rng_seed ^ 0x1337,
IndexTxOrder{});
1157 assert(linearization2 == linearization);
1169 reader >> Using<DepGraphFormatter>(depgraph);
1170 }
catch (
const std::ios_base::failure&) {}
1173 std::vector<DepGraphIndex> linearization;
1174 linearization = ReadLinearization(depgraph, reader);
1175 SanityCheck(depgraph, linearization);
1178 auto post_linearization = linearization;
1180 SanityCheck(depgraph, post_linearization);
1189 auto post_post_linearization = post_linearization;
1191 SanityCheck(depgraph, post_post_linearization);
1198 for (
const auto& [chunk_set, _chunk_feerate] : linchunking) {
1210 uint64_t rng_seed{0};
1212 uint8_t direction{0};
1214 reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
1215 }
catch (
const std::ios_base::failure&) {}
1217 auto depgraph_tree = BuildTreeGraph(depgraph_gen, direction);
1220 std::vector<DepGraphIndex> linearization;
1221 linearization = ReadLinearization(depgraph_tree, reader);
1222 SanityCheck(depgraph_tree, linearization);
1225 auto post_linearization = linearization;
1227 SanityCheck(depgraph_tree, post_linearization);
1237 auto post_post_linearization = post_linearization;
1239 SanityCheck(depgraph_tree, post_post_linearization);
1240 auto post_post_chunking =
ChunkLinearization(depgraph_tree, post_post_linearization);
1241 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
1246 auto [opt_linearization, _optimal, _cost] =
Linearize(depgraph_tree, 1000000, rng_seed,
IndexTxOrder{}, post_linearization);
1265 uint64_t fee_inc_code;
1266 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(fee_inc_code);
1267 fee_inc = fee_inc_code & 0x3ffff;
1268 }
catch (
const std::ios_base::failure&) {}
1269 if (depgraph.
TxCount() == 0)
return;
1272 auto lin = ReadLinearization(depgraph, reader);
1273 auto lin_leaf = ReadLinearization(depgraph, reader);
1277 std::vector<DepGraphIndex> lin_moved;
1278 for (
auto i : lin) {
1279 if (i != lin_leaf.back()) lin_moved.push_back(i);
1281 lin_moved.push_back(lin_leaf.back());
1285 SanityCheck(depgraph, lin_moved);
1289 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.
void StartMinimizing() noexcept
Initialize data structure for minimizing the chunks.
std::vector< DepGraphIndex > GetLinearization(const StrongComparator< DepGraphIndex > auto &fallback_order) noexcept
Construct a topologically-valid linearization from the current forest state.
bool MinimizeStep() noexcept
Try to reduce a chunk's size.
std::vector< FeeFrac > GetDiagram() const noexcept
Get the diagram for the current state, which must be topological.
void LoadLinearization(std::span< const DepGraphIndex > old_linearization) noexcept
Load an existing linearization.
bool OptimizeStep() noexcept
Try to improve the forest.
void StartOptimizing() noexcept
Initialize the data structure for optimization.
void MakeTopological() noexcept
Make state topological.
uint64_t GetCost() const noexcept
Determine how much work was performed so far.
void SanityCheck() const
Verify internal consistency of the data structure.
#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_cost, uint64_t rng_seed, const StrongComparator< DepGraphIndex > auto &fallback_order, std::span< const DepGraphIndex > old_linearization={}, bool is_topological=true) noexcept
Find or improve a linearization for a cluster.
std::compare_three_way IndexTxOrder
Simple default transaction ordering function for SpanningForestState::GetLinearization() and Lineariz...
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.