Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
test_ida_star.cc
Go to the documentation of this file.
1
9# include <gtest/gtest.h>
10# include <IDA_Star.H>
11# include <AStar.H>
12# include <Dijkstra.H>
13# include <tpl_graph.H>
14
15using namespace Aleph;
16
17// ============================================================================
18// Type aliases
19// ============================================================================
20
22using Node = GT::Node;
23using Arc = GT::Arc;
24
27using IArc = IGT::Arc;
28
31using DArc = DGT::Arc;
32
33// Zero heuristic (degrades to iterative deepening DFS with cost bound)
34template <class G>
36
37// ============================================================================
38// TEST 1: Basic shortest path (zero heuristic)
39// ============================================================================
41{
42 GT g;
43 auto n0 = g.insert_node(0);
44 auto n1 = g.insert_node(1);
45 auto n2 = g.insert_node(2);
46 auto n3 = g.insert_node(3);
47
48 g.insert_arc(n0, n1, 1.0);
49 g.insert_arc(n1, n2, 2.0);
50 g.insert_arc(n2, n3, 3.0);
51 g.insert_arc(n0, n3, 100.0);
52
54 Path<GT> path(g);
55 double d = ida(g, n0, n3, path);
56
57 EXPECT_EQ(d, 6.0);
58 EXPECT_FALSE(path.is_empty());
59}
60
61// ============================================================================
62// TEST 2: Path to self
63// ============================================================================
65{
66 GT g;
67 auto n0 = g.insert_node(0);
68
70 Path<GT> path(g);
71 double d = ida(g, n0, n0, path);
72
73 EXPECT_EQ(d, 0.0);
74}
75
76// ============================================================================
77// TEST 3: No path exists
78// ============================================================================
80{
81 GT g;
82 auto n0 = g.insert_node(0);
83 auto n1 = g.insert_node(1);
84
86 Path<GT> path(g);
87 double d = ida(g, n0, n1, path);
88
89 EXPECT_EQ(d, std::numeric_limits<double>::max());
90 EXPECT_TRUE(path.is_empty());
91}
92
93// ============================================================================
94// TEST 4: Single node
95// ============================================================================
97{
98 GT g;
99 auto n0 = g.insert_node(0);
100
102 Path<GT> path(g);
103 double d = ida(g, n0, n0, path);
104
105 EXPECT_EQ(d, 0.0);
106}
107
108// ============================================================================
109// TEST 5: Linear chain
110// ============================================================================
112{
113 GT g;
114 auto n0 = g.insert_node(0);
115 auto n1 = g.insert_node(1);
116 auto n2 = g.insert_node(2);
117 auto n3 = g.insert_node(3);
118
119 g.insert_arc(n0, n1, 1.0);
120 g.insert_arc(n1, n2, 1.0);
121 g.insert_arc(n2, n3, 1.0);
122
124 Path<GT> path(g);
125 double d = ida(g, n0, n3, path);
126
127 EXPECT_EQ(d, 3.0);
128}
129
130// ============================================================================
131// TEST 6: Two-node graph
132// ============================================================================
134{
135 GT g;
136 auto n0 = g.insert_node(0);
137 auto n1 = g.insert_node(1);
138
139 g.insert_arc(n0, n1, 5.0);
140
142 Path<GT> path(g);
143 double d = ida(g, n0, n1, path);
144
145 EXPECT_EQ(d, 5.0);
146}
147
148// ============================================================================
149// TEST 7: Null node validation
150// ============================================================================
152{
153 GT g;
154 auto n0 = g.insert_node(0);
155
157 Path<GT> path(g);
158
159 // Null checks
160 EXPECT_THROW(ida(g, nullptr, n0, path), std::domain_error);
161 EXPECT_THROW(ida(g, n0, nullptr, path), std::domain_error);
162
163 // Empty graph check
164 GT g_empty;
165 EXPECT_THROW(ida(g_empty, n0, n0, path), std::domain_error);
166
167 // Negative weight check
168 GT g_neg;
169 auto s = g_neg.insert_node(1);
170 auto t = g_neg.insert_node(2);
171 g_neg.insert_arc(s, t, -1.0);
172 EXPECT_THROW(ida(g_neg, s, t, path), std::domain_error);
173}
174
175// ============================================================================
176// TEST 8: Relaxation (finds cheaper path)
177// ============================================================================
179{
180 GT g;
181 auto n0 = g.insert_node(0);
182 auto n1 = g.insert_node(1);
183 auto n2 = g.insert_node(2);
184
185 g.insert_arc(n0, n1, 10.0);
186 g.insert_arc(n0, n2, 1.0);
187 g.insert_arc(n2, n1, 1.0);
188
190 Path<GT> path(g);
191 double d = ida(g, n0, n1, path);
192
193 EXPECT_EQ(d, 2.0);
194}
195
196// ============================================================================
197// TEST 9: Cross-validation with Dijkstra
198// ============================================================================
200{
201 GT g;
202 auto n0 = g.insert_node(0);
203 auto n1 = g.insert_node(1);
204 auto n2 = g.insert_node(2);
205 auto n3 = g.insert_node(3);
206 auto n4 = g.insert_node(4);
207
208 g.insert_arc(n0, n1, 4.0);
209 g.insert_arc(n0, n2, 2.0);
210 g.insert_arc(n1, n3, 5.0);
211 g.insert_arc(n2, n1, 1.0);
212 g.insert_arc(n2, n3, 8.0);
213 g.insert_arc(n3, n4, 2.0);
214 g.insert_arc(n0, n4, 10.0);
215
218 double ida_d = ida(g, n0, n4, ida_path);
219
222 double dij_d = dij(g, n0, n4, dij_path);
223
224 EXPECT_NEAR(ida_d, dij_d, 1e-9);
225}
226
227// ============================================================================
228// TEST 10: Cross-validation with A*
229// ============================================================================
231{
232 GT g;
233 auto n0 = g.insert_node(0);
234 auto n1 = g.insert_node(1);
235 auto n2 = g.insert_node(2);
236 auto n3 = g.insert_node(3);
237
238 g.insert_arc(n0, n1, 1.0);
239 g.insert_arc(n0, n2, 4.0);
240 g.insert_arc(n1, n2, 2.0);
241 g.insert_arc(n1, n3, 5.0);
242 g.insert_arc(n2, n3, 1.0);
243
246 double ida_d = ida(g, n0, n3, ida_path);
247
250 double astar_d = astar(g, n0, n3, astar_path);
251
252 EXPECT_NEAR(ida_d, astar_d, 1e-9);
253}
254
255// ============================================================================
256// TEST 11: Complete graph K4
257// ============================================================================
259{
260 GT g;
261 auto n0 = g.insert_node(0);
262 auto n1 = g.insert_node(1);
263 auto n2 = g.insert_node(2);
264 auto n3 = g.insert_node(3);
265
266 g.insert_arc(n0, n1, 1.0);
267 g.insert_arc(n0, n2, 1.0);
268 g.insert_arc(n0, n3, 1.0);
269 g.insert_arc(n1, n2, 1.0);
270 g.insert_arc(n1, n3, 1.0);
271 g.insert_arc(n2, n3, 1.0);
272
274 Path<GT> path(g);
275 double d = ida(g, n0, n3, path);
276
277 EXPECT_EQ(d, 1.0);
278}
279
280// ============================================================================
281// TEST 12: Graph with cycle
282// ============================================================================
284{
285 GT g;
286 auto n0 = g.insert_node(0);
287 auto n1 = g.insert_node(1);
288 auto n2 = g.insert_node(2);
289 auto n3 = g.insert_node(3);
290
291 g.insert_arc(n0, n1, 1.0);
292 g.insert_arc(n1, n2, 1.0);
293 g.insert_arc(n2, n0, 1.0); // cycle
294 g.insert_arc(n2, n3, 1.0);
295
297 Path<GT> path(g);
298 double d = ida(g, n0, n3, path);
299
300 // Undirected: n0-n2(via arc n2-n0, weight 1)->n3(1) = 2
301 // Or n0->n1->n2->n3 = 3. Best is 2.
302 EXPECT_EQ(d, 2.0);
303}
304
305// ============================================================================
306// TEST 13: Integer weights
307// ============================================================================
309{
310 IGT g;
311 auto n0 = g.insert_node(0);
312 auto n1 = g.insert_node(1);
313 auto n2 = g.insert_node(2);
314
315 g.insert_arc(n0, n1, 5);
316 g.insert_arc(n1, n2, 3);
317
319 Path<IGT> path(g);
320 int d = ida(g, n0, n2, path);
321
322 EXPECT_EQ(d, 8);
323}
324
325// ============================================================================
326// TEST 14: Multiple successive computations
327// ============================================================================
329{
330 GT g;
331 auto n0 = g.insert_node(0);
332 auto n1 = g.insert_node(1);
333 auto n2 = g.insert_node(2);
334
335 g.insert_arc(n0, n1, 1.0);
336 g.insert_arc(n1, n2, 2.0);
337
339
340 Path<GT> p1(g);
341 double d1 = ida(g, n0, n2, p1);
342 EXPECT_EQ(d1, 3.0);
343
344 Path<GT> p2(g);
345 double d2 = ida(g, n2, n0, p2);
346 EXPECT_EQ(d2, 3.0); // undirected
347}
348
349// ============================================================================
350// TEST 15: Path node-by-node verification
351// ============================================================================
353{
354 GT g;
355 auto n0 = g.insert_node(0);
356 auto n1 = g.insert_node(1);
357 auto n2 = g.insert_node(2);
358 auto n3 = g.insert_node(3);
359
360 g.insert_arc(n0, n1, 1.0);
361 g.insert_arc(n1, n2, 2.0);
362 g.insert_arc(n2, n3, 3.0);
363 g.insert_arc(n0, n3, 100.0);
364
366 Path<GT> path(g);
367 double d = ida(g, n0, n3, path);
368
369 EXPECT_EQ(d, 6.0);
370
372 for (Path<GT>::Iterator it(path); it.has_curr(); it.next())
373 nodes.append(it.get_current_node()->get_info());
374
375 ASSERT_EQ(nodes.size(), 4u);
376 auto pit = nodes.get_it();
377 EXPECT_EQ(pit.get_curr(), 0); pit.next();
378 EXPECT_EQ(pit.get_curr(), 1); pit.next();
379 EXPECT_EQ(pit.get_curr(), 2); pit.next();
380 EXPECT_EQ(pit.get_curr(), 3);
381}
382
383// ============================================================================
384// TEST 16: Zero weight edges
385// ============================================================================
387{
388 GT g;
389 auto n0 = g.insert_node(0);
390 auto n1 = g.insert_node(1);
391 auto n2 = g.insert_node(2);
392
393 g.insert_arc(n0, n1, 0.0);
394 g.insert_arc(n1, n2, 0.0);
395
397 Path<GT> path(g);
398 double d = ida(g, n0, n2, path);
399
400 EXPECT_EQ(d, 0.0);
401}
402
403// ============================================================================
404// TEST 17: Fractional weights
405// ============================================================================
407{
408 GT g;
409 auto n0 = g.insert_node(0);
410 auto n1 = g.insert_node(1);
411 auto n2 = g.insert_node(2);
412
413 g.insert_arc(n0, n1, 0.5);
414 g.insert_arc(n1, n2, 0.3);
415
417 Path<GT> path(g);
418 double d = ida(g, n0, n2, path);
419
420 EXPECT_NEAR(d, 0.8, 1e-9);
421}
422
423// ============================================================================
424// TEST 18: Directed graph
425// ============================================================================
427{
428 DGT g;
429 auto n0 = g.insert_node(0);
430 auto n1 = g.insert_node(1);
431 auto n2 = g.insert_node(2);
432
433 g.insert_arc(n0, n1, 1.0);
434 g.insert_arc(n1, n2, 1.0);
435
437 Path<DGT> path(g);
438 double d = ida(g, n0, n2, path);
439
440 EXPECT_EQ(d, 2.0);
441}
442
443// ============================================================================
444// TEST 19: Directed graph no return
445// ============================================================================
447{
448 DGT g;
449 auto n0 = g.insert_node(0);
450 auto n1 = g.insert_node(1);
451
452 g.insert_arc(n0, n1, 1.0);
453
455 Path<DGT> p(g);
456 double d = ida(g, n1, n0, p);
457
458 EXPECT_EQ(d, std::numeric_limits<double>::max());
459}
460
461// ============================================================================
462// TEST 20: Star graph
463// ============================================================================
465{
466 GT g;
467 auto center = g.insert_node(0);
468 auto n1 = g.insert_node(1);
469 auto n2 = g.insert_node(2);
470 auto n3 = g.insert_node(3);
471 auto n4 = g.insert_node(4);
472
473 g.insert_arc(center, n1, 1.0);
474 g.insert_arc(center, n2, 2.0);
475 g.insert_arc(center, n3, 3.0);
476 g.insert_arc(center, n4, 4.0);
477
479 Path<GT> path(g);
480 double d = ida(g, n1, n4, path);
481
482 // n1->center(1)->n4(4) = 5
483 EXPECT_EQ(d, 5.0);
484}
485
486// ============================================================================
487// TEST 21: Disconnected graph
488// ============================================================================
490{
491 GT g;
492 auto n0 = g.insert_node(0);
493 auto n1 = g.insert_node(1);
494 auto n2 = g.insert_node(2);
495 auto n3 = g.insert_node(3);
496
497 g.insert_arc(n0, n1, 1.0);
498 g.insert_arc(n2, n3, 1.0);
499
501 Path<GT> path(g);
502 double d = ida(g, n0, n3, path);
503
504 EXPECT_EQ(d, std::numeric_limits<double>::max());
505 EXPECT_TRUE(path.is_empty());
506}
507
508// ============================================================================
509// TEST 22: Larger graph cross-validation
510// ============================================================================
512{
513 GT g;
514 const int N = 10;
515
517 for (int i = 0; i < N; ++i)
518 nodes[i] = g.insert_node(i);
519
520 for (int i = 0; i < N - 1; ++i)
521 g.insert_arc(nodes[i], nodes[i + 1], static_cast<double>((i % 3) + 1));
522
523 // Add shortcuts
524 g.insert_arc(nodes[0], nodes[4], 20.0);
525 g.insert_arc(nodes[3], nodes[7], 15.0);
526
529
530 for (int end = 1; end < N; end += 2)
531 {
532 Path<GT> ip(g), dp(g);
533 double id = ida(g, nodes[0], nodes[end], ip);
534 double dd = dij(g, nodes[0], nodes[end], dp);
535 EXPECT_NEAR(id, dd, 1e-9) << "Mismatch for end=" << end;
536 }
537}
538
539// ============================================================================
540// === EXAMPLE: Weighted grid pathfinding ===
541// A small 3x2 grid with varying edge costs. Find the cheapest path
542// from top-left to bottom-right.
543//
544// (0) --2-- (1) --3-- (2)
545// | | |
546// 1 5 1
547// | | |
548// (3) --1-- (4) --1-- (5)
549//
550// Best path: (0)-1-(3)-1-(4)-1-(5) = 3
551// ============================================================================
553{
554 GT g;
555 auto n0 = g.insert_node(0);
556 auto n1 = g.insert_node(1);
557 auto n2 = g.insert_node(2);
558 auto n3 = g.insert_node(3);
559 auto n4 = g.insert_node(4);
560 auto n5 = g.insert_node(5);
561
562 // Horizontal
563 g.insert_arc(n0, n1, 2.0);
564 g.insert_arc(n1, n2, 3.0);
565 g.insert_arc(n3, n4, 1.0);
566 g.insert_arc(n4, n5, 1.0);
567
568 // Vertical
569 g.insert_arc(n0, n3, 1.0);
570 g.insert_arc(n1, n4, 5.0);
571 g.insert_arc(n2, n5, 1.0);
572
574 Path<GT> path(g);
575 double d = ida(g, n0, n5, path);
576
577 // Best: n0->n3(1)->n4(1)->n5(1) = 3
578 EXPECT_EQ(d, 3.0);
579}
580
581// ============================================================================
582// TEST 24: Self-loop
583// ============================================================================
584TEST(IDAStar, SelfLoop)
585{
586 GT g;
587 auto n0 = g.insert_node(0);
588 auto n1 = g.insert_node(1);
589
590 g.insert_arc(n0, n0, 5.0);
591 g.insert_arc(n0, n1, 2.0);
592
594 Path<GT> path(g);
595 double d = ida(g, n0, n1, path);
596
597 EXPECT_EQ(d, 2.0);
598}
599
600// ============================================================================
601// TEST 25: Chebyshev heuristic compilation check
602// ============================================================================
603// This just verifies the Chebyshev_Heuristic compiles correctly.
604// A full test would need a grid with coordinate-based node info.
606{
607 struct Coord { int x; int y; };
610
611 CG g;
612 auto n1 = g.insert_node(Coord{0, 0});
613 auto n2 = g.insert_node(Coord{3, 5});
614
615 CH ch;
616 // Chebyshev distance = max(|x1-x2|, |y1-y2|) = max(3, 5) = 5
617 EXPECT_EQ(ch(n1, n2), 5);
618 EXPECT_EQ(ch(n2, n1), 5);
619 EXPECT_EQ(ch(n1, n1), 0);
620}
A* shortest path algorithm.
Dijkstra's shortest path algorithm.
IDA* (Iterative Deepening A*) shortest path algorithm.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
A* algorithm for finding the shortest path between two nodes.
Definition AStar.H:157
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:194
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
Spanning tree calculation of all shortest paths from a given node according to Dijkstra's algorithm.
Definition Dijkstra.H:101
Dynamic singly linked list with functional programming support.
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
IDA* algorithm for memory-efficient shortest path search.
Definition IDA_Star.H:175
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Graph_Arc< double > 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
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.
Chebyshev (L-infinity) distance heuristic for 8-connected grids.
Definition IDA_Star.H:124
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Default heuristic for A* (zero heuristic, degrades to Dijkstra).
Definition AStar.H:77
DGT::Arc DArc
DGT::Node DNode
Generic graph and digraph implementations.