Aleph-w 3.0
A C++ Library for Data Structures and Algorithms
Loading...
Searching...
No Matches
tpl_maxflow.H
Go to the documentation of this file.
1/*
2 Aleph_w
3
4 Data structures & Algorithms
5 version 2.0.0b
6 https://github.com/lrleon/Aleph-w
7
8 This file is part of Aleph-w library
9
10 Copyright (c) 2002-2026 Leandro Rabindranath Leon
11
12 Permission is hereby granted, free of charge, to any person obtaining a copy
13 of this software and associated documentation files (the "Software"), to deal
14 in the Software without restriction, including without limitation the rights
15 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 copies of the Software, and to permit persons to whom the Software is
17 furnished to do so, subject to the following conditions:
18
19 The above copyright notice and this permission notice shall be included in all
20 copies or substantial portions of the Software.
21
22 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 SOFTWARE.
29*/
30
31
80#ifndef TPL_MAXFLOW_H
81#define TPL_MAXFLOW_H
82
83# include <ah-graph-concepts.H>
84
85#include <cassert>
86#include <limits>
87#include <sstream>
88#include <iomanip>
89#include <iostream>
90#include <algorithm>
91#include <tpl_array.H>
92#include <tpl_dynListQueue.H>
93#include <tpl_net.H>
94#include <tpl_dynDlist.H>
95#include <ah-errors.H>
96#include <cookie_guard.H>
97
98namespace Aleph
99{
100 //==============================================================================
101 // DINIC'S ALGORITHM
102 //==============================================================================
103
111 template <typename Flow_Type>
113 {
114 long level = -1;
115 bool blocked = false;
116 size_t current_arc = 0;
117
119 {
120 level = -1;
121 blocked = false;
122 current_arc = 0;
123 }
124 };
125
139 template <class Net>
140 inline long &dinic_level(typename Net::Node *p) noexcept
141 {
142 assert(NODE_COOKIE(p) != nullptr &&
143 "NODE_COOKIE not initialized; call dinic_maximum_flow() instead");
144 return static_cast<Dinic_Node_Info<typename Net::Flow_Type> *>
145 (NODE_COOKIE(p))->level;
146 }
147
161 template <class Net>
162 inline bool &dinic_blocked(typename Net::Node *p) noexcept
163 {
164 assert(NODE_COOKIE(p) != nullptr &&
165 "NODE_COOKIE not initialized; call dinic_maximum_flow() instead");
166 return static_cast<Dinic_Node_Info<typename Net::Flow_Type> *>
167 (NODE_COOKIE(p))->blocked;
168 }
169
181 template <class Net>
182 inline size_t &dinic_current_arc(typename Net::Node *p) noexcept
183 {
184 assert(NODE_COOKIE(p) != nullptr &&
185 "NODE_COOKIE not initialized; call dinic_maximum_flow() instead");
186 return static_cast<Dinic_Node_Info<typename Net::Flow_Type> *>
187 (NODE_COOKIE(p))->current_arc;
188 }
189
203 template <class Net>
204 inline bool is_dinic_cookie_valid(typename Net::Node *p) noexcept
205 {
206 return NODE_COOKIE(p) != nullptr;
207 }
208
227 template <class Net>
228 bool build_level_graph(Net & net, typename Net::Node *source,
229 typename Net::Node *sink)
230 {
231 using Node = typename Net::Node;
232 using Flow_Type = typename Net::Flow_Type;
233
234 // Validate NODE_COOKIE and reset all levels in a single pass
235 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
236 {
237 auto p = it.get_curr();
239 "NODE_COOKIE not initialized for Dinic's algorithm. "
240 "Call dinic_maximum_flow() instead of build_level_graph() directly, "
241 "or initialize Dinic_Node_Info for all nodes before calling.");
242 dinic_level<Net>(p) = -1;
243 dinic_blocked<Net>(p) = false;
245 }
246
247 // BFS from source
249 dinic_level<Net>(source) = 0;
250 queue.put(source);
251
252 while (not queue.is_empty())
253 {
254 Node *curr = queue.front();
255 queue.get();
256
257 // Explore all adjacent arcs
258 for (typename Net::Node_Arc_Iterator it(curr); it.has_curr(); it.next_ne())
259 {
260 auto arc = it.get_curr();
261 auto next = net.get_connected_node(arc, curr);
262
263 // Skip if already visited
264 if (dinic_level<Net>(next) >= 0)
265 continue;
266
267 // Check residual capacity
268 Flow_Type residual;
269 if (net.get_src_node(arc) == curr)
270 residual = arc->cap - arc->flow; // Forward edge
271 else
272 residual = arc->flow; // Backward edge
273
274 if (residual > Flow_Type{0})
275 {
277 queue.put(next);
278 }
279 }
280 }
281
282 return dinic_level<Net>(sink) >= 0;
283 }
284
301 template <class Net>
303 typename Net::Node *source,
304 typename Net::Node *sink)
305 {
306 using Node = typename Net::Node;
307 using Arc = typename Net::Arc;
308 using Flow_Type = typename Net::Flow_Type;
309
310 const size_t n = net.vsize();
311
312 // Path stack: stack[0]=source, ..., stack[depth-1]=current node
313 // path_arcs[i] = arc from stack[i] to stack[i+1]
314 // path_fwd[i] = true if path_arcs[i] is a forward arc
315 auto stack = Array<Node *>::create(n);
317 auto path_fwd = Array<char>::create(n); // char instead of bool for safety
318
319 stack[0] = source;
320 size_t depth = 1;
321 Flow_Type total_flow{0};
322
323 while (depth > 0)
324 {
325 Node *curr = stack[depth - 1];
326
327 if (curr == sink)
328 {
329 // Augmenting path found — compute bottleneck
330 Flow_Type bottleneck = std::numeric_limits<Flow_Type>::max();
331 size_t bottleneck_pos = 0;
332 for (size_t i = 0; i + 1 < depth; ++i)
333 {
335 ? (path_arcs[i]->cap - path_arcs[i]->flow)
336 : path_arcs[i]->flow;
337 if (res < bottleneck)
338 {
339 bottleneck = res;
340 bottleneck_pos = i;
341 }
342 }
343
344 // Augment flow along the path
345 for (size_t i = 0; i + 1 < depth; ++i)
346 {
347 if (path_fwd[i])
348 path_arcs[i]->flow += bottleneck;
349 else
350 path_arcs[i]->flow -= bottleneck;
351 }
352
353 total_flow += bottleneck;
354
355 // Retreat to the node whose outgoing arc was the bottleneck.
356 // That arc is now saturated, so this node will advance its
357 // current_arc past it on the next iteration.
358 depth = bottleneck_pos + 1;
359 continue;
360 }
361
362 // Try to advance from curr using its current-arc index
363 size_t &ca = dinic_current_arc<Net>(curr);
364 bool advanced = false;
365
366 while (ca < curr->num_arcs)
367 {
368 Arc *arc = static_cast<Arc *>(curr->arc_array[ca]);
369 Node *next = net.get_connected_node(arc, curr);
370
371 // Only follow edges to the next level
372 if (dinic_level<Net>(next) != dinic_level<Net>(curr) + 1)
373 {
374 ++ca;
375 continue;
376 }
377
378 // Skip blocked nodes (all their arcs are exhausted)
380 {
381 ++ca;
382 continue;
383 }
384
385 // Check residual capacity
386 const bool is_forward = (net.get_src_node(arc) == curr);
387 Flow_Type residual = is_forward
388 ? (arc->cap - arc->flow)
389 : arc->flow;
390
391 if (residual <= Flow_Type{0})
392 {
393 ++ca;
394 continue;
395 }
396
397 // Valid arc — push next onto the path
398 path_arcs[depth - 1] = arc;
399 path_fwd[depth - 1] = is_forward ? 1 : 0;
400 stack[depth] = next;
401 ++depth;
402 advanced = true;
403 break;
404 }
405
406 if (not advanced)
407 {
408 // Dead end — block this node and retreat
409 dinic_blocked<Net>(curr) = true;
410 --depth;
411 }
412 }
413
414 return total_flow;
415 }
416
458 template <FlowNetwork Net>
460 {
461 using Node = typename Net::Node;
462 using Flow_Type = typename Net::Flow_Type;
463
465 << "Network must have single source and single sink";
466
467 Node *source = net.get_source();
468 Node *sink = net.get_sink();
469
470 if (source == sink)
471 return Flow_Type{0};
472
473 // Allocate node info first so it outlives cookie_saver.
474 // C++ destroys locals in reverse order: cookie_saver restores
475 // original cookies before node_info storage is freed.
476 auto node_info = Array<Dinic_Node_Info<Flow_Type>>::create(net.vsize());
477 Cookie_Saver<Net> cookie_saver(net, true, false); // save nodes only
478 size_t idx = 0;
479 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne(), ++idx)
480 NODE_COOKIE(it.get_curr()) = &node_info[idx];
481
482 Flow_Type max_flow{0};
483
484 // Main loop: build level graph and find blocking flows
485 while (build_level_graph(net, source, sink))
486 max_flow += dinic_blocking_flow(net, source, sink);
487
488 // Cookie_Saver destructor will restore original NODE_COOKIE values
489 return max_flow;
490 }
491
495 template <FlowNetwork Net>
497 {
512 typename Net::Flow_Type operator()(Net & net) const
513 {
514 return dinic_maximum_flow(net);
515 }
516 };
517
518
519 //==============================================================================
520 // CAPACITY SCALING
521 //==============================================================================
522
558 template <FlowNetwork Net>
560 {
561 using Node = typename Net::Node;
562 using Arc = typename Net::Arc;
563 using Flow_Type = typename Net::Flow_Type;
564
566 << "Network must have single source and single sink";
567
568 Node *source = net.get_source();
569 Node *sink = net.get_sink();
570
571 // Find maximum capacity to determine starting Δ
572 Flow_Type max_cap{0};
573 for (Arc_Iterator<Net> it(net); it.has_curr(); it.next_ne())
574 max_cap = std::max(max_cap, it.get_curr()->cap);
575
576 if (max_cap == Flow_Type{0})
577 return Flow_Type{0};
578
579 // Start with the largest power of 2 ≤ max_cap
580 Flow_Type delta{1};
581 while (delta <= max_cap / 2)
582 delta *= 2;
583
584 Flow_Type max_flow{0};
585
586 // BFS augmentation helper.
587 // Finds and augments all s-t paths where every edge has
588 // residual >= min_residual. Returns total flow found.
590 {
592 bool found_path = true;
593 while (found_path)
594 {
595 found_path = false;
596
598 DynMapTree<Node *, Arc *> parent_arc;
600
602 queue.put(source);
603 parent[source] = nullptr;
604
605 while (not queue.is_empty() and not parent.has(sink))
606 {
607 Node *curr = queue.front();
608 queue.get();
609
610 for (typename Net::Node_Arc_Iterator it(curr); it.has_curr();
611 it.next_ne())
612 {
613 Arc *arc = it.get_curr();
614 Node *next = net.get_connected_node(arc, curr);
615
616 if (parent.has(next))
617 continue;
618
619 Flow_Type residual;
620 bool forward = (net.get_src_node(arc) == curr);
621
622 if (forward)
623 residual = arc->cap - arc->flow;
624 else
625 residual = arc->flow;
626
627 if (residual >= min_residual and residual > Flow_Type{0})
628 {
629 parent[next] = curr;
630 parent_arc[next] = arc;
631 is_forward[next] = forward;
632 queue.put(next);
633 }
634 }
635 }
636
637 if (parent.has(sink))
638 {
639 found_path = true;
640
641 Flow_Type path_flow = std::numeric_limits<Flow_Type>::max();
642 for (Node *n = sink; n != source; n = parent[n])
643 {
644 Arc *arc = parent_arc[n];
645 Flow_Type residual = is_forward[n] ?
646 (arc->cap - arc->flow) :
647 arc->flow;
648 path_flow = std::min(path_flow, residual);
649 }
650
651 for (Node *n = sink; n != source; n = parent[n])
652 {
653 Arc *arc = parent_arc[n];
654 if (is_forward[n])
655 arc->flow += path_flow;
656 else
657 arc->flow -= path_flow;
658 }
659
661 }
662 }
663 return phase_flow;
664 };
665
666 // Scaling phases (delta = largest power of 2 down to 1)
667 while (delta >= Flow_Type{1})
668 {
669 max_flow += bfs_augment(delta);
670 delta /= 2;
671 }
672
673 // Final cleanup: capture any fractional residual flow.
674 // For integer capacities the scaling phases already found all flow,
675 // so this is a single O(V+E) no-op BFS.
676 max_flow += bfs_augment(Flow_Type{0});
677
678 return max_flow;
679 }
680
684 template <FlowNetwork Net>
686 {
701 typename Net::Flow_Type operator()(Net & net) const
702 {
704 }
705 };
706
707
708 //==============================================================================
709 // FLOW DECOMPOSITION
710 //==============================================================================
711
721 template <class Net>
722 struct FlowPath
723 {
724 using Arc = typename Net::Arc;
725 using Node = typename Net::Node;
726 using Flow_Type = typename Net::Flow_Type;
727
731
739 [[nodiscard]] bool is_empty() const noexcept { return arcs.is_empty(); }
740
748 [[nodiscard]] size_t length() const noexcept { return arcs.size(); }
749 };
750
759 template <class Net>
761 {
762 using Arc = typename Net::Arc;
763 using Node = typename Net::Node;
764 using Flow_Type = typename Net::Flow_Type;
765
769 };
770
778 template <class Net>
780 {
781 using Flow_Type = typename Net::Flow_Type;
782
785
794 {
795 Flow_Type sum{0};
796 for (auto it = paths.get_it(); it.has_curr(); it.next_ne())
797 sum += it.get_curr().flow;
798 return sum;
799 }
800
808 [[nodiscard]] size_t num_paths() const noexcept { return paths.size(); }
809
817 [[nodiscard]] size_t num_cycles() const noexcept { return cycles.size(); }
818 };
819
861 template <FlowNetwork Net>
863 {
864 using Node = typename Net::Node;
865 using Arc = typename Net::Arc;
866 using Flow_Type = typename Net::Flow_Type;
867
869 << "Network must have single source and single sink";
870
872
873 // Create a copy of flow values (we'll modify them during decomposition)
875 for (Arc_Iterator<Net> it(net); it.has_curr(); it.next_ne())
876 {
877 Arc *arc = it.get_curr();
878 remaining_flow[arc] = arc->flow;
879 }
880
881 Node *source = net.get_source();
882 Node *sink = net.get_sink();
883
884 // Find paths from source to sink
885 while (true)
886 {
887 // Find an arc from source with positive remaining flow
888 Arc *start_arc = nullptr;
889 for (typename Net::Node_Arc_Iterator it(source); it.has_curr(); it.next_ne())
890 {
891 Arc *arc = it.get_curr();
892 if (net.get_src_node(arc) == source and remaining_flow[arc] > Flow_Type{0})
893 {
894 start_arc = arc;
895 break;
896 }
897 }
898
899 if (start_arc == nullptr)
900 break; // No more flow from source
901
902 // Build path by following positive flow, tracking visited nodes to detect cycles
903 FlowPath<Net> path;
904 path.nodes.append(source);
905
906 Node *curr = net.get_tgt_node(start_arc);
907 path.arcs.append(start_arc);
908 path.nodes.append(curr);
910
911 // Track visited nodes and their position in path for cycle detection
913 visited[source] = 0;
914 visited[curr] = 1;
915
916 while (curr != sink)
917 {
918 // Find outgoing arc with positive flow
919 Arc *next_arc = nullptr;
920 for (typename Net::Node_Arc_Iterator it(curr); it.has_curr(); it.next_ne())
921 {
922 Arc *arc = it.get_curr();
923 if (net.get_src_node(arc) == curr and remaining_flow[arc] > Flow_Type{0})
924 {
925 next_arc = arc;
926 break;
927 }
928 }
929
930 if (next_arc == nullptr)
931 break; // No outgoing arc with a positive flow
932
934
935 // Check if we've visited this node before (cycle detected)
936 if (visited.contains(next_node))
937 {
938 // Extract the cycle starting from the repeated node
939 const size_t cycle_start = visited[next_node];
942
943 // Build cycle from the position where we first saw next_node
944 auto node_it = path.nodes.get_it();
945 auto arc_it = path.arcs.get_it();
946
947 // Skip to the cycle start position
948 for (size_t i = 0; i < cycle_start; ++i)
949 {
950 node_it.next_ne();
951 arc_it.next_ne();
952 }
953
954 // Collect cycle nodes and arcs, compute minimum flow
955 while (arc_it.has_curr())
956 {
957 cycle.nodes.append(node_it.get_curr());
958 cycle.arcs.append(arc_it.get_curr());
959 cycle.flow = std::min(cycle.flow, remaining_flow[arc_it.get_curr()]);
960 node_it.next_ne();
961 arc_it.next_ne();
962 }
963 // Add the closing arc and closing node to complete the cycle
964 cycle.arcs.append(next_arc);
965 cycle.flow = std::min(cycle.flow, remaining_flow[next_arc]);
966 cycle.nodes.append(next_node);
967
968 // Subtract cycle flow from cycle arcs
969 for (auto it = cycle.arcs.get_it(); it.has_curr(); it.next_ne())
970 remaining_flow[it.get_curr()] -= cycle.flow;
971
972 if (cycle.flow > Flow_Type{0})
973 result.cycles.append(std::move(cycle));
974
975 // At this point we have removed all flow along the detected cycle
976 // from 'remaining_flow'. We intentionally discard the current
977 // partial path (the prefix up to 'cycle_start') and restart path
978 // construction from the source in the outer loop. This does not
979 // lose any flow because the prefix arcs were not modified and
980 // remain available for future paths/cycles. The trade-off is that
981 // we may re-traverse some nodes/edges, which is acceptable here
982 // under the assumption that cycles in the residual flow are rare.
983 break;
984 }
985
986 path.arcs.append(next_arc);
987 path.flow = std::min(path.flow, remaining_flow[next_arc]);
988 curr = next_node;
989 path.nodes.append(curr);
990 visited[curr] = path.nodes.size() - 1;
991 }
992
993 // Subtract path flow from all arcs (only if we reached sink)
994 if (curr == sink and path.flow > Flow_Type{0})
995 {
996 for (auto it = path.arcs.get_it(); it.has_curr(); it.next_ne())
997 remaining_flow[it.get_curr()] -= path.flow;
998 result.paths.append(std::move(path));
999 }
1000 }
1001
1002 // Second phase: find cycles in the remaining flow (not connected to source)
1003 // These are cycles that exist independently of the s-t flow
1004 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1005 {
1006 Node *start = it.get_curr();
1007
1008 // Try to find a cycle starting from this node
1009 while (true)
1010 {
1011 // Find an outgoing arc with positive remaining flow
1012 Arc *first_arc = nullptr;
1013 for (typename Net::Node_Arc_Iterator ait(start); ait.has_curr(); ait.next_ne())
1014 {
1015 Arc *arc = ait.get_curr();
1016 if (net.get_src_node(arc) == start and remaining_flow[arc] > Flow_Type{0})
1017 {
1018 first_arc = arc;
1019 break;
1020 }
1021 }
1022
1023 if (first_arc == nullptr)
1024 break; // No outgoing flow from this node
1025
1026 // Follow the flow to find a cycle
1031
1032 path_nodes.append(start);
1033 visited[start] = 0;
1034
1035 Node *curr = net.get_tgt_node(first_arc);
1037 path_nodes.append(curr);
1038
1039 bool found_cycle = false;
1040
1041 while (not found_cycle)
1042 {
1043 if (visited.contains(curr))
1044 {
1045 // Found a cycle starting at 'curr'
1046 const size_t cycle_start = visited[curr];
1047 cycle.flow = std::numeric_limits<Flow_Type>::max();
1048
1049 // Skip to cycle start in path
1050 auto node_it = path_nodes.get_it();
1051 auto arc_it = path_arcs.get_it();
1052 for (size_t i = 0; i < cycle_start; ++i)
1053 {
1054 node_it.next_ne();
1055 arc_it.next_ne();
1056 }
1057
1058 // Collect cycle and find minimum flow
1059 while (arc_it.has_curr())
1060 {
1061 cycle.nodes.append(node_it.get_curr());
1062 cycle.arcs.append(arc_it.get_curr());
1063 cycle.flow = std::min(cycle.flow, remaining_flow[arc_it.get_curr()]);
1064 node_it.next_ne();
1065 arc_it.next_ne();
1066 }
1067 // Add the closing node to complete the cycle (as Phase 1 does)
1068 cycle.nodes.append(curr);
1069
1070 found_cycle = true;
1071 break;
1072 }
1073
1074 visited[curr] = path_nodes.size() - 1;
1075
1076 // Find next arc with positive flow
1077 Arc *next_arc = nullptr;
1078 for (typename Net::Node_Arc_Iterator ait(curr); ait.has_curr(); ait.next_ne())
1079 {
1080 Arc *arc = ait.get_curr();
1081 if (net.get_src_node(arc) == curr and remaining_flow[arc] > Flow_Type{0})
1082 {
1083 next_arc = arc;
1084 break;
1085 }
1086 }
1087
1088 if (next_arc == nullptr)
1089 break; // Dead end, no cycle from this path
1090
1091 path_arcs.append(next_arc);
1092 curr = net.get_tgt_node(next_arc);
1093 path_nodes.append(curr);
1094 }
1095
1096 if (found_cycle and cycle.flow > Flow_Type{0})
1097 {
1098 // Subtract cycle flow from all cycle arcs
1099 for (auto cit = cycle.arcs.get_it(); cit.has_curr(); cit.next_ne())
1100 remaining_flow[cit.get_curr()] -= cycle.flow;
1101 result.cycles.append(std::move(cycle));
1102 }
1103 else
1104 break; // No cycle found from this start node
1105 }
1106 }
1107
1108 return result;
1109 }
1110
1114 template <FlowNetwork Net>
1116 {
1132 {
1133 return decompose_flow(net);
1134 }
1135 };
1136
1137
1138 //==============================================================================
1139 // HIGHEST LABEL PREFLOW-PUSH (HLPP)
1140 //==============================================================================
1141
1146 template <typename Flow_Type>
1148 {
1149 long height = 0;
1151 };
1152
1154 template <class Net>
1155 inline long &hlpp_height(typename Net::Node *p) noexcept
1156 {
1157 return static_cast<HLPP_Node_Info<typename Net::Flow_Type> *>
1158 (NODE_COOKIE(p))->height;
1159 }
1160
1162 template <class Net>
1163 inline typename Net::Flow_Type &hlpp_excess(typename Net::Node *p) noexcept
1164 {
1165 return static_cast<HLPP_Node_Info<typename Net::Flow_Type> *>
1166 (NODE_COOKIE(p))->excess;
1167 }
1168
1184 template <FlowNetwork Net>
1186 {
1187 using Node = typename Net::Node;
1188 using Arc = typename Net::Arc;
1189 using Flow_Type = typename Net::Flow_Type;
1190
1192 << "Network must have single source and single sink";
1193
1194 Node *source = net.get_source();
1195 Node *sink = net.get_sink();
1196 const size_t n_nodes = net.vsize();
1197 const long n = static_cast<long>(n_nodes);
1198
1199 // Allocate node info first so it outlives cookie_saver.
1200 // C++ destroys locals in reverse order: cookie_saver restores
1201 // original cookies before node_info storage is freed.
1202 auto node_info = Array<HLPP_Node_Info<Flow_Type>>::create(n_nodes);
1203 Cookie_Saver<Net> cookie_saver(net, true, false); // save nodes only
1204 {
1205 size_t idx = 0;
1206 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne(), ++idx)
1207 NODE_COOKIE(it.get_curr()) = &node_info[idx];
1208 }
1209
1210 // --- Initialize heights via reverse BFS from sink ---
1211 // All heights start at n (unreachable); BFS sets exact distances
1212 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1213 hlpp_height<Net>(it.get_curr()) = n;
1214
1215 hlpp_height<Net>(sink) = 0;
1216
1217 {
1219 queue.put(sink);
1220 while (not queue.is_empty())
1221 {
1222 Node *curr = queue.get();
1223 for (typename Net::Node_Arc_Iterator ait(curr);
1224 ait.has_curr(); ait.next_ne())
1225 {
1226 auto arc = ait.get_curr();
1227 auto next = net.get_connected_node(arc, curr);
1228
1229 if (next == source or
1230 hlpp_height<Net>(next) != n)
1231 continue;
1232
1233 // In the residual graph (flow = 0 initially), there is a
1234 // residual arc next→curr iff the original arc goes
1235 // next→curr with cap > 0.
1236 if (net.get_src_node(arc) == next and
1237 arc->cap > Flow_Type{0})
1238 {
1240 queue.put(next);
1241 }
1242 }
1243 }
1244 }
1245
1246 // Source always has height = n
1247 hlpp_height<Net>(source) = n;
1248
1249 // --- Saturate all arcs from source ---
1250 for (typename Net::Node_Arc_Iterator it(source); it.has_curr(); it.next_ne())
1251 if (Arc *arc = it.get_curr(); net.get_src_node(arc) == source)
1252 {
1253 Flow_Type delta = arc->cap - arc->flow;
1254 arc->flow = arc->cap;
1255 hlpp_excess<Net>(net.get_tgt_node(arc)) += delta;
1256 hlpp_excess<Net>(source) -= delta;
1257 }
1258
1259 // --- Initialize bucket structure ---
1260 const size_t bucket_count = static_cast<size_t>(2 * n + 1);
1261 auto buckets = Array<DynList<Node *>>::create(bucket_count);
1262 long max_height = 0;
1263
1264 // Add active nodes to buckets
1265 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1266 {
1267 auto p = it.get_curr();
1268 if (p != source and p != sink and
1270 {
1271 long h = hlpp_height<Net>(p);
1272 buckets[h].append(p);
1273 max_height = std::max(max_height, h);
1274 }
1275 }
1276
1277 // --- Main push/relabel loop ---
1278 while (max_height >= 0)
1279 {
1280 if (buckets[max_height].is_empty())
1281 {
1282 --max_height;
1283 continue;
1284 }
1285
1286 Node *u = buckets[max_height].remove_first();
1287
1288 // Push/Relabel
1289 bool pushed = false;
1290 long min_height = 2 * n;
1291
1292 for (typename Net::Node_Arc_Iterator it(u);
1294 it.next_ne())
1295 {
1296 Arc *arc = it.get_curr();
1297 Node *v = net.get_connected_node(arc, u);
1298
1299 Flow_Type residual;
1300 const bool is_forward = (net.get_src_node(arc) == u);
1301 if (is_forward)
1302 residual = arc->cap - arc->flow;
1303 else
1304 residual = arc->flow;
1305
1306 if (residual > Flow_Type{0})
1307 {
1308 if (hlpp_height<Net>(u) == hlpp_height<Net>(v) + 1)
1309 {
1310 // Push
1311 Flow_Type delta = std::min(hlpp_excess<Net>(u), residual);
1312 if (is_forward)
1313 arc->flow += delta;
1314 else
1315 arc->flow -= delta;
1316
1317 const bool was_inactive =
1318 (v != source and v != sink and
1319 hlpp_excess<Net>(v) <= Flow_Type{0});
1320
1321 hlpp_excess<Net>(u) -= delta;
1322 hlpp_excess<Net>(v) += delta;
1323
1325 {
1326 buckets[hlpp_height<Net>(v)].append(v);
1327 max_height = std::max(max_height, hlpp_height<Net>(v));
1328 }
1329
1330 pushed = true;
1331 }
1332 else
1333 min_height = std::min(min_height, hlpp_height<Net>(v));
1334 }
1335 }
1336
1337 if (hlpp_excess<Net>(u) > Flow_Type{0})
1338 {
1339 if (not pushed)
1340 {
1341 if (min_height >= 2 * n)
1342 continue; // no admissible neighbour; drop the node
1344 }
1345 buckets[hlpp_height<Net>(u)].append(u);
1346 max_height = std::max(max_height, hlpp_height<Net>(u));
1347 }
1348 }
1349
1350 return hlpp_excess<Net>(sink);
1351 }
1352
1356 template <FlowNetwork Net>
1358 {
1373 typename Net::Flow_Type operator()(Net & net) const
1374 {
1375 return hlpp_maximum_flow(net);
1376 }
1377 };
1378
1379
1380 //==============================================================================
1381 // FLOW STATISTICS
1382 //==============================================================================
1383
1387 template <typename Flow_Type>
1389 {
1393 size_t num_empty_arcs{0};
1395 double utilization{0.0};
1396
1407 [[nodiscard]] std::string to_string() const
1408 {
1409 std::ostringstream oss;
1410 oss << "=== Flow Statistics ===\n"
1411 << "Total flow: " << total_flow << "\n"
1412 << "Total capacity: " << total_capacity << "\n"
1413 << "Saturated arcs: " << num_saturated_arcs << "\n"
1414 << "Empty arcs: " << num_empty_arcs << "\n"
1415 << "Partial arcs: " << num_partial_arcs << "\n"
1416 << "Utilization: " << std::fixed << std::setprecision(2)
1417 << (utilization * 100) << "%\n";
1418 return oss.str();
1419 }
1420
1430 void print() const
1431 {
1432 std::cout << to_string();
1433 }
1434 };
1435
1448 template <class Net>
1450 {
1451 using Flow_Type = typename Net::Flow_Type;
1452
1454
1455 for (Arc_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1456 {
1457 auto arc = it.get_curr();
1458 stats.total_capacity += arc->cap;
1459
1460 if (arc->flow == arc->cap)
1461 ++stats.num_saturated_arcs;
1462 else if (arc->flow == Flow_Type{0})
1463 ++stats.num_empty_arcs;
1464 else
1465 ++stats.num_partial_arcs;
1466 }
1467
1468 if (net.is_single_source())
1469 stats.total_flow = net.flow_value();
1470
1471 if (stats.total_capacity > Flow_Type{0})
1472 stats.utilization = static_cast<double>(stats.total_flow) /
1473 static_cast<double>(stats.total_capacity);
1474
1475 return stats;
1476 }
1477} // namespace Aleph
1478
1479#endif // TPL_MAXFLOW_H
Exception handling system with formatted messages for Aleph-w.
#define ah_domain_error_if(C)
Throws std::domain_error if condition holds.
Definition ah-errors.H:527
C++20 concepts for the protocol shared by graph algorithms.
WeightedDigraph::Node Node
WeightedDigraph::Arc Arc
long double h
Definition btreepic.C:154
bool has_curr() const noexcept
Check if there is a current valid item.
Definition array_it.H:231
Simple dynamic array with automatic resizing and functional operations.
Definition tpl_array.H:138
static Array create(size_t n)
Create an array with n logical elements.
Definition tpl_array.H:196
RAII guard that saves and restores graph cookies.
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.
Doubly-linked list (defined in tpl_dynList.H).
Definition htlist.H:1155
T & append(const T &item)
Definition htlist.H:1271
Generic key-value map implemented on top of a binary search tree.
Pair * append(const Key &key, const Data &data)
bool has(const Key &key) const noexcept
bool contains(const Key &key) const noexcept
const size_t & size() const
Returns the cardinality of the set.
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
constexpr bool is_empty() const noexcept
Definition htlist.H:419
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1065
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1207
Node * get_src_node(Arc *arc) const noexcept
Return the source node of arc (only for directed graphs)
Definition graph-dry.H:779
Node * get_connected_node(Arc *arc, Node *node) const noexcept
Return the adjacent node to node through arc.
Definition graph-dry.H:820
constexpr size_t vsize() const noexcept
Definition graph-dry.H:746
Node * get_tgt_node(Arc *arc) const noexcept
Return the target node of arc (only for directed graphs)
Definition graph-dry.H:785
auto get_it() const
Return a properly initialized iterator positioned at the first item on the container.
Definition ah-dry.H:228
RAII guards for graph node/arc cookies.
#define NODE_COOKIE(p)
Return the node cookie
size_t blossom_maximum_cardinality_matching(const GT &g, DynDlist< typename GT::Arc * > &matching, SA sa=SA())
Alias of compute_maximum_cardinality_general_matching().
Definition Blossom.H:466
double Flow_Type
Main namespace for Aleph-w library functions.
Definition ah-arena.H:89
Net::Flow_Type remaining_flow(typename Net::Node *src, typename Net::Arc *a) noexcept
Return the remaining flow of a as seen from src.
Definition tpl_net.H:237
FlowStatistics< typename Net::Flow_Type > compute_flow_statistics(const Net &net)
Compute statistics about the current flow in a network.
bool is_dinic_cookie_valid(typename Net::Node *p) noexcept
Check if a node's cookie is properly initialized for Dinic's algorithm.
Net::Flow_Type dinic_maximum_flow(Net &net)
Compute maximum flow using Dinic's algorithm.
long & hlpp_height(typename Net::Node *p) noexcept
Access the height label stored in the node's cookie.
bool build_level_graph(Net &net, typename Net::Node *source, typename Net::Node *sink)
Build level graph using BFS from source.
long & dinic_level(typename Net::Node *p) noexcept
Access the node level in Dinic's algorithm.
FlowDecomposition< Net > decompose_flow(const Net &net)
Decompose network flow into paths and cycles.
and
Check uniqueness with explicit hash + equality functors.
Net::Flow_Type capacity_scaling_maximum_flow(Net &net)
Compute maximum flow using capacity scaling.
bool & dinic_blocked(typename Net::Node *p) noexcept
Check if a node is blocked in Dinic's algorithm.
size_t & dinic_current_arc(typename Net::Node *p) noexcept
Access the current arc index in Dinic's algorithm.
Net::Flow_Type hlpp_maximum_flow(Net &net)
Compute maximum flow using Highest-Label Preflow-Push.
Net::Flow_Type & hlpp_excess(typename Net::Node *p) noexcept
Access the excess flow stored in the node's cookie.
void next()
Advance all underlying iterators (bounds-checked).
Definition ah-zip.H:171
Net::Flow_Type dinic_blocking_flow(Net &net, typename Net::Node *source, typename Net::Node *sink)
Find all blocking flows using iterative DFS with current-arc optimization.
T sum(const Container &container, const T &init=T{})
Compute sum of all elements.
Filtered iterator on all the arcs of a graph.
Definition tpl_graph.H:1165
Functor wrapper for capacity scaling.
Net::Flow_Type operator()(Net &net) const
Invoke capacity scaling maximum flow algorithm on the given network.
Functor wrapper for flow decomposition.
FlowDecomposition< Net > operator()(const Net &net) const
Invoke flow decomposition on the given network.
Functor wrapper for Dinic's algorithm.
Net::Flow_Type operator()(Net &net) const
Invoke Dinic's maximum flow algorithm on the given network.
Level information for Dinic's algorithm.
size_t current_arc
Current arc index for current-arc optimization.
void reset() noexcept
bool blocked
Whether the node is blocked in the current phase.
long level
BFS level from source (-1 = unreachable)
Represents a flow cycle in the network.
typename Net::Flow_Type Flow_Type
Flow_Type flow
Flow on this cycle.
typename Net::Arc Arc
DynList< Node * > nodes
Nodes in the cycle.
DynList< Arc * > arcs
Arcs in the cycle.
typename Net::Node Node
Result of flow decomposition.
DynList< FlowCycle< Net > > cycles
Cycles (if any)
typename Net::Flow_Type Flow_Type
Flow_Type total_flow() const noexcept
Total flow (sum of path flows).
size_t num_paths() const noexcept
Number of paths.
size_t num_cycles() const noexcept
Number of cycles.
DynList< FlowPath< Net > > paths
Source-to-sink paths.
Represents a flow path from source to sink.
DynList< Node * > nodes
Nodes in the path.
bool is_empty() const noexcept
Check if a path is empty.
Flow_Type flow
Flow on this path.
typename Net::Flow_Type Flow_Type
size_t length() const noexcept
Get path length (number of arcs).
typename Net::Arc Arc
DynList< Arc * > arcs
Arcs in the path (source to sink order)
typename Net::Node Node
Statistics about a network flow.
std::string to_string() const
Format statistics as a string.
size_t num_saturated_arcs
Arcs with flow = capacity.
size_t num_empty_arcs
Arcs with flow = 0.
Flow_Type total_capacity
Sum of all capacities.
Flow_Type total_flow
Total flow value.
void print() const
Print statistics to standard output.
size_t num_partial_arcs
Arcs with 0 < flow < capacity.
double utilization
flow / capacity ratio
Functor wrapper for HLPP.
Net::Flow_Type operator()(Net &net) const
Invoke the Highest-Label Preflow-Push algorithm on the given network.
Node info for HLPP algorithm.
Flow network implemented with adjacency lists.
Definition tpl_net.H:261
constexpr bool is_single_source() const noexcept
Return true if the network has a single source.
Definition tpl_net.H:369
Node * get_source() const
Return an arbitrary source node.
Definition tpl_net.H:548
ArcT Arc
Arc type.
Definition tpl_net.H:272
Node * get_sink() const
Return an arbitrary sink node.
Definition tpl_net.H:551
Flow_Type flow_value() const
Return the total flow value of the network.
Definition tpl_net.H:821
typename Arc::Flow_Type Flow_Type
Capacity/flow numeric type.
Definition tpl_net.H:278
constexpr bool is_single_sink() const noexcept
Return true if the network has a single sink.
Definition tpl_net.H:372
NodeT Node
Node type.
Definition tpl_net.H:275
Dynamic array container with automatic resizing.
Dynamic doubly linked list implementation.
Dynamic queue implementation based on linked lists.
Network flow graph structures.