From 02494ac5f09e22c7ef78834952fd8cb8068e8a67 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Fri, 15 May 2026 01:30:21 +0300 Subject: [PATCH 01/12] add dfuds implementation and tests --- include/pixie/dfuds_tree.h | 1 + include/pixie/utils.h | 33 +++++++++++++++++++++++++++++++++ src/tests/dfuds_tree_tests.cpp | 10 +++------- 3 files changed, 37 insertions(+), 7 deletions(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index 8215b84..8ae243d 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -18,6 +18,7 @@ class DFUDSTree { RmMTree rmm_; public: + struct Node { size_t number; diff --git a/include/pixie/utils.h b/include/pixie/utils.h index 2ce0234..38b8e5b 100644 --- a/include/pixie/utils.h +++ b/include/pixie/utils.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -8,6 +9,8 @@ using pixie::LoudsNode; +using Node = pixie::DFUDSTree::Node; + std::vector> generate_random_tree(size_t tree_size, std::mt19937_64& rng) { if (tree_size == 0) { @@ -85,6 +88,28 @@ std::vector adj_to_louds( return louds; } +std::vector adj_to_dfuds( + size_t tree_size, + const std::vector>& adj) { + size_t dfuds_size = tree_size * 2 - 1; + std::vector dfuds((dfuds_size + 63) / 64, 0); + std::vector stack; + stack.push_back(0); + size_t pos = 0; + while (!stack.empty()) { + auto v = stack.back(); + stack.pop_back(); + size_t edge_count = adj[v].size(); + for (size_t i = 0; i < edge_count - 1; ++i) { // edge 0 goes to parent + dfuds[pos >> 6] = dfuds[pos >> 6] | (1ULL << (pos & 63)); + pos++; + stack.push_back(adj[v][edge_count - 1 - i]); + } + pos++; + } + return dfuds; +} + struct AdjListNode { size_t number; }; @@ -97,6 +122,14 @@ bool operator==(const LoudsNode& b, const AdjListNode& a) { return a.number == b.number; } +bool operator==(const AdjListNode& a, const Node& b) { + return a.number == b.number; +} + +bool operator==(const Node& b, const AdjListNode& a) { + return a.number == b.number; +} + class AdjListTree { private: std::vector> adj; diff --git a/src/tests/dfuds_tree_tests.cpp b/src/tests/dfuds_tree_tests.cpp index 4426250..ad5cd9b 100644 --- a/src/tests/dfuds_tree_tests.cpp +++ b/src/tests/dfuds_tree_tests.cpp @@ -7,7 +7,6 @@ #include using Node = pixie::DFUDSTree::Node; -using pixie::adj_to_dfuds; using pixie::DFUDSTree; TEST(DfudsTreeTest, Basic) { @@ -50,12 +49,10 @@ TEST(DfudsTreeTest, RandomTreeDFS) { EXPECT_EQ(dfuds_tree.parent(cur), debug_tree.parent(debug)); if (cur.number > 0) { - EXPECT_EQ(dfuds_tree.is_last_child(cur), - debug_tree.is_last_child(debug)); + EXPECT_EQ(dfuds_tree.is_last_child(cur), debug_tree.is_last_child(debug)); } size_t deg = dfuds_tree.degree(cur); EXPECT_EQ(deg, debug_tree.degree(debug)); - EXPECT_EQ(dfuds_tree.is_leaf(cur), debug_tree.is_leaf(debug)); if (deg == 0) { continue; @@ -71,6 +68,7 @@ TEST(DfudsTreeTest, RandomTreeDFS) { } } + TEST(DfudsTreeTest, RandomTreeBFS) { for (size_t tree_size = 8; tree_size < (1 << 22); tree_size <<= 1) { std::mt19937_64 rng(42); @@ -91,12 +89,10 @@ TEST(DfudsTreeTest, RandomTreeBFS) { EXPECT_EQ(dfuds_tree.parent(cur), debug_tree.parent(debug)); if (cur.number > 0) { - EXPECT_EQ(dfuds_tree.is_last_child(cur), - debug_tree.is_last_child(debug)); + EXPECT_EQ(dfuds_tree.is_last_child(cur), debug_tree.is_last_child(debug)); } size_t deg = dfuds_tree.degree(cur); EXPECT_EQ(deg, debug_tree.degree(debug)); - EXPECT_EQ(dfuds_tree.is_leaf(cur), debug_tree.is_leaf(debug)); if (deg == 0) { continue; From 42915a63e31262ff0909b6173c8aef625e878016 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Fri, 15 May 2026 02:05:25 +0300 Subject: [PATCH 02/12] fix DFUDSTree.size --- include/pixie/dfuds_tree.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index 8ae243d..8215b84 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -18,7 +18,6 @@ class DFUDSTree { RmMTree rmm_; public: - struct Node { size_t number; From 5f8cfb53f6f4b6daceb63cb83c18f284d10e7eea Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Sat, 16 May 2026 02:08:15 +0300 Subject: [PATCH 03/12] fix clang format --- src/tests/dfuds_tree_tests.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/tests/dfuds_tree_tests.cpp b/src/tests/dfuds_tree_tests.cpp index ad5cd9b..9c9966f 100644 --- a/src/tests/dfuds_tree_tests.cpp +++ b/src/tests/dfuds_tree_tests.cpp @@ -49,7 +49,8 @@ TEST(DfudsTreeTest, RandomTreeDFS) { EXPECT_EQ(dfuds_tree.parent(cur), debug_tree.parent(debug)); if (cur.number > 0) { - EXPECT_EQ(dfuds_tree.is_last_child(cur), debug_tree.is_last_child(debug)); + EXPECT_EQ(dfuds_tree.is_last_child(cur), + debug_tree.is_last_child(debug)); } size_t deg = dfuds_tree.degree(cur); EXPECT_EQ(deg, debug_tree.degree(debug)); @@ -68,7 +69,6 @@ TEST(DfudsTreeTest, RandomTreeDFS) { } } - TEST(DfudsTreeTest, RandomTreeBFS) { for (size_t tree_size = 8; tree_size < (1 << 22); tree_size <<= 1) { std::mt19937_64 rng(42); @@ -89,7 +89,8 @@ TEST(DfudsTreeTest, RandomTreeBFS) { EXPECT_EQ(dfuds_tree.parent(cur), debug_tree.parent(debug)); if (cur.number > 0) { - EXPECT_EQ(dfuds_tree.is_last_child(cur), debug_tree.is_last_child(debug)); + EXPECT_EQ(dfuds_tree.is_last_child(cur), + debug_tree.is_last_child(debug)); } size_t deg = dfuds_tree.degree(cur); EXPECT_EQ(deg, debug_tree.degree(debug)); From b070dc0b1bf477bcd8ef761c10bb27d9a920af5b Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Tue, 19 May 2026 11:43:43 +0300 Subject: [PATCH 04/12] rafactor dfuds_tree and utils --- include/pixie/dfuds_tree.h | 12 ++++++------ include/pixie/utils.h | 32 -------------------------------- src/tests/dfuds_tree_tests.cpp | 1 + 3 files changed, 7 insertions(+), 38 deletions(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index 8215b84..dddf5ac 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -2,6 +2,7 @@ #include +#include #include #include "utils.h" @@ -106,10 +107,9 @@ class DFUDSTree { return root(); } size_t open = rmm_.open( - node.pos - - 1); // node.pos in 0-based and rmm_.open uses 1-based argument. - // Thus, we use node.pos meaning the parenthesis before - // first parenthesis of the current node + node.pos); // node.pos in 0-based and rmm_.open uses 1-based argument. + // Thus, we use node.pos meaning the parenthesis before + // first parenthesis of the current node size_t rank = rmm_.rank0(open); size_t pos = rmm_.select0(rank) + @@ -124,8 +124,8 @@ class DFUDSTree { bool is_last_child(const Node& node) const { size_t end = rmm_.fwdsearch(node.pos, -1); size_t pos = end + 1; - size_t op = rmm_.open(node.pos - 1); - size_t op2 = rmm_.open(pos - 1); + size_t op = rmm_.open(node.pos); + size_t op2 = rmm_.open(pos); return pos == num_bits_ || op != op2 + 1; } }; diff --git a/include/pixie/utils.h b/include/pixie/utils.h index 38b8e5b..55e0e72 100644 --- a/include/pixie/utils.h +++ b/include/pixie/utils.h @@ -9,8 +9,6 @@ using pixie::LoudsNode; -using Node = pixie::DFUDSTree::Node; - std::vector> generate_random_tree(size_t tree_size, std::mt19937_64& rng) { if (tree_size == 0) { @@ -88,28 +86,6 @@ std::vector adj_to_louds( return louds; } -std::vector adj_to_dfuds( - size_t tree_size, - const std::vector>& adj) { - size_t dfuds_size = tree_size * 2 - 1; - std::vector dfuds((dfuds_size + 63) / 64, 0); - std::vector stack; - stack.push_back(0); - size_t pos = 0; - while (!stack.empty()) { - auto v = stack.back(); - stack.pop_back(); - size_t edge_count = adj[v].size(); - for (size_t i = 0; i < edge_count - 1; ++i) { // edge 0 goes to parent - dfuds[pos >> 6] = dfuds[pos >> 6] | (1ULL << (pos & 63)); - pos++; - stack.push_back(adj[v][edge_count - 1 - i]); - } - pos++; - } - return dfuds; -} - struct AdjListNode { size_t number; }; @@ -122,14 +98,6 @@ bool operator==(const LoudsNode& b, const AdjListNode& a) { return a.number == b.number; } -bool operator==(const AdjListNode& a, const Node& b) { - return a.number == b.number; -} - -bool operator==(const Node& b, const AdjListNode& a) { - return a.number == b.number; -} - class AdjListTree { private: std::vector> adj; diff --git a/src/tests/dfuds_tree_tests.cpp b/src/tests/dfuds_tree_tests.cpp index 9c9966f..b30f9ed 100644 --- a/src/tests/dfuds_tree_tests.cpp +++ b/src/tests/dfuds_tree_tests.cpp @@ -8,6 +8,7 @@ using Node = pixie::DFUDSTree::Node; using pixie::DFUDSTree; +using pixie::adj_to_dfuds; TEST(DfudsTreeTest, Basic) { std::vector> adj = {{0, 1}, {0, 2}, {1, 3}, {2, 4}, {3}}; From fc9519a81f159ffb6099fb87a1cade4d69fd5d75 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Tue, 19 May 2026 12:35:58 +0300 Subject: [PATCH 05/12] add dfuds benchmark --- src/benchmarks/dfuds_tree_benchmarks.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/benchmarks/dfuds_tree_benchmarks.cpp b/src/benchmarks/dfuds_tree_benchmarks.cpp index b4d4202..c90991a 100644 --- a/src/benchmarks/dfuds_tree_benchmarks.cpp +++ b/src/benchmarks/dfuds_tree_benchmarks.cpp @@ -5,8 +5,9 @@ #include using Node = pixie::DFUDSTree::Node; -using pixie::adj_to_dfuds; using pixie::DFUDSTree; +using pixie::adj_to_dfuds; + /** * DFS with O(1) extra memory @@ -65,4 +66,4 @@ BENCHMARK(BM_DfudsTreeDFS) ->ArgNames({"tree_size"}) ->RangeMultiplier(2) ->Range(1ull << 18, 1ull << 26) - ->Iterations(10); + ->Iterations(10); \ No newline at end of file From 772b8d50e9f9ab26837cc6dc1a24176940653c78 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Tue, 19 May 2026 13:33:08 +0300 Subject: [PATCH 06/12] fix build errors --- include/pixie/dfuds_tree.h | 1 - include/pixie/utils.h | 1 - src/benchmarks/dfuds_tree_benchmarks.cpp | 5 ++--- src/tests/dfuds_tree_tests.cpp | 2 +- 4 files changed, 3 insertions(+), 6 deletions(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index dddf5ac..578fcb9 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -2,7 +2,6 @@ #include -#include #include #include "utils.h" diff --git a/include/pixie/utils.h b/include/pixie/utils.h index 55e0e72..2ce0234 100644 --- a/include/pixie/utils.h +++ b/include/pixie/utils.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include diff --git a/src/benchmarks/dfuds_tree_benchmarks.cpp b/src/benchmarks/dfuds_tree_benchmarks.cpp index c90991a..b4d4202 100644 --- a/src/benchmarks/dfuds_tree_benchmarks.cpp +++ b/src/benchmarks/dfuds_tree_benchmarks.cpp @@ -5,9 +5,8 @@ #include using Node = pixie::DFUDSTree::Node; -using pixie::DFUDSTree; using pixie::adj_to_dfuds; - +using pixie::DFUDSTree; /** * DFS with O(1) extra memory @@ -66,4 +65,4 @@ BENCHMARK(BM_DfudsTreeDFS) ->ArgNames({"tree_size"}) ->RangeMultiplier(2) ->Range(1ull << 18, 1ull << 26) - ->Iterations(10); \ No newline at end of file + ->Iterations(10); diff --git a/src/tests/dfuds_tree_tests.cpp b/src/tests/dfuds_tree_tests.cpp index b30f9ed..fcaaa39 100644 --- a/src/tests/dfuds_tree_tests.cpp +++ b/src/tests/dfuds_tree_tests.cpp @@ -7,8 +7,8 @@ #include using Node = pixie::DFUDSTree::Node; -using pixie::DFUDSTree; using pixie::adj_to_dfuds; +using pixie::DFUDSTree; TEST(DfudsTreeTest, Basic) { std::vector> adj = {{0, 1}, {0, 2}, {1, 3}, {2, 4}, {3}}; From a9afe373de9a2aee055234c8ede68fb9a385856a Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Wed, 27 May 2026 18:37:31 +0300 Subject: [PATCH 07/12] refactor everything --- include/pixie/dfuds_tree.h | 2 +- include/pixie/rmm_base.h | 2 +- include/pixie/rmm_tree.h | 48 +++++++++++++++++++------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index 578fcb9..ddf98ee 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -53,7 +53,7 @@ class DFUDSTree { * @brief Indicates if @p node is a leaf */ bool is_leaf(const Node& node) const { - return (node.pos + 1 == num_bits_) or rmm_.bit(node.pos) == 0; + return (node.pos + 1 == num_bits_) or rmm_.bit_impl(node.pos) == 0; } /** diff --git a/include/pixie/rmm_base.h b/include/pixie/rmm_base.h index 9bf68dd..a2c8efb 100644 --- a/include/pixie/rmm_base.h +++ b/include/pixie/rmm_base.h @@ -196,7 +196,7 @@ class RmMBase { /** * @brief Read bit at position @p position (LSB-first across words). */ - int bit(const size_t& position) const noexcept { + int bit_impl(const size_t& position) const noexcept { return impl().bit_impl(position); } diff --git a/include/pixie/rmm_tree.h b/include/pixie/rmm_tree.h index a522c62..b1229f6 100644 --- a/include/pixie/rmm_tree.h +++ b/include/pixie/rmm_tree.h @@ -246,7 +246,7 @@ class RmMTree : public RmMBase { pattern_count += rr_in_block(block_begin, end_position); // boundary between the last full node and the leaf tail if (block_index > 0 && end_position > block_begin && - previous_last_bit == 1 && bit(block_begin) == 0) { + previous_last_bit == 1 && bit_impl(block_begin) == 0) { ++pattern_count; } return pattern_count; @@ -710,7 +710,7 @@ class RmMTree : public RmMBase { std::min(range_end, begin_block_end - 1); for (size_t position = range_begin; position <= end_of_first_chunk; ++position) { - current_excess += bit(position) ? +1 : -1; + current_excess += bit_impl(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; local_count = 1; @@ -745,7 +745,7 @@ class RmMTree : public RmMBase { int current_excess = 0, min_value = INT_MAX, local_count = 0; for (size_t position = end_block_start; position <= range_end; ++position) { - current_excess += bit(position) ? +1 : -1; + current_excess += bit_impl(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; local_count = 1; @@ -973,7 +973,7 @@ class RmMTree : public RmMBase { /** * @brief Read bit at position @p position (LSB-first across words). */ - inline int bit(const size_t& position) const noexcept { + inline int bit_impl(const size_t& position) const noexcept { return (bits[position >> 6] >> (position & 63)) & 1u; } @@ -1414,7 +1414,7 @@ class RmMTree : public RmMBase { pos += 16; } while (pos < end) { - cur += bit(pos) ? +1 : -1; + cur += bit_impl(pos) ? +1 : -1; if (cur == required_delta) { return pos; } @@ -1447,7 +1447,7 @@ class RmMTree : public RmMBase { size_t pos = start; while (pos < end && (pos & 7)) { - cur += bit(pos) ? +1 : -1; + cur += bit_impl(pos) ? +1 : -1; if (cur == required_delta) { if (out_total) { *out_total = cur; @@ -1488,7 +1488,7 @@ class RmMTree : public RmMBase { } while (pos < end) { - cur += bit(pos) ? +1 : -1; + cur += bit_impl(pos) ? +1 : -1; if (cur == required_delta) { if (out_total) { *out_total = cur; @@ -1537,7 +1537,7 @@ class RmMTree : public RmMBase { } while (position < search_end) { - current_excess += bit(position) ? 1 : -1; + current_excess += bit_impl(position) ? 1 : -1; if (current_excess == required_delta) { return position; } @@ -1663,14 +1663,14 @@ class RmMTree : public RmMBase { return npos; } const size_t bit_pos = pos_end - 1; - cur_end -= bit(bit_pos) ? +1 : -1; // move one bit to the left + cur_end -= bit_impl(bit_pos) ? +1 : -1; // move one bit to the left pos_end = bit_pos; } #else size_t last_boundary = npos; int cur = 0; for (size_t pos = block_begin; pos < boundary_max; ++pos) { - cur += bit(pos) ? +1 : -1; + cur += bit_impl(pos) ? +1 : -1; if (cur == required_delta) { last_boundary = pos + 1; } @@ -1909,7 +1909,7 @@ class RmMTree : public RmMBase { if (block_end == seg_end) { const int total = node_total_excess[node_index]; if (!allow_right_boundary && block_end > segment_base) { - prefix_override = total - (bit(block_end - 1) ? +1 : -1); + prefix_override = total - (bit_impl(block_end - 1) ? +1 : -1); } else { prefix_override = total; } @@ -2240,7 +2240,7 @@ class RmMTree : public RmMBase { } // to byte alignment while (range_begin <= range_end && (range_begin & 7)) { - current_excess += bit(range_begin) ? +1 : -1; + current_excess += bit_impl(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; count = 1; @@ -2265,7 +2265,7 @@ class RmMTree : public RmMBase { } // tail while (range_begin <= range_end) { - current_excess += bit(range_begin) ? +1 : -1; + current_excess += bit_impl(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; count = 1; @@ -2335,7 +2335,7 @@ class RmMTree : public RmMBase { size_t position = range_begin; while (position <= range_end && (position & 7)) { - current_excess += bit(position) ? +1 : -1; + current_excess += bit_impl(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; } @@ -2349,7 +2349,7 @@ class RmMTree : public RmMBase { position += 8; } while (position <= range_end) { - current_excess += bit(position) ? +1 : -1; + current_excess += bit_impl(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; } @@ -2361,7 +2361,7 @@ class RmMTree : public RmMBase { // to byte alignment while (position <= range_end && (position & 7)) { - current_excess += bit(position) ? +1 : -1; + current_excess += bit_impl(position) ? +1 : -1; if (current_excess == min_value) { if (--target_min_rank == 0) { return position; @@ -2392,7 +2392,7 @@ class RmMTree : public RmMBase { // tail while (position <= range_end) { - current_excess += bit(position) ? +1 : -1; + current_excess += bit_impl(position) ? +1 : -1; if (current_excess == min_value) { if (--target_min_rank == 0) { return position; @@ -2495,7 +2495,7 @@ class RmMTree : public RmMBase { // prefix at boundary_max = start_position-1: // leaf_delta is prefix at start_position, subtract last step (start_position > leaf_block_begin - ? (leaf_delta - (bit(start_position - 1) ? +1 : -1)) + ? (leaf_delta - (bit_impl(start_position - 1) ? +1 : -1)) : 0)); } @@ -2516,7 +2516,7 @@ class RmMTree : public RmMBase { // to byte allignment while (range_begin <= range_end && (range_begin & 7)) { - current_excess += bit(range_begin) ? +1 : -1; + current_excess += bit_impl(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; first_position = range_begin; @@ -2538,7 +2538,7 @@ class RmMTree : public RmMBase { // tail while (range_begin <= range_end) { - current_excess += bit(range_begin) ? +1 : -1; + current_excess += bit_impl(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; first_position = range_begin; @@ -2565,7 +2565,7 @@ class RmMTree : public RmMBase { first_position = npos; while (range_begin <= range_end && (range_begin & 7)) { - current_excess += bit(range_begin) ? +1 : -1; + current_excess += bit_impl(range_begin) ? +1 : -1; if (current_excess > max_value) { max_value = current_excess; first_position = range_begin; @@ -2585,7 +2585,7 @@ class RmMTree : public RmMBase { } while (range_begin <= range_end) { - current_excess += bit(range_begin) ? +1 : -1; + current_excess += bit_impl(range_begin) ? +1 : -1; if (current_excess > max_value) { max_value = current_excess; first_position = range_begin; @@ -2649,7 +2649,7 @@ class RmMTree : public RmMBase { segment_size_bits[leaf_node_index] = segment_end - segment_begin; if (segment_begin < segment_end) { - node_first_bit[leaf_node_index] = bit(segment_begin); + node_first_bit[leaf_node_index] = bit_impl(segment_begin); } const auto& aggregates_table = LUT8(); @@ -2693,7 +2693,7 @@ class RmMTree : public RmMBase { // Tail < 8 bits while (position < segment_end) { - const uint8_t bit_value = bit(position); + const uint8_t bit_value = bit_impl(position); if (previous_bit == 1 && bit_value == 0) { pattern10_count++; } From 91740f6a627498bd03625755360dddcc42a0c0b8 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Wed, 27 May 2026 19:10:48 +0300 Subject: [PATCH 08/12] fix compilation error --- include/pixie/rmm_tree.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/pixie/rmm_tree.h b/include/pixie/rmm_tree.h index b1229f6..c69a8b7 100644 --- a/include/pixie/rmm_tree.h +++ b/include/pixie/rmm_tree.h @@ -2209,6 +2209,13 @@ class RmMTree : public RmMBase { build(leaf_block_bits, max_overhead); } + /** + * @brief Read bit at position @p position (LSB-first across words). + */ + inline int bit(const size_t& position) const noexcept { + return (bits[position >> 6] >> (position & 63)) & 1u; + } + /** * @brief Number of ones in node @p node_index computed from size and total * excess. From 40eaaf135b7a867385c69b0a0065a56b6e36c294 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Wed, 27 May 2026 19:18:10 +0300 Subject: [PATCH 09/12] remove duplicated bit member funcion --- include/pixie/rmm_tree.h | 55 ++++++++++++++++++---------------------- 1 file changed, 24 insertions(+), 31 deletions(-) diff --git a/include/pixie/rmm_tree.h b/include/pixie/rmm_tree.h index c69a8b7..a522c62 100644 --- a/include/pixie/rmm_tree.h +++ b/include/pixie/rmm_tree.h @@ -246,7 +246,7 @@ class RmMTree : public RmMBase { pattern_count += rr_in_block(block_begin, end_position); // boundary between the last full node and the leaf tail if (block_index > 0 && end_position > block_begin && - previous_last_bit == 1 && bit_impl(block_begin) == 0) { + previous_last_bit == 1 && bit(block_begin) == 0) { ++pattern_count; } return pattern_count; @@ -710,7 +710,7 @@ class RmMTree : public RmMBase { std::min(range_end, begin_block_end - 1); for (size_t position = range_begin; position <= end_of_first_chunk; ++position) { - current_excess += bit_impl(position) ? +1 : -1; + current_excess += bit(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; local_count = 1; @@ -745,7 +745,7 @@ class RmMTree : public RmMBase { int current_excess = 0, min_value = INT_MAX, local_count = 0; for (size_t position = end_block_start; position <= range_end; ++position) { - current_excess += bit_impl(position) ? +1 : -1; + current_excess += bit(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; local_count = 1; @@ -973,7 +973,7 @@ class RmMTree : public RmMBase { /** * @brief Read bit at position @p position (LSB-first across words). */ - inline int bit_impl(const size_t& position) const noexcept { + inline int bit(const size_t& position) const noexcept { return (bits[position >> 6] >> (position & 63)) & 1u; } @@ -1414,7 +1414,7 @@ class RmMTree : public RmMBase { pos += 16; } while (pos < end) { - cur += bit_impl(pos) ? +1 : -1; + cur += bit(pos) ? +1 : -1; if (cur == required_delta) { return pos; } @@ -1447,7 +1447,7 @@ class RmMTree : public RmMBase { size_t pos = start; while (pos < end && (pos & 7)) { - cur += bit_impl(pos) ? +1 : -1; + cur += bit(pos) ? +1 : -1; if (cur == required_delta) { if (out_total) { *out_total = cur; @@ -1488,7 +1488,7 @@ class RmMTree : public RmMBase { } while (pos < end) { - cur += bit_impl(pos) ? +1 : -1; + cur += bit(pos) ? +1 : -1; if (cur == required_delta) { if (out_total) { *out_total = cur; @@ -1537,7 +1537,7 @@ class RmMTree : public RmMBase { } while (position < search_end) { - current_excess += bit_impl(position) ? 1 : -1; + current_excess += bit(position) ? 1 : -1; if (current_excess == required_delta) { return position; } @@ -1663,14 +1663,14 @@ class RmMTree : public RmMBase { return npos; } const size_t bit_pos = pos_end - 1; - cur_end -= bit_impl(bit_pos) ? +1 : -1; // move one bit to the left + cur_end -= bit(bit_pos) ? +1 : -1; // move one bit to the left pos_end = bit_pos; } #else size_t last_boundary = npos; int cur = 0; for (size_t pos = block_begin; pos < boundary_max; ++pos) { - cur += bit_impl(pos) ? +1 : -1; + cur += bit(pos) ? +1 : -1; if (cur == required_delta) { last_boundary = pos + 1; } @@ -1909,7 +1909,7 @@ class RmMTree : public RmMBase { if (block_end == seg_end) { const int total = node_total_excess[node_index]; if (!allow_right_boundary && block_end > segment_base) { - prefix_override = total - (bit_impl(block_end - 1) ? +1 : -1); + prefix_override = total - (bit(block_end - 1) ? +1 : -1); } else { prefix_override = total; } @@ -2209,13 +2209,6 @@ class RmMTree : public RmMBase { build(leaf_block_bits, max_overhead); } - /** - * @brief Read bit at position @p position (LSB-first across words). - */ - inline int bit(const size_t& position) const noexcept { - return (bits[position >> 6] >> (position & 63)) & 1u; - } - /** * @brief Number of ones in node @p node_index computed from size and total * excess. @@ -2247,7 +2240,7 @@ class RmMTree : public RmMBase { } // to byte alignment while (range_begin <= range_end && (range_begin & 7)) { - current_excess += bit_impl(range_begin) ? +1 : -1; + current_excess += bit(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; count = 1; @@ -2272,7 +2265,7 @@ class RmMTree : public RmMBase { } // tail while (range_begin <= range_end) { - current_excess += bit_impl(range_begin) ? +1 : -1; + current_excess += bit(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; count = 1; @@ -2342,7 +2335,7 @@ class RmMTree : public RmMBase { size_t position = range_begin; while (position <= range_end && (position & 7)) { - current_excess += bit_impl(position) ? +1 : -1; + current_excess += bit(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; } @@ -2356,7 +2349,7 @@ class RmMTree : public RmMBase { position += 8; } while (position <= range_end) { - current_excess += bit_impl(position) ? +1 : -1; + current_excess += bit(position) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; } @@ -2368,7 +2361,7 @@ class RmMTree : public RmMBase { // to byte alignment while (position <= range_end && (position & 7)) { - current_excess += bit_impl(position) ? +1 : -1; + current_excess += bit(position) ? +1 : -1; if (current_excess == min_value) { if (--target_min_rank == 0) { return position; @@ -2399,7 +2392,7 @@ class RmMTree : public RmMBase { // tail while (position <= range_end) { - current_excess += bit_impl(position) ? +1 : -1; + current_excess += bit(position) ? +1 : -1; if (current_excess == min_value) { if (--target_min_rank == 0) { return position; @@ -2502,7 +2495,7 @@ class RmMTree : public RmMBase { // prefix at boundary_max = start_position-1: // leaf_delta is prefix at start_position, subtract last step (start_position > leaf_block_begin - ? (leaf_delta - (bit_impl(start_position - 1) ? +1 : -1)) + ? (leaf_delta - (bit(start_position - 1) ? +1 : -1)) : 0)); } @@ -2523,7 +2516,7 @@ class RmMTree : public RmMBase { // to byte allignment while (range_begin <= range_end && (range_begin & 7)) { - current_excess += bit_impl(range_begin) ? +1 : -1; + current_excess += bit(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; first_position = range_begin; @@ -2545,7 +2538,7 @@ class RmMTree : public RmMBase { // tail while (range_begin <= range_end) { - current_excess += bit_impl(range_begin) ? +1 : -1; + current_excess += bit(range_begin) ? +1 : -1; if (current_excess < min_value) { min_value = current_excess; first_position = range_begin; @@ -2572,7 +2565,7 @@ class RmMTree : public RmMBase { first_position = npos; while (range_begin <= range_end && (range_begin & 7)) { - current_excess += bit_impl(range_begin) ? +1 : -1; + current_excess += bit(range_begin) ? +1 : -1; if (current_excess > max_value) { max_value = current_excess; first_position = range_begin; @@ -2592,7 +2585,7 @@ class RmMTree : public RmMBase { } while (range_begin <= range_end) { - current_excess += bit_impl(range_begin) ? +1 : -1; + current_excess += bit(range_begin) ? +1 : -1; if (current_excess > max_value) { max_value = current_excess; first_position = range_begin; @@ -2656,7 +2649,7 @@ class RmMTree : public RmMBase { segment_size_bits[leaf_node_index] = segment_end - segment_begin; if (segment_begin < segment_end) { - node_first_bit[leaf_node_index] = bit_impl(segment_begin); + node_first_bit[leaf_node_index] = bit(segment_begin); } const auto& aggregates_table = LUT8(); @@ -2700,7 +2693,7 @@ class RmMTree : public RmMBase { // Tail < 8 bits while (position < segment_end) { - const uint8_t bit_value = bit_impl(position); + const uint8_t bit_value = bit(position); if (previous_bit == 1 && bit_value == 0) { pattern10_count++; } From f341656d68bb52a5fd63654554fb3b821311f365 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Mon, 1 Jun 2026 01:01:33 +0300 Subject: [PATCH 10/12] fix implementation to supprot 0-based indexation in rmm.open --- include/pixie/dfuds_tree.h | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index ddf98ee..5ebe426 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -106,9 +106,10 @@ class DFUDSTree { return root(); } size_t open = rmm_.open( - node.pos); // node.pos in 0-based and rmm_.open uses 1-based argument. - // Thus, we use node.pos meaning the parenthesis before - // first parenthesis of the current node + node.pos - + 1); // node.pos in 0-based and rmm_.open uses 1-based argument. + // Thus, we use node.pos meaning the parenthesis before + // first parenthesis of the current node size_t rank = rmm_.rank0(open); size_t pos = rmm_.select0(rank) + @@ -123,8 +124,8 @@ class DFUDSTree { bool is_last_child(const Node& node) const { size_t end = rmm_.fwdsearch(node.pos, -1); size_t pos = end + 1; - size_t op = rmm_.open(node.pos); - size_t op2 = rmm_.open(pos); + size_t op = rmm_.open(node.pos - 1); + size_t op2 = rmm_.open(pos - 1); return pos == num_bits_ || op != op2 + 1; } }; From 037aa4a9cabb9598d64099172f48c204f4ad701d Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Mon, 1 Jun 2026 18:24:52 +0300 Subject: [PATCH 11/12] rename rmm.bit_impl to rmm.bit and enchance tests --- include/pixie/dfuds_tree.h | 2 +- include/pixie/rmm_base.h | 2 +- src/tests/dfuds_tree_tests.cpp | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index 5ebe426..8215b84 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -53,7 +53,7 @@ class DFUDSTree { * @brief Indicates if @p node is a leaf */ bool is_leaf(const Node& node) const { - return (node.pos + 1 == num_bits_) or rmm_.bit_impl(node.pos) == 0; + return (node.pos + 1 == num_bits_) or rmm_.bit(node.pos) == 0; } /** diff --git a/include/pixie/rmm_base.h b/include/pixie/rmm_base.h index a2c8efb..9bf68dd 100644 --- a/include/pixie/rmm_base.h +++ b/include/pixie/rmm_base.h @@ -196,7 +196,7 @@ class RmMBase { /** * @brief Read bit at position @p position (LSB-first across words). */ - int bit_impl(const size_t& position) const noexcept { + int bit(const size_t& position) const noexcept { return impl().bit_impl(position); } diff --git a/src/tests/dfuds_tree_tests.cpp b/src/tests/dfuds_tree_tests.cpp index fcaaa39..4426250 100644 --- a/src/tests/dfuds_tree_tests.cpp +++ b/src/tests/dfuds_tree_tests.cpp @@ -55,6 +55,7 @@ TEST(DfudsTreeTest, RandomTreeDFS) { } size_t deg = dfuds_tree.degree(cur); EXPECT_EQ(deg, debug_tree.degree(debug)); + EXPECT_EQ(dfuds_tree.is_leaf(cur), debug_tree.is_leaf(debug)); if (deg == 0) { continue; @@ -95,6 +96,7 @@ TEST(DfudsTreeTest, RandomTreeBFS) { } size_t deg = dfuds_tree.degree(cur); EXPECT_EQ(deg, debug_tree.degree(debug)); + EXPECT_EQ(dfuds_tree.is_leaf(cur), debug_tree.is_leaf(debug)); if (deg == 0) { continue; From cfac24fa489ef49d86cad42a76dc6609431aecd3 Mon Sep 17 00:00:00 2001 From: Sanya239 Date: Tue, 9 Jun 2026 01:19:22 +0300 Subject: [PATCH 12/12] Support SdslRmMTree in DFUDS benchmarks --- CMakeLists.txt | 4 ++++ include/pixie/dfuds_tree.h | 11 ++--------- src/benchmarks/dfuds_tree_benchmarks.cpp | 12 ++++++++++-- src/tests/dfuds_tree_tests.cpp | 12 ++++++++++-- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 87c59ad..e2b9d47 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,10 @@ if(PIXIE_BENCHMARKS) benchmark benchmark_main ${PIXIE_DIAGNOSTICS_LIBS}) + if(PIXIE_THIRD_PARTY_BACKENDS) + target_include_directories(dfuds_tree_benchmarks + PRIVATE ${sdsl_lite_SOURCE_DIR}/include) + endif() add_executable(alignment_comparison src/benchmarks/alignment_comparison.cpp) diff --git a/include/pixie/dfuds_tree.h b/include/pixie/dfuds_tree.h index 8215b84..a332b54 100644 --- a/include/pixie/dfuds_tree.h +++ b/include/pixie/dfuds_tree.h @@ -12,10 +12,11 @@ namespace pixie { * @brief A tree class based on the depth-first unary degree sequence (DFUDS) * representation */ +template class DFUDSTree { private: const size_t num_bits_; - RmMTree rmm_; + RMMTree rmm_; public: struct Node { @@ -152,12 +153,4 @@ std::vector adj_to_dfuds( return dfuds; } -bool operator==(const AdjListNode& a, const DFUDSTree::Node& b) { - return a.number == b.number; -} - -bool operator==(const DFUDSTree::Node& b, const AdjListNode& a) { - return a.number == b.number; -} - } // namespace pixie diff --git a/src/benchmarks/dfuds_tree_benchmarks.cpp b/src/benchmarks/dfuds_tree_benchmarks.cpp index b4d4202..7eff6fa 100644 --- a/src/benchmarks/dfuds_tree_benchmarks.cpp +++ b/src/benchmarks/dfuds_tree_benchmarks.cpp @@ -4,9 +4,17 @@ #include -using Node = pixie::DFUDSTree::Node; +#ifdef SDSL_SUPPORT +#pragma message("SDSL_SUPPORT enabled") +#include "pixie/rmm_tree_sdsl.h" +using DFUDSTree = pixie::DFUDSTree; +#else +#pragma message("SDSL_SUPPORT disabled") +using DFUDSTree = pixie::DFUDSTree; +#endif + +using Node = DFUDSTree::Node; using pixie::adj_to_dfuds; -using pixie::DFUDSTree; /** * DFS with O(1) extra memory diff --git a/src/tests/dfuds_tree_tests.cpp b/src/tests/dfuds_tree_tests.cpp index 4426250..a83f416 100644 --- a/src/tests/dfuds_tree_tests.cpp +++ b/src/tests/dfuds_tree_tests.cpp @@ -6,9 +6,17 @@ #include #include -using Node = pixie::DFUDSTree::Node; +using DFUDSTree = pixie::DFUDSTree; +using Node = DFUDSTree::Node; using pixie::adj_to_dfuds; -using pixie::DFUDSTree; + +bool operator==(const AdjListNode& a, const DFUDSTree::Node& b) { + return a.number == b.number; +} + +bool operator==(const DFUDSTree::Node& b, const AdjListNode& a) { + return a.number == b.number; +} TEST(DfudsTreeTest, Basic) { std::vector> adj = {{0, 1}, {0, 2}, {1, 3}, {2, 4}, {3}};