Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
floyd.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
38#include <gtest/gtest.h>
39
40#include <tpl_graph_utils.H>
41#include <Floyd_Warshall.H>
42
43using namespace Aleph;
44using namespace std;
45
46// Graph type using proper node/arc wrappers
50
51namespace {
52
53// Helper to insert arc between nodes by value
54void insert_arc_by_value(Grafo& g, int src_val, int tgt_val, int weight)
55{
56 GNode* src = nullptr;
57 GNode* tgt = nullptr;
58
59 for (Grafo::Node_Iterator it(g); it.has_curr(); it.next_ne()) {
60 auto n = it.get_curr();
61 if (n->get_info() == src_val) src = n;
62 if (n->get_info() == tgt_val) tgt = n;
63 }
64
65 if (src && tgt)
66 g.insert_arc(src, tgt, weight);
67}
68
69} // namespace
70
71// Test basic Floyd algorithm on a simple graph
73{
74 Grafo g;
75 auto n0 = g.insert_node(0);
76 auto n1 = g.insert_node(1);
77 auto n2 = g.insert_node(2);
78
79 // Create edges: 0->1 (weight 1), 1->2 (weight 2), 0->2 (weight 5)
80 insert_arc_by_value(g, 0, 1, 1);
81 insert_arc_by_value(g, 1, 2, 2);
82 insert_arc_by_value(g, 0, 2, 5);
83
85
86 EXPECT_FALSE(floyd.has_negative_cycle());
87
88 const auto& dist = floyd.get_dist_mat();
89
90 const long i0 = floyd.index_node(n0);
91 const long i1 = floyd.index_node(n1);
92 const long i2 = floyd.index_node(n2);
93
94 // Distance from node to itself should be 0
95 EXPECT_EQ(dist(i0, i0), 0);
96 EXPECT_EQ(dist(i1, i1), 0);
97 EXPECT_EQ(dist(i2, i2), 0);
98
99 // Distance 0->1 should be 1 (direct edge)
100 EXPECT_EQ(dist(i0, i1), 1);
101
102 // Distance 0->2 should be 3 (0->1->2, not direct 0->2 with weight 5)
103 EXPECT_EQ(dist(i0, i2), 3);
104
105 // Distance 1->2 should be 2 (direct edge)
106 EXPECT_EQ(dist(i1, i2), 2);
107}
108
109// Test handling of unreachable nodes
111{
112 Grafo g;
113 auto n0 = g.insert_node(0);
114 auto n1 = g.insert_node(1);
115 // No edges between nodes
116
118
119 const auto& dist = floyd.get_dist_mat();
120 const int Inf = numeric_limits<int>::max();
121
122 const long i0 = floyd.index_node(n0);
123 const long i1 = floyd.index_node(n1);
124
125 // Distances to unreachable nodes should be infinity
126 EXPECT_EQ(dist(i0, i1), Inf);
127 EXPECT_EQ(dist(i1, i0), Inf);
128
129 // Unreachable paths should be reported as empty paths
130 EXPECT_TRUE(floyd.get_min_path(i0, i1).is_empty());
131 EXPECT_TRUE(floyd.get_min_path(i1, i0).is_empty());
132}
133
134// Test negative weights without negative cycles
136{
137 Grafo g;
138 auto n0 = g.insert_node(0);
139 auto n1 = g.insert_node(1);
140 auto n2 = g.insert_node(2);
141
142 // 0->1 (weight -1), 1->2 (weight 2), 0->2 (weight 3)
143 g.insert_arc(n0, n1, -1);
144 g.insert_arc(n1, n2, 2);
145 g.insert_arc(n0, n2, 3);
146
148
149 EXPECT_FALSE(floyd.has_negative_cycle());
150
151 // Get actual indices for nodes
152 long i0 = floyd.index_node(n0);
153 long i1 = floyd.index_node(n1);
154 long i2 = floyd.index_node(n2);
155
156 const auto& dist = floyd.get_dist_mat();
157
158 // Distance 0->1 should be -1 (direct negative edge)
159 EXPECT_EQ(dist(i0, i1), -1);
160
161 // Distance 0->2 should be 1 (0->1->2: -1+2=1, better than direct 0->2: 3)
162 EXPECT_EQ(dist(i0, i2), 1);
163}
164
165// Test negative cycle detection
167{
168 Grafo g;
169 g.insert_node(0);
170 g.insert_node(1);
171 g.insert_node(2);
172
173 // Create cycle: 0->1->2->0 with total weight -1
174 insert_arc_by_value(g, 0, 1, 1);
175 insert_arc_by_value(g, 1, 2, -1);
176 insert_arc_by_value(g, 2, 0, -1);
177
179
180 EXPECT_TRUE(floyd.has_negative_cycle());
181}
182
183// Test index_node method
185{
186 Grafo g;
187 g.insert_node(10);
188 g.insert_node(20);
189 g.insert_node(30);
190
191 insert_arc_by_value(g, 10, 20, 1);
192 insert_arc_by_value(g, 20, 30, 1);
193
195
196 const auto nodes = floyd.get_nodes();
197
198 // Test that we can find each node's index
199 for (size_t i = 0; i < nodes.size(); ++i) {
200 auto node = floyd.select_node(i);
201 EXPECT_EQ(floyd.index_node(node), static_cast<long>(i));
202 }
203}
204
205// Test null pointer handling
207{
208 Grafo g;
209 g.insert_node(0);
210
212
213 EXPECT_THROW(floyd.index_node(nullptr), std::invalid_argument);
214}
215
216// Test path reconstruction with indices
218{
219 Grafo g;
220 g.insert_node(0);
221 g.insert_node(1);
222 insert_arc_by_value(g, 0, 1, 1);
223
225 const long n = g.get_num_nodes();
226
227 EXPECT_THROW(floyd.get_min_path(-1L, 0L), std::out_of_range);
228 EXPECT_THROW(floyd.get_min_path(0L, n), std::out_of_range);
229 EXPECT_THROW(floyd.get_min_path(n, 0L), std::out_of_range);
230}
231
232// Test path reconstruction returns valid path
234{
235 Grafo g;
236 auto n0 = g.insert_node(0);
237 auto n1 = g.insert_node(1);
238 auto n2 = g.insert_node(2);
239
240 g.insert_arc(n0, n1, 1);
241 g.insert_arc(n1, n2, 2);
242 g.insert_arc(n0, n2, 5);
243
245
246 // Get actual indices
247 long i0 = floyd.index_node(n0);
248 long i2 = floyd.index_node(n2);
249
250 // Get shortest path from n0 to n2
251 auto path = floyd.get_min_path(i0, i2);
252
253 // Path should have at least 2 nodes (start and end)
254 EXPECT_GE(path.size(), 2u);
255
256 // First node should be n0, last should be n2
257 EXPECT_EQ(path.get_first_node(), n0);
258 EXPECT_EQ(path.get_last_node(), n2);
259}
260
261// Test self-path
263{
264 Grafo g;
265 g.insert_node(0);
266
268
269 auto path = floyd.get_min_path(0L, 0L);
270 EXPECT_EQ(path.size(), 1u);
271}
272
273// Test entry formatting
275{
276 Grafo g;
277 g.insert_node(0);
278
280
281 EXPECT_EQ(floyd.entry(42), "42");
282 EXPECT_EQ(floyd.entry(numeric_limits<int>::max()), "Inf");
283 EXPECT_EQ(floyd.entry(-5), "-5");
284}
285
286// Test path matrix structure
288{
289 Grafo g;
290 g.insert_node(0);
291 g.insert_node(1);
292 g.insert_node(2);
293
294 insert_arc_by_value(g, 0, 1, 1);
295 insert_arc_by_value(g, 1, 2, 1);
296 insert_arc_by_value(g, 0, 2, 5);
297
299
300 const auto& path_mat = floyd.get_path_mat();
301 const auto& dist = floyd.get_dist_mat();
302 const int Inf = numeric_limits<int>::max();
303 const long n = g.get_num_nodes();
304
305 EXPECT_EQ(path_mat.rows(), n);
306 EXPECT_EQ(path_mat.cols(), n);
307
308 // Path matrix entries should be valid node indices
309 for (long i = 0; i < n; ++i) {
310 for (long j = 0; j < n; ++j) {
311 if (dist(i, j) == Inf) {
312 EXPECT_EQ(path_mat(i, j), -1);
313 continue;
314 }
315
316 // Reachable (including i == j): must be a valid index
317 long k = path_mat(i, j);
318 EXPECT_GE(k, 0);
319 EXPECT_LT(k, n);
320 }
321 }
322}
323
324// Test complete graph
326{
327 const int N = 10;
328 Grafo g;
330
331 for (int i = 0; i < N; ++i)
332 nodes.push_back(g.insert_node(i));
333
334 // Create complete graph
335 for (int i = 0; i < N; ++i)
336 for (int j = 0; j < N; ++j)
337 if (i != j)
338 g.insert_arc(nodes[i], nodes[j], i + j + 1);
339
341
342 EXPECT_FALSE(floyd.has_negative_cycle());
343
344 const auto& dist = floyd.get_dist_mat();
345
346 // All diagonal elements should be 0
347 for (int i = 0; i < N; ++i)
348 EXPECT_EQ(dist(i, i), 0);
349
350 // All off-diagonal elements should be finite and positive
351 for (int i = 0; i < N; ++i)
352 for (int j = 0; j < N; ++j)
353 if (i != j) {
354 EXPECT_GT(dist(i, j), 0);
355 EXPECT_LT(dist(i, j), numeric_limits<double>::max());
356 }
357}
358
359// Test single node graph
361{
362 Grafo g;
363 g.insert_node(42);
364
366
367 EXPECT_FALSE(floyd.has_negative_cycle());
368
369 const auto& dist = floyd.get_dist_mat();
370 EXPECT_EQ(dist.rows(), 1);
371 EXPECT_EQ(dist.cols(), 1);
372 EXPECT_EQ(dist(0, 0), 0);
373
374 // Test path reconstruction for single node
375 auto path = floyd.get_min_path(0L, 0L);
376 EXPECT_EQ(path.size(), 1u);
377 EXPECT_EQ(path.get_first_node()->get_info(), 42);
378}
Floyd-Warshall algorithm for all-pairs shortest paths.
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
typename BaseGraph::Arc Arc
Definition graph-dry.H:3852
typename BaseGraph::Node Node
Definition graph-dry.H:3851
Compute the all-pairs shortest path distance matrix and the path reconstruction matrix for a graph g ...
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
#define TEST(name)
#define N
Definition fib.C:294
Grafo::Arc GArc
Definition floyd.cc:49
Grafo::Node GNode
Definition floyd.cc:48
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Utility algorithms and operations for graphs.