Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_maxflow_test.cc
Go to the documentation of this file.
1/* Tests for tpl_maxflow.H - Advanced maximum flow algorithms
2 *
3 * Tests cover:
4 * - Dinic's algorithm
5 * - Capacity scaling
6 * - Flow decomposition
7 * - HLPP (Highest Label Preflow-Push)
8 * - Flow statistics
9 */
10
11#include <gtest/gtest.h>
12#include <tpl_maxflow.H>
13#include <tpl_net.H>
14#include <chrono>
15#include <random>
16#include <stdexcept>
17
18using namespace Aleph;
19
20namespace
21{
22 // Network type for tests
24 using Node = TestNet::Node;
25 using Arc = TestNet::Arc;
26
27 // Build a simple linear network: s -> a -> b -> t
28 // Returns the source node
30 {
31 auto s = net.insert_node(0);
32 auto a = net.insert_node(1);
33 auto b = net.insert_node(2);
34 auto t = net.insert_node(3);
35
36 net.insert_arc(s, a, 10); // cap=10
37 net.insert_arc(a, b, 5); // cap=5 (bottleneck)
38 net.insert_arc(b, t, 10); // cap=10
39
40 return s;
41 }
42
43 // Build a diamond network: s -> a,b -> t
44 Node* build_diamond_network(TestNet& net)
45 {
46 auto s = net.insert_node(0);
47 auto a = net.insert_node(1);
48 auto b = net.insert_node(2);
49 auto t = net.insert_node(3);
50
51 net.insert_arc(s, a, 10); // cap=10
52 net.insert_arc(s, b, 10); // cap=10
53 net.insert_arc(a, t, 10); // cap=10
54 net.insert_arc(b, t, 10); // cap=10
55
56 return s;
57 }
58
59 // Build a complex network with multiple paths
61 {
62 auto s = net.insert_node(0);
63 auto a = net.insert_node(1);
64 auto b = net.insert_node(2);
65 auto c = net.insert_node(3);
66 auto d = net.insert_node(4);
67 auto t = net.insert_node(5);
68
69 net.insert_arc(s, a, 16);
70 net.insert_arc(s, b, 13);
71 net.insert_arc(a, b, 10);
72 net.insert_arc(a, c, 12);
73 net.insert_arc(b, a, 4);
74 net.insert_arc(b, d, 14);
75 net.insert_arc(c, b, 9);
76 net.insert_arc(c, t, 20);
77 net.insert_arc(d, c, 7);
78 net.insert_arc(d, t, 4);
79
80 return s;
81 }
82
83 // Build a grid network with directed arcs (flow only goes right and down)
84 // This ensures single source (top-left) and single sink (bottom-right)
85 Node* build_grid_network(TestNet& net, int rows, int cols, double capacity)
86 {
87 std::vector<std::vector<Node*>> nodes(rows, std::vector<Node*>(cols));
88 int id = 0;
89
90 // Create nodes
91 for (int i = 0; i < rows; ++i)
92 for (int j = 0; j < cols; ++j)
93 nodes[i][j] = net.insert_node(id++);
94
95 // Create horizontal arcs (left to right only)
96 for (int i = 0; i < rows; ++i)
97 for (int j = 0; j < cols - 1; ++j)
98 net.insert_arc(nodes[i][j], nodes[i][j+1], capacity);
99
100 // Create vertical arcs (top to bottom only)
101 for (int i = 0; i < rows - 1; ++i)
102 for (int j = 0; j < cols; ++j)
103 net.insert_arc(nodes[i][j], nodes[i+1][j], capacity);
104
105 return nodes[0][0];
106 }
107
108 [[maybe_unused]] void reset_flow(TestNet& net)
109 {
110 for (Arc_Iterator<TestNet> it(net); it.has_curr(); it.next_ne())
111 it.get_curr()->flow = 0;
112 }
113}
114
115
116//==============================================================================
117// Dinic's Algorithm Tests
118//==============================================================================
119
121{
122 TestNet net;
124
125 const auto flow = dinic_maximum_flow(net);
126
127 EXPECT_DOUBLE_EQ(flow, 5.0); // Bottleneck is middle arc
129}
130
132{
133 TestNet net;
134 build_diamond_network(net);
135
136 const auto flow = dinic_maximum_flow(net);
137 (void)flow;
138
139 EXPECT_DOUBLE_EQ(flow, 20.0); // Both paths can carry 10
141}
142
144{
145 TestNet net;
147
148 auto flow = dinic_maximum_flow(net);
149
150 // Known max flow for this network is 23
151 EXPECT_DOUBLE_EQ(flow, 23.0);
153}
154
156{
157 // Test with zero capacity arc - should give zero flow
158 TestNet net;
159 auto s = net.insert_node(0);
160 auto t = net.insert_node(1);
161
162 net.insert_arc(s, t, 0); // Zero capacity
163
164 auto flow = dinic_maximum_flow(net);
165 EXPECT_DOUBLE_EQ(flow, 0.0);
166}
167
169{
170 TestNet net;
171 auto s = net.insert_node(0);
172 auto t = net.insert_node(1);
173 net.insert_arc(s, t, 42);
174
175 auto flow = dinic_maximum_flow(net);
176 EXPECT_DOUBLE_EQ(flow, 42.0);
177}
178
179
180//==============================================================================
181// Capacity Scaling Tests
182//==============================================================================
183
185{
186 TestNet net;
188
189 auto flow = capacity_scaling_maximum_flow(net);
190
191 EXPECT_DOUBLE_EQ(flow, 5.0);
193}
194
196{
197 TestNet net;
198 build_diamond_network(net);
199
200 auto flow = capacity_scaling_maximum_flow(net);
201
202 EXPECT_DOUBLE_EQ(flow, 20.0);
204}
205
207{
208 TestNet net;
210
211 auto flow = capacity_scaling_maximum_flow(net);
212
213 EXPECT_DOUBLE_EQ(flow, 23.0);
215}
216
218{
219 TestNet net;
220 auto s = net.insert_node(0);
221 auto a = net.insert_node(1);
222 auto t = net.insert_node(2);
223
224 net.insert_arc(s, a, 1000000);
225 net.insert_arc(a, t, 1000000);
226
227 auto flow = capacity_scaling_maximum_flow(net);
228 EXPECT_DOUBLE_EQ(flow, 1000000.0);
229}
230
231
232//==============================================================================
233// HLPP Tests
234//==============================================================================
235
237{
238 TestNet net;
240
241 auto flow = hlpp_maximum_flow(net);
242
243 EXPECT_DOUBLE_EQ(flow, 5.0);
245}
246
248{
249 TestNet net;
250 build_diamond_network(net);
251
252 auto flow = hlpp_maximum_flow(net);
253
254 EXPECT_DOUBLE_EQ(flow, 20.0);
256}
257
259{
260 TestNet net;
262
263 auto flow = hlpp_maximum_flow(net);
264
265 EXPECT_DOUBLE_EQ(flow, 23.0);
267}
268
269
270//==============================================================================
271// Flow Decomposition Tests
272//==============================================================================
273
275{
276 TestNet net;
279
280 auto decomp = decompose_flow(net);
281
282 EXPECT_EQ(decomp.num_paths(), 1);
283 EXPECT_DOUBLE_EQ(decomp.total_flow(), 5.0);
284}
285
287{
288 TestNet net;
289 build_diamond_network(net);
291
292 auto decomp = decompose_flow(net);
293
294 // Should decompose into 2 paths (through a and through b)
295 EXPECT_EQ(decomp.num_paths(), 2);
296 EXPECT_DOUBLE_EQ(decomp.total_flow(), 20.0);
297}
298
300{
301 TestNet net;
304
305 auto decomp = decompose_flow(net);
306
307 // Total flow should match
308 EXPECT_DOUBLE_EQ(decomp.total_flow(), 23.0);
309 // Should have multiple paths
310 EXPECT_GT(decomp.num_paths(), 1);
311}
312
314{
315 // Build a network with a path but zero flow
316 TestNet net;
317 auto s = net.insert_node(0);
318 auto a = net.insert_node(1);
319 auto t = net.insert_node(2);
320
321 net.insert_arc(s, a, 10);
322 net.insert_arc(a, t, 10);
323 // Don't run max-flow, so all arcs have 0 flow
324
325 auto decomp = decompose_flow(net);
326
327 EXPECT_EQ(decomp.num_paths(), 0);
328 EXPECT_DOUBLE_EQ(decomp.total_flow(), 0.0);
329}
330
332{
333 // Build a network with a cycle: s -> a -> b -> a (cycle a-b-a) -> t
334 TestNet net;
335 auto s = net.insert_node(0);
336 auto a = net.insert_node(1);
337 auto b = net.insert_node(2);
338 auto t = net.insert_node(3);
339
340 // Main path s -> a -> t
341 auto arc_sa = net.insert_arc(s, a, 10);
342 auto arc_at = net.insert_arc(a, t, 10);
343
344 // Cycle a -> b -> a
345 auto arc_ab = net.insert_arc(a, b, 5);
346 auto arc_ba = net.insert_arc(b, a, 5);
347
348 // Manually set flow to create a cycle
349 // Flow goes s -> a -> t (5 units)
350 // And circulates a -> b -> a (5 units)
351 arc_sa->flow = 5;
352 arc_at->flow = 5;
353 arc_ab->flow = 5;
354 arc_ba->flow = 5;
355
356 // Verify manual flow setup is valid (conservation holds)
357 // Node a: in=5(s)+5(b)=10, out=5(t)+5(b)=10. OK.
358 // Node b: in=5(a), out=5(a). OK.
359
360 auto decomp = decompose_flow(net);
361
362 // Should have 1 path (s->a->t) and 1 cycle (a->b->a)
363 EXPECT_EQ(decomp.num_paths(), 1);
364 EXPECT_EQ(decomp.num_cycles(), 1);
365
366 EXPECT_DOUBLE_EQ(decomp.total_flow(), 5.0); // Only path flow counts towards total s-t flow
367
368 // Verify cycle details
369 const auto& cycle = decomp.cycles.get_first();
370 EXPECT_DOUBLE_EQ(cycle.flow, 5.0);
371
372 // Verify path details
373 const auto& path = decomp.paths.get_first();
374 EXPECT_DOUBLE_EQ(path.flow, 5.0);
375}
376
377
378//==============================================================================
379// Flow Statistics Tests
380//==============================================================================
381
383{
384 TestNet net;
385 build_diamond_network(net);
386
387 auto stats = compute_flow_statistics(net);
388
389 EXPECT_DOUBLE_EQ(stats.total_capacity, 40.0);
390 EXPECT_EQ(stats.num_empty_arcs, 4);
391 EXPECT_EQ(stats.num_saturated_arcs, 0);
392 EXPECT_EQ(stats.num_partial_arcs, 0);
393}
394
396{
397 TestNet net;
398 build_diamond_network(net);
400
401 auto stats = compute_flow_statistics(net);
402
403 EXPECT_DOUBLE_EQ(stats.total_flow, 20.0);
404 EXPECT_DOUBLE_EQ(stats.utilization, 0.5); // 20/40
405}
406
407
408//==============================================================================
409// Min-Cut Validation Tests (Max-Flow Min-Cut Theorem)
410//==============================================================================
411
412// Helper function to compute min-cut capacity by BFS from source
413template <class Net>
415{
416 using Node = typename Net::Node;
417 using Arc = typename Net::Arc;
418
419 auto source = net.get_source();
420 (void)net.get_sink();
421
422 // Find reachable nodes from source via residual network
423 std::set<Node*> reachable;
425
426 reachable.insert(source);
427 q.put(source);
428
429 while (!q.is_empty())
430 {
431 Node* u = q.front();
432 q.get();
433
434 // Explore all adjacent arcs
435 for (typename Net::Node_Arc_Iterator it(u); it.has_curr(); it.next_ne())
436 {
437 Arc* arc = it.get_curr();
438 Node* v = net.get_connected_node(arc, u);
439
440 if (reachable.count(v) > 0)
441 continue; // Already visited
442
443 // Check if there is residual capacity
444 bool forward = (net.get_src_node(arc) == u);
445 double residual = forward ? (arc->cap - arc->flow) : arc->flow;
446
447 if (residual > 1e-9) // Has residual capacity
448 {
449 reachable.insert(v);
450 q.put(v);
451 }
452 }
453 }
454
455 // Compute cut capacity: sum of capacities of arcs from reachable to non-reachable
456 double cut_capacity = 0.0;
457
458 for (auto u : reachable)
459 {
460 for (Out_Iterator<Net> it(u); it.has_curr(); it.next_ne())
461 {
462 Arc* arc = it.get_curr();
463 Node* v = net.get_tgt_node(arc);
464
465 if (reachable.count(v) == 0) // Arc crosses the cut
466 cut_capacity += arc->cap;
467 }
468 }
469
470 return cut_capacity;
471}
472
473// Helper to verify max-flow = min-cut
474template <class Net>
475bool verify_max_flow_min_cut(Net& net, double max_flow)
476{
477 double min_cut = compute_min_cut_capacity(net);
478 return std::abs(max_flow - min_cut) < 1e-6;
479}
480
482{
483 TestNet net;
485
486 auto flow = dinic_maximum_flow(net);
487
488 // Verify max-flow = min-cut theorem
490
491 // The min-cut should be at the bottleneck (middle arc with cap=5)
492 double min_cut = compute_min_cut_capacity(net);
494}
495
497{
498 TestNet net;
499 build_diamond_network(net);
500
501 auto flow = dinic_maximum_flow(net);
502
504
505 double min_cut = compute_min_cut_capacity(net);
507}
508
510{
511 TestNet net;
513
514 auto flow = dinic_maximum_flow(net);
515
517
518 double min_cut = compute_min_cut_capacity(net);
520}
521
523{
524 TestNet net;
525 build_grid_network(net, 5, 5, 10);
526
527 auto flow = edmonds_karp_maximum_flow(net);
528
530}
531
533{
534 // Build network where min-cut is obvious
535 TestNet net;
536 auto s = net.insert_node(0);
537 auto a = net.insert_node(1);
538 auto b = net.insert_node(2);
539 auto t = net.insert_node(3);
540
541 // Wide source side, narrow bottleneck, wide sink side
542 net.insert_arc(s, a, 100);
543 net.insert_arc(s, b, 100);
544 net.insert_arc(a, t, 5); // Bottleneck
545 net.insert_arc(b, t, 5); // Bottleneck
546
547 auto flow = dinic_maximum_flow(net);
548
549 EXPECT_DOUBLE_EQ(flow, 10.0);
551
552 double min_cut = compute_min_cut_capacity(net);
554}
555
557{
558 TestNet net;
559 auto s = net.insert_node(0);
560 auto t = net.insert_node(1);
561
562 // Three parallel arcs
563 net.insert_arc(s, t, 10);
564 net.insert_arc(s, t, 20);
565 net.insert_arc(s, t, 30);
566
567 auto flow = dinic_maximum_flow(net);
568
569 EXPECT_DOUBLE_EQ(flow, 60.0);
571
572 double min_cut = compute_min_cut_capacity(net);
574}
575
606
608{
609 // Verify that the cut actually partitions the graph correctly
610 TestNet net;
611 build_diamond_network(net);
612
613 const auto flow = dinic_maximum_flow(net);
614 (void)flow;
615
616 auto source = net.get_source();
617 auto sink = net.get_sink();
618
619 // Find reachable set from source
620 std::set<Node*> S_side; // Source side of cut
622
623 S_side.insert(source);
624 q.put(source);
625
626 while (!q.is_empty())
627 {
628 Node* u = q.front();
629 q.get();
630
631 for (typename TestNet::Node_Arc_Iterator it(u); it.has_curr(); it.next_ne())
632 {
633 Arc* arc = it.get_curr();
634 Node* v = net.get_connected_node(arc, u);
635
636 if (S_side.count(v) > 0)
637 continue;
638
639 bool forward = (net.get_src_node(arc) == u);
640 double residual = forward ? (arc->cap - arc->flow) : arc->flow;
641
642 if (residual > 1e-9)
643 {
644 S_side.insert(v);
645 q.put(v);
646 }
647 }
648 }
649
650 // Source should be in S, sink should not be in S
651 EXPECT_TRUE(S_side.count(source) > 0);
652 EXPECT_FALSE(S_side.count(sink) > 0);
653
654 // Verify all arcs crossing the cut are saturated (forward) or empty (backward)
655 for (auto u : S_side)
656 {
657 for (Out_Iterator<TestNet> it(u); it.has_curr(); it.next_ne())
658 {
659 Arc* arc = it.get_curr();
660 Node* v = net.get_tgt_node(arc);
661
662 if (S_side.count(v) == 0) // Arc crosses the cut
663 {
664 // Forward arc crossing cut should be saturated
665 EXPECT_NEAR(arc->flow, arc->cap, 1e-6);
666 }
667 }
668
669 for (In_Iterator<TestNet> it(u); it.has_curr(); it.next_ne())
670 {
671 Arc* arc = it.get_curr();
672 Node* v = net.get_src_node(arc);
673
674 if (S_side.count(v) == 0) // Arc crosses the cut (backward)
675 {
676 // Backward arc crossing cut should have zero flow
677 EXPECT_NEAR(arc->flow, 0.0, 1e-6);
678 }
679 }
680 }
681}
682
683
684//==============================================================================
685// Algorithm Comparison Tests
686//==============================================================================
687
706
723
724
725//==============================================================================
726// Edge Cases
727//==============================================================================
728
730{
731 TestNet net;
732 auto s = net.insert_node(0);
733 auto t = net.insert_node(1);
734
735 // Multiple arcs between same nodes
736 net.insert_arc(s, t, 10);
737 net.insert_arc(s, t, 20);
738 net.insert_arc(s, t, 30);
739
740 auto flow = dinic_maximum_flow(net);
741 EXPECT_DOUBLE_EQ(flow, 60.0); // Sum of capacities
742}
743
745{
746 TestNet net;
747 auto s = net.insert_node(0);
748 auto a = net.insert_node(1);
749 auto t = net.insert_node(2);
750
751 net.insert_arc(s, a, 5); // Bottleneck at source
752 net.insert_arc(a, t, 100);
753
754 auto flow = dinic_maximum_flow(net);
755 EXPECT_DOUBLE_EQ(flow, 5.0);
756}
757
759{
760 TestNet net;
761 auto s = net.insert_node(0);
762 auto a = net.insert_node(1);
763 auto t = net.insert_node(2);
764
765 net.insert_arc(s, a, 100);
766 net.insert_arc(a, t, 5); // Bottleneck at sink
767
768 auto flow = dinic_maximum_flow(net);
769 EXPECT_DOUBLE_EQ(flow, 5.0);
770}
771
772
773//==============================================================================
774// Functor Tests
775//==============================================================================
776
778{
779 TestNet net;
780 build_diamond_network(net);
781
783 auto flow = algo(net);
784 EXPECT_DOUBLE_EQ(flow, 20.0);
785}
786
788{
789 TestNet net;
790 build_diamond_network(net);
791
793 auto flow = algo(net);
794 EXPECT_DOUBLE_EQ(flow, 20.0);
795}
796
798{
799 TestNet net;
800 build_diamond_network(net);
801
803 auto flow = algo(net);
804 EXPECT_DOUBLE_EQ(flow, 20.0);
805}
806
808{
809 TestNet net;
810 build_diamond_network(net);
812
814 auto decomp = decomposer(net);
815
816 EXPECT_DOUBLE_EQ(decomp.total_flow(), 20.0);
817}
818
819
820//==============================================================================
821// Performance Benchmark
822//==============================================================================
823
825{
826 const int grid_size = 10;
827 std::cout << "\n=== Max Flow Performance Benchmark (Grid "
828 << grid_size << "x" << grid_size << ") ===\n";
829
830 auto benchmark = [&](const std::string& name, auto algo) {
831 TestNet net;
833
834 auto start = std::chrono::high_resolution_clock::now();
835 auto flow = algo(net);
836 auto end = std::chrono::high_resolution_clock::now();
837 double ms = std::chrono::duration<double, std::milli>(end - start).count();
838
839 std::cout << name << ": " << ms << " ms, flow=" << flow << "\n";
840 return flow;
841 };
842
843 auto flow_ek = benchmark("Edmonds-Karp ", [](TestNet& n) {
845 });
846
847 auto flow_dinic = benchmark("Dinic ", [](TestNet& n) {
848 return dinic_maximum_flow(n);
849 });
850
851 auto flow_cs = benchmark("Capacity Scaling", [](TestNet& n) {
853 });
854
855 // All should give same result
858}
859
860
861// =============================================================================
862// Regression Tests
863// =============================================================================
864
866{
867 // This test covers a bug where HLPP could set height[u] = 2*n+1
868 // when relabeling a node with no residual neighbors, causing
869 // out-of-bounds access to buckets[2*n+1].
870 //
871 // Network structure that can trigger this:
872 // s -> a -> t (bottleneck at a->t)
873 // If 'a' has excess but all outgoing arcs are saturated and
874 // all incoming arcs have no reverse capacity, relabel would
875 // set height[a] = min_height+1 where min_height could be 2*n.
876
877 TestNet net;
878 auto s = net.insert_node(0); // source (inferred)
879 auto a = net.insert_node(1); // intermediate
880 auto t = net.insert_node(2); // sink (inferred)
881
882 // Create a very small capacity to sink, so 'a' can get excess
883 net.insert_arc(s, a, 100.0); // large capacity
884 net.insert_arc(a, t, 1.0); // small capacity (bottleneck)
885
886 // Run HLPP - should not crash or access out of bounds
887 double flow = hlpp_maximum_flow(net);
888
889 // The max flow is limited by the bottleneck
890 EXPECT_DOUBLE_EQ(flow, 1.0);
891
892 // Verify the flow is valid (conservation at intermediate node)
893 double flow_in_a = 0, flow_out_a = 0;
894 for (typename TestNet::Node_Arc_Iterator it(a); it.has_curr(); it.next_ne())
895 {
896 auto arc = it.get_curr();
897 if (net.get_tgt_node(arc) == a)
898 flow_in_a += arc->flow;
899 else
900 flow_out_a += arc->flow;
901 }
903}
904
905
906//==============================================================================
907// Bug-hunting tests: Anti-parallel arcs
908//==============================================================================
909
910// Helper: build network with anti-parallel arcs (A->B and B->A)
911// This stresses backward-edge residual logic because there are REAL
912// reverse arcs in addition to the virtual backward edges.
913//
914// s --20--> a --10--> t
915// | ^
916// 7 | | 5
917// v |
918// b --8--> t
919//
920// Plus anti-parallel: a->b(7), b->a(5)
921// Max flow should be 20 (limited by s->a), with paths:
922// s->a->t: 10, s->a->b->t: 7, and s->a flow via b->a back to a->t: up to 5
924{
925 auto s = net.insert_node(0);
926 auto a = net.insert_node(1);
927 auto b = net.insert_node(2);
928 auto t = net.insert_node(3);
929
930 net.insert_arc(s, a, 20);
931 net.insert_arc(a, t, 10);
932 net.insert_arc(a, b, 7); // Forward a->b
933 net.insert_arc(b, a, 5); // Anti-parallel b->a
934 net.insert_arc(b, t, 8);
935
936 return s;
937}
938
940{
941 // All algorithms must give the same result on anti-parallel arcs
948
954
955 // Max flow is 18: s->a(20), a->t(10), a->b(7), b->t(8) => 10+8=18
956 // (limited by outgoing capacity from a: 10+7=17? no, b->a adds 5 more to a)
957 // Actually just verify they all agree and conservation holds
962
963 EXPECT_TRUE(net1.check_network());
964 EXPECT_TRUE(net2.check_network());
965 EXPECT_TRUE(net3.check_network());
966 EXPECT_TRUE(net4.check_network());
967 EXPECT_TRUE(net5.check_network());
968
969 // Verify min-cut theorem for all
973}
974
976{
977 // s -> a -> t AND s -> b -> a (with b->a anti-parallel to a->b implicit)
978 // But also a -> b exists
979 TestNet net;
980 auto s = net.insert_node(0);
981 auto a = net.insert_node(1);
982 auto b = net.insert_node(2);
983 auto t = net.insert_node(3);
984
985 net.insert_arc(s, a, 10);
986 net.insert_arc(s, b, 10);
987 net.insert_arc(a, b, 5); // a->b
988 net.insert_arc(b, a, 5); // b->a (anti-parallel)
989 net.insert_arc(a, t, 15);
990 net.insert_arc(b, t, 15);
991
992 auto flow = dinic_maximum_flow(net);
993 EXPECT_DOUBLE_EQ(flow, 20.0); // Both s->a and s->b push 10 each
995}
996
997
998//==============================================================================
999// Bug-hunting tests: Capacity Scaling with fractional capacities
1000//==============================================================================
1001
1003{
1004 // Capacity scaling now scales down to the minimum positive capacity,
1005 // so fractional capacities are handled correctly.
1006 auto build_net = [](TestNet& net) {
1007 auto s = net.insert_node(0);
1008 auto t = net.insert_node(1);
1009 net.insert_arc(s, t, 0.5);
1010 };
1011
1015
1018
1020 EXPECT_DOUBLE_EQ(flow_cs, 0.5); // Now correctly finds fractional flow
1021}
1022
1024{
1025 // Network where integer-capacity paths exist but fractional adds more
1026 // Must use separate networks since algorithms modify flow in-place
1027
1028 auto build_net = [](TestNet& net) {
1029 auto s = net.insert_node(0);
1030 auto a = net.insert_node(1);
1031 auto b = net.insert_node(2);
1032 auto t = net.insert_node(3);
1033 net.insert_arc(s, a, 10);
1034 net.insert_arc(s, b, 0.7); // Fractional
1035 net.insert_arc(a, t, 10);
1036 net.insert_arc(b, t, 0.7); // Fractional
1037 };
1038
1042
1045
1046 // Both should find the full flow: 10 + 0.7 = 10.7
1049}
1050
1051
1052//==============================================================================
1053// Bug-hunting tests: HLPP stress with conservation verification
1054//==============================================================================
1055
1056// Explicit conservation check at every intermediate node
1057template <class Net>
1059{
1060 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1061 {
1062 auto p = it.get_curr();
1063 if (net.is_source(p) or net.is_sink(p))
1064 continue;
1065
1066 double in_flow = 0, out_flow = 0;
1067 for (typename Net::Node_Arc_Iterator ait(p); ait.has_curr(); ait.next_ne())
1068 {
1069 auto arc = ait.get_curr();
1070 if (net.get_tgt_node(arc) == p)
1071 in_flow += arc->flow;
1072 else if (net.get_src_node(arc) == p)
1073 out_flow += arc->flow;
1074 }
1075
1076 EXPECT_NEAR(in_flow, out_flow, 1e-6)
1077 << "Conservation violated at node with in=" << in_flow
1078 << " out=" << out_flow;
1079 }
1080}
1081
1083{
1084 // Ladder graph: two parallel chains connected by rungs
1085 // Forces many relabeling operations
1086 //
1087 // s -> a0 -> a1 -> a2 -> ... -> aN -> t
1088 // | | | |
1089 // v v v v
1090 // s -> b0 -> b1 -> b2 -> ... -> bN -> t
1091 //
1092 const int N = 15;
1093 TestNet net;
1094 auto s = net.insert_node(0);
1095 auto t = net.insert_node(1);
1096
1099 for (int i = 0; i < N; ++i)
1100 {
1101 chain_a[i] = net.insert_node(10 + i);
1102 chain_b[i] = net.insert_node(30 + i);
1103 }
1104
1105 // Connect source to first nodes
1106 net.insert_arc(s, chain_a[0], 100);
1107 net.insert_arc(s, chain_b[0], 100);
1108
1109 // Chains
1110 for (int i = 0; i < N - 1; ++i)
1111 {
1112 net.insert_arc(chain_a[i], chain_a[i+1], 50);
1113 net.insert_arc(chain_b[i], chain_b[i+1], 50);
1114 }
1115
1116 // Rungs (cross connections)
1117 for (int i = 0; i < N; ++i)
1118 {
1119 net.insert_arc(chain_a[i], chain_b[i], 10);
1120 net.insert_arc(chain_b[i], chain_a[i], 10); // Anti-parallel rungs
1121 }
1122
1123 // Connect last nodes to sink
1124 net.insert_arc(chain_a[N-1], t, 100);
1125 net.insert_arc(chain_b[N-1], t, 100);
1126
1127 // Build identical networks (can't copy Net_Graph, so rebuild)
1128 auto build_ladder = [&](TestNet& n) {
1129 auto s2 = n.insert_node(0);
1130 auto t2 = n.insert_node(1);
1131 auto ca = Array<Node *>::create(N);
1132 auto cb = Array<Node *>::create(N);
1133 for (int i = 0; i < N; ++i)
1134 {
1135 ca[i] = n.insert_node(10 + i);
1136 cb[i] = n.insert_node(30 + i);
1137 }
1138 n.insert_arc(s2, ca[0], 100);
1139 n.insert_arc(s2, cb[0], 100);
1140 for (int i = 0; i < N - 1; ++i)
1141 {
1142 n.insert_arc(ca[i], ca[i+1], 50);
1143 n.insert_arc(cb[i], cb[i+1], 50);
1144 }
1145 for (int i = 0; i < N; ++i)
1146 {
1147 n.insert_arc(ca[i], cb[i], 10);
1148 n.insert_arc(cb[i], ca[i], 10);
1149 }
1150 n.insert_arc(ca[N-1], t2, 100);
1151 n.insert_arc(cb[N-1], t2, 100);
1152 };
1153
1154 TestNet net2, net3;
1157
1158 auto flow_dinic = dinic_maximum_flow(net);
1161
1164
1165 // Explicit conservation check
1167 EXPECT_TRUE(net2.check_network());
1170}
1171
1173{
1174 // Graph designed to create large excess at intermediate nodes
1175 // s pushes 1000 to a, but a can only drain 1 to t
1176 // HLPP must push 999 back to s
1177 TestNet net;
1178 auto s = net.insert_node(0);
1179 auto a = net.insert_node(1);
1180 auto b = net.insert_node(2);
1181 auto c = net.insert_node(3);
1182 auto t = net.insert_node(4);
1183
1184 net.insert_arc(s, a, 1000);
1185 net.insert_arc(s, b, 1000);
1186 net.insert_arc(a, c, 1); // Tiny bottleneck
1187 net.insert_arc(b, c, 1); // Tiny bottleneck
1188 net.insert_arc(c, t, 2);
1189 net.insert_arc(a, b, 500); // Cross connection
1190 net.insert_arc(b, a, 500); // Anti-parallel
1191
1192 auto flow = hlpp_maximum_flow(net);
1193
1194 EXPECT_DOUBLE_EQ(flow, 2.0);
1197}
1198
1199
1200//==============================================================================
1201// Bug-hunting tests: Flow decomposition consistency
1202//==============================================================================
1203
1205{
1206 // After decomposition, for each arc:
1207 // sum of (path.flow for paths using that arc) == arc.flow
1208 TestNet net;
1210 dinic_maximum_flow(net);
1211
1212 // Save original flows
1214 for (Arc_Iterator<TestNet> it(net); it.has_curr(); it.next_ne())
1215 {
1216 auto arc = it.get_curr();
1217 original_flow[arc] = arc->flow;
1218 }
1219
1220 auto decomp = decompose_flow(net);
1221
1222 // Rebuild flow from decomposition
1224 for (Arc_Iterator<TestNet> it(net); it.has_curr(); it.next_ne())
1225 reconstructed_flow[it.get_curr()] = 0.0;
1226
1227 for (auto path_it = decomp.paths.get_it(); path_it.has_curr(); path_it.next_ne())
1228 {
1229 const auto& path = path_it.get_curr();
1230 for (auto arc_it = path.arcs.get_it(); arc_it.has_curr(); arc_it.next_ne())
1231 reconstructed_flow[arc_it.get_curr()] += path.flow;
1232 }
1233
1234 for (auto cycle_it = decomp.cycles.get_it(); cycle_it.has_curr(); cycle_it.next_ne())
1235 {
1236 const auto& cycle = cycle_it.get_curr();
1237 for (auto arc_it = cycle.arcs.get_it(); arc_it.has_curr(); arc_it.next_ne())
1238 reconstructed_flow[arc_it.get_curr()] += cycle.flow;
1239 }
1240
1241 // Every arc's flow should be fully accounted for
1242 for (Arc_Iterator<TestNet> it(net); it.has_curr(); it.next_ne())
1243 {
1244 auto arc = it.get_curr();
1246 << "Arc flow mismatch: reconstructed=" << reconstructed_flow[arc]
1247 << " original=" << original_flow[arc];
1248 }
1249}
1250
1252{
1253 // Create a flow with a cycle NOT reachable from source (Phase 2 territory)
1254 // After fixing the Phase-2 bug, cycle.nodes should include the closing node
1255 TestNet net;
1256 auto s = net.insert_node(0);
1257 auto a = net.insert_node(1);
1258 auto b = net.insert_node(2);
1259 auto c = net.insert_node(3);
1260 auto t = net.insert_node(4);
1261
1262 // Main path s->a->t
1263 auto arc_sa = net.insert_arc(s, a, 10);
1264 auto arc_at = net.insert_arc(a, t, 10);
1265
1266 // Isolated cycle b->c->b (not connected to source outflow)
1267 auto arc_bc = net.insert_arc(b, c, 5);
1268 auto arc_cb = net.insert_arc(c, b, 5);
1269
1270 // Dummy arcs to keep b,c in the network but no flow from source to them
1271 net.insert_arc(s, b, 0); // zero capacity
1272 net.insert_arc(c, t, 0); // zero capacity
1273
1274 // Set up flow manually: path s->a->t with flow 5, cycle b->c->b with flow 3
1275 arc_sa->flow = 5;
1276 arc_at->flow = 5;
1277 arc_bc->flow = 3;
1278 arc_cb->flow = 3;
1279
1280 auto decomp = decompose_flow(net);
1281
1282 EXPECT_EQ(decomp.num_paths(), 1);
1283 EXPECT_DOUBLE_EQ(decomp.total_flow(), 5.0);
1284
1285 // Should find the b->c->b cycle in Phase 2
1286 EXPECT_GE(decomp.num_cycles(), 1);
1287
1288 if (decomp.num_cycles() >= 1)
1289 {
1290 const auto& cycle = decomp.cycles.get_first();
1291 EXPECT_DOUBLE_EQ(cycle.flow, 3.0);
1292
1293 // Verify closing node: cycle.nodes should have one more element than
1294 // cycle.arcs, and first node == last node
1295 size_t n_nodes = 0, n_arcs = 0;
1296 for (auto it = cycle.nodes.get_it(); it.has_curr(); it.next_ne())
1297 ++n_nodes;
1298 for (auto it = cycle.arcs.get_it(); it.has_curr(); it.next_ne())
1299 ++n_arcs;
1300
1302 << "Cycle should have nodes.size() == arcs.size() + 1 (closing node)";
1303
1304 // First and last node should be the same (cycle is closed)
1305 auto first_node = cycle.nodes.get_first();
1306 auto last_node = cycle.nodes.get_last();
1308 << "Cycle first and last node should be the same";
1309 }
1310}
1311
1313{
1314 // Also verify Phase-1 cycles have proper format for symmetry
1315 TestNet net;
1316 auto s = net.insert_node(0);
1317 auto a = net.insert_node(1);
1318 auto b = net.insert_node(2);
1319 auto t = net.insert_node(3);
1320
1321 auto arc_sa = net.insert_arc(s, a, 10);
1322 auto arc_at = net.insert_arc(a, t, 10);
1323 auto arc_ab = net.insert_arc(a, b, 5);
1324 auto arc_ba = net.insert_arc(b, a, 5);
1325
1326 // Flow: s->a->t(5), cycle a->b->a(5)
1327 arc_sa->flow = 5;
1328 arc_at->flow = 5;
1329 arc_ab->flow = 5;
1330 arc_ba->flow = 5;
1331
1332 auto decomp = decompose_flow(net);
1333
1334 EXPECT_EQ(decomp.num_paths(), 1);
1335 EXPECT_GE(decomp.num_cycles(), 1);
1336
1337 if (decomp.num_cycles() >= 1)
1338 {
1339 const auto& cycle = decomp.cycles.get_first();
1340
1341 size_t n_nodes = 0, n_arcs = 0;
1342 for (auto it = cycle.nodes.get_it(); it.has_curr(); it.next_ne())
1343 ++n_nodes;
1344 for (auto it = cycle.arcs.get_it(); it.has_curr(); it.next_ne())
1345 ++n_arcs;
1346
1348 << "Phase-1 cycle should also have nodes.size() == arcs.size() + 1";
1349
1350 auto first_node = cycle.nodes.get_first();
1351 auto last_node = cycle.nodes.get_last();
1353 << "Phase-1 cycle first and last node should be the same";
1354 }
1355}
1356
1357
1358//==============================================================================
1359// Bug-hunting tests: Disconnected sink
1360//==============================================================================
1361
1363{
1364 // Single-source/single-sink network where all paths have zero capacity
1365 // This is the closest to "disconnected" that Net_Graph allows
1366 TestNet net;
1367 auto s = net.insert_node(0);
1368 auto a = net.insert_node(1);
1369 auto t = net.insert_node(2);
1370
1371 net.insert_arc(s, a, 0); // Zero capacity bottleneck
1372 net.insert_arc(a, t, 10);
1373
1374 auto flow = dinic_maximum_flow(net);
1375 EXPECT_DOUBLE_EQ(flow, 0.0);
1376}
1377
1379{
1380 // A truly disconnected network violates single source/sink
1381 // (Net_Graph determines source/sink by in/out-degree)
1382 // Algorithms should throw domain_error
1383 TestNet net;
1384 auto s = net.insert_node(0);
1385 auto a = net.insert_node(1);
1386 auto b = net.insert_node(2); // Another "source" (no incoming arcs)
1387 auto t = net.insert_node(3);
1388
1389 net.insert_arc(s, a, 10);
1390 net.insert_arc(b, t, 10);
1391
1392 EXPECT_THROW(dinic_maximum_flow(net), std::exception);
1393 EXPECT_THROW(capacity_scaling_maximum_flow(net), std::exception);
1394 EXPECT_THROW(hlpp_maximum_flow(net), std::exception);
1395}
1396
1397
1398//==============================================================================
1399// Bug-hunting tests: Dinic with deep level graph
1400//==============================================================================
1401
1403{
1404 // s -> n1 -> n2 -> ... -> n50 -> t
1405 // Forces 1 BFS phase with 51 levels
1406 const int chain_len = 50;
1407 TestNet net;
1408 auto s = net.insert_node(0);
1409 auto t = net.insert_node(1);
1410
1411 auto prev = s;
1412 for (int i = 0; i < chain_len; ++i)
1413 {
1414 auto curr = net.insert_node(10 + i);
1415 net.insert_arc(prev, curr, 100);
1416 prev = curr;
1417 }
1418 net.insert_arc(prev, t, 100);
1419
1420 auto flow = dinic_maximum_flow(net);
1421 EXPECT_DOUBLE_EQ(flow, 100.0);
1423}
1424
1426{
1427 // s -> {a1, a2, ..., a100} -> t, all with cap 1
1428 // Many parallel paths
1429 const int width = 100;
1430 TestNet net;
1431 auto s = net.insert_node(0);
1432 auto t = net.insert_node(1);
1433
1434 for (int i = 0; i < width; ++i)
1435 {
1436 auto mid = net.insert_node(10 + i);
1437 net.insert_arc(s, mid, 1);
1438 net.insert_arc(mid, t, 1);
1439 }
1440
1441 // Build a second identical network for HLPP
1442 TestNet net2;
1443 auto s2 = net2.insert_node(0);
1444 auto t2 = net2.insert_node(1);
1445 for (int i = 0; i < width; ++i)
1446 {
1447 auto mid = net2.insert_node(10 + i);
1448 net2.insert_arc(s2, mid, 1);
1449 net2.insert_arc(mid, t2, 1);
1450 }
1451
1452 auto flow_dinic = dinic_maximum_flow(net);
1454
1455 EXPECT_DOUBLE_EQ(flow_dinic, static_cast<double>(width));
1456 EXPECT_DOUBLE_EQ(flow_hlpp, static_cast<double>(width));
1458 EXPECT_TRUE(net2.check_network());
1459}
1460
1461
1462//==============================================================================
1463// Randomized stress tests: all algorithms must agree on random graphs
1464//==============================================================================
1465
1466namespace
1467{
1468 // Integer flow network type
1470
1471 // Build a random network with n nodes, m arcs, max_cap capacity
1472 template <class NetT>
1473 void build_random_network(NetT & net, int n, int m, int max_cap,
1474 unsigned seed)
1475 {
1477 << "build_random_network requires n >= 3";
1478
1479 using NodeT = typename NetT::Node;
1480 std::mt19937 rng(seed);
1481 std::uniform_int_distribution<int> node_dist(0, n - 1);
1482 std::uniform_int_distribution<int> cap_dist(1, max_cap);
1483
1484 std::vector<NodeT *> nodes(n);
1485 for (int i = 0; i < n; ++i)
1486 nodes[i] = net.insert_node(i);
1487
1488 // Ensure source (0) has outgoing and sink (n-1) has incoming
1489 net.insert_arc(nodes[0], nodes[1], cap_dist(rng));
1490 net.insert_arc(nodes[n - 2], nodes[n - 1], cap_dist(rng));
1491
1492 for (int i = 0; i < m - 2; ++i)
1493 {
1494 int u = node_dist(rng);
1495 int v = node_dist(rng);
1496 if (u == v) v = (u + 1 < n) ? (u + 1) : 0;
1497 // Avoid creating additional sources/sinks
1498 if (v == 0) v = 1; // don't point to source
1499 if (u == n - 1) u = n - 2; // don't leave from sink
1500 if (u < 0) u = 0;
1501 if (u == v) continue;
1502 net.insert_arc(nodes[u], nodes[v], cap_dist(rng));
1503 }
1504 }
1505
1506 // Build identical random networks (since copy doesn't work)
1507 template <class NetT>
1508 void build_n_random_networks(std::vector<NetT> & nets,
1509 int count, int n, int m,
1510 int max_cap, unsigned seed)
1511 {
1512 nets.resize(count);
1513 for (int i = 0; i < count; ++i)
1514 build_random_network(nets[i], n, m, max_cap, seed);
1515 }
1516}
1517
1519{
1520 // Run 30 random small graphs, compare all 5 algorithms
1521 for (unsigned seed = 100; seed < 130; ++seed)
1522 {
1523 std::vector<TestNet> nets;
1524 build_n_random_networks(nets, 5, 8, 15, 100, seed);
1525
1526 if (not (nets[0].is_single_source() and nets[0].is_single_sink()))
1527 continue; // skip invalid graphs
1528
1530 auto f_dinic = dinic_maximum_flow(nets[1]);
1532 auto f_hlpp = hlpp_maximum_flow(nets[3]);
1533
1535 << "EK vs Dinic disagree at seed=" << seed;
1537 << "EK vs CapScale disagree at seed=" << seed;
1539 << "EK vs HLPP disagree at seed=" << seed;
1540
1541 EXPECT_TRUE(nets[0].check_network()) << "EK invalid at seed=" << seed;
1542 EXPECT_TRUE(nets[1].check_network()) << "Dinic invalid at seed=" << seed;
1543 EXPECT_TRUE(nets[2].check_network()) << "CapScale invalid at seed=" << seed;
1544 EXPECT_TRUE(nets[3].check_network()) << "HLPP invalid at seed=" << seed;
1545 }
1546}
1547
1549{
1550 // 10 medium random graphs, 50 nodes, 150 arcs
1551 for (unsigned seed = 200; seed < 210; ++seed)
1552 {
1553 std::vector<TestNet> nets;
1554 build_n_random_networks(nets, 4, 50, 150, 1000, seed);
1555
1556 if (not (nets[0].is_single_source() and nets[0].is_single_sink()))
1557 continue;
1558
1560 auto f_dinic = dinic_maximum_flow(nets[1]);
1561 auto f_hlpp = hlpp_maximum_flow(nets[2]);
1562
1564 << "EK vs Dinic disagree at seed=" << seed;
1566 << "EK vs HLPP disagree at seed=" << seed;
1567
1571 }
1572}
1573
1574
1575//==============================================================================
1576// Integer Flow_Type tests
1577//==============================================================================
1578
1580{
1581 // Diamond network with integer capacities
1583
1584 auto build_int_diamond = [](IntNet & net) {
1585 auto s = net.insert_node(0);
1586 auto a = net.insert_node(1);
1587 auto b = net.insert_node(2);
1588 auto t = net.insert_node(3);
1589 net.insert_arc(s, a, 10);
1590 net.insert_arc(s, b, 10);
1591 net.insert_arc(a, t, 10);
1592 net.insert_arc(b, t, 10);
1593 };
1594
1599
1604
1605 EXPECT_EQ(f_ek, 20);
1606 EXPECT_EQ(f_dinic, 20);
1607 EXPECT_EQ(f_cs, 20);
1608 EXPECT_EQ(f_hlpp, 20);
1609
1610 EXPECT_TRUE(net1.check_network());
1611 EXPECT_TRUE(net2.check_network());
1612 EXPECT_TRUE(net4.check_network());
1613}
1614
1616{
1617 IntNet net1, net2, net3;
1618
1619 auto build = [](IntNet & net) {
1620 auto s = net.insert_node(0);
1621 auto a = net.insert_node(1);
1622 auto b = net.insert_node(2);
1623 auto c = net.insert_node(3);
1624 auto d = net.insert_node(4);
1625 auto t = net.insert_node(5);
1626
1627 net.insert_arc(s, a, 16);
1628 net.insert_arc(s, c, 13);
1629 net.insert_arc(a, b, 12);
1630 net.insert_arc(a, c, 10);
1631 net.insert_arc(b, t, 20);
1632 net.insert_arc(c, d, 14);
1633 net.insert_arc(d, b, 7);
1634 net.insert_arc(d, t, 4);
1635 };
1636
1637 build(net1);
1638 build(net2);
1639 build(net3);
1640
1644
1645 EXPECT_EQ(f_dinic, 23);
1646 EXPECT_EQ(f_cs, 23);
1647 EXPECT_EQ(f_hlpp, 23);
1648
1649 EXPECT_TRUE(net1.check_network());
1650 EXPECT_TRUE(net3.check_network());
1651}
1652
1654{
1655 // Random integer graphs, all algorithms must agree
1656 for (unsigned seed = 300; seed < 320; ++seed)
1657 {
1658 std::vector<IntNet> nets;
1659 build_n_random_networks(nets, 4, 12, 25, 50, seed);
1660
1661 if (not (nets[0].is_single_source() and nets[0].is_single_sink()))
1662 continue;
1663
1665 auto f_dinic = dinic_maximum_flow(nets[1]);
1667 auto f_hlpp = hlpp_maximum_flow(nets[3]);
1668
1670 << "EK vs Dinic disagree at seed=" << seed;
1672 << "EK vs CapScale disagree at seed=" << seed;
1674 << "EK vs HLPP disagree at seed=" << seed;
1675
1676 EXPECT_TRUE(nets[0].check_network()) << "seed=" << seed;
1677 EXPECT_TRUE(nets[1].check_network()) << "seed=" << seed;
1678 EXPECT_TRUE(nets[2].check_network()) << "seed=" << seed;
1679 EXPECT_TRUE(nets[3].check_network()) << "seed=" << seed;
1680 }
1681}
1682
1684{
1685 // Verify max-flow = min-cut for integer networks
1686 IntNet net1, net2;
1687
1688 auto build = [](IntNet & net) {
1689 auto s = net.insert_node(0);
1690 auto a = net.insert_node(1);
1691 auto b = net.insert_node(2);
1692 auto c = net.insert_node(3);
1693 auto t = net.insert_node(4);
1694
1695 net.insert_arc(s, a, 3);
1696 net.insert_arc(s, b, 2);
1697 net.insert_arc(a, b, 1);
1698 net.insert_arc(a, c, 3);
1699 net.insert_arc(b, c, 1);
1700 net.insert_arc(c, t, 4);
1701 };
1702
1703 build(net1);
1704 build(net2);
1705
1706 auto flow = dinic_maximum_flow(net1);
1707 EXPECT_EQ(flow, 4);
1708
1709 // Verify min-cut via HLPP reachability
1711
1712 // After HLPP, the min cut separates nodes reachable from source
1713 // in the residual graph from those not reachable.
1714 // The min-cut capacity should equal the max-flow.
1715 int cut_cap = 0;
1716 for (Arc_Iterator<IntNet> it(net2); it.has_curr(); it.next_ne())
1717 {
1718 auto arc = it.get_curr();
1719 // Arc crosses cut if src is source-side and tgt is sink-side
1720 // Check via residual: source-side nodes can reach source via
1721 // residual arcs, sink-side nodes cannot
1722 if (arc->flow == arc->cap) // saturated arcs are candidates
1723 cut_cap += arc->cap;
1724 }
1725 // Total saturated capacity >= max flow (some saturated arcs may not be cut arcs)
1726 EXPECT_GE(cut_cap, flow);
1727}
1728
1729
1730int main(int argc, char** argv)
1731{
1732 ::testing::InitGoogleTest(&argc, argv);
1733 return RUN_ALL_TESTS();
1734}
#define ah_invalid_argument_if(C)
Throws std::invalid_argument if condition holds.
Definition ah-errors.H:639
int main()
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
bool has_curr() const noexcept
Check if there is a current valid item.
Definition array_it.H:219
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:191
bool has_curr() const noexcept
Return true the iterator has an current arc.
Definition tpl_graph.H:1645
Dynamic queue of elements of generic type T based on single linked list.
T & put(const T &data)
The type of element.
T get()
Remove the oldest item of the queue.
T & front()
Return a modifiable reference to the oldest item in the queue.
bool is_empty() const noexcept
Return true if this is empty.
T & insert(const T &item)
Insert a new item by copy.
Definition htlist.H:1502
T & get_last() const
Return the last item of the list.
Definition htlist.H:1663
T & get_first() const
Return the first item of the list.
Definition htlist.H:1675
Generic key-value map implemented on top of a binary search tree.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
Filtered iterator for incoming arcs of a node.
Definition tpl_graph.H:1795
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1206
Filtered iterator for outcoming arcs of a node.
Definition tpl_graph.H:1750
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:731
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:772
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:737
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)
static mt19937 rng
#define N
Definition fib.C:294
DynArray< Graph::Node * > nodes
Definition graphpic.C:406
Net build_grid_network(int rows, int cols, FlowType base_cap=10.0)
Build a grid network for stress testing.
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
FlowStatistics< typename Net::Flow_Type > compute_flow_statistics(const Net &net)
Compute statistics about the current flow in a network.
Net::Flow_Type edmonds_karp_maximum_flow(Net &net)
Maximize flow using the Edmonds-Karp algorithm.
Definition tpl_net.H:1551
size_t size(Node *root) noexcept
Net::Flow_Type dinic_maximum_flow(Net &net)
Compute maximum flow using Dinic's algorithm.
FlowDecomposition< Net > decompose_flow(const Net &net)
Decompose network flow into paths and cycles.
Net::Flow_Type capacity_scaling_maximum_flow(Net &net)
Compute maximum flow using capacity scaling.
Net::Flow_Type hlpp_maximum_flow(Net &net)
Compute maximum flow using Highest-Label Preflow-Push.
Net::Flow_Type ford_fulkerson_maximum_flow(Net &net)
Maximize flow using the Ford-Fulkerson algorithm.
Definition tpl_net.H:1523
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.
Itor::difference_type count(const Itor &beg, const Itor &end, const T &value)
Count elements equal to a value.
Definition ahAlgo.H:127
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1164
Functor wrapper for capacity scaling.
Functor wrapper for flow decomposition.
Functor wrapper for Dinic's algorithm.
Functor wrapper for HLPP.
Arc of a flow network implemented with adjacency lists.
Definition tpl_net.H:115
Flow network implemented with adjacency lists.
Definition tpl_net.H:261
Node * insert_node(const Node_Type &node_info)
Insert a new node by copying node_info.
Definition tpl_net.H:559
bool check_network() const
Validate flow-conservation and capacity constraints.
Definition tpl_net.H:804
bool is_source(Node *node) const noexcept
Return true if node is a source.
Definition tpl_net.H:363
Node * get_source() const
Return an arbitrary source node.
Definition tpl_net.H:548
Arc * insert_arc(Node *src_node, Node *tgt_node, const Flow_Type &cap, const Flow_Type &flow, const typename Arc::Arc_Type &arc_info=Arc_Type())
Insert a capacitated arc with an initial flow.
Definition tpl_net.H:607
ArcT Arc
Arc type.
Definition tpl_net.H:272
Node * get_sink() const
Return an arbitrary sink node.
Definition tpl_net.H:551
bool is_sink(Node *node) const noexcept
Return true if node is a sink.
Definition tpl_net.H:366
NodeT Node
Node type.
Definition tpl_net.H:275
Advanced maximum flow algorithms.
void verify_flow_conservation(const Net &net)
Node * build_antiparallel_network(TestNet &net)
bool verify_max_flow_min_cut(Net &net, double max_flow)
double compute_min_cut_capacity(Net &net)
Network flow graph structures.