Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
karger.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_sgraph.H>
41#include <Karger.H>
42
43using namespace Aleph;
44using namespace std;
45
46// Graph types for testing
50
51namespace {
52
53// Create a simple triangle graph (3 nodes, 3 edges)
54// Minimum cut is 2 (remove any node leaves 2 edges)
56{
57 Grafo g;
58 auto n0 = g.insert_node(0);
59 auto n1 = g.insert_node(1);
60 auto n2 = g.insert_node(2);
61
62 g.insert_arc(n0, n1, 1);
63 g.insert_arc(n1, n2, 1);
64 g.insert_arc(n0, n2, 1);
65
66 return g;
67}
68
69// Create a square graph (4 nodes, 4 edges)
70// Minimum cut is 2
71Grafo create_square()
72{
73 Grafo g;
74 auto n0 = g.insert_node(0);
75 auto n1 = g.insert_node(1);
76 auto n2 = g.insert_node(2);
77 auto n3 = g.insert_node(3);
78
79 g.insert_arc(n0, n1, 1);
80 g.insert_arc(n1, n2, 1);
81 g.insert_arc(n2, n3, 1);
82 g.insert_arc(n3, n0, 1);
83
84 return g;
85}
86
87// Create a graph with an obvious minimum cut of 1
88// Two clusters connected by a single edge
90{
91 Grafo g;
92 // Left cluster
93 auto a0 = g.insert_node(0);
94 auto a1 = g.insert_node(1);
95 auto a2 = g.insert_node(2);
96
97 g.insert_arc(a0, a1, 1);
98 g.insert_arc(a1, a2, 1);
99 g.insert_arc(a0, a2, 1);
100
101 // Right cluster
102 auto b0 = g.insert_node(10);
103 auto b1 = g.insert_node(11);
104 auto b2 = g.insert_node(12);
105
106 g.insert_arc(b0, b1, 1);
107 g.insert_arc(b1, b2, 1);
108 g.insert_arc(b0, b2, 1);
109
110 // Bridge (single edge connecting clusters)
111 g.insert_arc(a0, b0, 1);
112
113 return g;
114}
115
116// Create a complete graph K_n
117Grafo create_complete_graph(int n)
118{
119 Grafo g;
120 vector<Node*> nodes;
121
122 for (int i = 0; i < n; ++i)
123 nodes.push_back(g.insert_node(i));
124
125 for (int i = 0; i < n; ++i)
126 for (int j = i + 1; j < n; ++j)
127 g.insert_arc(nodes[i], nodes[j], 1);
128
129 return g;
130}
131
132// Create a path graph (n nodes connected in a line)
133// Minimum cut is 1
134Grafo create_path(int n)
135{
136 Grafo g;
137 vector<Node*> nodes;
138
139 for (int i = 0; i < n; ++i)
140 nodes.push_back(g.insert_node(i));
141
142 for (int i = 0; i < n - 1; ++i)
143 g.insert_arc(nodes[i], nodes[i + 1], 1);
144
145 return g;
146}
147
148// Create a cycle graph (n nodes in a ring)
149// Minimum cut is 2
150Grafo create_cycle(int n)
151{
152 Grafo g;
153 vector<Node*> nodes;
154
155 for (int i = 0; i < n; ++i)
156 nodes.push_back(g.insert_node(i));
157
158 for (int i = 0; i < n; ++i)
159 g.insert_arc(nodes[i], nodes[(i + 1) % n], 1);
160
161 return g;
162}
163
164} // namespace
165
166// Test basic construction
171
176
177// Test error handling
179{
180 Grafo g;
181 g.insert_node(0);
182 g.insert_node(1);
183 // No arcs
184
186 DynList<Node*> vs, vt;
187 DynList<Arc*> cut;
188
189 EXPECT_THROW(karger(g, vs, vt, cut), std::domain_error);
190}
191
193{
194 Grafo g;
195 g.insert_node(0);
196
198 DynList<Node*> vs, vt;
199 DynList<Arc*> cut;
200
201 EXPECT_THROW(karger(g, vs, vt, cut), std::domain_error);
202}
203
205{
206 Grafo g;
207 auto n0 = g.insert_node(0);
208 g.insert_arc(n0, n0, 1); // Self-loop
209
211 DynList<Node*> vs, vt;
212 DynList<Arc*> cut;
213
214 // Should throw because graph has only 1 node
215 EXPECT_THROW(karger(g, vs, vt, cut), std::domain_error);
216}
217
219{
220 Grafo g;
221 auto n0 = g.insert_node(0);
222 auto n1 = g.insert_node(1);
223
224 g.insert_arc(n0, n1, 1);
225 g.insert_arc(n0, n0, 1); // Self-loop
226
228 DynList<Node*> vs, vt;
229 DynList<Arc*> cut;
230
231 EXPECT_THROW(karger(g, vs, vt, cut, 10), std::domain_error);
232 EXPECT_THROW(karger.compute_min_cut_size(g, 10), std::domain_error);
233 EXPECT_THROW(karger.fast(g, vs, vt, cut, 1), std::domain_error);
234}
235
237{
238 Grafo g;
239 auto n0 = g.insert_node(0);
240 auto n1 = g.insert_node(1);
241 auto n2 = g.insert_node(2);
242 auto n3 = g.insert_node(3);
243
244 g.insert_arc(n0, n1, 1);
245 g.insert_arc(n2, n3, 1);
246
248 DynList<Node*> vs, vt;
249 DynList<Arc*> cut;
250
251 EXPECT_THROW(karger(g, vs, vt, cut, 10), std::domain_error);
252 EXPECT_THROW(karger.compute_min_cut_size(g, 10), std::domain_error);
253 EXPECT_THROW(karger.fast(g, vs, vt, cut, 1), std::domain_error);
254}
255
256// Test basic minimum cut on simple graphs
258{
259 auto g = create_triangle();
261
262 DynList<Node*> vs, vt;
263 DynList<Arc*> cut;
264
265 size_t min_cut = karger(g, vs, vt, cut, 100);
266
267 // Minimum cut of triangle is 2
268 EXPECT_EQ(min_cut, 2);
269 EXPECT_EQ(cut.size(), 2u);
270
271 // Partitions should cover all nodes
272 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
273
274 // Each partition should be non-empty
275 EXPECT_GT(vs.size(), 0u);
276 EXPECT_GT(vt.size(), 0u);
277}
278
280{
281 auto g = create_square();
283
284 DynList<Node*> vs, vt;
285 DynList<Arc*> cut;
286
287 size_t min_cut = karger(g, vs, vt, cut, 100);
288
289 // Minimum cut of square is 2
290 EXPECT_EQ(min_cut, 2);
291 EXPECT_EQ(cut.size(), 2u);
292
293 // Partitions should be balanced (2 nodes each for min cut)
294 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
295}
296
298{
299 auto g = create_barbell();
301
302 DynList<Node*> vs, vt;
303 DynList<Arc*> cut;
304
305 size_t min_cut = karger(g, vs, vt, cut, 200);
306
307 // The barbell has a single bridge, so min cut is 1
308 EXPECT_EQ(min_cut, 1);
309 EXPECT_EQ(cut.size(), 1u);
310
311 // Each partition should have 3 nodes (one cluster each)
312 EXPECT_EQ(vs.size(), 3u);
313 EXPECT_EQ(vt.size(), 3u);
314}
315
317{
318 auto g = create_path(5);
320
321 DynList<Node*> vs, vt;
322 DynList<Arc*> cut;
323
324 size_t min_cut = karger(g, vs, vt, cut, 100);
325
326 // Path has minimum cut of 1 (any edge)
327 EXPECT_EQ(min_cut, 1);
328 EXPECT_EQ(cut.size(), 1u);
329}
330
332{
333 auto g = create_cycle(6);
335
336 DynList<Node*> vs, vt;
337 DynList<Arc*> cut;
338
339 size_t min_cut = karger(g, vs, vt, cut, 100);
340
341 // Cycle has minimum cut of 2
342 EXPECT_EQ(min_cut, 2);
343 EXPECT_EQ(cut.size(), 2u);
344}
345
346// Test complete graphs
348{
349 auto g = create_complete_graph(4);
351
352 DynList<Node*> vs, vt;
353 DynList<Arc*> cut;
354
355 size_t min_cut = karger(g, vs, vt, cut, 100);
356
357 // K4 minimum cut: removing 1 node requires cutting 3 edges
358 // Optimal is splitting 2-2: needs 4 edges (each pair connects to 2 in other)
359 // Actually min cut of K4 = 3 (isolate one vertex)
360 EXPECT_EQ(min_cut, 3);
361}
362
364{
365 auto g = create_complete_graph(5);
367
368 DynList<Node*> vs, vt;
369 DynList<Arc*> cut;
370
371 size_t min_cut = karger(g, vs, vt, cut, 200);
372
373 // K5 minimum cut = 4 (isolate one vertex)
374 EXPECT_EQ(min_cut, 4);
375}
376
377// Test reproducibility with same seed
379{
380 auto g = create_barbell();
381
385 size_t result1 = karger1(g, vs1, vt1, cut1, 50);
386
387 g = create_barbell(); // recreate graph
391 size_t result2 = karger2(g, vs2, vt2, cut2, 50);
392
395}
396
397// Test that more iterations don't make result worse
399{
400 auto g = create_complete_graph(6);
401
403
406 size_t result_few = karger(g, vs1, vt1, cut1, 10);
407
408 g = create_complete_graph(6); // recreate
411 size_t result_many = karger(g, vs2, vt2, cut2, 100);
412
413 // More iterations should find at least as good a cut
415}
416
417// Test default iteration count
419{
420 auto g = create_triangle();
422
423 DynList<Node*> vs, vt;
424 DynList<Arc*> cut;
425
426 // Use default iteration count
427 size_t min_cut = karger(g, vs, vt, cut);
428
429 EXPECT_EQ(min_cut, 2);
430 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
431}
432
433// Test partition validity
435{
436 auto g = create_barbell();
438
439 DynList<Node*> vs, vt;
440 DynList<Arc*> cut;
441
442 karger(g, vs, vt, cut, 100);
443
444 // Check that all nodes in partitions are from the original graph
445 DynSetTree<Node*> all_nodes;
446 for (Grafo::Node_Iterator it(g); it.has_curr(); it.next_ne())
447 all_nodes.insert(it.get_curr());
448
449 for (auto it = vs.get_it(); it.has_curr(); it.next())
450 EXPECT_TRUE(all_nodes.has(it.get_curr()));
451
452 for (auto it = vt.get_it(); it.has_curr(); it.next())
453 EXPECT_TRUE(all_nodes.has(it.get_curr()));
454
455 // Check no overlap between partitions
457 for (auto it = vs.get_it(); it.has_curr(); it.next())
458 vs_set.insert(it.get_curr());
459
460 for (auto it = vt.get_it(); it.has_curr(); it.next())
461 EXPECT_FALSE(vs_set.has(it.get_curr()));
462}
463
464// Test cut edges validity
466{
467 auto g = create_barbell();
469
470 DynList<Node*> vs, vt;
471 DynList<Arc*> cut;
472
473 karger(g, vs, vt, cut, 100);
474
475 // Check that all cut edges are from the original graph
476 DynSetTree<Arc*> all_arcs;
477 for (Grafo::Arc_Iterator it(g); it.has_curr(); it.next_ne())
478 all_arcs.insert(it.get_curr());
479
480 for (auto it = cut.get_it(); it.has_curr(); it.next())
481 EXPECT_TRUE(all_arcs.has(it.get_curr()));
482}
483
484// Test two-node graph (smallest possible)
486{
487 Grafo g;
488 auto n0 = g.insert_node(0);
489 auto n1 = g.insert_node(1);
490 g.insert_arc(n0, n1, 1);
491
493 DynList<Node*> vs, vt;
494 DynList<Arc*> cut;
495
496 size_t min_cut = karger(g, vs, vt, cut, 10);
497
498 EXPECT_EQ(min_cut, 1);
499 EXPECT_EQ(vs.size(), 1u);
500 EXPECT_EQ(vt.size(), 1u);
501}
502
503// Test graph with multiple parallel edges (multigraph behavior)
505{
506 Grafo g;
507 auto n0 = g.insert_node(0);
508 auto n1 = g.insert_node(1);
509 auto n2 = g.insert_node(2);
510
511 // Multiple edges between n0 and n1
512 g.insert_arc(n0, n1, 1);
513 g.insert_arc(n0, n1, 2);
514 g.insert_arc(n0, n1, 3);
515
516 // Single edge to n2
517 g.insert_arc(n1, n2, 4);
518
520 DynList<Node*> vs, vt;
521 DynList<Arc*> cut;
522
523 size_t min_cut = karger(g, vs, vt, cut, 100);
524
525 // Min cut should be 1 (separate n2 from the rest)
526 EXPECT_EQ(min_cut, 1u);
527}
528
529// Stress test with larger graph
531{
532 auto g = create_complete_graph(10);
534
535 DynList<Node*> vs, vt;
536 DynList<Arc*> cut;
537
538 size_t min_cut = karger(g, vs, vt, cut, 50);
539
540 // K10 minimum cut = 9 (isolate one vertex)
541 EXPECT_EQ(min_cut, 9);
542 EXPECT_EQ(vs.size() + vt.size(), 10u);
543}
544
545// Custom arc filter that excludes arcs with weight > threshold
546template <class GT>
548{
550
551 Weight_Filter(int t = 5) : threshold(t) {}
552
553 bool operator()(typename GT::Arc * a) const
554 {
555 return a->get_info() <= threshold;
556 }
557};
558
559// Test arc filter functionality
561{
562 // Create two clusters connected by both low and high weight edges
563 Grafo g;
564 auto n0 = g.insert_node(0);
565 auto n1 = g.insert_node(1);
566 auto n2 = g.insert_node(2);
567 auto n3 = g.insert_node(3);
568
569 // Cluster 1: n0-n1 (low weight)
570 g.insert_arc(n0, n1, 1);
571
572 // Cluster 2: n2-n3 (low weight)
573 g.insert_arc(n2, n3, 2);
574
575 // Cross-cluster connections:
576 // - Low weight bridge (will be included): n1-n2
577 g.insert_arc(n1, n2, 3);
578 // - High weight bridge (will be filtered): n0-n3
579 g.insert_arc(n0, n3, 10);
580
581 // Without filter: min cut could use either bridge
582 // With filter (threshold=5): only see low weight edges
583 // Graph becomes: n0-n1-n2-n3 path, min cut = 1
584
587
588 DynList<Node*> vs, vt;
589 DynList<Arc*> cut;
590
591 size_t min_cut = karger_filtered(g, vs, vt, cut, 100);
592
593 // Path graph has min cut = 1
594 EXPECT_EQ(min_cut, 1u);
595
596 // All cut edges should have weight <= threshold
597 for (auto it = cut.get_it(); it.has_curr(); it.next())
598 EXPECT_LE(it.get_curr()->get_info(), 5);
599}
600
602{
603 Grafo g;
604 auto n0 = g.insert_node(0);
605 auto n1 = g.insert_node(1);
606 auto n2 = g.insert_node(2);
607
608 g.insert_arc(n0, n1, 10);
609 g.insert_arc(n1, n2, 10);
610
613
614 DynList<Node*> vs, vt;
615 DynList<Arc*> cut;
616
617 EXPECT_THROW(karger_filtered(g, vs, vt, cut, 10), std::domain_error);
618 EXPECT_THROW(karger_filtered.compute_min_cut_size(g, 10), std::domain_error);
619 EXPECT_THROW(karger_filtered.fast(g, vs, vt, cut, 1), std::domain_error);
620}
621
622// Test that default filter includes all arcs
624{
625 Grafo g;
626 auto n0 = g.insert_node(0);
627 auto n1 = g.insert_node(1);
628 auto n2 = g.insert_node(2);
629
630 g.insert_arc(n0, n1, 100);
631 g.insert_arc(n1, n2, 100);
632 g.insert_arc(n0, n2, 100);
633
634 // Default filter should include all arcs regardless of weight
636
637 DynList<Node*> vs, vt;
638 DynList<Arc*> cut;
639
640 size_t min_cut = karger(g, vs, vt, cut, 100);
641
642 // Triangle min cut = 2
643 EXPECT_EQ(min_cut, 2u);
644}
645
646// Test arc filter with explicit SA template parameter
648{
649 Grafo g;
650 auto n0 = g.insert_node(0);
651 auto n1 = g.insert_node(1);
652 g.insert_arc(n0, n1, 1);
653
654 // Explicit default filter
657
658 DynList<Node*> vs, vt;
659 DynList<Arc*> cut;
660
661 size_t min_cut = karger(g, vs, vt, cut, 10);
662
663 EXPECT_EQ(min_cut, 1u);
664}
665
666// Test that cut edges actually cross the partition
668{
669 auto g = create_barbell();
671
672 DynList<Node*> vs, vt;
673 DynList<Arc*> cut;
674
675 karger(g, vs, vt, cut, 100);
676
677 // Build sets for fast lookup
679 for (auto it = vs.get_it(); it.has_curr(); it.next())
680 vs_set.insert(it.get_curr());
681 for (auto it = vt.get_it(); it.has_curr(); it.next())
682 vt_set.insert(it.get_curr());
683
684 // Every cut edge should have one endpoint in vs and one in vt
685 for (auto it = cut.get_it(); it.has_curr(); it.next())
686 {
687 auto arc = it.get_curr();
688 auto src = g.get_src_node(arc);
689 auto tgt = g.get_tgt_node(arc);
690
691 bool src_in_vs = vs_set.has(src);
692 bool tgt_in_vs = vs_set.has(tgt);
693 bool src_in_vt = vt_set.has(src);
694 bool tgt_in_vt = vt_set.has(tgt);
695
696 // Edge crosses partition: one end in vs, other in vt
698 }
699}
700
701// Test graph with disconnected component would still work if filter excludes it
703{
704 // Star graph: center connected to all others
705 Grafo g;
706 auto center = g.insert_node(0);
707 vector<Node*> leaves;
708
709 for (int i = 1; i <= 5; ++i)
710 {
711 auto leaf = g.insert_node(i);
712 leaves.push_back(leaf);
713 g.insert_arc(center, leaf, 1);
714 }
715
717 DynList<Node*> vs, vt;
718 DynList<Arc*> cut;
719
720 size_t min_cut = karger(g, vs, vt, cut, 100);
721
722 // Star graph min cut is 1 (separate any leaf)
723 EXPECT_EQ(min_cut, 1u);
724}
725
726// Test with zero iterations returns max value (no cut found)
728{
729 auto g = create_triangle();
731
732 DynList<Node*> vs, vt;
733 DynList<Arc*> cut;
734
735 // With 0 iterations, should return max size_t (no cut found)
736 size_t result = karger(g, vs, vt, cut, 0);
737
738 // Result should be very large (max size_t)
739 EXPECT_GT(result, 1000000u);
740
741 // Partitions and cut should be empty (no iteration ran)
742 EXPECT_TRUE(vs.is_empty());
744 EXPECT_TRUE(cut.is_empty());
745}
746
747// Test that class is not copyable (compile-time check would fail if tried)
748// This is a documentation test - actual copy would fail to compile
750{
751 // Move construction should work
753 // Cannot test move directly as it's implicitly deleted when copy is deleted
754 // This test just verifies the basic construction works
756}
757
758// ============= Tests for fast() (Karger-Stein algorithm) =============
759// Note: Karger-Stein is probabilistic and may not find optimal in one run
760
762{
763 auto g = create_barbell();
765
766 DynList<Node*> vs, vt;
767 DynList<Arc*> cut;
768
769 size_t min_cut = karger.fast(g, vs, vt, cut);
770
771 // Fast version may not find optimal, but should find a valid cut
772 // Barbell has min cut = 1, but algorithm may return more
773 EXPECT_GE(min_cut, 1u); // At least 1
774 EXPECT_LE(min_cut, 7u); // At most all edges except internal cluster edges
775 EXPECT_EQ(cut.size(), min_cut);
776 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
777}
778
780{
781 auto g = create_path(8);
783
784 DynList<Node*> vs, vt;
785 DynList<Arc*> cut;
786
787 size_t min_cut = karger.fast(g, vs, vt, cut);
788
789 // Path has min cut = 1
790 EXPECT_EQ(min_cut, 1u);
791}
792
794{
795 auto g = create_cycle(8);
797
798 DynList<Node*> vs, vt;
799 DynList<Arc*> cut;
800
801 size_t min_cut = karger.fast(g, vs, vt, cut);
802
803 // Cycle has min cut = 2
804 EXPECT_EQ(min_cut, 2u);
805}
806
808{
809 const size_t n = 8;
810 auto g = create_complete_graph(static_cast<int>(n));
812
813 DynList<Node*> vs, vt;
814 DynList<Arc*> cut;
815
816 size_t min_cut = karger.fast(g, vs, vt, cut);
817
818 // K_n min cut is n-1; upper bound is floor(n^2/4) for a 2-partition
819 const size_t max_cut = (n / 2) * (n - (n / 2));
820 EXPECT_GE(min_cut, n - 1);
822}
823
825{
826 auto g = create_barbell();
828
829 DynList<Node*> vs, vt;
830 DynList<Arc*> cut;
831
832 karger.fast(g, vs, vt, cut);
833
834 // Check partitions cover all nodes
835 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
836
837 // Check no overlap
839 for (auto it = vs.get_it(); it.has_curr(); it.next())
840 vs_set.insert(it.get_curr());
841
842 for (auto it = vt.get_it(); it.has_curr(); it.next())
843 EXPECT_FALSE(vs_set.has(it.get_curr()));
844}
845
847{
848 auto g = create_barbell();
850
851 DynList<Node*> vs, vt;
852 DynList<Arc*> cut;
853
854 karger.fast(g, vs, vt, cut);
855
856 // Build sets for validation
858 for (auto it = vs.get_it(); it.has_curr(); it.next())
859 vs_set.insert(it.get_curr());
860 for (auto it = vt.get_it(); it.has_curr(); it.next())
861 vt_set.insert(it.get_curr());
862
863 // Every cut edge should cross the partition
864 for (auto it = cut.get_it(); it.has_curr(); it.next())
865 {
866 auto arc = it.get_curr();
867 auto src = g.get_src_node(arc);
868 auto tgt = g.get_tgt_node(arc);
869
870 bool crosses = (vs_set.has(src) && vt_set.has(tgt)) ||
871 (vt_set.has(src) && vs_set.has(tgt));
873 }
874}
875
877{
878 auto g = create_barbell();
880
881 DynList<Node*> vs, vt;
882 DynList<Arc*> cut;
883
884 // Test with explicit num_iter
885 size_t min_cut = karger.fast(g, vs, vt, cut, 10);
886
887 EXPECT_GE(min_cut, 1u);
890}
891
892// ================================================================
893// Tests for new features: get_seed(), move semantics, early termination
894// ================================================================
895
897{
899 EXPECT_EQ(karger1.get_seed(), 12345ul);
900
902 EXPECT_EQ(karger2.get_seed(), 99999ul);
903}
904
906{
908 unsigned long seed = original.get_seed();
909
911
912 // Moved object has the seed
913 EXPECT_EQ(moved.get_seed(), seed);
914
915 // Can use moved object
916 auto g = create_triangle();
917 DynList<Node*> vs, vt;
918 DynList<Arc*> cut;
919 EXPECT_NO_THROW(moved(g, vs, vt, cut, 1));
920}
921
923{
926
927 karger1 = std::move(karger2);
928
929 EXPECT_EQ(karger1.get_seed(), 222ul);
930
931 // Can use karger1 after move assignment
932 auto g = create_triangle();
933 DynList<Node*> vs, vt;
934 DynList<Arc*> cut;
935 EXPECT_NO_THROW(karger1(g, vs, vt, cut, 1));
936}
937
939{
940 // Create a path graph (line): A - B - C - D
941 // The minimum cut is 1 (any edge)
942 Grafo g;
943 auto a = g.insert_node(1);
944 auto b = g.insert_node(2);
945 auto c = g.insert_node(3);
946 auto d = g.insert_node(4);
947
948 g.insert_arc(a, b);
949 g.insert_arc(b, c);
950 g.insert_arc(c, d);
951
953 DynList<Node*> vs, vt;
954 DynList<Arc*> cut;
955
956 // Should find min cut of 1 and terminate early
957 size_t min_cut = karger(g, vs, vt, cut, 100);
958
959 EXPECT_EQ(min_cut, 1u);
960 EXPECT_EQ(cut.size(), 1u);
961 EXPECT_EQ(vs.size() + vt.size(), 4u);
962}
963
965{
966 // Path graph
967 Grafo g;
968 auto a = g.insert_node(1);
969 auto b = g.insert_node(2);
970 auto c = g.insert_node(3);
971 auto d = g.insert_node(4);
972
973 g.insert_arc(a, b);
974 g.insert_arc(b, c);
975 g.insert_arc(c, d);
976
978 DynList<Node*> vs, vt;
979 DynList<Arc*> cut;
980
981 size_t min_cut = karger.fast(g, vs, vt, cut);
982
983 EXPECT_EQ(min_cut, 1u);
984 EXPECT_EQ(cut.size(), 1u);
985}
986
987// ================================================================
988// Tests for reseed() and compute_min_cut_size()
989// ================================================================
990
992{
993 auto g = create_complete_graph(6);
994
998 karger(g, vs1, vt1, cut1, 5);
999
1000 // Reseed and run again
1001 karger.reseed(12345);
1002 EXPECT_EQ(karger.get_seed(), 12345ul);
1003
1006 karger(g, vs2, vt2, cut2, 5);
1007
1008 // Both should find valid cuts (we can't guarantee different results
1009 // due to randomness, but both should work)
1012}
1013
1015{
1016 // The algorithm now uses a deterministic arc index (vector-based with
1017 // insertion order), so same seed should produce identical results.
1018
1019 auto g = create_barbell();
1020
1023
1024 // Reseed karger2 to same seed as karger1
1025 karger2.reseed(999);
1026 EXPECT_EQ(karger1.get_seed(), karger2.get_seed());
1027
1030
1031 size_t result1 = karger1(g, vs1, vt1, cut1, 10);
1032 size_t result2 = karger2(g, vs2, vt2, cut2, 10);
1033
1034 // With same seed and deterministic ordering, results must be identical
1036 EXPECT_EQ(cut1.size(), cut2.size());
1037 EXPECT_EQ(vs1.size(), vs2.size());
1038 EXPECT_EQ(vt1.size(), vt2.size());
1039}
1040
1042{
1043 auto g = create_barbell();
1045
1046 size_t min_cut = karger.compute_min_cut_size(g, 50);
1047
1048 // Barbell has min cut of 1
1049 EXPECT_EQ(min_cut, 1u);
1050}
1051
1053{
1054 auto g = create_triangle();
1056
1057 // Call with default iterations (0 means use n^2)
1058 size_t min_cut = karger.compute_min_cut_size(g);
1059
1060 // Triangle has min cut of 2
1061 EXPECT_EQ(min_cut, 2u);
1062}
1063
1065{
1068
1069 EXPECT_THROW(karger.compute_min_cut_size(empty_graph), std::domain_error);
1070}
1071
1073{
1074 auto g = create_complete_graph(5);
1077
1078 DynList<Node*> vs, vt;
1079 DynList<Arc*> cut;
1080
1081 size_t full_result = karger1(g, vs, vt, cut, 50);
1082 size_t size_only = karger2.compute_min_cut_size(g, 50);
1083
1084 // With same seed and iterations, should get same result
1086}
Karger's randomized min-cut algorithm.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
Graph create_triangle()
Creates a triangle (K_3) - the simplest non-bipartite graph.
Generic directed graph (digraph) wrapper template.
Definition graph-dry.H:3848
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
Dynamic set backed by balanced binary search trees with automatic memory management.
Key * insert(const Key &key)
Inserts a key into the dynamic set.
bool has(const Key &key) const
Arc for graphs implemented with simple adjacency lists.
Definition tpl_sgraph.H:197
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Karger's randomized minimum cut algorithm.
Definition Karger.H:158
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
__Graph_Arc Arc
Definition tpl_sgraph.H:277
__Graph_Node Node
Definition tpl_sgraph.H:276
ArcInfo & get_info() noexcept
Return a modifiable reference to the arc data.
Definition graph-dry.H:595
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:190
#define TEST(name)
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Container2< typename Container1::Item_Type > filter(Container1 &container, Operation &operation)
Filter elements that satisfy operation.
Net::Flow_Type min_cut(Net &net, DynSetTree< typename Net::Node * > &vs, DynSetTree< typename Net::Node * > &vt, DynList< typename Net::Arc * > &cuts, DynList< typename Net::Arc * > &cutt)
Compute max flow and the corresponding minimum cut.
Definition tpl_net.H:1864
DynList< T > maps(const C &c, Op op)
Classic map operation.
STL namespace.
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
Weight_Filter(int t=5)
Definition karger.cc:551
bool operator()(typename GT::Arc *a) const
Definition karger.cc:553
GT create_cycle(size_t n)
Simple graph implementation with adjacency lists.