Skip to content

Commit 065f931

Browse files
committed
extended dfs tmpl to accept traversal direction
1 parent 5c12c4a commit 065f931

3 files changed

Lines changed: 591 additions & 159 deletions

File tree

include/hgl/algorithm/templates/dfs.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
namespace hgl::algorithm {
1212

1313
template <
14+
traversal_direction Dir = traversal_direction::forward,
1415
hgl::traits::c_hypergraph H,
1516
traits::c_forward_range_of<search_node<H>> InitQueueRangeType = std::vector<search_node<H>>,
1617
traits::c_optional_predicate<const search_node<H>&> VisitVertexPredicate = empty_callback,
@@ -30,7 +31,7 @@ bool dfs(
3031
const PreVisitCallback& pre_visit = {},
3132
const PostVisitCallback& post_visit = {}
3233
) {
33-
using policy = traversal_policy<H>;
34+
using policy = traversal_policy<H, Dir>;
3435

3536
if (std::ranges::empty(initial_queue_content))
3637
return false;
Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
#include "doctest.h"
2+
#include "testing/common/io.hpp"
3+
#include "testing/hgl/constants.hpp"
4+
5+
#include <hgl/algorithm/traversal/bf_search.hpp>
6+
#include <hgl/constants.hpp>
7+
8+
#include <algorithm>
9+
10+
namespace hgl_testing {
11+
12+
TEST_SUITE_BEGIN("test_alg_bfs");
13+
14+
TEST_CASE_TEMPLATE_DEFINE(
15+
"backward_search should properly enforce tail dependencies during traversal",
16+
HypergraphTraitsType,
17+
backward_search_directed_hypergraph_template
18+
) {
19+
using hypergraph_type = hgl::hypergraph<HypergraphTraitsType>;
20+
using id_type = typename hypergraph_type::id_type;
21+
using node_type = hgl::algorithm::search_node<hypergraph_type>;
22+
23+
hypergraph_type hypergraph;
24+
std::vector<id_type> root_vertices;
25+
26+
std::vector<id_type> expected_previsit_order;
27+
std::vector<id_type> expected_postvisit_order;
28+
std::vector<id_type> expected_pred_map;
29+
std::vector<id_type> expected_in_hyperedges;
30+
31+
/* Topology:
32+
V = {v0, v1, v2, v3, v4}
33+
e0 = {v0, v1} -> {v2}
34+
e1 = {v2} -> {v3, v4}
35+
e2 = {v1} -> {v4}
36+
37+
v0 v3
38+
\ /
39+
-->-- v2 -->--
40+
/ \
41+
v1 ------->------ v4
42+
*/
43+
44+
const auto order = 5uz;
45+
hypergraph.add_vertices(order);
46+
47+
const auto e0 = hypergraph.add_hyperedge().id();
48+
hypergraph.bind_tail(0uz, e0);
49+
hypergraph.bind_tail(1uz, e0);
50+
hypergraph.bind_head(2uz, e0);
51+
52+
const auto e1 = hypergraph.add_hyperedge().id();
53+
hypergraph.bind_tail(2uz, e1);
54+
hypergraph.bind_head(3uz, e1);
55+
hypergraph.bind_head(4uz, e1);
56+
57+
const auto e2 = hypergraph.add_hyperedge().id();
58+
hypergraph.bind_tail(1uz, e2);
59+
hypergraph.bind_head(4uz, e2);
60+
61+
SUBCASE("single root v0 (immediate halt)") {
62+
root_vertices = {0u};
63+
expected_previsit_order = {0u};
64+
expected_postvisit_order = {0u};
65+
expected_pred_map.resize(order, hgl::invalid_id);
66+
expected_pred_map[0uz] = 0u;
67+
expected_in_hyperedges.resize(order, hgl::invalid_id);
68+
}
69+
70+
SUBCASE("roots v0 and v1 (complete traversal)") {
71+
root_vertices = {0u, 1u};
72+
expected_previsit_order = {0u, 1u, 2u, 4u, 3u};
73+
expected_postvisit_order = expected_previsit_order;
74+
// v0->v0(root), v1->v1(root), v1->v2 (e0), v2->v3 (e1), v2->v4 (e2)
75+
expected_pred_map = {0u, 1u, 1u, 2u, 1u};
76+
expected_in_hyperedges = {hgl::invalid_id, hgl::invalid_id, e0, e1, e2};
77+
}
78+
79+
CAPTURE(hypergraph);
80+
CAPTURE(root_vertices);
81+
CAPTURE(expected_previsit_order);
82+
CAPTURE(expected_postvisit_order);
83+
CAPTURE(expected_pred_map);
84+
CAPTURE(expected_in_hyperedges);
85+
86+
// --- noret search ---
87+
88+
std::vector<id_type> previsit_order;
89+
std::vector<id_type> postvisit_order;
90+
std::vector<id_type> noret_pred_map(hypergraph.order(), hgl::invalid_id);
91+
std::vector<id_type> noret_in_hyperedges(hypergraph.order(), hgl::invalid_id);
92+
93+
hgl::algorithm::backward_search<gl::algorithm::noret>(
94+
hypergraph,
95+
root_vertices,
96+
[&](const auto& node) {
97+
previsit_order.push_back(node.vertex_id);
98+
noret_pred_map[node.vertex_id] = node.pred_id;
99+
noret_in_hyperedges[node.vertex_id] = node.hyperedge_id;
100+
},
101+
[&](const auto& node) { postvisit_order.push_back(node.vertex_id); }
102+
);
103+
104+
CHECK_EQ(previsit_order, expected_previsit_order);
105+
CHECK_EQ(postvisit_order, expected_postvisit_order);
106+
CHECK_EQ(noret_pred_map, expected_pred_map);
107+
CHECK_EQ(noret_in_hyperedges, expected_in_hyperedges);
108+
109+
// --- ret search ---
110+
111+
const auto search_tree =
112+
hgl::algorithm::backward_search<gl::algorithm::ret>(hypergraph, root_vertices);
113+
114+
const auto ret_pred_map =
115+
search_tree | std::views::transform(&node_type::pred_id) | std::ranges::to<std::vector>();
116+
const auto ret_in_hyperedges =
117+
search_tree | std::views::transform(&node_type::hyperedge_id)
118+
| std::ranges::to<std::vector>();
119+
120+
CHECK_EQ(ret_pred_map, expected_pred_map);
121+
CHECK_EQ(ret_in_hyperedges, expected_in_hyperedges);
122+
}
123+
124+
TEST_CASE_TEMPLATE_INSTANTIATE(
125+
backward_search_directed_hypergraph_template,
126+
hgl::list_hypergraph_traits<
127+
hgl::impl::bidirectional_t,
128+
hgl::bf_directed_t>, // bidirectional incidence list
129+
hgl::list_hypergraph_traits<
130+
hgl::impl::hyperedge_major_t,
131+
hgl::bf_directed_t>, // hyperedge-major incidence list
132+
hgl::list_hypergraph_traits<
133+
hgl::impl::vertex_major_t,
134+
hgl::bf_directed_t>, // vertex-major incidence list
135+
hgl::flat_list_hypergraph_traits<
136+
hgl::impl::bidirectional_t,
137+
hgl::bf_directed_t>, // bidirectional flat incidence list
138+
hgl::flat_list_hypergraph_traits<
139+
hgl::impl::hyperedge_major_t,
140+
hgl::bf_directed_t>, // hyperedge-major flat incidence list
141+
hgl::flat_list_hypergraph_traits<
142+
hgl::impl::vertex_major_t,
143+
hgl::bf_directed_t>, // vertex-major flat incidence list
144+
hgl::matrix_hypergraph_traits<
145+
hgl::impl::hyperedge_major_t,
146+
hgl::bf_directed_t>, // hyperedge-major incidence matrix
147+
hgl::matrix_hypergraph_traits<
148+
hgl::impl::vertex_major_t,
149+
hgl::bf_directed_t>, // vertex-major incidence matrix
150+
hgl::flat_matrix_hypergraph_traits<
151+
hgl::impl::hyperedge_major_t,
152+
hgl::bf_directed_t>, // hyperedge-major flat incidence matrix
153+
hgl::flat_matrix_hypergraph_traits<
154+
hgl::impl::vertex_major_t,
155+
hgl::bf_directed_t> // vertex-major flat incidence matrix
156+
);
157+
158+
TEST_CASE_TEMPLATE_DEFINE(
159+
"forward_search should properly enforce head dependencies during traversal",
160+
HypergraphTraitsType,
161+
forward_search_directed_hypergraph_template
162+
) {
163+
using hypergraph_type = hgl::hypergraph<HypergraphTraitsType>;
164+
using id_type = typename hypergraph_type::id_type;
165+
using node_type = hgl::algorithm::search_node<hypergraph_type>;
166+
167+
hypergraph_type hypergraph;
168+
std::vector<id_type> root_vertices;
169+
170+
std::vector<id_type> expected_previsit_order;
171+
std::vector<id_type> expected_postvisit_order;
172+
std::vector<id_type> expected_pred_map;
173+
std::vector<id_type> expected_in_hyperedges;
174+
175+
/* Topology:
176+
V = {v0, v1, v2, v3, v4}
177+
e0 = {v0, v1} -> {v2}
178+
e1 = {v2} -> {v3, v4}
179+
e2 = {v1} -> {v4}
180+
181+
v0 v3
182+
\ /
183+
-->-- v2 -->--
184+
/ \
185+
v1 ------->------ v4
186+
*/
187+
188+
const auto order = 5uz;
189+
hypergraph.add_vertices(order);
190+
191+
const auto e0 = hypergraph.add_hyperedge().id();
192+
hypergraph.bind_tail(0uz, e0);
193+
hypergraph.bind_tail(1uz, e0);
194+
hypergraph.bind_head(2uz, e0);
195+
196+
const auto e1 = hypergraph.add_hyperedge().id();
197+
hypergraph.bind_tail(2uz, e1);
198+
hypergraph.bind_head(3uz, e1);
199+
hypergraph.bind_head(4uz, e1);
200+
201+
const auto e2 = hypergraph.add_hyperedge().id();
202+
hypergraph.bind_tail(1uz, e2);
203+
hypergraph.bind_head(4uz, e2);
204+
205+
SUBCASE("single root v4 (partial backward traversal)") {
206+
// Rooting at v4. e1 cannot be traversed backwards because it also requires v3.
207+
// e2 CAN be traversed because v4 is its only head.
208+
root_vertices = {4u};
209+
210+
expected_previsit_order = {4u, 1u};
211+
expected_postvisit_order = expected_previsit_order;
212+
213+
expected_pred_map.resize(order, hgl::invalid_id);
214+
expected_pred_map[4uz] = 4u; // Root
215+
expected_pred_map[1uz] = 4u; // v1 reached backward from v4 via e2
216+
217+
expected_in_hyperedges.resize(order, hgl::invalid_id);
218+
expected_in_hyperedges[1uz] = e2;
219+
}
220+
221+
SUBCASE("roots v3 and v4 (complete backward traversal)") {
222+
// Rooting at v3 and v4 unlocks e1, which unlocks v2, which unlocks e0, which unlocks v0 and v1.
223+
root_vertices = {3u, 4u};
224+
225+
// Queue trace:
226+
// Init: [v3, v4]
227+
// Pop v3 -> sees e1 (needs v4, so wait)
228+
// Pop v4 -> sees e1 (unlocked -> enqueues v2), sees e2 (unlocked -> enqueues v1)
229+
// Queue: [v2, v1]
230+
// Pop v2 -> sees e0 (unlocked -> enqueues v0, v1) - v1 is already visited
231+
// Queue: [v1, v0]
232+
// Pop v1 -> no incoming edges
233+
// Pop v0 -> no incoming edges
234+
expected_previsit_order = {3u, 4u, 2u, 1u, 0u};
235+
expected_postvisit_order = expected_previsit_order;
236+
237+
expected_pred_map = {
238+
2u, // v0 reached backward from v2 via e0
239+
4u, // v1 reached backward from v4 via e2
240+
4u, // v2 reached backward from v4 via e1
241+
3u, // v3 is a root
242+
4u // v4 is a root
243+
};
244+
245+
expected_in_hyperedges = {
246+
e0, // v0 reached via e0
247+
e2, // v1 reached via e2
248+
e1, // v2 reached via e1
249+
hgl::invalid_id, // v3 root
250+
hgl::invalid_id // v4 root
251+
};
252+
}
253+
254+
CAPTURE(hypergraph);
255+
CAPTURE(root_vertices);
256+
CAPTURE(expected_previsit_order);
257+
CAPTURE(expected_postvisit_order);
258+
CAPTURE(expected_pred_map);
259+
CAPTURE(expected_in_hyperedges);
260+
261+
// --- noret search ---
262+
263+
std::vector<id_type> previsit_order;
264+
std::vector<id_type> postvisit_order;
265+
std::vector<id_type> noret_pred_map(hypergraph.order(), hgl::invalid_id);
266+
std::vector<id_type> noret_in_hyperedges(hypergraph.order(), hgl::invalid_id);
267+
268+
hgl::algorithm::forward_search<gl::algorithm::noret>(
269+
hypergraph,
270+
root_vertices,
271+
[&](const auto& node) {
272+
previsit_order.push_back(node.vertex_id);
273+
noret_pred_map[node.vertex_id] = node.pred_id;
274+
noret_in_hyperedges[node.vertex_id] = node.hyperedge_id;
275+
},
276+
[&](const auto& node) { postvisit_order.push_back(node.vertex_id); }
277+
);
278+
279+
CHECK_EQ(previsit_order, expected_previsit_order);
280+
CHECK_EQ(postvisit_order, expected_postvisit_order);
281+
CHECK_EQ(noret_pred_map, expected_pred_map);
282+
CHECK_EQ(noret_in_hyperedges, expected_in_hyperedges);
283+
284+
// --- ret search ---
285+
286+
const auto search_tree =
287+
hgl::algorithm::forward_search<gl::algorithm::ret>(hypergraph, root_vertices);
288+
289+
const auto ret_pred_map =
290+
search_tree | std::views::transform(&node_type::pred_id) | std::ranges::to<std::vector>();
291+
const auto ret_in_hyperedges =
292+
search_tree | std::views::transform(&node_type::hyperedge_id)
293+
| std::ranges::to<std::vector>();
294+
295+
CHECK_EQ(ret_pred_map, expected_pred_map);
296+
CHECK_EQ(ret_in_hyperedges, expected_in_hyperedges);
297+
}
298+
299+
TEST_CASE_TEMPLATE_INSTANTIATE(
300+
forward_search_directed_hypergraph_template,
301+
hgl::list_hypergraph_traits<
302+
hgl::impl::bidirectional_t,
303+
hgl::bf_directed_t>, // bidirectional incidence list
304+
hgl::list_hypergraph_traits<
305+
hgl::impl::hyperedge_major_t,
306+
hgl::bf_directed_t>, // hyperedge-major incidence list
307+
hgl::list_hypergraph_traits<
308+
hgl::impl::vertex_major_t,
309+
hgl::bf_directed_t>, // vertex-major incidence list
310+
hgl::flat_list_hypergraph_traits<
311+
hgl::impl::bidirectional_t,
312+
hgl::bf_directed_t>, // bidirectional flat incidence list
313+
hgl::flat_list_hypergraph_traits<
314+
hgl::impl::hyperedge_major_t,
315+
hgl::bf_directed_t>, // hyperedge-major flat incidence list
316+
hgl::flat_list_hypergraph_traits<
317+
hgl::impl::vertex_major_t,
318+
hgl::bf_directed_t>, // vertex-major flat incidence list
319+
hgl::matrix_hypergraph_traits<
320+
hgl::impl::hyperedge_major_t,
321+
hgl::bf_directed_t>, // hyperedge-major incidence matrix
322+
hgl::matrix_hypergraph_traits<
323+
hgl::impl::vertex_major_t,
324+
hgl::bf_directed_t>, // vertex-major incidence matrix
325+
hgl::flat_matrix_hypergraph_traits<
326+
hgl::impl::hyperedge_major_t,
327+
hgl::bf_directed_t>, // hyperedge-major flat incidence matrix
328+
hgl::flat_matrix_hypergraph_traits<
329+
hgl::impl::vertex_major_t,
330+
hgl::bf_directed_t> // vertex-major flat incidence matrix
331+
);
332+
333+
TEST_SUITE_END(); // test_alg_bfs
334+
335+
} // namespace hgl_testing

0 commit comments

Comments
 (0)