Skip to content

Commit 46ee754

Browse files
authored
Louds implementation (#26)
1 parent 221b5f0 commit 46ee754

17 files changed

Lines changed: 905 additions & 1 deletion

.github/workflows/build-test.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ jobs:
2727
- name: Run Unittests
2828
working-directory: ./build
2929
run: ./unittests
30+
31+
- name: Run LOUDS Tree Tests
32+
working-directory: ./build
33+
run: ./louds_tree_tests
3034

3135
build-and-test-with-SDE:
3236
runs-on: ubuntu-latest
@@ -53,4 +57,8 @@ jobs:
5357

5458
- name: Run Unittests
5559
working-directory: ./build
56-
run: sde-external-9.58.0-2025-06-16-lin/sde64 -icl -emu-xinuse 0 -- ./unittests
60+
run: sde-external-9.58.0-2025-06-16-lin/sde64 -icl -emu-xinuse 0 -- ./unittests
61+
62+
- name: Run LOUDS Tree Tests
63+
working-directory: ./build
64+
run: sde-external-9.58.0-2025-06-16-lin/sde64 -icl -emu-xinuse 0 -- ./louds_tree_tests

CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,20 @@ target_include_directories(bench_rmm
9494
target_link_libraries(bench_rmm
9595
benchmark)
9696

97+
98+
add_executable(louds_tree_benchmarks
99+
src/louds_tree_benchmarks.cpp)
100+
101+
target_include_directories(louds_tree_benchmarks
102+
PUBLIC include
103+
)
104+
105+
target_link_libraries(louds_tree_benchmarks
106+
benchmark
107+
benchmark_main
108+
)
109+
110+
97111
add_executable(test_rmm
98112
src/test_rmm.cpp)
99113
target_include_directories(test_rmm
@@ -104,6 +118,15 @@ target_link_libraries(test_rmm
104118
gtest
105119
gtest_main)
106120

121+
add_executable(louds_tree_tests
122+
src/louds_tree_tests.cpp)
123+
target_include_directories(louds_tree_tests
124+
PUBLIC include
125+
PUBLIC ${GOOGLETEST_SOURCE_DIR}/include
126+
)
127+
target_link_libraries(louds_tree_tests
128+
gtest
129+
gtest_main)
107130

108131
add_executable(alignment_comparison
109132
src/alignment_comparison.cpp)

include/louds_tree.h

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#pragma once
2+
3+
#include <cstdint>
4+
#include <span>
5+
6+
#include "bitvector.h"
7+
8+
namespace pixie {
9+
10+
/**
11+
* @brief A node class of LOUDS tree
12+
*/
13+
struct LoudsNode {
14+
size_t number;
15+
size_t pos;
16+
17+
LoudsNode(size_t node_number, size_t louds_pos)
18+
: number(node_number), pos(louds_pos) {}
19+
};
20+
21+
/**
22+
* @brief A tree class based on the level order unary degree sequence (LOUDS)
23+
* representation
24+
*/
25+
class LoudsTree {
26+
private:
27+
BitVector bv;
28+
29+
public:
30+
/**
31+
* @brief Constructor from an external array of uint64_t
32+
*/
33+
explicit LoudsTree(std::span<const uint64_t> louds, size_t tree_size)
34+
: bv(louds, 2 * tree_size - 1) {}
35+
36+
/**
37+
* @brief Returns the root node
38+
*/
39+
LoudsNode root() const { return LoudsNode(0, 0); }
40+
41+
/**
42+
* @brief Returns the size of the tree
43+
*/
44+
size_t size() const { return (bv.size() + 1) / 2; }
45+
46+
/**
47+
* @brief Indicates if @p node is a leaf
48+
*/
49+
bool is_leaf(const LoudsNode& node) const {
50+
return (node.pos + 1 == bv.size()) or bv[node.pos + 1];
51+
}
52+
53+
/**
54+
* @brief Indicates if @p node is a root
55+
*/
56+
bool is_root(const LoudsNode& node) const { return node.number == 0; }
57+
58+
/**
59+
* @brief Returns the number of children of a @p node
60+
*/
61+
size_t degree(const LoudsNode& node) const {
62+
if (is_leaf(node)) {
63+
return 0;
64+
}
65+
return bv.select(node.number + 2) - node.pos - 1;
66+
}
67+
68+
/**
69+
* @brief Returns the i-th child of @p node
70+
* Indexing starts at 0
71+
*/
72+
LoudsNode child(const LoudsNode& node, size_t i) const {
73+
size_t zeros = node.pos + i + 1 - node.number;
74+
return LoudsNode(zeros, bv.select(zeros + 1));
75+
}
76+
77+
/**
78+
* @brief Returns first child of a @p node
79+
*/
80+
LoudsNode first_child(const LoudsNode& node) const {
81+
size_t zeros = node.pos + 1 - node.number;
82+
return LoudsNode(zeros, bv.select(zeros + 1));
83+
}
84+
85+
/**
86+
* @brief Returns the parent of a @p node if @p node is not root,
87+
* else returns root
88+
*/
89+
LoudsNode parent(const LoudsNode& node) const {
90+
if (node.number == 0) {
91+
return root();
92+
}
93+
size_t zero_pos = bv.select0(node.number);
94+
size_t parent_number = zero_pos - node.number;
95+
return LoudsNode(parent_number, bv.select(parent_number + 1));
96+
}
97+
98+
/**
99+
* @brief Indicates if @p node is last child
100+
*/
101+
bool is_last_child(const LoudsNode& node) const {
102+
size_t zero_pos = bv.select0(node.number);
103+
return bv[zero_pos + 1];
104+
}
105+
106+
/**
107+
* @brief Returns next sibling of a @p node
108+
*/
109+
LoudsNode next_sibling(const LoudsNode& node) const {
110+
size_t sibling_number = node.number + 1;
111+
return LoudsNode(sibling_number, bv.select(sibling_number + 1));
112+
}
113+
};
114+
115+
} // namespace pixie

include/utils.h

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
#pragma once
2+
3+
#include <queue>
4+
#include <random>
5+
#include <vector>
6+
7+
#include "louds_tree.h"
8+
9+
using pixie::LoudsNode;
10+
11+
std::vector<std::vector<size_t>> generate_random_tree(size_t tree_size,
12+
std::mt19937_64& rng) {
13+
if (tree_size == 0) {
14+
return {};
15+
}
16+
std::vector<std::vector<size_t>> adj(tree_size);
17+
adj[0].push_back(0);
18+
for (size_t i = 1; i < tree_size; i++) {
19+
size_t parent = rng() % i;
20+
adj[i].push_back(parent);
21+
adj[parent].push_back(i);
22+
}
23+
return adj;
24+
}
25+
26+
std::vector<std::vector<size_t>> bfs_order(
27+
size_t tree_size,
28+
const std::vector<std::vector<size_t>>& adj) {
29+
std::vector<std::vector<size_t>> bfs_adj(tree_size);
30+
std::queue<std::pair<size_t, size_t>> q;
31+
bfs_adj[0].push_back(0);
32+
q.push({0, 0});
33+
int cnt = 1;
34+
while (!q.empty()) {
35+
size_t old_v = q.front().first;
36+
size_t cur_v = q.front().second;
37+
q.pop();
38+
for (size_t i = 1; i < adj[old_v].size(); i++) {
39+
size_t old_u = adj[old_v][i];
40+
size_t cur_u = cnt++;
41+
q.push({old_u, cur_u});
42+
bfs_adj[cur_u].push_back(cur_v);
43+
bfs_adj[cur_v].push_back(cur_u);
44+
}
45+
}
46+
return bfs_adj;
47+
}
48+
49+
std::vector<uint64_t> adj_to_louds(
50+
size_t tree_size,
51+
const std::vector<std::vector<size_t>>& adj) {
52+
size_t louds_size = tree_size * 2 - 1;
53+
std::vector<uint64_t> louds((louds_size + 63) / 64, 0);
54+
size_t pos = 0;
55+
for (size_t i = 0; i < tree_size; i++) {
56+
louds[pos >> 6] = louds[pos >> 6] | (1ULL << (pos & 63));
57+
pos += adj[i].size();
58+
}
59+
return louds;
60+
}
61+
62+
struct AdjListNode {
63+
size_t number;
64+
};
65+
66+
bool operator==(const AdjListNode& a, const LoudsNode& b) {
67+
return a.number == b.number;
68+
}
69+
70+
bool operator==(const LoudsNode& b, const AdjListNode& a) {
71+
return a.number == b.number;
72+
}
73+
74+
class AdjListTree {
75+
private:
76+
std::vector<std::vector<size_t>> adj;
77+
78+
public:
79+
/**
80+
* @brief Constructor from adjacency list (root is 0)
81+
*/
82+
explicit AdjListTree(const std::vector<std::vector<size_t>>& adjacency_list)
83+
: adj(adjacency_list) {}
84+
85+
/**
86+
* @brief Returns the root node
87+
*/
88+
AdjListNode root() const { return AdjListNode(0); }
89+
90+
/**
91+
* @brief Checks if @p node is a leaf
92+
*/
93+
bool is_leaf(const AdjListNode& node) const {
94+
return adj[node.number].size() <= 1;
95+
}
96+
97+
/**
98+
* @brief Checks if @p node is a root
99+
*/
100+
bool is_root(const AdjListNode& node) const { return node.number == 0; }
101+
102+
/**
103+
* @brief Returns the number of children of @p node
104+
*/
105+
size_t degree(const AdjListNode& node) const {
106+
return adj[node.number].size() - 1;
107+
}
108+
109+
/**
110+
* @brief Returns the i-th child of @p node
111+
* Indexing starts at 0
112+
*/
113+
AdjListNode child(const AdjListNode& node, size_t i) const {
114+
return AdjListNode(adj[node.number][i + 1]);
115+
}
116+
117+
/**
118+
* @brief Returns the first child of @p node
119+
*/
120+
AdjListNode first_child(const AdjListNode& node) const {
121+
return AdjListNode(adj[node.number][1]);
122+
}
123+
124+
/**
125+
* @brief Returns the parent of a @p node if @p node is not root,
126+
* else returns root
127+
*/
128+
AdjListNode parent(const AdjListNode& node) const {
129+
return AdjListNode(adj[node.number][0]);
130+
}
131+
132+
/**
133+
* @brief Indicates if @p node is last child
134+
*/
135+
bool is_last_child(const AdjListNode& node) const {
136+
size_t p = parent(node).number;
137+
return adj[p].back() == node.number;
138+
}
139+
140+
/**
141+
* @brief Returns next sibling of a @p node
142+
*/
143+
AdjListNode next_sibling(const AdjListNode& node) const {
144+
return AdjListNode(node.number + 1);
145+
}
146+
};

report/data/dfs.txt

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
BM_LoudsTreeDFS/tree_size:256/iterations:10 17158 ns 15960 ns 10
2+
BM_LoudsTreeDFS/tree_size:512/iterations:10 32205 ns 30170 ns 10
3+
BM_LoudsTreeDFS/tree_size:1024/iterations:10 64570 ns 60450 ns 10
4+
BM_LoudsTreeDFS/tree_size:2048/iterations:10 149284 ns 139960 ns 10
5+
BM_LoudsTreeDFS/tree_size:4096/iterations:10 306694 ns 287710 ns 10
6+
BM_LoudsTreeDFS/tree_size:8192/iterations:10 639481 ns 599620 ns 10
7+
BM_LoudsTreeDFS/tree_size:16384/iterations:10 1321981 ns 1239680 ns 10
8+
BM_LoudsTreeDFS/tree_size:32768/iterations:10 2621919 ns 2458610 ns 10
9+
BM_LoudsTreeDFS/tree_size:65536/iterations:10 5418396 ns 5081010 ns 10
10+
BM_LoudsTreeDFS/tree_size:131072/iterations:10 11477777 ns 10762480 ns 10
11+
BM_LoudsTreeDFS/tree_size:262144/iterations:10 22475017 ns 21071420 ns 10
12+
BM_LoudsTreeDFS/tree_size:524288/iterations:10 51507287 ns 48337760 ns 10
13+
BM_LoudsTreeDFS/tree_size:1048576/iterations:10 139215927 ns 130654490 ns 10
14+
BM_LoudsTreeDFS/tree_size:2097152/iterations:10 274188404 ns 257485140 ns 10
15+
BM_LoudsTreeDFS/tree_size:4194304/iterations:10 393504994 ns 379028300 ns 10
16+
BM_LoudsTreeDFS/tree_size:8388608/iterations:10 772261790 ns 742915900 ns 10
17+
BM_LoudsTreeDFS/tree_size:16777216/iterations:10 1924302888 ns 1847300630 ns 10
18+
BM_LoudsTreeDFS/tree_size:33554432/iterations:10 4033885540 ns 3862881520 ns 10
19+
BM_AdjListTreeDFS/tree_size:256/iterations:10 7004 ns 6500 ns 10
20+
BM_AdjListTreeDFS/tree_size:512/iterations:10 12709 ns 11890 ns 10
21+
BM_AdjListTreeDFS/tree_size:1024/iterations:10 27121 ns 25400 ns 10
22+
BM_AdjListTreeDFS/tree_size:2048/iterations:10 61831 ns 57990 ns 10
23+
BM_AdjListTreeDFS/tree_size:4096/iterations:10 173807 ns 163320 ns 10
24+
BM_AdjListTreeDFS/tree_size:8192/iterations:10 346847 ns 325820 ns 10
25+
BM_AdjListTreeDFS/tree_size:16384/iterations:10 697983 ns 655640 ns 10
26+
BM_AdjListTreeDFS/tree_size:32768/iterations:10 1422141 ns 1336020 ns 10
27+
BM_AdjListTreeDFS/tree_size:65536/iterations:10 2862014 ns 2688070 ns 10
28+
BM_AdjListTreeDFS/tree_size:131072/iterations:10 6096719 ns 5727260 ns 10
29+
BM_AdjListTreeDFS/tree_size:262144/iterations:10 13984236 ns 13137420 ns 10
30+
BM_AdjListTreeDFS/tree_size:524288/iterations:10 36108305 ns 33921960 ns 10
31+
BM_AdjListTreeDFS/tree_size:1048576/iterations:10 97158888 ns 91052080 ns 10
32+
BM_AdjListTreeDFS/tree_size:2097152/iterations:10 130849640 ns 122713210 ns 10
33+
BM_AdjListTreeDFS/tree_size:4194304/iterations:10 519976249 ns 492515650 ns 10
34+
BM_AdjListTreeDFS/tree_size:8388608/iterations:10 1076193064 ns 1019121630 ns 10
35+
BM_AdjListTreeDFS/tree_size:16777216/iterations:10 2151390243 ns 2036195610 ns 10
36+
BM_AdjListTreeDFS/tree_size:33554432/iterations:10 4386640878 ns 4148986820 ns 10

report/data/dfs_comparison.png

154 KB
Loading
290 KB
Loading
241 KB
Loading

0 commit comments

Comments
 (0)