Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
prim_test.cc
Go to the documentation of this file.
1
13#include <gtest/gtest.h>
14
15#include <limits>
16#include <random>
17#include <set>
18#include <vector>
19
20#include <Prim.H>
21#include <Kruskal.H> // For comparison tests
22#include <tpl_graph.H>
23#include <tpl_graph_utils.H>
24
25using namespace Aleph;
26
27// Graph types for tests
29using Node = GT::Node;
30using Arc = GT::Arc;
31
32namespace {
33
34// Helper to compute total weight of tree
35template <class G>
36int tree_total_weight(const G &tree)
37{
38 int total = 0;
39 for (auto it = tree.get_arc_it(); it.has_curr(); it.next_ne())
40 total += it.get_current_arc_ne()->get_info();
41 return total;
42}
43
44// Check if tree is connected (for undirected graphs)
45template <class G>
46bool is_tree_connected(const G &tree)
47{
48 if (tree.get_num_nodes() == 0)
49 return true;
50 if (tree.get_num_nodes() == 1)
51 return true;
52
53 // A tree with V nodes should have V-1 arcs
54 if (tree.get_num_arcs() != tree.get_num_nodes() - 1)
55 return false;
56
57 // BFS to check connectivity
58 tree.reset_nodes();
59 auto first = tree.get_first_node();
60 NODE_BITS(first).set_bit(Aleph::Spanning_Tree, true);
61
63 queue.append(first);
64 size_t visited = 1;
65
66 while (not queue.is_empty())
67 {
68 auto curr = queue.remove_first();
69 for (Node_Arc_Iterator<G> it(curr); it.has_curr(); it.next_ne())
70 {
71 auto tgt = it.get_tgt_node_ne();
73 {
74 NODE_BITS(tgt).set_bit(Aleph::Spanning_Tree, true);
75 queue.append(tgt);
76 ++visited;
77 }
78 }
79 }
80
81 return visited == tree.get_num_nodes();
82}
83
84} // namespace
85
86// =============================================================================
87// Basic Functionality Tests
88// =============================================================================
89
91{
92 GT g;
93 GT tree;
94
96
97 // Prim throws exception when graph has no nodes
98 EXPECT_THROW(prim(g, tree), std::exception);
99}
100
102{
103 GT g;
104 g.insert_node(1);
105
106 GT tree;
108 prim(g, tree);
109
110 EXPECT_EQ(tree.get_num_nodes(), 1u);
111 EXPECT_EQ(tree.get_num_arcs(), 0u);
112}
113
115{
116 GT g;
117 auto n1 = g.insert_node(1);
118 auto n2 = g.insert_node(2);
119 g.insert_arc(n1, n2, 10);
120
121 GT tree;
123 prim(g, tree);
124
125 EXPECT_EQ(tree.get_num_nodes(), 2u);
126 EXPECT_EQ(tree.get_num_arcs(), 1u);
127 EXPECT_EQ(tree_total_weight(tree), 10);
128}
129
131{
132 GT g;
133 auto n1 = g.insert_node(1);
134 auto n2 = g.insert_node(2);
135 auto n3 = g.insert_node(3);
136
137 // Triangle with weights 1, 2, 3
138 g.insert_arc(n1, n2, 1);
139 g.insert_arc(n2, n3, 2);
140 g.insert_arc(n1, n3, 3);
141
142 GT tree;
144 prim(g, tree);
145
146 EXPECT_EQ(tree.get_num_nodes(), 3u);
147 EXPECT_EQ(tree.get_num_arcs(), 2u);
148 EXPECT_EQ(tree_total_weight(tree), 3); // MST uses edges 1 and 2
150}
151
153{
154 GT g;
155 auto n1 = g.insert_node(1);
156 auto n2 = g.insert_node(2);
157 auto n3 = g.insert_node(3);
158 auto n4 = g.insert_node(4);
159
160 // Square: 1-2-3-4-1 with diagonal 1-3
161 g.insert_arc(n1, n2, 1);
162 g.insert_arc(n2, n3, 2);
163 g.insert_arc(n3, n4, 3);
164 g.insert_arc(n4, n1, 4);
165 g.insert_arc(n1, n3, 5); // diagonal
166
167 GT tree;
169 prim(g, tree);
170
171 EXPECT_EQ(tree.get_num_nodes(), 4u);
172 EXPECT_EQ(tree.get_num_arcs(), 3u);
173 EXPECT_EQ(tree_total_weight(tree), 6); // MST uses edges 1, 2, 3
175}
176
177// =============================================================================
178// Comparison with Kruskal Tests
179// =============================================================================
180
182{
183 GT g;
184 auto n1 = g.insert_node(1);
185 auto n2 = g.insert_node(2);
186 auto n3 = g.insert_node(3);
187 auto n4 = g.insert_node(4);
188 auto n5 = g.insert_node(5);
189
190 g.insert_arc(n1, n2, 1);
191 g.insert_arc(n1, n3, 3);
192 g.insert_arc(n2, n3, 2);
193 g.insert_arc(n2, n4, 4);
194 g.insert_arc(n3, n4, 5);
195 g.insert_arc(n3, n5, 6);
196 g.insert_arc(n4, n5, 7);
197
199
201 prim(g, prim_tree);
202
205
209}
210
212{
213 std::mt19937 rng(42); // Fixed seed for reproducibility
214 std::uniform_int_distribution<int> weight_dist(1, 100);
215
216 const int N = 20;
217 GT g;
218 std::vector<Node *> nodes;
219
220 for (int i = 0; i < N; ++i)
221 nodes.push_back(g.insert_node(i));
222
223 // Create a connected graph
224 for (int i = 1; i < N; ++i)
225 g.insert_arc(nodes[i-1], nodes[i], weight_dist(rng));
226
227 // Add some random edges
228 for (int i = 0; i < N * 2; ++i)
229 {
230 int a = rng() % N;
231 int b = rng() % N;
232 if (a != b)
234 }
235
237
239 prim(g, prim_tree);
240
243
245}
246
247// =============================================================================
248// Edge Cases Tests
249// =============================================================================
250
252{
253 GT g;
254 auto n1 = g.insert_node(1);
255 auto n2 = g.insert_node(2);
256 auto n3 = g.insert_node(3);
257 auto n4 = g.insert_node(4);
258
259 // Complete graph with all equal weights
260 g.insert_arc(n1, n2, 1);
261 g.insert_arc(n1, n3, 1);
262 g.insert_arc(n1, n4, 1);
263 g.insert_arc(n2, n3, 1);
264 g.insert_arc(n2, n4, 1);
265 g.insert_arc(n3, n4, 1);
266
267 GT tree;
269 prim(g, tree);
270
271 EXPECT_EQ(tree.get_num_nodes(), 4u);
272 EXPECT_EQ(tree.get_num_arcs(), 3u);
275}
276
278{
279 GT g;
280 auto n1 = g.insert_node(1);
281 auto n2 = g.insert_node(2);
282 auto n3 = g.insert_node(3);
283 auto n4 = g.insert_node(4);
284 auto n5 = g.insert_node(5);
285
286 // Linear chain: 1-2-3-4-5
287 g.insert_arc(n1, n2, 1);
288 g.insert_arc(n2, n3, 2);
289 g.insert_arc(n3, n4, 3);
290 g.insert_arc(n4, n5, 4);
291
292 GT tree;
294 prim(g, tree);
295
296 EXPECT_EQ(tree.get_num_nodes(), 5u);
297 EXPECT_EQ(tree.get_num_arcs(), 4u);
298 EXPECT_EQ(tree_total_weight(tree), 10); // 1+2+3+4
300}
301
303{
304 GT g;
305 auto center = g.insert_node(0);
306
307 // Star graph with center connected to all others
308 for (int i = 1; i <= 5; ++i)
309 {
310 auto leaf = g.insert_node(i);
311 g.insert_arc(center, leaf, i);
312 }
313
314 GT tree;
316 prim(g, tree);
317
318 EXPECT_EQ(tree.get_num_nodes(), 6u);
319 EXPECT_EQ(tree.get_num_arcs(), 5u);
320 EXPECT_EQ(tree_total_weight(tree), 15); // 1+2+3+4+5
322}
323
324// =============================================================================
325// Stress Tests
326// =============================================================================
327
329{
330 std::mt19937 rng(12345);
331 std::uniform_int_distribution<int> weight_dist(1, 1000);
332
333 const int N = 100;
334 GT g;
335 std::vector<Node *> nodes;
336
337 for (int i = 0; i < N; ++i)
338 nodes.push_back(g.insert_node(i));
339
340 // Create a connected graph (chain)
341 for (int i = 1; i < N; ++i)
342 g.insert_arc(nodes[i-1], nodes[i], weight_dist(rng));
343
344 // Add random edges to make it denser
345 for (int i = 0; i < N * 3; ++i)
346 {
347 int a = rng() % N;
348 int b = rng() % N;
349 if (a != b)
351 }
352
354
356 prim(g, prim_tree);
357
360
361 EXPECT_EQ(prim_tree.get_num_nodes(), static_cast<size_t>(N));
362 EXPECT_EQ(prim_tree.get_num_arcs(), static_cast<size_t>(N - 1));
364
365 // Both algorithms should produce trees with the same total weight
367}
368
370{
371 std::mt19937 rng(54321);
372 std::uniform_int_distribution<int> weight_dist(1, 100);
373
374 const int N = 30;
375 GT g;
376 std::vector<Node *> nodes;
377
378 for (int i = 0; i < N; ++i)
379 nodes.push_back(g.insert_node(i));
380
381 // Create almost complete graph
382 for (int i = 0; i < N; ++i)
383 for (int j = i + 1; j < N; ++j)
385
387
389 prim(g, prim_tree);
390
393
394 EXPECT_EQ(prim_tree.get_num_nodes(), static_cast<size_t>(N));
395 EXPECT_EQ(prim_tree.get_num_arcs(), static_cast<size_t>(N - 1));
397
398 // Both algorithms should produce trees with the same total weight
400}
401
402// =============================================================================
403// Node Mapping Tests
404// =============================================================================
405
407{
408 GT g;
409 auto n1 = g.insert_node(1);
410 auto n2 = g.insert_node(2);
411 auto n3 = g.insert_node(3);
412
413 g.insert_arc(n1, n2, 1);
414 g.insert_arc(n2, n3, 2);
415
416 GT tree;
418 prim(g, tree);
419
420 // Verify that each node in g maps to a node in tree
421 for (auto it = g.get_node_it(); it.has_curr(); it.next_ne())
422 {
423 auto gnode = it.get_curr();
425 EXPECT_NE(tnode, nullptr);
426 EXPECT_EQ(gnode->get_info(), tnode->get_info());
427 }
428}
429
430// =============================================================================
431// Textbook Example
432// =============================================================================
433
435{
436 // Classic MST example from textbooks
437 // Graph with nodes A(0), B(1), C(2), D(3), E(4), F(5)
438 GT g;
439 auto a = g.insert_node(0);
440 auto b = g.insert_node(1);
441 auto c = g.insert_node(2);
442 auto d = g.insert_node(3);
443 auto e = g.insert_node(4);
444 auto f = g.insert_node(5);
445
446 g.insert_arc(a, b, 6);
447 g.insert_arc(a, c, 1);
448 g.insert_arc(a, d, 5);
449 g.insert_arc(b, c, 2);
450 g.insert_arc(b, e, 5);
451 g.insert_arc(c, d, 2);
452 g.insert_arc(c, e, 6);
453 g.insert_arc(c, f, 4);
454 g.insert_arc(d, f, 4);
455 g.insert_arc(e, f, 3);
456
457 GT tree;
459 prim(g, tree);
460
461 EXPECT_EQ(tree.get_num_nodes(), 6u);
462 EXPECT_EQ(tree.get_num_arcs(), 5u);
463 // MST weight: 1 + 2 + 2 + 3 + 4 = 12 (or could be 1+2+2+4+3=12)
464 EXPECT_EQ(tree_total_weight(tree), 12);
466}
Kruskal's minimum spanning tree algorithm.
Prim's algorithm for minimum spanning trees.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
Computes the minimum spanning tree of a graph using Kruskal's algorithm.
Definition Kruskal.H:90
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
Calcula el árbol abarcador mínimo de un grafo según el algoritmo de Prim.
Definition Prim.H:214
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
auto get_node_it() const noexcept
Obtains an iterator to the nodes of graph.
Definition graph-dry.H:2780
#define TEST(name)
static mt19937 rng
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
#define IS_NODE_VISITED(p, bit)
Determine whether the control bit is set or not to one.
#define NODE_BITS(p)
Get the control bits of a node.
@ Spanning_Tree
Definition aleph-graph.H:79
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
DynList< T > maps(const C &c, Op op)
Classic map operation.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Filtered iterator of adjacent arcs of a node.
Definition tpl_graph.H:1119
Generic graph and digraph implementations.
Utility algorithms and operations for graphs.