Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
astar_test.cc
Go to the documentation of this file.
1
2/*
3 Aleph_w
4
5 Data structures & Algorithms
6 version 2.0.0b
7 https://github.com/lrleon/Aleph-w
8
9 This file is part of Aleph-w library
10
11 Copyright (c) 2002-2026 Leandro Rabindranath Leon
12
13 Permission is hereby granted, free of charge, to any person obtaining a copy
14 of this software and associated documentation files (the "Software"), to deal
15 in the Software without restriction, including without limitation the rights
16 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
17 copies of the Software, and to permit persons to whom the Software is
18 furnished to do so, subject to the following conditions:
19
20 The above copyright notice and this permission notice shall be included in all
21 copies or substantial portions of the Software.
22
23 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
28 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
29 SOFTWARE.
30*/
31
32
45#include <gtest/gtest.h>
46#include <tpl_graph.H>
47#include <AStar.H>
48#include <Dijkstra.H>
49#include <tpl_graph_utils.H>
50#include <random>
51#include <cmath>
52
53using namespace Aleph;
54
55// ==================== Heap Type Tags ====================
56
58{
59 template <class G, class D, class A>
61};
62
64{
65 template <class G, class D, class A>
67};
68
69// ==================== Type definitions ====================
70
71// Simple weighted graph
72struct SimpleNode : public Graph_Node<int>
73{
74 using Graph_Node<int>::Graph_Node;
75};
76
77struct SimpleArc : public Graph_Arc<double>
78{
79 using Graph_Arc<double>::Graph_Arc;
80};
81
83
84// Grid-based node with coordinates
85struct GridNode : public Graph_Node<int>
86{
87 int x = 0;
88 int y = 0;
89
90 GridNode() = default;
91 GridNode(int id) : Graph_Node<int>(id), x(0), y(0) {}
92 GridNode(int id, int _x, int _y) : Graph_Node<int>(id), x(_x), y(_y) {}
93};
94
95struct GridArc : public Graph_Arc<double>
96{
97 using Graph_Arc<double>::Graph_Arc;
98};
99
101
102// Distance accessor for double weights
104{
106
108 {
109 return arc->get_info();
110 }
111
113 {
114 return arc->get_info();
115 }
116
117 static void set_zero(SimpleGraph::Arc* arc)
118 {
119 arc->get_info() = 0.0;
120 }
121};
122
123// Euclidean heuristic for GridGraph
125{
127
129 {
130 double dx = from->x - to->x;
131 double dy = from->y - to->y;
132 return std::sqrt(dx*dx + dy*dy);
133 }
134};
135
136// Manhattan heuristic for GridGraph
138{
140
142 {
143 return std::abs(from->x - to->x) + std::abs(from->y - to->y);
144 }
145};
146
147// ==================== Test Fixtures ====================
148
149class AStarBasicTest : public ::testing::Test
150{
151protected:
153 std::vector<SimpleGraph::Node*> nodes;
154
155 void SetUp() override
156 {
157 // Create a simple graph:
158 // 1
159 // 0 ----> 1
160 // | |
161 // 4 | | 2
162 // v v
163 // 3 ----> 2
164 // 1
165 for (int i = 0; i < 4; ++i)
166 nodes.push_back(g.insert_node(i));
167
168 g.insert_arc(nodes[0], nodes[1], 1.0);
169 g.insert_arc(nodes[1], nodes[2], 2.0);
170 g.insert_arc(nodes[0], nodes[3], 4.0);
171 g.insert_arc(nodes[3], nodes[2], 1.0);
172 }
173};
174
175class AStarGridTest : public ::testing::Test
176{
177protected:
179 std::vector<GridGraph::Node*> nodes;
180 static constexpr int GRID_SIZE = 5;
181
182 void SetUp() override
183 {
184 // Create a 5x5 grid graph
185 nodes.resize(GRID_SIZE * GRID_SIZE);
186
187 for (int y = 0; y < GRID_SIZE; ++y)
188 for (int x = 0; x < GRID_SIZE; ++x)
189 {
190 int idx = y * GRID_SIZE + x;
191 GridNode node(idx, x, y); // Stack-allocated, no leak
192 nodes[idx] = g.insert_node(node.get_info());
193 nodes[idx]->x = x;
194 nodes[idx]->y = y;
195 }
196
197 // Connect adjacent cells (4-connected grid)
198 for (int y = 0; y < GRID_SIZE; ++y)
199 for (int x = 0; x < GRID_SIZE; ++x)
200 {
201 int idx = y * GRID_SIZE + x;
202
203 // Right neighbor
204 if (x + 1 < GRID_SIZE)
205 {
206 int right = y * GRID_SIZE + (x + 1);
207 g.insert_arc(nodes[idx], nodes[right], 1.0);
208 g.insert_arc(nodes[right], nodes[idx], 1.0);
209 }
210
211 // Down neighbor
212 if (y + 1 < GRID_SIZE)
213 {
214 int down = (y + 1) * GRID_SIZE + x;
215 g.insert_arc(nodes[idx], nodes[down], 1.0);
216 g.insert_arc(nodes[down], nodes[idx], 1.0);
217 }
218 }
219 }
220
222 {
223 return nodes[y * GRID_SIZE + x];
224 }
225};
226
227// ==================== Tests ====================
228
229// Test basic path finding with zero heuristic (should match Dijkstra)
244
245// Test finding shortest path
247{
248 Path<SimpleGraph> path(g);
250
251 auto cost = astar.find_path(g, nodes[0], nodes[2], path);
252
253 // Shortest path: 0 -> 1 -> 2 with cost 3
254 EXPECT_DOUBLE_EQ(cost, 3.0);
255 EXPECT_EQ(path.size(), 3);
256}
257
258// Test paint_path version
260{
262
263 bool found = astar.paint_path(g, nodes[0], nodes[2]);
265 EXPECT_TRUE(astar.is_painted());
266
267 Path<SimpleGraph> path(g);
268 auto cost = astar.get_min_path(nodes[2], path);
269 EXPECT_DOUBLE_EQ(cost, 3.0);
270}
271
272// Test compute_path (tree building version)
274{
275 SimpleGraph tree;
277
278 auto end_tree_node = astar.compute_path(g, nodes[0], nodes[2], tree);
279
280 EXPECT_NE(end_tree_node, nullptr);
281 EXPECT_GE(tree.get_num_nodes(), 2u);
282}
283
284// Test no path exists
286{
287 // Add isolated node
288 auto isolated = g.insert_node(99);
289
290 Path<SimpleGraph> path(g);
292
293 auto cost = astar.find_path(g, nodes[0], isolated, path);
294
295 EXPECT_EQ(cost, std::numeric_limits<double>::max());
296 EXPECT_TRUE(path.is_empty());
297}
298
299// Test start equals end
301{
302 Path<SimpleGraph> path(g);
304
305 // When start == end, paint_path should find it immediately
306 bool found = astar.paint_path(g, nodes[0], nodes[0]);
308}
309
310// Test grid with Euclidean heuristic
312{
313 Path<GridGraph> path(g);
315
316 auto start = get_node(0, 0);
317 auto end = get_node(GRID_SIZE-1, GRID_SIZE-1);
318
319 auto cost = astar.find_path(g, start, end, path);
320
321 // Manhattan distance in 4-connected grid
322 double expected = (GRID_SIZE-1) + (GRID_SIZE-1);
324}
325
326// Test grid with Manhattan heuristic
328{
329 Path<GridGraph> path(g);
331
332 auto start = get_node(0, 0);
333 auto end = get_node(GRID_SIZE-1, GRID_SIZE-1);
334
335 auto cost = astar.find_path(g, start, end, path);
336
337 double expected = (GRID_SIZE-1) + (GRID_SIZE-1);
339}
340
341// Test that A* with good heuristic explores fewer nodes than Dijkstra
343{
344 // This is a qualitative test - A* should find the path
345 Path<GridGraph> path(g);
347
348 auto start = get_node(0, 0);
349 auto end = get_node(4, 4);
350
351 auto cost = astar.find_path(g, start, end, path);
352
353 EXPECT_FALSE(path.is_empty());
354 EXPECT_EQ(cost, 8.0); // Manhattan distance in grid
355}
356
357// Test operator() interface
359{
360 Path<SimpleGraph> path(g);
362
363 auto cost = astar(g, nodes[0], nodes[2], path);
364
365 EXPECT_DOUBLE_EQ(cost, 3.0);
366}
367
368// Test single node graph
370{
371 SimpleGraph g;
372 auto node = g.insert_node(0);
373
374 Path<SimpleGraph> path(g);
376
377 bool found = astar.paint_path(g, node, node);
379}
380
381// Test null parameters throw
383{
384 SimpleGraph g;
385 auto node = g.insert_node(0);
386
387 Path<SimpleGraph> path(g);
389
390 EXPECT_THROW(astar.find_path(g, nullptr, node, path), std::domain_error);
391 EXPECT_THROW(astar.find_path(g, node, nullptr, path), std::domain_error);
392}
393
394// Test empty graph throws
396{
398 SimpleGraph g;
399 auto node = g.insert_node(0);
400
403
404 EXPECT_THROW(astar.find_path(empty_g, node, node, path), std::domain_error);
405}
406
407// Test comparison: A* vs Dijkstra on random graph
409{
410 SimpleGraph g;
411 std::vector<SimpleGraph::Node*> nodes;
412 std::mt19937 rng(42);
413 std::uniform_real_distribution<double> weight_dist(0.1, 10.0);
414
415 // Create 20 nodes
416 for (int i = 0; i < 20; ++i)
417 nodes.push_back(g.insert_node(i));
418
419 // Create random edges
420 for (size_t i = 0; i < nodes.size(); ++i)
421 for (size_t j = i + 1; j < nodes.size(); ++j)
422 if (rng() % 3 == 0)
423 {
424 double w = weight_dist(rng);
425 g.insert_arc(nodes[i], nodes[j], w);
426 g.insert_arc(nodes[j], nodes[i], w);
427 }
428
429 // Compare paths for multiple pairs
432
433 for (int i = 0; i < 5; ++i)
434 {
435 auto start = nodes[rng() % nodes.size()];
436 auto end = nodes[rng() % nodes.size()];
437
438 if (start == end)
439 continue;
440
443
444 auto astar_cost = astar.find_path(g, start, end, astar_path);
445 auto dijkstra_cost = dijkstra.find_min_path(g, start, end, dijkstra_path);
446
447 // Costs should match (both should be optimal)
449 << "Mismatch for start=" << start->get_info()
450 << " end=" << end->get_info();
451 }
452}
453
454// ==================== Tests for New Dijkstra-Compatible Methods ====================
455
456class AStarDijkstraModeTest : public ::testing::Test
457{
458protected:
460 std::vector<SimpleGraph::Node*> nodes;
461
462 void SetUp() override
463 {
464 // Create a larger graph for testing complete tree
465 // 1 2
466 // 0 ----> 1 ----> 2
467 // \ | ^
468 // \5 |3 |1
469 // \ v |
470 // --> 3 ------+
471 for (int i = 0; i < 4; ++i)
472 nodes.push_back(g.insert_node(i));
473
474 g.insert_arc(nodes[0], nodes[1], 1.0);
475 g.insert_arc(nodes[1], nodes[2], 2.0);
476 g.insert_arc(nodes[0], nodes[3], 5.0);
477 g.insert_arc(nodes[1], nodes[3], 3.0);
478 g.insert_arc(nodes[3], nodes[2], 1.0);
479 }
480};
481
482// Test compute_min_paths_tree (Dijkstra mode - complete tree)
484{
485 SimpleGraph tree;
487
488 auto root = astar.compute_min_paths_tree(g, nodes[0], tree);
489
490 ASSERT_NE(root, nullptr);
491 // Tree should have all reachable nodes
492 EXPECT_EQ(tree.get_num_nodes(), g.get_num_nodes());
493 // Tree should have n-1 arcs for n nodes (spanning tree)
494 EXPECT_EQ(tree.get_num_arcs(), g.get_num_nodes() - 1);
495}
496
497// Test paint_min_paths_tree (Dijkstra mode - complete)
499{
501
502 astar.paint_min_paths_tree(g, nodes[0]);
503
504 EXPECT_TRUE(astar.is_painted());
505 EXPECT_TRUE(astar.has_computation());
506 EXPECT_EQ(astar.get_start_node(), nodes[0]);
507
508 // All nodes should be reachable
509 for (auto node : nodes)
510 {
511 Path<SimpleGraph> path(g);
512 auto dist = astar.get_min_path(node, path);
513 EXPECT_LT(dist, std::numeric_limits<double>::max());
514 }
515}
516
517// Test find_min_path (Dijkstra mode - no heuristic)
532
533// Test compute_partial_min_paths_tree (without heuristic)
535{
536 SimpleGraph tree;
538
539 astar.compute_partial_min_paths_tree(g, nodes[0], nodes[2], tree);
540
541 // Tree should contain at least the path nodes
542 EXPECT_GE(tree.get_num_nodes(), 2u);
543 // But may not contain all nodes (partial)
544 EXPECT_LE(tree.get_num_nodes(), g.get_num_nodes());
545}
546
547// Test paint_partial_min_paths_tree (without heuristic)
549{
551
552 bool found = astar.paint_partial_min_paths_tree(g, nodes[0], nodes[2]);
553
555 EXPECT_TRUE(astar.is_painted());
556
557 // Can extract path
558 Path<SimpleGraph> path(g);
559 auto cost = astar.get_min_path(nodes[2], path);
560 EXPECT_EQ(cost, 3.0); // 0->1->2
561}
562
563// Test get_distance after painting
565{
567
568 astar.paint_min_paths_tree(g, nodes[0]);
569
570 // Distance to node 0 (start) should be 0
571 EXPECT_DOUBLE_EQ(astar.get_distance(nodes[0]), 0.0);
572
573 // Distance to node 1 should be 1
574 EXPECT_DOUBLE_EQ(astar.get_distance(nodes[1]), 1.0);
575
576 // Distance to node 2 should be 3 (0->1->2)
577 EXPECT_DOUBLE_EQ(astar.get_distance(nodes[2]), 3.0);
578
579 // Distance to node 3 should be 4 (0->1->3)
580 EXPECT_DOUBLE_EQ(astar.get_distance(nodes[3]), 4.0);
581}
582
583// Test copy_painted_min_paths_tree
585{
587
588 astar.paint_min_paths_tree(g, nodes[0]);
589
590 SimpleGraph tree;
591 auto total_dist = astar.copy_painted_min_paths_tree(g, tree);
592 (void)total_dist; // May be 0 depending on Paint_Filt implementation
593
594 // Tree should have all nodes
595 EXPECT_EQ(tree.get_num_nodes(), g.get_num_nodes());
596 // Tree should have n-1 arcs
597 EXPECT_EQ(tree.get_num_arcs(), g.get_num_nodes() - 1);
598}
599
600// Test operator() for Dijkstra mode (tree version)
602{
603 SimpleGraph tree;
605
606 astar(g, nodes[0], tree);
607
608 EXPECT_EQ(tree.get_num_nodes(), g.get_num_nodes());
609}
610
611// Test get_distance throws if not painted
613{
614 SimpleGraph g;
615 auto node = g.insert_node(0);
616
618
619 EXPECT_THROW(astar.get_distance(node), std::domain_error);
620}
621
622// Test copy_painted throws if not painted
624{
625 SimpleGraph g;
626 g.insert_node(0);
627
628 SimpleGraph tree;
630
631 EXPECT_THROW(astar.copy_painted_min_paths_tree(g, tree), std::domain_error);
632}
633
634// Compare A* Dijkstra mode trees with actual Dijkstra trees
636{
638
641
642 astar.compute_min_paths_tree(g, nodes[0], astar_tree);
643 dijkstra.compute_min_paths_tree(g, nodes[0], dijkstra_tree);
644
645 // Same number of nodes and arcs
646 EXPECT_EQ(astar_tree.get_num_nodes(), dijkstra_tree.get_num_nodes());
647 EXPECT_EQ(astar_tree.get_num_arcs(), dijkstra_tree.get_num_arcs());
648}
649
650// Test disconnected graph
652{
653 SimpleGraph g;
654 auto n0 = g.insert_node(0);
655 auto n1 = g.insert_node(1);
656 (void)g.insert_node(2); // n2 is intentionally disconnected
657
658 g.insert_arc(n0, n1, 1.0);
659
660 SimpleGraph tree;
662
663 astar.compute_min_paths_tree(g, n0, tree);
664
665 // Tree should only contain connected component
666 EXPECT_EQ(tree.get_num_nodes(), 2u);
667}
668
669// Test state getters
671{
673
674 // Before computation
675 EXPECT_FALSE(astar.has_computation());
676 EXPECT_FALSE(astar.is_painted());
677 EXPECT_EQ(astar.get_start_node(), nullptr);
678 EXPECT_EQ(astar.get_graph(), nullptr);
679
680 astar.paint_min_paths_tree(g, nodes[0]);
681
682 // After computation
683 EXPECT_TRUE(astar.has_computation());
684 EXPECT_TRUE(astar.is_painted());
685 EXPECT_EQ(astar.get_start_node(), nodes[0]);
686 EXPECT_EQ(astar.get_graph(), &g);
687}
688
689// ==================== Typed Tests for Both Heap Types ====================
690
691template <typename HeapTag>
692class AStarHeapTest : public ::testing::Test
693{
694protected:
696 std::vector<SimpleGraph::Node*> nodes;
697
698 void SetUp() override
699 {
700 for (int i = 0; i < 5; ++i)
701 nodes.push_back(g.insert_node(i));
702
703 // Create graph: 0 -> 1 -> 2 -> 3 -> 4
704 // \---------^
705 g.insert_arc(nodes[0], nodes[1], 1.0);
706 g.insert_arc(nodes[1], nodes[2], 2.0);
707 g.insert_arc(nodes[2], nodes[3], 3.0);
708 g.insert_arc(nodes[3], nodes[4], 4.0);
709 g.insert_arc(nodes[0], nodes[2], 10.0); // Long path
710 }
711};
712
713using HeapTypes = ::testing::Types<BinHeapTag, FibHeapTag>;
715
716// Test A* with both heap types - find_path (with heuristic)
718{
722 TypeParam::template Heap>;
723 AStar astar;
724 Path<SimpleGraph> path(this->g);
725
726 auto cost = astar.find_path(this->g, this->nodes[0], this->nodes[4], path);
727
728 EXPECT_EQ(cost, 10.0); // 1+2+3+4
729 EXPECT_FALSE(path.is_empty());
730}
731
732// Test A* with both heap types - compute_min_paths_tree (Dijkstra mode)
734{
738 TypeParam::template Heap>;
739 AStar astar;
740 SimpleGraph tree;
741
742 auto root = astar.compute_min_paths_tree(this->g, this->nodes[0], tree);
743
744 EXPECT_NE(root, nullptr);
745 EXPECT_EQ(tree.get_num_nodes(), 5u);
746 EXPECT_EQ(tree.get_num_arcs(), 4u);
747}
748
749// Test A* with both heap types - paint and get_distance
751{
755 TypeParam::template Heap>;
756 AStar astar;
757
758 astar.paint_min_paths_tree(this->g, this->nodes[0]);
759
760 EXPECT_EQ(astar.get_distance(this->nodes[0]), 0.0);
761 EXPECT_EQ(astar.get_distance(this->nodes[1]), 1.0);
762 EXPECT_EQ(astar.get_distance(this->nodes[2]), 3.0); // 1+2
763 EXPECT_EQ(astar.get_distance(this->nodes[3]), 6.0); // 1+2+3
764 EXPECT_EQ(astar.get_distance(this->nodes[4]), 10.0); // 1+2+3+4
765}
766
767// Test A* vs Dijkstra with both heap types
769{
773 TypeParam::template Heap>;
776 TypeParam::template Heap>;
777
778 AStar astar;
780
783
784 auto astar_cost = astar.find_min_path(this->g, this->nodes[0], this->nodes[4], astar_path);
785 auto dijkstra_cost = dijkstra.find_min_path(this->g, this->nodes[0], this->nodes[4], dijkstra_path);
786
789}
790
791// ==================== Additional Edge Case Tests ====================
792
793// Test with self-loop (arc from node to itself)
795{
796 SimpleGraph g;
797 auto n1 = g.insert_node(1);
798 auto n2 = g.insert_node(2);
799
800 g.insert_arc(n1, n2, 5.0);
801 g.insert_arc(n2, n2, 1.0); // Self-loop
802
804 AStar astar;
805 Path<SimpleGraph> path(g);
806
807 auto cost = astar.find_min_path(g, n1, n2, path);
808
809 EXPECT_DOUBLE_EQ(cost, 5.0);
810 EXPECT_EQ(path.size(), 2u);
811}
812
813// Test with multiple arcs between same nodes
815{
816 SimpleGraph g;
817 auto n1 = g.insert_node(1);
818 auto n2 = g.insert_node(2);
819
820 g.insert_arc(n1, n2, 10.0);
821 g.insert_arc(n1, n2, 5.0); // Better path
822 g.insert_arc(n1, n2, 15.0);
823
825 AStar astar;
826 Path<SimpleGraph> path(g);
827
828 auto cost = astar.find_min_path(g, n1, n2, path);
829
830 EXPECT_DOUBLE_EQ(cost, 5.0); // Should find shortest
831}
832
833// Test with all arcs having same weight
835{
836 SimpleGraph g;
837 std::vector<SimpleGraph::Node*> nodes;
838
839 for (int i = 0; i < 4; ++i)
840 nodes.push_back(g.insert_node(i));
841
842 // Create square graph with all edges weight 1.0
843 g.insert_arc(nodes[0], nodes[1], 1.0);
844 g.insert_arc(nodes[1], nodes[2], 1.0);
845 g.insert_arc(nodes[2], nodes[3], 1.0);
846 g.insert_arc(nodes[0], nodes[3], 1.0);
847
849 AStar astar;
850 Path<SimpleGraph> path(g);
851
852 auto cost = astar.find_min_path(g, nodes[0], nodes[3], path);
853
854 EXPECT_DOUBLE_EQ(cost, 1.0); // Direct path
855 EXPECT_EQ(path.size(), 2u);
856}
857
858// Test with very large weights (close to overflow)
860{
861 SimpleGraph g;
862 auto n1 = g.insert_node(1);
863 auto n2 = g.insert_node(2);
864 auto n3 = g.insert_node(3);
865
866 double large = 1e100;
867 g.insert_arc(n1, n2, large);
868 g.insert_arc(n2, n3, large);
869
871 AStar astar;
872 Path<SimpleGraph> path(g);
873
874 auto cost = astar.find_min_path(g, n1, n3, path);
875
876 EXPECT_GT(cost, large); // Should handle large values
877 EXPECT_FALSE(path.is_empty());
878}
879
880// Test reusability: multiple searches on same object
882{
883 SimpleGraph g;
884 std::vector<SimpleGraph::Node*> nodes;
885
886 for (int i = 0; i < 5; ++i)
887 nodes.push_back(g.insert_node(i));
888
889 g.insert_arc(nodes[0], nodes[1], 1.0);
890 g.insert_arc(nodes[1], nodes[2], 2.0);
891 g.insert_arc(nodes[2], nodes[3], 3.0);
892 g.insert_arc(nodes[3], nodes[4], 4.0);
893
895 AStar astar; // Single instance
896
897 // First search
898 {
900 auto cost1 = astar.find_min_path(g, nodes[0], nodes[2], path1);
902 }
903
904 // Second search (should reset state correctly)
905 {
907 auto cost2 = astar.find_min_path(g, nodes[1], nodes[4], path2);
909 }
910
911 // Third search with different graph structure
912 {
914 auto cost3 = astar.find_min_path(g, nodes[0], nodes[4], path3);
915 EXPECT_DOUBLE_EQ(cost3, 10.0);
916 }
917}
918
919// Test with zero-weight arcs
921{
922 SimpleGraph g;
923 auto n1 = g.insert_node(1);
924 auto n2 = g.insert_node(2);
925 auto n3 = g.insert_node(3);
926
927 g.insert_arc(n1, n2, 0.0); // Zero weight
928 g.insert_arc(n2, n3, 5.0);
929
931 AStar astar;
932 Path<SimpleGraph> path(g);
933
934 auto cost = astar.find_min_path(g, n1, n3, path);
935
936 EXPECT_DOUBLE_EQ(cost, 5.0);
937 EXPECT_EQ(path.size(), 3u);
938}
939
940// Test with inadmissible heuristic (should still find a path, but may be suboptimal)
942{
943 GridGraph g;
944 auto n1 = g.insert_node(1);
945 n1->x = 0; n1->y = 0;
946
947 auto n2 = g.insert_node(2);
948 n2->x = 1; n2->y = 0;
949
950 auto n3 = g.insert_node(3);
951 n3->x = 2; n3->y = 0;
952
953 g.insert_arc(n1, n2, 1.0);
954 g.insert_arc(n2, n3, 1.0);
955 g.insert_arc(n1, n3, 10.0); // Suboptimal direct path
956
957 // Inadmissible heuristic that overestimates
958 struct BadHeuristic
959 {
960 using Distance_Type = double;
961 Distance_Type operator()(GridGraph::Node* from, GridGraph::Node* to) const
962 {
963 double dx = from->x - to->x;
964 double dy = from->y - to->y;
965 return 100.0 * std::sqrt(dx*dx + dy*dy); // Massively overestimate
966 }
967 };
968
970 AStar astar;
971 Path<GridGraph> path(g);
972
973 auto cost = astar.find_path(g, n1, n3, path);
974
975 // With inadmissible heuristic, may get suboptimal result
976 // But should still find SOME path
977 EXPECT_GT(cost, 0.0);
978 EXPECT_FALSE(path.is_empty());
979}
980
981int main(int argc, char **argv)
982{
983 ::testing::InitGoogleTest(&argc, argv);
984 return RUN_ALL_TESTS();
985}
A* shortest path algorithm.
Dijkstra's shortest path algorithm.
int main()
TYPED_TEST(AStarHeapTest, FindPathWithHeuristic)
List_Graph< SimpleNode, SimpleArc > SimpleGraph
Definition astar_test.cc:82
TEST_F(AStarBasicTest, ZeroHeuristicMatchesDijkstra)
::testing::Types< BinHeapTag, FibHeapTag > HeapTypes
TYPED_TEST_SUITE(AStarHeapTest, HeapTypes)
long double w
Definition btreepic.C:153
void SetUp() override
std::vector< SimpleGraph::Node * > nodes
SimpleGraph g
void SetUp() override
std::vector< SimpleGraph::Node * > nodes
std::vector< GridGraph::Node * > nodes
GridGraph::Node * get_node(int x, int y)
void SetUp() override
static constexpr int GRID_SIZE
GridGraph g
void SetUp() override
SimpleGraph g
std::vector< SimpleGraph::Node * > nodes
A* algorithm for finding the shortest path between two nodes.
Definition AStar.H:157
Spanning tree calculation of all shortest paths from a given node according to Dijkstra's algorithm.
Definition Dijkstra.H:101
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Path on a graph.
Definition tpl_graph.H:2669
size_t size() const noexcept
Return the path length in nodes.
Definition tpl_graph.H:2807
bool is_empty() const noexcept
Return true if the path is empty.
Definition tpl_graph.H:2813
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
Definition graph-dry.H:595
NodeInfo & get_info() noexcept
Return a modifiable reference to the data contained in the node.
Definition graph-dry.H:494
constexpr size_t get_num_nodes() const noexcept
Return the total of nodes of graph.
Definition graph-dry.H:695
constexpr size_t get_num_arcs() const noexcept
Definition graph-dry.H:778
#define TEST(name)
static mt19937 rng
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
Definition gmpfrxx.h:4060
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
static mpfr_t y
Definition mpfr_mul_d.c:3
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
Default filter for filtered iterators on arcs.
Definition tpl_graph.H:1000
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Node belonging to a graph implemented with a double linked adjacency list.
Definition tpl_graph.H:121
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
Default heuristic for A* (zero heuristic, degrades to Dijkstra).
Definition AStar.H:77
Distance_Type operator()(GridGraph::Arc *arc) const
Distance_Type operator()(SimpleGraph::Arc *arc) const
static void set_zero(SimpleGraph::Arc *arc)
double Distance_Type
Distance_Type operator()(GridGraph::Node *from, GridGraph::Node *to) const
Distance_Type operator()(GridGraph::Node *from, GridGraph::Node *to) const
GridNode(int id, int _x, int _y)
Definition astar_test.cc:92
GridNode()=default
GridNode(int id)
Definition astar_test.cc:91
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.