Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
latex_floyd_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
44#include <gtest/gtest.h>
45#include <sstream>
46#include <fstream>
47#include <limits>
48#include <cmath>
49#include <filesystem>
50#if defined(_WIN32)
51# include <process.h>
52#else
53# include <unistd.h>
54#endif
55
56#include <tpl_graph.H>
57#include <tpl_matgraph.H>
58#include <latex_floyd.H>
59
60using namespace std;
61using namespace testing;
62using namespace Aleph;
63
64// ============================================================================
65// Arc type with distance for Floyd-Warshall
66// ============================================================================
67
70{
72
73 static constexpr Distance_Type Max_Distance = numeric_limits<double>::infinity();
74 static constexpr Distance_Type Zero_Distance = 0.0;
75
77
80
82
83 operator Distance_Type() const { return distance; }
84};
85
88{
90
91 static constexpr Distance_Type Max_Distance = numeric_limits<int>::max() / 2; // Avoid overflow
92 static constexpr Distance_Type Zero_Distance = 0;
93
95
98
100
101 operator Distance_Type() const { return distance; }
102};
103
104// ============================================================================
105// Test Fixtures
106// ============================================================================
107
110{
111protected:
116
122
123 void SetUp() override
124 {
125 // Create a simple graph:
126 // 1
127 // 0 -----> 1
128 // | |
129 // |4 |2
130 // v v
131 // 3 <----- 2
132 // 1
133 //
134 // Also: direct edge 0->2 with weight 5 (longer than 0->1->2 = 3)
135
136 n0 = g.insert_node(0);
137 n1 = g.insert_node(1);
138 n2 = g.insert_node(2);
139 n3 = g.insert_node(3);
140
141 g.insert_arc(n0, n1, DistanceArc(1.0)); // 0 -> 1: weight 1
142 g.insert_arc(n1, n2, DistanceArc(2.0)); // 1 -> 2: weight 2
143 g.insert_arc(n2, n3, DistanceArc(1.0)); // 2 -> 3: weight 1
144 g.insert_arc(n0, n3, DistanceArc(4.0)); // 0 -> 3: weight 4 (longer than 0->1->2->3 = 4)
145 g.insert_arc(n0, n2, DistanceArc(5.0)); // 0 -> 2: weight 5 (longer than 0->1->2 = 3)
146 }
147
148 // Helper to get node index by node pointer using matrix iteration
149 long get_index(Ady_Mat<Graph, Dist>& mat, Node* node) const
150 {
151 long n = mat.get_num_nodes();
152 for (long i = 0; i < n; ++i)
153 if (mat(i) == node)
154 return i;
155 return -1;
156 }
157};
158
161{
162protected:
167
169 vector<Node*> nodes;
170
171 static long long process_id() noexcept
172 {
173#if defined(_WIN32)
174 return static_cast<long long>(_getpid());
175#else
176 return static_cast<long long>(getpid());
177#endif
178 }
179
180 string latex_temp_filename() const
181 {
182 const auto *test_info = UnitTest::GetInstance()->current_test_info();
183 string base = string("floyd_test_latex_") + test_info->test_suite_name() + "_" +
184 test_info->name() + "_" + to_string(process_id()) + ".tex";
185
186 for (auto &ch : base)
187 if (ch == '/' or ch == '\\' or ch == ' ')
188 ch = '_';
189
190 return (std::filesystem::temp_directory_path() / base).string();
191 }
192
193 void SetUp() override
194 {
195 // Create a 4-node graph with known shortest paths
196 for (int i = 0; i < 4; ++i)
197 nodes.push_back(g.insert_node(i));
198
199 // Edges: forms a square with diagonal
200 // 0 --2-- 1
201 // | / |
202 // 3 1 4
203 // | / |
204 // 2 --5-- 3
205
206 g.insert_arc(nodes[0], nodes[1], IntDistanceArc(2));
207 g.insert_arc(nodes[1], nodes[0], IntDistanceArc(2));
208 g.insert_arc(nodes[0], nodes[2], IntDistanceArc(3));
209 g.insert_arc(nodes[2], nodes[0], IntDistanceArc(3));
210 g.insert_arc(nodes[1], nodes[2], IntDistanceArc(1)); // Diagonal shortcut
211 g.insert_arc(nodes[2], nodes[1], IntDistanceArc(1));
212 g.insert_arc(nodes[1], nodes[3], IntDistanceArc(4));
213 g.insert_arc(nodes[3], nodes[1], IntDistanceArc(4));
214 g.insert_arc(nodes[2], nodes[3], IntDistanceArc(5));
215 g.insert_arc(nodes[3], nodes[2], IntDistanceArc(5));
216 }
217};
218
219// ============================================================================
220// floyd_all_shortest_paths() Tests
221// ============================================================================
222
224{
225 Ady_Mat<Graph, Dist> dist(g);
226 Ady_Mat<Graph, long> path(g);
227
228 floyd_all_shortest_paths(g, dist, path);
229
230 // Diagonal should be zero
231 const long n = g.get_num_nodes();
232 for (long i = 0; i < n; ++i)
233 EXPECT_DOUBLE_EQ(dist(i, i), 0.0) << "Diagonal at " << i << " should be 0";
234}
235
237{
238 Ady_Mat<Graph, Dist> dist(g);
239 Ady_Mat<Graph, long> path(g);
240
241 floyd_all_shortest_paths(g, dist, path);
242
243 // Get indices for n0 and n1
244 long idx0 = get_index(dist, n0);
245 long idx1 = get_index(dist, n1);
246
247 // Direct edge 0->1 has weight 1
248 EXPECT_DOUBLE_EQ(dist(idx0, idx1), 1.0);
249}
250
252{
253 Ady_Mat<Graph, Dist> dist(g);
254 Ady_Mat<Graph, long> path(g);
255
256 floyd_all_shortest_paths(g, dist, path);
257
258 long idx0 = get_index(dist, n0);
259 long idx2 = get_index(dist, n2);
260
261 // 0->2: direct is 5, but 0->1->2 = 1+2 = 3
262 EXPECT_DOUBLE_EQ(dist(idx0, idx2), 3.0);
263}
264
266{
267 Ady_Mat<Graph, Dist> dist(g);
268 Ady_Mat<Graph, long> path(g);
269
270 floyd_all_shortest_paths(g, dist, path);
271
272 long idx0 = get_index(dist, n0);
273 long idx3 = get_index(dist, n3);
274
275 // 0->3: direct is 4, path 0->1->2->3 = 1+2+1 = 4 (same)
276 EXPECT_DOUBLE_EQ(dist(idx0, idx3), 4.0);
277}
278
280{
281 Ady_Mat<Graph, Dist> dist(g);
282 Ady_Mat<Graph, long> path(g);
283
284 floyd_all_shortest_paths(g, dist, path);
285
286 long idx1 = get_index(dist, n1);
287 long idx0 = get_index(dist, n0);
288
289 // Node 0 is not reachable from node 1 (directed graph)
290 EXPECT_TRUE(isinf(dist(idx1, idx0)));
291}
292
294{
295 Ady_Mat<Graph, Dist> dist(g);
296 Ady_Mat<Graph, long> path(g);
297
298 floyd_all_shortest_paths(g, dist, path);
299
300 const long n = g.get_num_nodes();
301
302 // For this undirected-like graph, dist(i,j) == dist(j,i)
303 for (long i = 0; i < n; ++i)
304 for (long j = 0; j < n; ++j)
305 EXPECT_EQ(dist(i, j), dist(j, i))
306 << "Distance should be symmetric for i=" << i << ", j=" << j;
307}
308
310{
311 Ady_Mat<Graph, Dist> dist(g);
312 Ady_Mat<Graph, long> path(g);
313
314 floyd_all_shortest_paths(g, dist, path);
315
316 // Find indices - nodes[0] should be at some index
317 long idx0 = -1, idx3 = -1;
318 const long n = g.get_num_nodes();
319 for (long i = 0; i < n; ++i) {
320 if (dist(i)->get_info() == 0) idx0 = i;
321 if (dist(i)->get_info() == 3) idx3 = i;
322 }
323
324 // 0->3: best path is 0->1->3 = 2+4 = 6
325 EXPECT_EQ(dist(idx0, idx3), 6);
326}
327
328// ============================================================================
329// find_min_path() Tests
330// ============================================================================
331
333{
334 Ady_Mat<Graph, Dist> dist(g);
335 Ady_Mat<Graph, long> path(g);
336
337 floyd_all_shortest_paths(g, dist, path);
338
339 Path<Graph> p(g);
340 long idx0 = get_index(dist, n0);
341
342 find_min_path(path, idx0, idx0, p);
343
344 // Path from node to itself should just contain the source
345 EXPECT_EQ(p.size(), 1u);
346}
347
349{
350 Ady_Mat<Graph, Dist> dist(g);
351 Ady_Mat<Graph, long> path(g);
352
353 floyd_all_shortest_paths(g, dist, path);
354
355 Path<Graph> p(g);
356 long idx0 = get_index(dist, n0);
357 long idx1 = get_index(dist, n1);
358
359 find_min_path(path, idx0, idx1, p);
360
361 // Path 0->1 should have 2 nodes
362 EXPECT_EQ(p.size(), 2u);
363}
364
366{
367 Ady_Mat<Graph, Dist> dist(g);
368 Ady_Mat<Graph, long> path(g);
369
370 floyd_all_shortest_paths(g, dist, path);
371
372 Path<Graph> p(g);
373 long idx0 = get_index(dist, n0);
374 long idx2 = get_index(dist, n2);
375
376 find_min_path(path, idx0, idx2, p);
377
378 // Shortest path 0->2 goes through 1, so path is 0->1->2 (3 nodes)
379 EXPECT_EQ(p.size(), 3u);
380}
381
382// ============================================================================
383// floyd_all_shortest_paths_latex() Tests
384// ============================================================================
385
386// Formatters for LaTeX output - must match mat_latex.H signatures
387
389template <class Mat>
391{
392 string operator()(Mat& mat, int i) const
393 {
394 return to_string(mat(static_cast<long>(i))->get_info());
395 }
396};
397
399template <class Mat>
401{
402 string operator()(Mat& mat, int i, int j) const
403 {
404 long val = mat(static_cast<long>(i), static_cast<long>(j));
405 return to_string(val);
406 }
407};
408
410template <class Mat>
412{
413 string operator()(Mat& mat, int i, int j) const
414 {
415 auto val = mat(static_cast<long>(i), static_cast<long>(j));
416 if (isinf(static_cast<double>(val)))
417 return "\\infty";
418 return to_string(static_cast<int>(val));
419 }
420};
421
423{
424 Ady_Mat<Graph, Dist> dist(g);
425 Ady_Mat<Graph, long> path(g);
426
427 // Create temp file
428 string filename = latex_temp_filename();
429 ofstream output(filename);
430
433 (g, dist, path, output);
434
435 output.close();
436
437 // Read back and check
438 ifstream input(filename);
439 stringstream ss;
440 ss << input.rdbuf();
441 string content = ss.str();
442
443 EXPECT_NE(content.find("\\begin{figure}"), string::npos);
444 EXPECT_NE(content.find("\\end{figure}"), string::npos);
445}
446
448{
449 Ady_Mat<Graph, Dist> dist(g);
450 Ady_Mat<Graph, long> path(g);
451
452 string filename = latex_temp_filename();
453 ofstream output(filename);
454
457 (g, dist, path, output);
458
459 output.close();
460
461 ifstream input(filename);
462 stringstream ss;
463 ss << input.rdbuf();
464 string content = ss.str();
465
466 // Should contain D_0, P_0, D_1, P_1, etc.
467 EXPECT_NE(content.find("D_0"), string::npos);
468 EXPECT_NE(content.find("P_0"), string::npos);
469 EXPECT_NE(content.find("D_1"), string::npos);
470 EXPECT_NE(content.find("P_1"), string::npos);
471}
472
474{
475 Ady_Mat<Graph, Dist> dist(g);
476 Ady_Mat<Graph, long> path(g);
477
478 string filename = latex_temp_filename();
479 ofstream output(filename);
480
483 (g, dist, path, output);
484
485 output.close();
486
487 ifstream input(filename);
488 stringstream ss;
489 ss << input.rdbuf();
490 string content = ss.str();
491
492 // For n=4 nodes, we should have D_0 through D_4 (5 matrices)
493 // Actually D_0 initial, then D_1 through D_n
494 const long n = g.get_num_nodes();
495 for (long i = 0; i <= n; ++i) {
496 string dmat = "D_" + to_string(i);
497 EXPECT_NE(content.find(dmat), string::npos) << "Missing " << dmat;
498 }
499}
500
501// ============================================================================
502// Edge Cases
503// ============================================================================
504
506{
508
509 Graph g;
510 g.insert_node(0);
511
512 Ady_Mat<Graph, int> dist(g);
513 Ady_Mat<Graph, long> path(g);
514
515 floyd_all_shortest_paths(g, dist, path);
516
517 // Single node: distance to itself is 0
518 EXPECT_EQ(dist(0L, 0L), 0);
519}
520
522{
524
525 Graph g;
526 auto* n0 = g.insert_node(0);
527 auto* n1 = g.insert_node(1);
528 g.insert_arc(n0, n1, IntDistanceArc(5));
529
530 Ady_Mat<Graph, int> dist(g);
531 Ady_Mat<Graph, long> path(g);
532
533 floyd_all_shortest_paths(g, dist, path);
534
535 // Find indices for nodes
536 long idx0 = -1, idx1 = -1;
537 for (long i = 0; i < 2; ++i) {
538 if (dist(i)->get_info() == 0) idx0 = i;
539 if (dist(i)->get_info() == 1) idx1 = i;
540 }
541
542 EXPECT_EQ(dist(idx0, idx0), 0);
543 EXPECT_EQ(dist(idx1, idx1), 0);
544 EXPECT_EQ(dist(idx0, idx1), 5);
545 EXPECT_EQ(dist(idx1, idx0), IntDistanceArc::Max_Distance); // Not reachable
546}
547
549{
551
552 Graph g;
553 auto* n0 = g.insert_node(0);
554 auto* n1 = g.insert_node(1);
555 auto* n2 = g.insert_node(2);
556 auto* n3 = g.insert_node(3);
557
558 // Two disconnected components: {0,1} and {2,3}
559 g.insert_arc(n0, n1, IntDistanceArc(1));
560 g.insert_arc(n1, n0, IntDistanceArc(1));
561 g.insert_arc(n2, n3, IntDistanceArc(2));
562 g.insert_arc(n3, n2, IntDistanceArc(2));
563
564 Ady_Mat<Graph, int> dist(g);
565 Ady_Mat<Graph, long> path(g);
566
567 floyd_all_shortest_paths(g, dist, path);
568
569 // Map node info -> matrix index (Ady_Mat sorts nodes by pointer value)
570 vector<long> idx(4, -1);
571 for (long i = 0; i < 4; ++i)
572 idx[dist(i)->get_info()] = i;
573
574 // Within component 1
575 EXPECT_EQ(dist(idx[0], idx[1]), 1);
576 EXPECT_EQ(dist(idx[1], idx[0]), 1);
577
578 // Within component 2
579 EXPECT_EQ(dist(idx[2], idx[3]), 2);
580 EXPECT_EQ(dist(idx[3], idx[2]), 2);
581
582 // Between components (unreachable)
583 EXPECT_EQ(dist(idx[0], idx[2]), IntDistanceArc::Max_Distance);
584 EXPECT_EQ(dist(idx[1], idx[3]), IntDistanceArc::Max_Distance);
585}
586
588{
590
591 Graph g;
592 auto* n0 = g.insert_node(0);
593 auto* n1 = g.insert_node(1);
594 auto* n2 = g.insert_node(2);
595 auto* n3 = g.insert_node(3);
596
597 // Component 1: negative edge but no negative cycle
598 g.insert_arc(n0, n1, IntDistanceArc(-5));
599 g.insert_arc(n1, n0, IntDistanceArc(6));
600
601 // Component 2: separate disconnected component
602 g.insert_arc(n2, n3, IntDistanceArc(2));
603 g.insert_arc(n3, n2, IntDistanceArc(2));
604
605 Ady_Mat<Graph, int> dist(g);
606 Ady_Mat<Graph, long> path(g);
607
608 floyd_all_shortest_paths(g, dist, path);
609
610 // Map node info -> matrix index
611 vector<long> idx(4, -1);
612 for (long i = 0; i < 4; ++i)
613 idx[dist(i)->get_info()] = i;
614
615 // Within component 1
616 EXPECT_EQ(dist(idx[0], idx[1]), -5);
617 EXPECT_EQ(dist(idx[1], idx[0]), 6);
618
619 // Between components (unreachable) must remain infinity sentinel even with negative weights
620 EXPECT_EQ(dist(idx[0], idx[2]), IntDistanceArc::Max_Distance);
621 EXPECT_EQ(dist(idx[1], idx[3]), IntDistanceArc::Max_Distance);
622 EXPECT_EQ(dist(idx[2], idx[0]), IntDistanceArc::Max_Distance);
623 EXPECT_EQ(dist(idx[3], idx[1]), IntDistanceArc::Max_Distance);
624}
625
627{
629
630 const int N = 5;
631 Graph g;
632 vector<Graph::Node*> nodes;
633
634 for (int i = 0; i < N; ++i)
635 nodes.push_back(g.insert_node(i));
636
637 // Complete graph with all edges having weight 1
638 for (int i = 0; i < N; ++i)
639 for (int j = 0; j < N; ++j)
640 if (i != j)
641 g.insert_arc(nodes[i], nodes[j], IntDistanceArc(1));
642
643 Ady_Mat<Graph, int> dist(g);
644 Ady_Mat<Graph, long> path(g);
645
646 floyd_all_shortest_paths(g, dist, path);
647
648 // In complete graph with unit weights, all distances should be 0 or 1
649 for (long i = 0; i < N; ++i) {
650 for (long j = 0; j < N; ++j) {
651 if (i == j)
652 EXPECT_EQ(dist(i, j), 0);
653 else
654 EXPECT_EQ(dist(i, j), 1);
655 }
656 }
657}
658
659// ============================================================================
660// Custom Compare and Plus Tests
661// ============================================================================
662
663// Max-plus semiring: find widest paths (max of min edge weights)
665{
666 bool operator()(int a, int b) const { return a > b; }
667};
668
670{
671 int operator()(int a, int b) const { return min(a, b); }
672};
673
675{
677
678 Graph g;
679 auto* n0 = g.insert_node(0);
680 auto* n1 = g.insert_node(1);
681 auto* n2 = g.insert_node(2);
682
683 // Path 0->2 direct: capacity 2
684 // Path 0->1->2: min(5, 3) = 3 (better!)
685 g.insert_arc(n0, n1, IntDistanceArc(5));
686 g.insert_arc(n1, n2, IntDistanceArc(3));
687 g.insert_arc(n0, n2, IntDistanceArc(2));
688
689 Ady_Mat<Graph, int> cap(g);
690 Ady_Mat<Graph, long> path(g);
691
692 // Note: This would need a different initialization for max-min
693 // This test just verifies custom functors compile and run
695
696 // The result depends on initialization which assumes min-plus
697 // So we just verify it runs without crashing
698 SUCCEED();
699}
700
701// ============================================================================
702// Stress Test
703// ============================================================================
704
706{
708
709 const int N = 20;
710 Graph g;
711 vector<Graph::Node*> nodes;
712
713 for (int i = 0; i < N; ++i)
714 nodes.push_back(g.insert_node(i));
715
716 // Create a chain with some shortcuts
717 for (int i = 0; i < N - 1; ++i)
718 g.insert_arc(nodes[i], nodes[i + 1], IntDistanceArc(1));
719
720 // Add some shortcuts (longer, so they won't be used)
721 for (int i = 0; i < N - 2; i += 2)
722 g.insert_arc(nodes[i], nodes[i + 2], IntDistanceArc(3)); // Longer than 2 hops
723
724 Ady_Mat<Graph, int> dist(g);
725 Ady_Mat<Graph, long> path(g);
726
727 floyd_all_shortest_paths(g, dist, path);
728
729 // Build index mapping from node info to matrix index
730 vector<long> idx(N, -1);
731 for (long m = 0; m < N; ++m) {
732 int info = dist(m)->get_info();
733 idx[info] = m;
734 }
735
736 // Check that chain distances are correct using proper indices
737 for (int i = 0; i < N; ++i)
738 for (int j = i; j < N; ++j)
739 EXPECT_EQ(dist(idx[i], idx[j]), j - i) << "Distance from " << i << " to " << j;
740}
741
742// ============================================================================
743// Matrix Properties Tests
744// ============================================================================
745
747{
748 Ady_Mat<Graph, Dist> dist(g);
749 Ady_Mat<Graph, long> path(g);
750
751 floyd_all_shortest_paths(g, dist, path);
752
753 const long n = g.get_num_nodes();
754
755 // For reachable nodes, path(i,j) should point to next hop or j itself
756 for (long i = 0; i < n; ++i) {
757 for (long j = 0; j < n; ++j) {
758 if (i == j) {
759 EXPECT_EQ(path(i, j), j) << "Diagonal should point to self";
760 } else if (dist(i, j) < IntDistanceArc::Max_Distance) {
761 long next = path(i, j);
762 EXPECT_GE(next, 0L);
763 EXPECT_LT(next, n);
764 }
765 }
766 }
767}
768
770{
771 Ady_Mat<Graph, Dist> dist(g);
772 Ady_Mat<Graph, long> path(g);
773
774 floyd_all_shortest_paths(g, dist, path);
775
776 const long n = g.get_num_nodes();
777
778 // Triangle inequality: dist(i,j) <= dist(i,k) + dist(k,j)
779 for (long i = 0; i < n; ++i) {
780 for (long j = 0; j < n; ++j) {
781 for (long k = 0; k < n; ++k) {
782 if (!isinf(dist(i, k)) && !isinf(dist(k, j))) {
783 EXPECT_LE(dist(i, j), dist(i, k) + dist(k, j))
784 << "Triangle inequality violated for i=" << i << ", j=" << j << ", k=" << k;
785 }
786 }
787 }
788 }
789}
790
791// ============================================================================
792// Initialize_Dist Tests (implicitly tested via floyd_all_shortest_paths)
793// ============================================================================
794
796{
797 Ady_Mat<Graph, Dist> dist(g);
798 Ady_Mat<Graph, long> path(g);
799
800 // Run Floyd-Warshall which internally uses Initialize_Dist
801 floyd_all_shortest_paths(g, dist, path);
802
803 // After running, we should have valid distances
804 const long n = g.get_num_nodes();
805
806 // Check diagonal is zero (set by initialization)
807 for (long i = 0; i < n; ++i)
808 EXPECT_EQ(dist(i, i), 0);
809
810 // Check that at least some edges have finite weights
811 bool found_finite = false;
812 for (long i = 0; i < n; ++i)
813 for (long j = 0; j < n; ++j)
814 if (i != j && dist(i, j) < IntDistanceArc::Max_Distance)
815 found_finite = true;
816
818}
819
820// ============================================================================
821// Negative Weights (Note: Floyd-Warshall handles them, but not negative cycles)
822// ============================================================================
823
825{
826 // Use double to allow negative values
828
829 Graph g;
830 auto* n0 = g.insert_node(0);
831 auto* n1 = g.insert_node(1);
832 auto* n2 = g.insert_node(2);
833
834 // 0 --1--> 1 ---(-3)---> 2
835 // 0 --2--> 2 (direct, but longer than 0->1->2 = 1 + (-3) = -2)
836 g.insert_arc(n0, n1, DistanceArc(1.0));
837 g.insert_arc(n1, n2, DistanceArc(-3.0)); // Negative weight
838 g.insert_arc(n0, n2, DistanceArc(2.0));
839
841 Ady_Mat<Graph, long> path(g);
842
843 floyd_all_shortest_paths(g, dist, path);
844
845 // Find indices
846 long idx0 = -1, idx2 = -1;
847 for (long i = 0; i < 3; ++i) {
848 if (dist(i)->get_info() == 0) idx0 = i;
849 if (dist(i)->get_info() == 2) idx2 = i;
850 }
851
852 // Shortest 0->2 should be -2 (through 1)
853 EXPECT_DOUBLE_EQ(dist(idx0, idx2), -2.0);
854}
855
856// ============================================================================
857// Large Dense Graph Test
858// ============================================================================
859
861{
863
864 const int N = 10;
865 Graph g;
866 vector<Graph::Node*> nodes;
867
868 for (int i = 0; i < N; ++i)
869 nodes.push_back(g.insert_node(i));
870
871 // Dense graph: edge from i to j with weight (i+j+1) mod N + 1
872 for (int i = 0; i < N; ++i)
873 for (int j = 0; j < N; ++j)
874 if (i != j)
875 g.insert_arc(nodes[i], nodes[j], IntDistanceArc((i + j + 1) % N + 1));
876
877 Ady_Mat<Graph, int> dist(g);
878 Ady_Mat<Graph, long> path(g);
879
880 floyd_all_shortest_paths(g, dist, path);
881
882 // Verify diagonal is zero
883 for (long i = 0; i < N; ++i)
884 EXPECT_EQ(dist(i, i), 0);
885
886 // Verify all pairs are reachable
887 for (long i = 0; i < N; ++i)
888 for (long j = 0; j < N; ++j)
890 << "Should be reachable from " << i << " to " << j;
891}
List_Graph< Node, Arc > Graph
Auxiliary adjacency matrix with custom entry type.
size_t get_num_nodes() const noexcept
Get number of nodes (matrix dimension)
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3960
typename BaseGraph::Arc Arc
Definition graph-dry.H:3964
typename BaseGraph::Node Node
Definition graph-dry.H:3963
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:525
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:605
Path on a graph.
Definition tpl_graph.H:2772
size_t size() const noexcept
Return the path length in nodes.
Definition tpl_graph.H:2910
Test fixture with integer weights.
string latex_temp_filename() const
static long long process_id() noexcept
vector< Node * > nodes
IntDistanceArc::Distance_Type Dist
void SetUp() override
Test fixture with a simple weighted graph.
long get_index(Ady_Mat< Graph, Dist > &mat, Node *node) const
DistanceArc::Distance_Type Dist
#define TEST(name)
#define N
Definition fib.C:294
__gmp_expr< typename __gmp_resolve_expr< T, V >::value_type, __gmp_binary_expr< __gmp_expr< T, U >, __gmp_expr< V, W >, __gmp_min_function > > min(const __gmp_expr< T, U > &expr1, const __gmp_expr< V, W > &expr2)
Definition gmpfrxx.h:4122
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
void floyd_all_shortest_paths_latex(GT &g, Ady_Mat< GT, typename GT::Arc_Type::Distance_Type > &dist, Ady_Mat< GT, long > &path, std::ofstream &output)
Floyd-Warshall algorithm with LaTeX step-by-step output.
void find_min_path(Mat &p, const long src_index, const long tgt_index, Path< typename Mat::Graph_Type > &path)
This is an overloaded member function, provided for convenience. It differs from the above function o...
void floyd_all_shortest_paths(GT &g, Ady_Mat< GT, typename GT::Arc_Type::Distance_Type > &dist, Ady_Mat< GT, long > &path)
Compute all-pairs shortest paths using Floyd-Warshall algorithm.
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
Floyd-Warshall algorithm with LaTeX output generation.
TEST_F(FloydSimpleGraphTest, DiagonalIsZero)
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
std::string to_string(const time_t t, const std::string &format)
Format a time_t value into a string using format.
Definition ah-date.H:140
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:171
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:223
Arc info type with required Distance_Type and constants.
Distance_Type get_distance() const
Distance_Type distance
static constexpr Distance_Type Zero_Distance
DistanceArc(Distance_Type d)
static constexpr Distance_Type Max_Distance
Integer distance arc type.
Distance_Type get_distance() const
Distance_Type distance
IntDistanceArc(Distance_Type d)
static constexpr Distance_Type Zero_Distance
static constexpr Distance_Type Max_Distance
bool operator()(int a, int b) const
int operator()(int a, int b) const
Distance formatter: takes matrix, i, j indices, returns formatted entry.
string operator()(Mat &mat, int i, int j) const
Index formatter: takes matrix and index, returns string.
string operator()(Mat &mat, int i) const
Path formatter: takes matrix, i, j indices, returns formatted entry.
string operator()(Mat &mat, int i, int j) const
FooMap m(5, fst_unit_pair_hash, snd_unit_pair_hash)
Dnode< int > Test
Definition testDnode.C:42
static int * k
Generic graph and digraph implementations.
Adjacency matrix representations for graphs.
ofstream output
Definition writeHeap.C:215