Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
stoer_wagner.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#include <gtest/gtest.h>
33#include <Stoer_Wagner.H>
34#include <tpl_graph.H>
35#include <tpl_sgraph.H>
36
37using namespace Aleph;
38using namespace std;
39
40// ============================================================================
41// Graph Types
42// ============================================================================
43
48
49// ============================================================================
50// Helper Graph Builders
51// ============================================================================
52
53namespace {
54
55// Create a simple triangle graph (3 nodes, 3 edges)
57{
58 IntGraph g;
59 auto n0 = g.insert_node(0);
60 auto n1 = g.insert_node(1);
61 auto n2 = g.insert_node(2);
62
63 g.insert_arc(n0, n1, 1);
64 g.insert_arc(n1, n2, 1);
65 g.insert_arc(n0, n2, 1);
66
67 return g;
68}
69
70// Create a square graph (4 nodes, 4 edges)
71IntGraph create_square()
72{
73 IntGraph 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 barbell graph: two K_k cliques connected by a single edge
88// Min-cut: 1 (the bridge)
90{
91 IntGraph g;
92 vector<IntGraph::Node*> left(k), right(k);
93
94 for (int i = 0; i < k; ++i)
95 {
96 left[i] = g.insert_node(i);
97 right[i] = g.insert_node(k + i);
98 }
99
100 // Left clique
101 for (int i = 0; i < k; ++i)
102 for (int j = i + 1; j < k; ++j)
103 g.insert_arc(left[i], left[j], 1);
104
105 // Right clique
106 for (int i = 0; i < k; ++i)
107 for (int j = i + 1; j < k; ++j)
108 g.insert_arc(right[i], right[j], 1);
109
110 // Bridge
111 g.insert_arc(left[0], right[0], 1);
112
113 return g;
114}
115
116// Create a path graph: 0 - 1 - 2 - ... - (n-1)
117// Min-cut: 1 (any edge)
119{
120 IntGraph g;
122
123 for (int i = 0; i < n; ++i)
124 nodes[i] = g.insert_node(i);
125
126 for (int i = 0; i < n - 1; ++i)
127 g.insert_arc(nodes[i], nodes[i + 1], 1);
128
129 return g;
130}
131
132// Create a cycle graph: 0 - 1 - 2 - ... - (n-1) - 0
133// Min-cut: 2 (need to cut two edges)
135{
136 IntGraph g;
138
139 for (int i = 0; i < n; ++i)
140 nodes[i] = g.insert_node(i);
141
142 for (int i = 0; i < n; ++i)
143 g.insert_arc(nodes[i], nodes[(i + 1) % n], 1);
144
145 return g;
146}
147
148// Create a complete graph K_n
149// Min-cut: n-1 (isolate any single vertex)
150IntGraph create_complete_graph(int n)
151{
152 IntGraph g;
154
155 for (int i = 0; i < n; ++i)
156 nodes[i] = g.insert_node(i);
157
158 for (int i = 0; i < n; ++i)
159 for (int j = i + 1; j < n; ++j)
160 g.insert_arc(nodes[i], nodes[j], 1);
161
162 return g;
163}
164
165// Create a star graph: center connected to all others
166// Min-cut: 1 (any spoke)
168{
169 IntGraph g;
170 auto center = g.insert_node(0);
171
172 for (int i = 1; i < n; ++i)
173 {
174 auto leaf = g.insert_node(i);
175 g.insert_arc(center, leaf, 1);
176 }
177
178 return g;
179}
180
181// Create two clusters connected by k edges
182// Min-cut: k * weight_per_edge
184{
185 IntGraph g;
187
188 for (int i = 0; i < cluster_size; ++i)
189 {
190 left[i] = g.insert_node(i);
191 right[i] = g.insert_node(cluster_size + i);
192 }
193
194 // Left cluster (fully connected with high weight)
195 for (int i = 0; i < cluster_size; ++i)
196 for (int j = i + 1; j < cluster_size; ++j)
197 g.insert_arc(left[i], left[j], 100);
198
199 // Right cluster (fully connected with high weight)
200 for (int i = 0; i < cluster_size; ++i)
201 for (int j = i + 1; j < cluster_size; ++j)
202 g.insert_arc(right[i], right[j], 100);
203
204 // Bridge edges with specified weight
205 for (int i = 0; i < bridge_count; ++i)
206 g.insert_arc(left[i % cluster_size], right[i % cluster_size], weight);
207
208 return g;
209}
210
211// Create a weighted chain: A -w1- B -w2- C -w3- D
213{
215 auto a = g.insert_node("A");
216 auto b = g.insert_node("B");
217 auto c = g.insert_node("C");
218 auto d = g.insert_node("D");
219
220 g.insert_arc(a, b, w1);
221 g.insert_arc(b, c, w2);
222 g.insert_arc(c, d, w3);
223
224 return g;
225}
226
227} // namespace
228
229// ============================================================================
230// Construction Tests
231// ============================================================================
232
237
239{
240 struct DoubleWeight
241 {
242 using Distance_Type = int;
243 int operator()(IntGraph::Arc* a) const { return a->get_info() * 2; }
244 };
245
247}
248
249// ============================================================================
250// Error Handling Tests
251// ============================================================================
252
254{
255 IntGraph g;
256 g.insert_node(0);
257
261
262 EXPECT_THROW(sw(g, vs, vt, cut), std::domain_error);
263}
264
266{
267 IntGraph g;
268 auto n0 = g.insert_node(0);
269 auto n1 = g.insert_node(1);
270 auto n2 = g.insert_node(2);
271 auto n3 = g.insert_node(3);
272
273 // Two disconnected components: {0,1} and {2,3}
274 g.insert_arc(n0, n1, 1);
275 g.insert_arc(n2, n3, 1);
276
280
281 // Disconnected graph has min-cut = 0
282 int min_cut = sw(g, vs, vt, cut);
283 EXPECT_EQ(min_cut, 0);
284 EXPECT_TRUE(cut.is_empty());
285}
286
300
301// ============================================================================
302// Basic Min-Cut Tests
303// ============================================================================
304
306{
307 IntGraph g;
308 auto n0 = g.insert_node(0);
309 auto n1 = g.insert_node(1);
310 g.insert_arc(n0, n1, 5);
311
315
316 int min_cut = sw(g, vs, vt, cut);
317
318 EXPECT_EQ(min_cut, 5);
319 EXPECT_EQ(cut.size(), 1u);
320 EXPECT_EQ(vs.size(), 1u);
321 EXPECT_EQ(vt.size(), 1u);
322}
323
325{
326 auto g = create_triangle();
328
331
332 int min_cut = sw(g, vs, vt, cut);
333
334 // Min-cut of triangle: isolate one vertex, cut 2 edges
335 EXPECT_EQ(min_cut, 2);
336 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
337}
338
340{
341 auto g = create_square();
343
346
347 int min_cut = sw(g, vs, vt, cut);
348
349 // Min-cut of square: any 2 edges that disconnect
350 EXPECT_EQ(min_cut, 2);
351}
352
354{
355 auto g = create_barbell(4);
357
360
361 int min_cut = sw(g, vs, vt, cut);
362
363 // Min-cut is the single bridge
364 EXPECT_EQ(min_cut, 1);
365 EXPECT_EQ(cut.size(), 1u);
366}
367
369{
370 auto g = create_path(6);
372
375
376 int min_cut = sw(g, vs, vt, cut);
377
378 // Path graph has min-cut = 1
379 EXPECT_EQ(min_cut, 1);
380 EXPECT_EQ(cut.size(), 1u);
381}
382
384{
385 auto g = create_cycle(6);
387
390
391 int min_cut = sw(g, vs, vt, cut);
392
393 // Cycle has min-cut = 2
394 EXPECT_EQ(min_cut, 2);
395 EXPECT_EQ(cut.size(), 2u);
396}
397
399{
400 auto g = create_star(6);
402
405
406 int min_cut = sw(g, vs, vt, cut);
407
408 // Star graph min-cut = 1 (any leaf)
409 EXPECT_EQ(min_cut, 1);
410}
411
412// ============================================================================
413// Complete Graph Tests
414// ============================================================================
415
417{
418 auto g = create_complete_graph(3);
420
423
424 int min_cut = sw(g, vs, vt, cut);
425
426 // K3 min-cut = 2 (isolate one vertex)
427 EXPECT_EQ(min_cut, 2);
428}
429
431{
432 auto g = create_complete_graph(4);
434
437
438 int min_cut = sw(g, vs, vt, cut);
439
440 // K4 min-cut = 3 (isolate one vertex)
441 EXPECT_EQ(min_cut, 3);
442}
443
445{
446 auto g = create_complete_graph(5);
448
451
452 int min_cut = sw(g, vs, vt, cut);
453
454 // K5 min-cut = 4 (isolate one vertex)
455 EXPECT_EQ(min_cut, 4);
456}
457
459{
460 auto g = create_complete_graph(6);
462
465
466 int min_cut = sw(g, vs, vt, cut);
467
468 // K6 min-cut = 5 (isolate one vertex)
469 EXPECT_EQ(min_cut, 5);
470}
471
472// ============================================================================
473// Weighted Graph Tests
474// ============================================================================
475
477{
478 auto g = create_weighted_chain(10, 1, 10);
480
483
484 int min_cut = sw(g, vs, vt, cut);
485
486 // Middle edge (weight 1) is the weakest
487 EXPECT_EQ(min_cut, 1);
488}
489
502
515
517{
518 auto g = create_two_clusters(4, 2, 1); // 2 bridges with weight 1 each
520
523
524 int min_cut = sw(g, vs, vt, cut);
525
526 // Min-cut: 2 bridges * weight 1 = 2
527 EXPECT_EQ(min_cut, 2);
528 EXPECT_EQ(cut.size(), 2u);
529}
530
532{
533 auto g = create_two_clusters(4, 1, 50); // 1 bridge with weight 50
535
538
539 int min_cut = sw(g, vs, vt, cut);
540
541 // Min-cut: 1 bridge * weight 50 = 50
542 EXPECT_EQ(min_cut, 50);
543 EXPECT_EQ(cut.size(), 1u);
544}
545
547{
549 auto a = g.insert_node("A");
550 auto b = g.insert_node("B");
551 auto c = g.insert_node("C");
552
553 // A-B: 1, B-C: 2, A-C: 3
554 g.insert_arc(a, b, 1);
555 g.insert_arc(b, c, 2);
556 g.insert_arc(a, c, 3);
557
561
562 int min_cut = sw(g, vs, vt, cut);
563
564 // Min-cut: isolate B, cut {A-B(1), B-C(2)} = 3
565 // Or isolate A, cut {A-B(1), A-C(3)} = 4
566 // Or isolate C, cut {B-C(2), A-C(3)} = 5
567 EXPECT_EQ(min_cut, 3);
568}
569
570// ============================================================================
571// Partition Validity Tests
572// ============================================================================
573
575{
576 auto g = create_barbell(5);
578
581
582 sw(g, vs, vt, cut);
583
584 EXPECT_EQ(vs.size() + vt.size(), g.get_num_nodes());
585}
586
588{
589 auto g = create_complete_graph(5);
591
594
595 sw(g, vs, vt, cut);
596
599}
600
602{
603 auto g = create_cycle(8);
605
608
609 sw(g, vs, vt, cut);
610
612 for (auto it = vs.get_it(); it.has_curr(); it.next())
613 vs_set.insert(it.get_curr());
614
615 for (auto it = vt.get_it(); it.has_curr(); it.next())
616 EXPECT_FALSE(vs_set.has(it.get_curr()));
617}
618
620{
621 auto g = create_barbell(4);
623
626
627 sw(g, vs, vt, cut);
628
630 for (auto it = vs.get_it(); it.has_curr(); it.next())
631 vs_set.insert(it.get_curr());
632 for (auto it = vt.get_it(); it.has_curr(); it.next())
633 vt_set.insert(it.get_curr());
634
635 for (auto it = cut.get_it(); it.has_curr(); it.next())
636 {
637 auto arc = it.get_curr();
638 auto src = g.get_src_node(arc);
639 auto tgt = g.get_tgt_node(arc);
640
641 bool crosses = (vs_set.has(src) && vt_set.has(tgt)) ||
642 (vt_set.has(src) && vs_set.has(tgt));
644 }
645}
646
647// ============================================================================
648// min_cut_weight Method Tests
649// ============================================================================
650
652{
653 auto g = create_barbell(4);
655
656 int weight = sw.min_cut_weight(g);
657
658 EXPECT_EQ(weight, 1);
659}
660
662{
663 auto g = create_weighted_chain(5, 2, 8);
665
666 int weight = sw.min_cut_weight(g);
667
668 EXPECT_EQ(weight, 2);
669}
670
672{
673 auto g = create_complete_graph(6);
674
678
679 int full_result = sw(g, vs, vt, cut);
680 int weight_only = sw.min_cut_weight(g);
681
683}
684
686{
687 IntGraph g;
688 g.insert_node(0);
689 g.insert_node(1);
690 // No edges
691
693 int weight = sw.min_cut_weight(g);
694
695 EXPECT_EQ(weight, 0);
696}
697
698// ============================================================================
699// Unit_Weight Functor Tests
700// ============================================================================
701
703{
704 IntGraph g;
705 auto n0 = g.insert_node(0);
706 auto n1 = g.insert_node(1);
707 auto n2 = g.insert_node(2);
708
709 // High weights that should be ignored
710 g.insert_arc(n0, n1, 999);
711 g.insert_arc(n1, n2, 888);
712
716
717 size_t min_cut = sw(g, vs, vt, cut);
718
719 // Path with unit weights: min-cut = 1
720 EXPECT_EQ(min_cut, 1u);
721}
722
724{
725 auto g = create_complete_graph(5);
727
730
731 size_t min_cut = sw(g, vs, vt, cut);
732
733 // K5 with unit weights: isolate one vertex = 4 edges
734 EXPECT_EQ(min_cut, 4u);
735}
736
737// ============================================================================
738// Custom Distance Functor Tests
739// ============================================================================
740
742{
743 struct DoubleWeight
744 {
745 using Distance_Type = int;
746 int operator()(IntGraph::Arc* a) const { return a->get_info() * 2; }
747 };
748
749 auto g = create_path(3); // 0-1-2 with weight 1 each
751
754
755 int min_cut = sw(g, vs, vt, cut);
756
757 // Original weight 1, doubled = 2
758 EXPECT_EQ(min_cut, 2);
759}
760
762{
763 struct ConstWeight
764 {
765 using Distance_Type = int;
766 int operator()(IntGraph::Arc*) const { return 7; }
767 };
768
769 auto g = create_triangle();
771
774
775 int min_cut = sw(g, vs, vt, cut);
776
777 // Triangle min-cut: 2 edges * constant 7 = 14
778 EXPECT_EQ(min_cut, 14);
779}
780
781// ============================================================================
782// Arc Filter Tests
783// ============================================================================
784
785template <class GT>
786struct WeightFilter
787{
788 int threshold;
789
790 WeightFilter(int t = 5) : threshold(t) {}
791
792 bool operator()(typename GT::Arc* a) const
793 {
794 return a->get_info() <= threshold;
795 }
796};
797
799{
800 IntGraph g;
801 auto n0 = g.insert_node(0);
802 auto n1 = g.insert_node(1);
803 auto n2 = g.insert_node(2);
804 auto n3 = g.insert_node(3);
805
806 // Path: 0 -1- 1 -1- 2 -1- 3
807 g.insert_arc(n0, n1, 1);
808 g.insert_arc(n1, n2, 1);
809 g.insert_arc(n2, n3, 1);
810
811 // High-weight edge that should be filtered
812 g.insert_arc(n0, n3, 10);
813
816
819
820 int min_cut = sw(g, vs, vt, cut);
821
822 // Without filter: n0-n3 edge would create a cycle
823 // With filter: graph is a path, min-cut = 1
824 EXPECT_EQ(min_cut, 1);
825}
826
827// ============================================================================
828// Different Graph Types
829// ============================================================================
830
832{
833 SGraph g;
834 auto n0 = g.insert_node(0);
835 auto n1 = g.insert_node(1);
836 auto n2 = g.insert_node(2);
837
838 g.insert_arc(n0, n1, 1);
839 g.insert_arc(n1, n2, 1);
840 g.insert_arc(n0, n2, 1);
841
845
846 int min_cut = sw(g, vs, vt, cut);
847
848 EXPECT_EQ(min_cut, 2);
849}
850
852{
853 DoubleGraph g;
854 auto n0 = g.insert_node(0);
855 auto n1 = g.insert_node(1);
856 auto n2 = g.insert_node(2);
857
858 g.insert_arc(n0, n1, 1.5);
859 g.insert_arc(n1, n2, 2.5);
860 g.insert_arc(n0, n2, 0.5);
861
862 struct DoubleDistance
863 {
864 using Distance_Type = double;
865 double operator()(DoubleGraph::Arc* a) const { return a->get_info(); }
866 };
867
871
872 double min_cut = sw(g, vs, vt, cut);
873
874 // Min-cut: isolate n2 via edge 0.5 + 2.5 = 3.0
875 // Or isolate n0 via edge 1.5 + 0.5 = 2.0
876 // Or isolate n1 via edge 1.5 + 2.5 = 4.0
878}
879
880// ============================================================================
881// Edge Cases
882// ============================================================================
883
885{
886 IntGraph g;
887 auto n0 = g.insert_node(0);
888 auto n1 = g.insert_node(1);
889 auto n2 = g.insert_node(2);
890
891 g.insert_arc(n0, n1, 0); // Zero weight
892 g.insert_arc(n1, n2, 5);
893
897
898 int min_cut = sw(g, vs, vt, cut);
899
900 // Min-cut should use zero-weight edge
901 EXPECT_EQ(min_cut, 0);
902}
903
905{
906 auto g = create_complete_graph(4);
908
911
912 int min_cut = sw(g, vs, vt, cut);
913
914 // K4 min-cut = 3 (all edges weight 1)
915 EXPECT_EQ(min_cut, 3);
916}
917
919{
920 IntGraph g;
921 auto n0 = g.insert_node(0);
922 auto n1 = g.insert_node(1);
923 auto n2 = g.insert_node(2);
924
925 g.insert_arc(n0, n1, 1000000);
926 g.insert_arc(n1, n2, 1);
927
931
932 int min_cut = sw(g, vs, vt, cut);
933
934 EXPECT_EQ(min_cut, 1);
935}
936
937// ============================================================================
938// Performance Tests
939// ============================================================================
940
942{
943 IntGraph g;
944 const int N = 50;
946
947 for (int i = 0; i < N; ++i)
948 nodes[i] = g.insert_node(i);
949
950 // Create a connected graph with path + extra edges
951 for (int i = 0; i < N - 1; ++i)
952 g.insert_arc(nodes[i], nodes[i + 1], 1);
953
954 // Add some cross edges
955 for (int i = 0; i < N; i += 5)
956 for (int j = i + 10; j < N; j += 10)
957 g.insert_arc(nodes[i], nodes[j], 1);
958
962
963 EXPECT_NO_THROW(sw(g, vs, vt, cut));
964 EXPECT_GT(vs.size(), 0u);
965 EXPECT_GT(vt.size(), 0u);
966}
967
969{
970 auto g = create_complete_graph(20);
972
975
976 int min_cut = sw(g, vs, vt, cut);
977
978 // K20 min-cut = 19
979 EXPECT_EQ(min_cut, 19);
980}
981
982// ============================================================================
983// Determinism Test
984// ============================================================================
985
987{
988 auto g = create_complete_graph(8);
990
993
994 int result1 = sw(g, vs1, vt1, cut1);
995 int result2 = sw(g, vs2, vt2, cut2);
996
997 // Stoer-Wagner is deterministic
1000}
Stoer-Wagner deterministic min-cut algorithm.
Graph create_triangle()
Creates a triangle (K_3) - the simplest non-bipartite graph.
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.
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
virtual Node * insert_node(Node *node) noexcept
Insertion of a node already allocated.
Definition tpl_graph.H:524
Arc * insert_arc(Node *src_node, Node *tgt_node, void *a)
Definition tpl_graph.H:604
Graph class implemented with singly-linked adjacency lists.
Definition tpl_sgraph.H:274
Arc * insert_arc(Node *src, Node *tgt, void *a)
Definition tpl_sgraph.H:497
virtual Node * insert_node(Node *p)
Insertion of a node whose memory has already been allocated.
Definition tpl_sgraph.H:487
Path on a graph.
Definition tpl_graph.H:2669
Stoer-Wagner deterministic minimum cut algorithm.
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)
#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
Container2< typename Container1::Item_Type > filter(Container1 &container, Operation &operation)
Filter elements that satisfy operation.
@ Cycle
Graph has an Eulerian cycle.
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.
Arc of graph implemented with double-linked adjacency lists.
Definition tpl_graph.H:222
Distance_Type operator()(SimpleGraph::Arc *arc) const
double Distance_Type
WeightFilter(int t=5)
bool operator()(typename GT::Arc *a) const
GT create_cycle(size_t n)
Generic graph and digraph implementations.
Simple graph implementation with adjacency lists.