Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
test_bidirectional_bfs.cc
Go to the documentation of this file.
1
9# include <gtest/gtest.h>
10# include <Bidirectional_BFS.H>
11# include <tpl_find_path.H>
12# include <tpl_graph.H>
13
14using namespace Aleph;
15
16// ============================================================================
17// Type aliases
18// ============================================================================
19
21using Node = GT::Node;
22using Arc = GT::Arc;
23
26using DArc = DGT::Arc;
27
28// Helper: count path length (number of edges)
29template <class G>
31{
32 if (p.is_empty())
33 return 0;
34 size_t count = 0;
35 for (typename Path<G>::Iterator it(p); it.has_curr(); it.next())
36 ++count;
37 return count > 0 ? count - 1 : 0; // nodes - 1 = edges
38}
39
40// ============================================================================
41// TEST 1: Basic shortest path
42// ============================================================================
44{
45 GT g;
46 auto n0 = g.insert_node(0);
47 auto n1 = g.insert_node(1);
48 auto n2 = g.insert_node(2);
49 auto n3 = g.insert_node(3);
50
51 g.insert_arc(n0, n1, 0);
52 g.insert_arc(n1, n2, 0);
53 g.insert_arc(n2, n3, 0);
54 g.insert_arc(n0, n3, 0); // direct edge
55
57 Path<GT> path(g);
58 bool found = bibfs(g, n0, n3, path);
59
60 ASSERT_TRUE(found);
61 EXPECT_EQ(path_edge_count(path), 1u); // direct edge n0->n3
62}
63
64// ============================================================================
65// TEST 2: Path to self
66// ============================================================================
68{
69 GT g;
70 auto n0 = g.insert_node(0);
71
73 Path<GT> path(g);
74 bool found = bibfs(g, n0, n0, path);
75
76 EXPECT_TRUE(found);
77}
78
79// ============================================================================
80// TEST 3: No path exists
81// ============================================================================
83{
84 GT g;
85 auto n0 = g.insert_node(0);
86 auto n1 = g.insert_node(1);
87
89 Path<GT> path(g);
90 bool found = bibfs(g, n0, n1, path);
91
92 EXPECT_FALSE(found);
93}
94
95// ============================================================================
96// TEST 4: Single node
97// ============================================================================
99{
100 GT g;
101 auto n0 = g.insert_node(0);
102
104 Path<GT> path(g);
105 bool found = bibfs(g, n0, n0, path);
106
107 EXPECT_TRUE(found);
108}
109
110// ============================================================================
111// TEST 5: Linear chain
112// ============================================================================
114{
115 GT g;
116 auto n0 = g.insert_node(0);
117 auto n1 = g.insert_node(1);
118 auto n2 = g.insert_node(2);
119 auto n3 = g.insert_node(3);
120
121 g.insert_arc(n0, n1, 0);
122 g.insert_arc(n1, n2, 0);
123 g.insert_arc(n2, n3, 0);
124
126 Path<GT> path(g);
127 bool found = bibfs(g, n0, n3, path);
128
129 ASSERT_TRUE(found);
130 EXPECT_EQ(path_edge_count(path), 3u);
131}
132
133// ============================================================================
134// TEST 6: Complete graph K4
135// ============================================================================
137{
138 GT g;
139 auto n0 = g.insert_node(0);
140 auto n1 = g.insert_node(1);
141 auto n2 = g.insert_node(2);
142 auto n3 = g.insert_node(3);
143
144 g.insert_arc(n0, n1, 0);
145 g.insert_arc(n0, n2, 0);
146 g.insert_arc(n0, n3, 0);
147 g.insert_arc(n1, n2, 0);
148 g.insert_arc(n1, n3, 0);
149 g.insert_arc(n2, n3, 0);
150
152 Path<GT> path(g);
153 bool found = bibfs(g, n0, n3, path);
154
155 ASSERT_TRUE(found);
156 EXPECT_EQ(path_edge_count(path), 1u); // direct edge
157}
158
159// ============================================================================
160// TEST 7: Null node validation
161// ============================================================================
163{
164 GT g;
165 auto n0 = g.insert_node(0);
166
168 Path<GT> path(g);
169
170 EXPECT_THROW(bibfs(g, nullptr, n0, path), std::domain_error);
171 EXPECT_THROW(bibfs(g, n0, nullptr, path), std::domain_error);
172}
173
174// ============================================================================
175// TEST 8: Disconnected components
176// ============================================================================
178{
179 GT g;
180 auto n0 = g.insert_node(0);
181 auto n1 = g.insert_node(1);
182 auto n2 = g.insert_node(2);
183 auto n3 = g.insert_node(3);
184
185 g.insert_arc(n0, n1, 0);
186 g.insert_arc(n2, n3, 0);
187
189 Path<GT> path(g);
190 bool found = bibfs(g, n0, n3, path);
191
192 EXPECT_FALSE(found);
193}
194
195// ============================================================================
196// TEST 9: Two-node graph
197// ============================================================================
199{
200 GT g;
201 auto n0 = g.insert_node(0);
202 auto n1 = g.insert_node(1);
203
204 g.insert_arc(n0, n1, 0);
205
207 Path<GT> path(g);
208 bool found = bibfs(g, n0, n1, path);
209
210 ASSERT_TRUE(found);
211 EXPECT_EQ(path_edge_count(path), 1u);
212}
213
214// ============================================================================
215// TEST 10: Graph with cycle
216// ============================================================================
218{
219 GT g;
220 auto n0 = g.insert_node(0);
221 auto n1 = g.insert_node(1);
222 auto n2 = g.insert_node(2);
223 auto n3 = g.insert_node(3);
224
225 // Cycle: n0-n1-n2-n3-n0
226 g.insert_arc(n0, n1, 0);
227 g.insert_arc(n1, n2, 0);
228 g.insert_arc(n2, n3, 0);
229 g.insert_arc(n3, n0, 0);
230
232 Path<GT> path(g);
233 bool found = bibfs(g, n0, n2, path);
234
235 ASSERT_TRUE(found);
236 EXPECT_EQ(path_edge_count(path), 2u); // n0->n1->n2 or n0->n3->n2
237}
238
239// ============================================================================
240// TEST 11: Odd-length path
241// ============================================================================
243{
244 GT g;
245 auto n0 = g.insert_node(0);
246 auto n1 = g.insert_node(1);
247 auto n2 = g.insert_node(2);
248
249 g.insert_arc(n0, n1, 0);
250 g.insert_arc(n1, n2, 0);
251
253 Path<GT> path(g);
254 bool found = bibfs(g, n0, n2, path);
255
256 ASSERT_TRUE(found);
257
258 // Verify exact path: n0 -> n1 -> n2
260 for (Path<GT>::Iterator it(path); it.has_curr(); it.next())
261 nodes.append(it.get_current_node()->get_info());
262
263 ASSERT_EQ(nodes.size(), 3u);
264 auto pit = nodes.get_it();
265 EXPECT_EQ(pit.get_curr(), 0); pit.next();
266 EXPECT_EQ(pit.get_curr(), 1); pit.next();
267 EXPECT_EQ(pit.get_curr(), 2);
268}
269
270// ============================================================================
271// TEST 12: Even-length path
272// ============================================================================
274{
275 GT g;
276 auto n0 = g.insert_node(0);
277 auto n1 = g.insert_node(1);
278 auto n2 = g.insert_node(2);
279 auto n3 = g.insert_node(3);
280
281 g.insert_arc(n0, n1, 0);
282 g.insert_arc(n1, n2, 0);
283 g.insert_arc(n2, n3, 0);
284
286 Path<GT> path(g);
287 bool found = bibfs(g, n0, n3, path);
288
289 ASSERT_TRUE(found);
290 EXPECT_EQ(path_edge_count(path), 3u);
291}
292
293// ============================================================================
294// TEST 13: Cross-validation with standard BFS
295// ============================================================================
297{
298 GT g;
299 auto n0 = g.insert_node(0);
300 auto n1 = g.insert_node(1);
301 auto n2 = g.insert_node(2);
302 auto n3 = g.insert_node(3);
303 auto n4 = g.insert_node(4);
304
305 g.insert_arc(n0, n1, 0);
306 g.insert_arc(n0, n2, 0);
307 g.insert_arc(n1, n3, 0);
308 g.insert_arc(n2, n4, 0);
309 g.insert_arc(n3, n4, 0);
310
311 // Bidirectional BFS
313 Path<GT> bi_path(g);
314 bool bi_found = bibfs(g, n0, n4, bi_path);
315
316 // Standard BFS
319 bool std_found = std_bfs(g, n0, n4, std_path);
320
324}
325
326// ============================================================================
327// TEST 14: Multiple successive computations
328// ============================================================================
330{
331 GT g;
332 auto n0 = g.insert_node(0);
333 auto n1 = g.insert_node(1);
334 auto n2 = g.insert_node(2);
335
336 g.insert_arc(n0, n1, 0);
337 g.insert_arc(n1, n2, 0);
338
340
341 Path<GT> p1(g);
342 EXPECT_TRUE(bibfs(g, n0, n2, p1));
343
344 Path<GT> p2(g);
345 EXPECT_TRUE(bibfs(g, n2, n0, p2));
346
348}
349
350// ============================================================================
351// TEST 15: Large graph
352// ============================================================================
354{
355 GT g;
356 const int N = 200;
357
359 for (int i = 0; i < N; ++i)
360 nodes[i] = g.insert_node(i);
361
362 for (int i = 0; i < N - 1; ++i)
363 g.insert_arc(nodes[i], nodes[i + 1], 0);
364
366 Path<GT> path(g);
367 bool found = bibfs(g, nodes[0], nodes[N - 1], path);
368
369 ASSERT_TRUE(found);
370 EXPECT_EQ(path_edge_count(path), static_cast<size_t>(N - 1));
371}
372
373// ============================================================================
374// TEST 16: Operator returning Path
375// ============================================================================
377{
378 GT g;
379 auto n0 = g.insert_node(0);
380 auto n1 = g.insert_node(1);
381 auto n2 = g.insert_node(2);
382
383 g.insert_arc(n0, n1, 0);
384 g.insert_arc(n1, n2, 0);
385
387 auto path = bibfs(g, n0, n2);
388
389 EXPECT_FALSE(path.is_empty());
390 EXPECT_EQ(path_edge_count(path), 2u);
391}
392
393// ============================================================================
394// TEST 17: Star graph
395// ============================================================================
397{
398 GT g;
399 auto center = g.insert_node(0);
400 auto n1 = g.insert_node(1);
401 auto n2 = g.insert_node(2);
402 auto n3 = g.insert_node(3);
403 auto n4 = g.insert_node(4);
404
405 g.insert_arc(center, n1, 0);
406 g.insert_arc(center, n2, 0);
407 g.insert_arc(center, n3, 0);
408 g.insert_arc(center, n4, 0);
409
411 Path<GT> path(g);
412 bool found = bibfs(g, n1, n4, path);
413
414 ASSERT_TRUE(found);
415 EXPECT_EQ(path_edge_count(path), 2u); // n1->center->n4
416}
417
418// ============================================================================
419// TEST 18: Binary tree-like graph
420// ============================================================================
422{
423 GT g;
424 // 0
425 // / \ (backslash)
426 // 1 2
427 // /| |\ (backslash)
428 // 3 4 5 6
429 auto n0 = g.insert_node(0);
430 auto n1 = g.insert_node(1);
431 auto n2 = g.insert_node(2);
432 auto n3 = g.insert_node(3);
433 auto n4 = g.insert_node(4);
434 auto n5 = g.insert_node(5);
435 auto n6 = g.insert_node(6);
436
437 g.insert_arc(n0, n1, 0);
438 g.insert_arc(n0, n2, 0);
439 g.insert_arc(n1, n3, 0);
440 g.insert_arc(n1, n4, 0);
441 g.insert_arc(n2, n5, 0);
442 g.insert_arc(n2, n6, 0);
443
445 Path<GT> path(g);
446 bool found = bibfs(g, n3, n6, path);
447
448 ASSERT_TRUE(found);
449 EXPECT_EQ(path_edge_count(path), 4u); // n3->n1->n0->n2->n6
450}
451
452// ============================================================================
453// TEST 19: Digraph forward only
454// ============================================================================
456{
457 DGT g;
458 auto n0 = g.insert_node(0);
459 auto n1 = g.insert_node(1);
460 auto n2 = g.insert_node(2);
461
462 g.insert_arc(n0, n1, 0);
463 g.insert_arc(n1, n2, 0);
464
465 // For digraphs, we need Out_Iterator for forward and In_Iterator for backward
467 Path<DGT> path(g);
468 bool found = bibfs(g, n0, n2, path);
469
470 ASSERT_TRUE(found);
471 EXPECT_EQ(path_edge_count(path), 2u);
472}
473
474// ============================================================================
475// TEST 20: Digraph no backward path
476// ============================================================================
478{
479 DGT g;
480 auto n0 = g.insert_node(0);
481 auto n1 = g.insert_node(1);
482
483 g.insert_arc(n0, n1, 0);
484
486 Path<DGT> p2(g);
487 bool found = bibfs(g, n1, n0, p2);
488
489 EXPECT_FALSE(found);
490}
491
492// ============================================================================
493// TEST 21: Digraph with In_Iterator backward search
494// ============================================================================
496{
497 DGT g;
498 auto n0 = g.insert_node(0);
499 auto n1 = g.insert_node(1);
500 auto n2 = g.insert_node(2);
501 auto n3 = g.insert_node(3);
502
503 g.insert_arc(n0, n1, 0);
504 g.insert_arc(n1, n2, 0);
505 g.insert_arc(n2, n3, 0);
506
508 Path<DGT> path(g);
509 bool found = bibfs(g, n0, n3, path);
510
511 ASSERT_TRUE(found);
512 EXPECT_EQ(path_edge_count(path), 3u);
513
514 // Verify path
516 for (Path<DGT>::Iterator it(path); it.has_curr(); it.next())
517 nodes.append(it.get_current_node()->get_info());
518
519 ASSERT_EQ(nodes.size(), 4u);
520 auto pit = nodes.get_it();
521 EXPECT_EQ(pit.get_curr(), 0); pit.next();
522 EXPECT_EQ(pit.get_curr(), 1); pit.next();
523 EXPECT_EQ(pit.get_curr(), 2); pit.next();
524 EXPECT_EQ(pit.get_curr(), 3);
525}
526
527// ============================================================================
528// === EXAMPLE: Social network ===
529// Find the shortest connection between two people in a social network.
530//
531// Alice -- Bob -- Charlie
532// | |
533// Diana -- Eve -- Frank
534//
535// Shortest Alice->Frank: Alice->Diana->Eve->Frank (3 hops)
536// or Alice->Bob->Charlie->Frank (3 hops)
537// ============================================================================
539{
540 GT g;
541 auto alice = g.insert_node(0); // Alice
542 auto bob = g.insert_node(1); // Bob
543 auto charlie = g.insert_node(2); // Charlie
544 auto diana = g.insert_node(3); // Diana
545 auto eve = g.insert_node(4); // Eve
546 auto frank = g.insert_node(5); // Frank
547
548 g.insert_arc(alice, bob, 0);
549 g.insert_arc(bob, charlie, 0);
550 g.insert_arc(charlie, frank, 0);
551 g.insert_arc(alice, diana, 0);
552 g.insert_arc(diana, eve, 0);
553 g.insert_arc(eve, frank, 0);
554
556 Path<GT> path(g);
557 bool found = bibfs(g, alice, frank, path);
558
559 ASSERT_TRUE(found);
560 EXPECT_EQ(path_edge_count(path), 3u);
561}
562
563// ============================================================================
564// TEST 23: Path correctness (first and last node)
565// ============================================================================
567{
568 GT g;
569 auto n0 = g.insert_node(0);
570 auto n1 = g.insert_node(1);
571 auto n2 = g.insert_node(2);
572 auto n3 = g.insert_node(3);
573 auto n4 = g.insert_node(4);
574
575 g.insert_arc(n0, n1, 0);
576 g.insert_arc(n1, n2, 0);
577 g.insert_arc(n2, n3, 0);
578 g.insert_arc(n3, n4, 0);
579
581 Path<GT> path(g);
582 bool found = bibfs(g, n0, n4, path);
583
584 ASSERT_TRUE(found);
585
586 // Verify first node is n0 and last is n4
588 for (Path<GT>::Iterator it(path); it.has_curr(); it.next())
589 nodes.append(it.get_current_node()->get_info());
590
591 ASSERT_GE(nodes.size(), 2u);
592 EXPECT_EQ(nodes.get_first(), 0);
593 EXPECT_EQ(nodes.get_last(), 4);
594}
595
596// ============================================================================
597// TEST 24: Cross-validation with BFS on various endpoints
598// ============================================================================
600{
601 GT g;
602 const int N = 10;
603
605 for (int i = 0; i < N; ++i)
606 nodes[i] = g.insert_node(i);
607
608 // Create a connected graph
609 for (int i = 0; i < N - 1; ++i)
610 g.insert_arc(nodes[i], nodes[i + 1], 0);
611
612 // Add some shortcuts
613 g.insert_arc(nodes[0], nodes[5], 0);
614 g.insert_arc(nodes[3], nodes[8], 0);
615
616 for (int start = 0; start < N; start += 3)
617 for (int end = start + 1; end < N; end += 2)
618 {
621
623 bool bi_found = bibfs(g, nodes[start], nodes[end], bi_path);
624 bool std_found = std_bfs(g, nodes[start], nodes[end], std_path);
625
627 << "Mismatch for start=" << start << " end=" << end;
630 << "Length mismatch for start=" << start << " end=" << end;
631 }
632}
633
634// ============================================================================
635// TEST 25: Grid graph
636// ============================================================================
638{
639 GT g;
640 const int ROWS = 4, COLS = 4;
641 Node * grid[ROWS][COLS];
642
643 for (int r = 0; r < ROWS; ++r)
644 for (int c = 0; c < COLS; ++c)
645 grid[r][c] = g.insert_node(r * COLS + c);
646
647 for (int r = 0; r < ROWS; ++r)
648 for (int c = 0; c < COLS; ++c)
649 {
650 if (c + 1 < COLS)
651 g.insert_arc(grid[r][c], grid[r][c + 1], 0);
652 if (r + 1 < ROWS)
653 g.insert_arc(grid[r][c], grid[r + 1][c], 0);
654 }
655
657 Path<GT> path(g);
658 bool found = bibfs(g, grid[0][0], grid[ROWS - 1][COLS - 1], path);
659
660 ASSERT_TRUE(found);
661 // Manhattan distance = (ROWS-1) + (COLS-1)
662 EXPECT_EQ(path_edge_count(path), static_cast<size_t>(ROWS + COLS - 2));
663}
664
665// ============================================================================
666// TEST 26: Meeting-point regression (must return shortest path)
667// ============================================================================
669{
670 GT g;
671 auto s = g.insert_node(0);
672 auto a1 = g.insert_node(1);
673 auto a2 = g.insert_node(2);
674 auto a3 = g.insert_node(3);
675 auto b1 = g.insert_node(4);
676 auto b2 = g.insert_node(5);
677 auto t = g.insert_node(6);
678
679 // Longer branch: s-a1-a2-a3-t (4 edges)
680 g.insert_arc(s, a1, 0);
681 g.insert_arc(a1, a2, 0);
682 g.insert_arc(a2, a3, 0);
683 g.insert_arc(a3, t, 0);
684
685 // Shorter branch: s-b1-b2-t (3 edges)
686 g.insert_arc(s, b1, 0);
687 g.insert_arc(b1, b2, 0);
688 g.insert_arc(b2, t, 0);
689
692
694 ASSERT_TRUE(bibfs(g, s, t, bi_path));
695 ASSERT_TRUE(std_bfs(g, s, t, std_path));
696
699}
Bidirectional BFS for shortest unweighted path.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
Bidirectional BFS for finding shortest unweighted paths.
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3854
typename BaseGraph::Arc Arc
Definition graph-dry.H:3858
typename BaseGraph::Node Node
Definition graph-dry.H:3857
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Busca en amplitud un camino entre un par de nodos.
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Graph_Arc< int > Arc
The node class type.
Definition tpl_graph.H:433
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Iterator on nodes and arcs of a path.
Definition tpl_graph.H:3208
Path on a graph.
Definition tpl_graph.H:2669
bool is_empty() const noexcept
Return true if the path is empty.
Definition tpl_graph.H:2813
#define TEST(name)
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
and
Check uniqueness with explicit hash + equality functors.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
size_t path_edge_count(Path< G > &p)
DGT::Arc DArc
DGT::Node DNode
gsl_rng * r
Path finding algorithms in graphs.
Generic graph and digraph implementations.