86template<
typename SetType>
87class SimpleCandidateFinder
97 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
100 void MarkDone(SetType select)
noexcept { m_todo -= select; }
103 bool AllDone() const noexcept {
return m_todo.None(); }
113 std::pair<SetInfo<SetType>, uint64_t> FindCandidateSet(uint64_t max_iterations)
const noexcept
115 uint64_t iterations_left = max_iterations;
119 std::vector<std::pair<SetType, SetType>> queue;
121 queue.emplace_back(SetType{}, m_todo);
126 while (!queue.empty() && iterations_left) {
128 auto [inc, und] = queue.back();
131 bool inc_none = inc.None();
132 for (
auto split : und) {
140 queue.emplace_back(new_inc.transactions, und - new_inc.transactions);
144 if (new_inc.feerate > best.feerate) best = new_inc;
149 return {std::move(best), max_iterations - iterations_left};
158template<
typename SetType>
159class ExhaustiveCandidateFinder
169 m_depgraph(depgraph), m_todo{depgraph.
Positions()} {}
172 void MarkDone(SetType select)
noexcept { m_todo -= select; }
175 bool AllDone() const noexcept {
return m_todo.None(); }
186 uint64_t limit = (uint64_t{1} << m_todo.Count()) - 1;
188 for (uint64_t x = 1; x < limit; ++x) {
193 for (
auto i : m_todo) {
194 if (x_shifted & 1) txn |= m_depgraph.
Ancestors(i);
197 SetInfo cur(m_depgraph, txn & m_todo);
198 if (cur.feerate > best.feerate) best = cur;
210template<
typename SetType>
211std::pair<std::vector<DepGraphIndex>,
bool> SimpleLinearize(
const DepGraph<SetType>& depgraph, uint64_t max_iterations)
213 std::vector<DepGraphIndex> linearization;
214 SimpleCandidateFinder finder(depgraph);
218 auto [candidate, iterations_done] = finder.FindCandidateSet(max_iterations);
219 if (iterations_done == max_iterations) optimal =
false;
220 depgraph.
AppendTopo(linearization, candidate.transactions);
221 todo -= candidate.transactions;
222 finder.MarkDone(candidate.transactions);
223 max_iterations -= iterations_done;
225 return {std::move(linearization), optimal};
235template<
typename SetType>
239 std::vector<DepGraphIndex> linearization;
240 std::vector<FeeFrac> chunking;
242 std::vector<DepGraphIndex> perm_linearization;
249 TestBitSet perm_done;
250 while (topo_length < perm_linearization.size()) {
251 auto i = perm_linearization[topo_length];
253 if (!depgraph.
Ancestors(i).IsSubsetOf(perm_done))
break;
256 if (topo_length == perm_linearization.size()) {
263 if (linearization.empty() || cmp > 0 || (cmp == 0 && perm_chunking.size() > chunking.size())) {
264 linearization = perm_linearization;
265 chunking = perm_chunking;
270 auto first_non_topo = perm_linearization.begin() + topo_length;
271 assert(std::is_sorted(first_non_topo + 1, perm_linearization.end()));
272 std::reverse(first_non_topo + 1, perm_linearization.end());
274 }
while(std::next_permutation(perm_linearization.begin(), perm_linearization.end()));
276 return linearization;
291 depgraph.
AddDependencies(BS::Singleton(comp.Last()), nextcomp.First());
298template<
typename SetType>
306 }
catch(
const std::ios_base::failure&) {}
307 if (mask != uint64_t(-1)) mask += non_empty;
310 for (
auto i : todo) {
321 if (non_empty &&
ret.None()) {
333 std::vector<DepGraphIndex> linearization;
338 TestBitSet potential_next;
339 for (
auto j : todo) {
340 if ((depgraph.
Ancestors(j) & todo) == TestBitSet::Singleton(j)) {
341 potential_next.Set(j);
345 assert(potential_next.Any());
350 }
catch (
const std::ios_base::failure&) {}
351 idx %= potential_next.Count();
353 for (
auto j : potential_next) {
356 linearization.push_back(j);
364 return linearization;
379 std::array<std::optional<std::pair<FeeFrac, TestBitSet>>, TestBitSet::Size()> sim;
384 auto idx_fn = [&]() {
387 if (!sim[i].has_value())
continue;
388 if (offset == 0)
return i;
396 auto subset_fn = [&]() {
397 auto range = (uint64_t{1} << num_tx_sim) - 1;
399 auto mask_shifted = mask;
402 if (!sim[i].has_value())
continue;
403 if (mask_shifted & 1) {
408 assert(mask_shifted == 0);
413 auto set_fn = [&]() {
414 auto range = (uint64_t{1} << sim.size()) - 1;
418 if ((mask >> i) & 1) {
426 auto anc_update_fn = [&]() {
430 if (!sim[chl].has_value())
continue;
431 for (
auto par : sim[chl]->second) {
432 if (!sim[chl]->second.IsSupersetOf(sim[par]->second)) {
433 sim[chl]->second |= sim[par]->second;
446 if (sim[i].has_value()) {
457 if (num_tx_sim == 0 || ((
command % 3) <= 0 && num_tx_sim < TestBitSet::Size())) {
465 assert(!sim[idx].has_value());
467 if (!sim[i].has_value()) {
473 sim[idx] = {feerate, TestBitSet::Singleton(idx)};
477 if ((
command % 3) <= 1 && num_tx_sim > 0) {
480 auto parents = subset_fn();
484 sim[child]->second |= parents;
487 if (num_tx_sim > 0) {
494 for (
auto i : del) check_fn(i);
499 if (sim[i].has_value()) {
502 sim[i] = std::nullopt;
504 sim[i]->second -= del;
531 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(par_code) >>
VARINT(chl_code);
532 }
catch (
const std::ios_base::failure&) {}
533 SanityCheck(depgraph);
539 if (depgraph.
TxCount() < 2)
return;
542 par_code %= depgraph.
TxCount();
551 auto ancestors = depgraph.
Ancestors(par) - TestBitSet::Singleton(par);
552 if (ancestors.None())
return;
553 chl_code %= ancestors.Count();
554 for (
auto i : ancestors) {
574 std::vector<DepGraphIndex> linearization;
576 reader >> Using<DepGraphFormatter>(depgraph);
577 }
catch (
const std::ios_base::failure&) {}
582 std::optional<DepGraphIndex> picked;
584 uint64_t picked_num{0};
586 reader >>
VARINT(picked_num);
587 }
catch (
const std::ios_base::failure&) {}
588 if (picked_num < todo.Size() && todo[picked_num]) {
598 assert(component.IsSubsetOf(todo));
602 if (picked)
assert(component[*picked]);
614 for (
auto i : component) {
620 for (
auto i : component) {
622 TestBitSet reachable = TestBitSet::Singleton(i);
625 TestBitSet new_reachable = reachable;
626 for (
auto j : new_reachable) {
627 new_reachable |= depgraph.
Ancestors(j) & todo;
630 if (new_reachable == reachable)
break;
631 reachable = new_reachable;
634 assert(component == reachable);
638 uint64_t subset_bits{0};
640 reader >>
VARINT(subset_bits);
641 }
catch (
const std::ios_base::failure&) {}
645 if (subset_bits & 1) subset.Set(i);
650 if (subset.None()) subset = TestBitSet::Singleton(todo.First());
666 reader >> Using<DepGraphFormatter>(depgraph);
667 }
catch (
const std::ios_base::failure&) {}
668 MakeConnected(depgraph);
669 SanityCheck(depgraph);
681 reader >> Using<DepGraphFormatter>(depgraph);
682 }
catch (
const std::ios_base::failure&) {}
685 auto linearization = ReadLinearization(depgraph, reader);
691 for (
size_t i = 1; i < chunking.size(); ++i) {
692 assert(!(chunking[i] >> chunking[i - 1]));
697 for (
const auto& chunk_feerate : chunking) {
702 accumulator.
Set(depgraph, idx);
723 reader >> Using<DepGraphFormatter>(depgraph);
724 }
catch (
const std::ios_base::failure&) {}
734 assert(best_anc.transactions.Any());
735 assert(best_anc.transactions.IsSubsetOf(todo));
736 assert(depgraph.
FeeRate(best_anc.transactions) == best_anc.feerate);
739 for (
auto i : best_anc.transactions) {
740 assert((depgraph.
Ancestors(i) & todo).IsSubsetOf(best_anc.transactions));
744 std::optional<SetInfo<TestBitSet>> real_best_anc;
745 for (
auto i : todo) {
747 if (!real_best_anc.has_value() || info.
feerate > real_best_anc->feerate) {
748 real_best_anc = info;
752 assert(real_best_anc.has_value());
753 assert(*real_best_anc == best_anc);
758 auto del_set = ReadTopologicalSet(depgraph, todo, reader,
true);
782 reader >> Using<DepGraphFormatter>(depgraph);
783 }
catch (
const std::ios_base::failure&) {}
787 SimpleCandidateFinder smp_finder(depgraph);
788 ExhaustiveCandidateFinder exh_finder(depgraph);
793 assert(!smp_finder.AllDone());
794 assert(!exh_finder.AllDone());
804 assert(found.transactions.Any());
805 assert(found.transactions.IsSubsetOf(todo));
806 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
808 for (
auto i : found.transactions) {
815 assert(iterations_done <= (uint64_t{1} << (todo.Count() - 1)));
825 assert(anc.feerate <= found.feerate);
827 if (todo.Count() <= 12) {
830 auto exhaustive = exh_finder.FindCandidateSet();
831 assert(exhaustive.feerate == found.feerate);
836 auto read_topo = ReadTopologicalSet(depgraph, todo, reader,
true);
843 auto del_set = ReadTopologicalSet(depgraph, todo, reader,
true);
845 smp_finder.MarkDone(del_set);
846 exh_finder.MarkDone(del_set);
850 assert(smp_finder.AllDone());
851 assert(exh_finder.AllDone());
865 uint64_t rng_seed{0};
866 uint8_t make_connected{1};
868 reader >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected;
869 }
catch (
const std::ios_base::failure&) {}
872 if (make_connected) MakeConnected(depgraph);
876 SimpleCandidateFinder smp_finder(depgraph);
882 assert(!smp_finder.AllDone());
887 uint64_t max_iterations = 1;
889 reader >>
VARINT(max_iterations);
890 }
catch (
const std::ios_base::failure&) {}
891 max_iterations &= 0xfffff;
894 auto init_set = ReadTopologicalSet(depgraph, todo, reader,
false);
895 SetInfo init_best(depgraph, init_set);
898 auto [found, iterations_done] = src_finder.
FindCandidateSet(max_iterations, init_best);
899 bool optimal = iterations_done < max_iterations;
902 assert(iterations_done <= max_iterations);
903 assert(found.transactions.Any());
904 assert(found.transactions.IsSubsetOf(todo));
905 assert(depgraph.
FeeRate(found.transactions) == found.feerate);
908 for (
auto i : found.transactions) {
916 assert(iterations_done <= (uint64_t{1} << (todo.Count() - 1)));
920 if (iterations_done >= 1 && todo.Count() <= 63) {
921 Assume((iterations_done - 1) * (iterations_done - 1) <= uint64_t{1} << todo.Count());
931 assert(found.feerate >= simple.feerate);
933 assert(found.feerate == simple.feerate);
938 assert(found.feerate >= anc.feerate);
942 auto read_topo = ReadTopologicalSet(depgraph, todo, reader,
true);
949 auto del_set = ReadTopologicalSet(depgraph, todo, reader,
true);
952 smp_finder.MarkDone(del_set);
957 assert(smp_finder.AllDone());
970 reader >> Using<DepGraphFormatter>(depgraph);
971 }
catch (
const std::ios_base::failure&) {}
976 auto subset =
SetInfo(depgraph, ReadTopologicalSet(depgraph, todo, reader,
false));
979 auto linearization = ReadLinearization(depgraph, reader);
990 std::vector<DepGraphIndex> linearization_left;
991 for (
auto i : linearization) {
992 if (todo[i]) linearization_left.push_back(i);
1005 TestBitSet combined;
1007 const auto& chunk_info = chunking.
GetChunk(i);
1009 assert(chunk_info.transactions.Any());
1011 if (i > 0)
assert(!(chunk_info.feerate >> chunking.
GetChunk(i - 1).feerate));
1013 assert(chunk_info.transactions.IsSubsetOf(todo));
1015 assert(depgraph.
FeeRate(chunk_info.transactions) == chunk_info.feerate);
1018 for (
auto j : linearization) {
1019 if (todo[j] && !combined[j]) {
1020 accumulator.
Set(depgraph, j);
1029 assert(!chunk_info.transactions.Overlaps(combined));
1030 combined |= chunk_info.transactions;
1032 for (
auto idx : chunk_info.transactions) {
1036 assert(combined == todo);
1043 TestBitSet intersect_anc;
1044 for (
auto idx : intersect.transactions) {
1045 intersect_anc |= (depgraph.
Ancestors(idx) & todo);
1047 assert(intersect.transactions == intersect_anc);
1049 assert(intersect.feerate == depgraph.
FeeRate(intersect.transactions));
1051 assert(intersect.transactions.Any() == subset.transactions.Any());
1053 assert(intersect.feerate >= subset.feerate);
1059 auto reintersect =
SetInfo(depgraph,
prefix & intersect.transactions);
1060 if (!reintersect.feerate.IsEmpty()) {
1061 assert(reintersect.feerate <= intersect.feerate);
1068 auto done = ReadTopologicalSet(depgraph, todo, reader,
true);
1071 subset =
SetInfo(depgraph, subset.transactions - done);
1085 uint64_t iter_count{0};
1088 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph);
1089 }
catch (
const std::ios_base::failure&) {}
1093 auto [linearization, optimal] = SimpleLinearize(depgraph, iter_count);
1094 SanityCheck(depgraph, linearization);
1100 const uint64_t n = depgraph.
TxCount();
1101 if (n <= 63 && (iter_count >> n)) {
1107 if (optimal && depgraph.
TxCount() <= 8) {
1108 auto exh_linearization = ExhaustiveLinearize(depgraph);
1112 assert(simple_chunking.size() == exh_chunking.size());
1117 auto read = ReadLinearization(depgraph, reader);
1132 uint64_t rng_seed{0};
1133 uint64_t iter_count{0};
1134 uint8_t make_connected{1};
1136 reader >>
VARINT(iter_count) >> Using<DepGraphFormatter>(depgraph) >> rng_seed >> make_connected;
1137 }
catch (
const std::ios_base::failure&) {}
1140 if (make_connected) MakeConnected(depgraph);
1143 std::vector<DepGraphIndex> old_linearization;
1145 uint8_t have_old_linearization{0};
1147 reader >> have_old_linearization;
1148 }
catch(
const std::ios_base::failure&) {}
1149 if (have_old_linearization & 1) {
1150 old_linearization = ReadLinearization(depgraph, reader);
1151 SanityCheck(depgraph, old_linearization);
1156 iter_count &= 0x7ffff;
1157 auto [linearization, optimal, cost] =
Linearize(depgraph, iter_count, rng_seed, old_linearization);
1158 assert(cost <= iter_count);
1159 SanityCheck(depgraph, linearization);
1163 if (!old_linearization.empty()) {
1170 if (iter_count >= MaxOptimalLinearizationIters(depgraph.
TxCount())) {
1178 SanityCheck(depgraph, simple_linearization);
1184 if (simple_optimal)
assert(cmp == 0);
1187 if (cmp == 0)
assert(chunking.size() >= simple_chunking.size());
1190 auto read = ReadLinearization(depgraph, reader);
1205 reader >> Using<DepGraphFormatter>(depgraph);
1206 }
catch (
const std::ios_base::failure&) {}
1209 std::vector<DepGraphIndex> linearization;
1210 linearization = ReadLinearization(depgraph, reader);
1211 SanityCheck(depgraph, linearization);
1214 auto post_linearization = linearization;
1216 SanityCheck(depgraph, post_linearization);
1225 auto post_post_linearization = post_linearization;
1227 SanityCheck(depgraph, post_post_linearization);
1247 uint64_t rng_seed{0};
1249 uint8_t direction{0};
1251 reader >> direction >> rng_seed >> Using<DepGraphFormatter>(depgraph_gen);
1252 }
catch (
const std::ios_base::failure&) {}
1268 if (direction & 1) {
1271 if (children.Any()) {
1272 depgraph_tree.
AddDependencies(TestBitSet::Singleton(i), children.First());
1278 if (parents.Any()) {
1279 depgraph_tree.
AddDependencies(TestBitSet::Singleton(parents.First()), i);
1285 std::vector<DepGraphIndex> linearization;
1286 linearization = ReadLinearization(depgraph_tree, reader);
1287 SanityCheck(depgraph_tree, linearization);
1290 auto post_linearization = linearization;
1292 SanityCheck(depgraph_tree, post_linearization);
1302 auto post_post_linearization = post_linearization;
1304 SanityCheck(depgraph_tree, post_linearization);
1305 auto post_post_chunking =
ChunkLinearization(depgraph_tree, post_post_linearization);
1306 auto cmp_post =
CompareChunks(post_post_chunking, post_chunking);
1311 auto [opt_linearization, _optimal, _cost] =
Linearize(depgraph_tree, 100000, rng_seed, post_linearization);
1330 uint64_t fee_inc_code;
1331 reader >> Using<DepGraphFormatter>(depgraph) >>
VARINT(fee_inc_code);
1332 fee_inc = fee_inc_code & 0x3ffff;
1333 }
catch (
const std::ios_base::failure&) {}
1334 if (depgraph.
TxCount() == 0)
return;
1337 auto lin = ReadLinearization(depgraph, reader);
1338 auto lin_leaf = ReadLinearization(depgraph, reader);
1342 std::vector<DepGraphIndex> lin_moved;
1343 for (
auto i : lin) {
1344 if (i != lin_leaf.back()) lin_moved.push_back(i);
1346 lin_moved.push_back(lin_leaf.back());
1350 SanityCheck(depgraph, lin_moved);
1354 depgraph.
FeeRate(lin_leaf.back()).
fee += fee_inc;
1366 reader >> Using<DepGraphFormatter>(depgraph);
1367 }
catch (
const std::ios_base::failure&) {}
1370 auto lin1 = ReadLinearization(depgraph, reader);
1371 auto lin2 = ReadLinearization(depgraph, reader);
1394 reader >> Using<DepGraphFormatter>(depgraph);
1395 }
catch (
const std::ios_base::failure&) {}
1398 std::vector<DepGraphIndex> linearization;
1401 while (todo.Any()) {
1406 }
catch (
const std::ios_base::failure&) {}
1407 val %= todo.Count();
1409 for (
auto idx : todo) {
1411 linearization.push_back(idx);
1423 size_t topo_prefix = 0;
1425 while (topo_prefix < linearization.size()) {
1428 if (todo.Overlaps(depgraph.
Ancestors(idx)))
break;
1433 auto linearization_fixed = linearization;
1436 SanityCheck(depgraph, linearization_fixed);
1439 assert(std::equal(linearization.begin(), linearization.begin() + topo_prefix,
1440 linearization_fixed.begin()));
1443 if (topo_prefix == linearization.size()) {
1444 assert(linearization == linearization_fixed);
#define Assume(val)
Assume is the identity function.
T ConsumeIntegralInRange(T min, T max)
Minimal stream for reading from an existing byte array by std::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.
DepGraphIndex 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,...
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.
const SetType & Positions() const noexcept
Get the set of transactions positions in use.
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.
Data structure encapsulating the chunking of a linearization, permitting removal of subsets.
DepGraphIndex NumChunksLeft() const noexcept
Determine how many chunks remain in the linearization.
const SetInfo< SetType > & GetChunk(DepGraphIndex n) const noexcept
Access a chunk.
SetInfo< SetType > IntersectPrefixes(const SetInfo< SetType > &subset) const noexcept
Find the shortest intersection between subset and the prefixes of remaining chunks of the linearizati...
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::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.
std::vector< DepGraphIndex > MergeLinearizations(const DepGraph< SetType > &depgraph, std::span< const DepGraphIndex > lin1, std::span< const DepGraphIndex > lin2)
Merge two linearizations for the same cluster into one that is as good as both.
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.
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.