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 <cassert>
84#include <limits>
85#include <sstream>
86#include <iomanip>
87#include <iostream>
88#include <algorithm>
89#include <tpl_array.H>
90#include <tpl_dynListQueue.H>
91#include <tpl_net.H>
92#include <tpl_dynDlist.H>
93#include <ah-errors.H>
94#include <cookie_guard.H>
95
96namespace Aleph
97{
98 //==============================================================================
99 // DINIC'S ALGORITHM
100 //==============================================================================
101
109 template <typename Flow_Type>
111 {
112 long level = -1;
113 bool blocked = false;
114 size_t current_arc = 0;
115
117 {
118 level = -1;
119 blocked = false;
120 current_arc = 0;
121 }
122 };
123
137 template <class Net>
138 inline long &dinic_level(typename Net::Node *p) noexcept
139 {
140 assert(NODE_COOKIE(p) != nullptr &&
141 "NODE_COOKIE not initialized; call dinic_maximum_flow() instead");
142 return static_cast<Dinic_Node_Info<typename Net::Flow_Type> *>
143 (NODE_COOKIE(p))->level;
144 }
145
159 template <class Net>
160 inline bool &dinic_blocked(typename Net::Node *p) noexcept
161 {
162 assert(NODE_COOKIE(p) != nullptr &&
163 "NODE_COOKIE not initialized; call dinic_maximum_flow() instead");
164 return static_cast<Dinic_Node_Info<typename Net::Flow_Type> *>
165 (NODE_COOKIE(p))->blocked;
166 }
167
179 template <class Net>
180 inline size_t &dinic_current_arc(typename Net::Node *p) noexcept
181 {
182 assert(NODE_COOKIE(p) != nullptr &&
183 "NODE_COOKIE not initialized; call dinic_maximum_flow() instead");
184 return static_cast<Dinic_Node_Info<typename Net::Flow_Type> *>
185 (NODE_COOKIE(p))->current_arc;
186 }
187
201 template <class Net>
202 inline bool is_dinic_cookie_valid(typename Net::Node *p) noexcept
203 {
204 return NODE_COOKIE(p) != nullptr;
205 }
206
225 template <class Net>
226 bool build_level_graph(Net & net, typename Net::Node *source,
227 typename Net::Node *sink)
228 {
229 using Node = typename Net::Node;
230 using Flow_Type = typename Net::Flow_Type;
231
232 // Validate NODE_COOKIE and reset all levels in a single pass
233 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
234 {
235 auto p = it.get_curr();
237 "NODE_COOKIE not initialized for Dinic's algorithm. "
238 "Call dinic_maximum_flow() instead of build_level_graph() directly, "
239 "or initialize Dinic_Node_Info for all nodes before calling.");
240 dinic_level<Net>(p) = -1;
241 dinic_blocked<Net>(p) = false;
243 }
244
245 // BFS from source
247 dinic_level<Net>(source) = 0;
248 queue.put(source);
249
250 while (not queue.is_empty())
251 {
252 Node *curr = queue.front();
253 queue.get();
254
255 // Explore all adjacent arcs
256 for (typename Net::Node_Arc_Iterator it(curr); it.has_curr(); it.next_ne())
257 {
258 auto arc = it.get_curr();
259 auto next = net.get_connected_node(arc, curr);
260
261 // Skip if already visited
262 if (dinic_level<Net>(next) >= 0)
263 continue;
264
265 // Check residual capacity
266 Flow_Type residual;
267 if (net.get_src_node(arc) == curr)
268 residual = arc->cap - arc->flow; // Forward edge
269 else
270 residual = arc->flow; // Backward edge
271
272 if (residual > Flow_Type{0})
273 {
275 queue.put(next);
276 }
277 }
278 }
279
280 return dinic_level<Net>(sink) >= 0;
281 }
282
299 template <class Net>
301 typename Net::Node *source,
302 typename Net::Node *sink)
303 {
304 using Node = typename Net::Node;
305 using Arc = typename Net::Arc;
306 using Flow_Type = typename Net::Flow_Type;
307
308 const size_t n = net.vsize();
309
310 // Path stack: stack[0]=source, ..., stack[depth-1]=current node
311 // path_arcs[i] = arc from stack[i] to stack[i+1]
312 // path_fwd[i] = true if path_arcs[i] is a forward arc
313 auto stack = Array<Node *>::create(n);
315 auto path_fwd = Array<char>::create(n); // char instead of bool for safety
316
317 stack[0] = source;
318 size_t depth = 1;
319 Flow_Type total_flow{0};
320
321 while (depth > 0)
322 {
323 Node *curr = stack[depth - 1];
324
325 if (curr == sink)
326 {
327 // Augmenting path found — compute bottleneck
328 Flow_Type bottleneck = std::numeric_limits<Flow_Type>::max();
329 size_t bottleneck_pos = 0;
330 for (size_t i = 0; i + 1 < depth; ++i)
331 {
333 ? (path_arcs[i]->cap - path_arcs[i]->flow)
334 : path_arcs[i]->flow;
335 if (res < bottleneck)
336 {
337 bottleneck = res;
338 bottleneck_pos = i;
339 }
340 }
341
342 // Augment flow along the path
343 for (size_t i = 0; i + 1 < depth; ++i)
344 {
345 if (path_fwd[i])
346 path_arcs[i]->flow += bottleneck;
347 else
348 path_arcs[i]->flow -= bottleneck;
349 }
350
351 total_flow += bottleneck;
352
353 // Retreat to the node whose outgoing arc was the bottleneck.
354 // That arc is now saturated, so this node will advance its
355 // current_arc past it on the next iteration.
356 depth = bottleneck_pos + 1;
357 continue;
358 }
359
360 // Try to advance from curr using its current-arc index
361 size_t &ca = dinic_current_arc<Net>(curr);
362 bool advanced = false;
363
364 while (ca < curr->num_arcs)
365 {
366 Arc *arc = static_cast<Arc *>(curr->arc_array[ca]);
367 Node *next = net.get_connected_node(arc, curr);
368
369 // Only follow edges to the next level
370 if (dinic_level<Net>(next) != dinic_level<Net>(curr) + 1)
371 {
372 ++ca;
373 continue;
374 }
375
376 // Skip blocked nodes (all their arcs are exhausted)
378 {
379 ++ca;
380 continue;
381 }
382
383 // Check residual capacity
384 const bool is_forward = (net.get_src_node(arc) == curr);
385 Flow_Type residual = is_forward
386 ? (arc->cap - arc->flow)
387 : arc->flow;
388
389 if (residual <= Flow_Type{0})
390 {
391 ++ca;
392 continue;
393 }
394
395 // Valid arc — push next onto the path
396 path_arcs[depth - 1] = arc;
397 path_fwd[depth - 1] = is_forward ? 1 : 0;
398 stack[depth] = next;
399 ++depth;
400 advanced = true;
401 break;
402 }
403
404 if (not advanced)
405 {
406 // Dead end — block this node and retreat
407 dinic_blocked<Net>(curr) = true;
408 --depth;
409 }
410 }
411
412 return total_flow;
413 }
414
456 template <class Net>
458 {
459 using Node = typename Net::Node;
460 using Flow_Type = typename Net::Flow_Type;
461
463 << "Network must have single source and single sink";
464
465 Node *source = net.get_source();
466 Node *sink = net.get_sink();
467
468 if (source == sink)
469 return Flow_Type{0};
470
471 // Allocate node info first so it outlives cookie_saver.
472 // C++ destroys locals in reverse order: cookie_saver restores
473 // original cookies before node_info storage is freed.
474 auto node_info = Array<Dinic_Node_Info<Flow_Type>>::create(net.vsize());
475 Cookie_Saver<Net> cookie_saver(net, true, false); // save nodes only
476 size_t idx = 0;
477 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne(), ++idx)
478 NODE_COOKIE(it.get_curr()) = &node_info[idx];
479
480 Flow_Type max_flow{0};
481
482 // Main loop: build level graph and find blocking flows
483 while (build_level_graph(net, source, sink))
484 max_flow += dinic_blocking_flow(net, source, sink);
485
486 // Cookie_Saver destructor will restore original NODE_COOKIE values
487 return max_flow;
488 }
489
493 template <class Net>
495 {
510 typename Net::Flow_Type operator()(Net & net) const
511 {
512 return dinic_maximum_flow(net);
513 }
514 };
515
516
517 //==============================================================================
518 // CAPACITY SCALING
519 //==============================================================================
520
556 template <class Net>
558 {
559 using Node = typename Net::Node;
560 using Arc = typename Net::Arc;
561 using Flow_Type = typename Net::Flow_Type;
562
564 << "Network must have single source and single sink";
565
566 Node *source = net.get_source();
567 Node *sink = net.get_sink();
568
569 // Find maximum capacity to determine starting Δ
570 Flow_Type max_cap{0};
571 for (Arc_Iterator<Net> it(net); it.has_curr(); it.next_ne())
572 max_cap = std::max(max_cap, it.get_curr()->cap);
573
574 if (max_cap == Flow_Type{0})
575 return Flow_Type{0};
576
577 // Start with the largest power of 2 ≤ max_cap
578 Flow_Type delta{1};
579 while (delta <= max_cap / 2)
580 delta *= 2;
581
582 Flow_Type max_flow{0};
583
584 // BFS augmentation helper.
585 // Finds and augments all s-t paths where every edge has
586 // residual >= min_residual. Returns total flow found.
588 {
590 bool found_path = true;
591 while (found_path)
592 {
593 found_path = false;
594
596 DynMapTree<Node *, Arc *> parent_arc;
598
600 queue.put(source);
601 parent[source] = nullptr;
602
603 while (not queue.is_empty() and not parent.has(sink))
604 {
605 Node *curr = queue.front();
606 queue.get();
607
608 for (typename Net::Node_Arc_Iterator it(curr); it.has_curr();
609 it.next_ne())
610 {
611 Arc *arc = it.get_curr();
612 Node *next = net.get_connected_node(arc, curr);
613
614 if (parent.has(next))
615 continue;
616
617 Flow_Type residual;
618 bool forward = (net.get_src_node(arc) == curr);
619
620 if (forward)
621 residual = arc->cap - arc->flow;
622 else
623 residual = arc->flow;
624
625 if (residual >= min_residual and residual > Flow_Type{0})
626 {
627 parent[next] = curr;
628 parent_arc[next] = arc;
629 is_forward[next] = forward;
630 queue.put(next);
631 }
632 }
633 }
634
635 if (parent.has(sink))
636 {
637 found_path = true;
638
639 Flow_Type path_flow = std::numeric_limits<Flow_Type>::max();
640 for (Node *n = sink; n != source; n = parent[n])
641 {
642 Arc *arc = parent_arc[n];
643 Flow_Type residual = is_forward[n] ?
644 (arc->cap - arc->flow) :
645 arc->flow;
646 path_flow = std::min(path_flow, residual);
647 }
648
649 for (Node *n = sink; n != source; n = parent[n])
650 {
651 Arc *arc = parent_arc[n];
652 if (is_forward[n])
653 arc->flow += path_flow;
654 else
655 arc->flow -= path_flow;
656 }
657
659 }
660 }
661 return phase_flow;
662 };
663
664 // Scaling phases (delta = largest power of 2 down to 1)
665 while (delta >= Flow_Type{1})
666 {
667 max_flow += bfs_augment(delta);
668 delta /= 2;
669 }
670
671 // Final cleanup: capture any fractional residual flow.
672 // For integer capacities the scaling phases already found all flow,
673 // so this is a single O(V+E) no-op BFS.
674 max_flow += bfs_augment(Flow_Type{0});
675
676 return max_flow;
677 }
678
682 template <class Net>
684 {
699 typename Net::Flow_Type operator()(Net & net) const
700 {
702 }
703 };
704
705
706 //==============================================================================
707 // FLOW DECOMPOSITION
708 //==============================================================================
709
719 template <class Net>
720 struct FlowPath
721 {
722 using Arc = typename Net::Arc;
723 using Node = typename Net::Node;
724 using Flow_Type = typename Net::Flow_Type;
725
729
737 [[nodiscard]] bool is_empty() const noexcept { return arcs.is_empty(); }
738
746 [[nodiscard]] size_t length() const noexcept { return arcs.size(); }
747 };
748
757 template <class Net>
759 {
760 using Arc = typename Net::Arc;
761 using Node = typename Net::Node;
762 using Flow_Type = typename Net::Flow_Type;
763
767 };
768
776 template <class Net>
778 {
779 using Flow_Type = typename Net::Flow_Type;
780
783
792 {
793 Flow_Type sum{0};
794 for (auto it = paths.get_it(); it.has_curr(); it.next_ne())
795 sum += it.get_curr().flow;
796 return sum;
797 }
798
806 [[nodiscard]] size_t num_paths() const noexcept { return paths.size(); }
807
815 [[nodiscard]] size_t num_cycles() const noexcept { return cycles.size(); }
816 };
817
859 template <class Net>
861 {
862 using Node = typename Net::Node;
863 using Arc = typename Net::Arc;
864 using Flow_Type = typename Net::Flow_Type;
865
867 << "Network must have single source and single sink";
868
870
871 // Create a copy of flow values (we'll modify them during decomposition)
873 for (Arc_Iterator<Net> it(net); it.has_curr(); it.next_ne())
874 {
875 Arc *arc = it.get_curr();
876 remaining_flow[arc] = arc->flow;
877 }
878
879 Node *source = net.get_source();
880 Node *sink = net.get_sink();
881
882 // Find paths from source to sink
883 while (true)
884 {
885 // Find an arc from source with positive remaining flow
886 Arc *start_arc = nullptr;
887 for (typename Net::Node_Arc_Iterator it(source); it.has_curr(); it.next_ne())
888 {
889 Arc *arc = it.get_curr();
890 if (net.get_src_node(arc) == source and remaining_flow[arc] > Flow_Type{0})
891 {
892 start_arc = arc;
893 break;
894 }
895 }
896
897 if (start_arc == nullptr)
898 break; // No more flow from source
899
900 // Build path by following positive flow, tracking visited nodes to detect cycles
901 FlowPath<Net> path;
902 path.nodes.append(source);
903
904 Node *curr = net.get_tgt_node(start_arc);
905 path.arcs.append(start_arc);
906 path.nodes.append(curr);
908
909 // Track visited nodes and their position in path for cycle detection
911 visited[source] = 0;
912 visited[curr] = 1;
913
914 while (curr != sink)
915 {
916 // Find outgoing arc with positive flow
917 Arc *next_arc = nullptr;
918 for (typename Net::Node_Arc_Iterator it(curr); it.has_curr(); it.next_ne())
919 {
920 Arc *arc = it.get_curr();
921 if (net.get_src_node(arc) == curr and remaining_flow[arc] > Flow_Type{0})
922 {
923 next_arc = arc;
924 break;
925 }
926 }
927
928 if (next_arc == nullptr)
929 break; // No outgoing arc with a positive flow
930
932
933 // Check if we've visited this node before (cycle detected)
934 if (visited.contains(next_node))
935 {
936 // Extract the cycle starting from the repeated node
937 const size_t cycle_start = visited[next_node];
940
941 // Build cycle from the position where we first saw next_node
942 auto node_it = path.nodes.get_it();
943 auto arc_it = path.arcs.get_it();
944
945 // Skip to the cycle start position
946 for (size_t i = 0; i < cycle_start; ++i)
947 {
948 node_it.next_ne();
949 arc_it.next_ne();
950 }
951
952 // Collect cycle nodes and arcs, compute minimum flow
953 while (arc_it.has_curr())
954 {
955 cycle.nodes.append(node_it.get_curr());
956 cycle.arcs.append(arc_it.get_curr());
957 cycle.flow = std::min(cycle.flow, remaining_flow[arc_it.get_curr()]);
958 node_it.next_ne();
959 arc_it.next_ne();
960 }
961 // Add the closing arc and closing node to complete the cycle
962 cycle.arcs.append(next_arc);
963 cycle.flow = std::min(cycle.flow, remaining_flow[next_arc]);
964 cycle.nodes.append(next_node);
965
966 // Subtract cycle flow from cycle arcs
967 for (auto it = cycle.arcs.get_it(); it.has_curr(); it.next_ne())
968 remaining_flow[it.get_curr()] -= cycle.flow;
969
970 if (cycle.flow > Flow_Type{0})
971 result.cycles.append(std::move(cycle));
972
973 // At this point we have removed all flow along the detected cycle
974 // from 'remaining_flow'. We intentionally discard the current
975 // partial path (the prefix up to 'cycle_start') and restart path
976 // construction from the source in the outer loop. This does not
977 // lose any flow because the prefix arcs were not modified and
978 // remain available for future paths/cycles. The trade-off is that
979 // we may re-traverse some nodes/edges, which is acceptable here
980 // under the assumption that cycles in the residual flow are rare.
981 break;
982 }
983
984 path.arcs.append(next_arc);
985 path.flow = std::min(path.flow, remaining_flow[next_arc]);
986 curr = next_node;
987 path.nodes.append(curr);
988 visited[curr] = path.nodes.size() - 1;
989 }
990
991 // Subtract path flow from all arcs (only if we reached sink)
992 if (curr == sink and path.flow > Flow_Type{0})
993 {
994 for (auto it = path.arcs.get_it(); it.has_curr(); it.next_ne())
995 remaining_flow[it.get_curr()] -= path.flow;
996 result.paths.append(std::move(path));
997 }
998 }
999
1000 // Second phase: find cycles in the remaining flow (not connected to source)
1001 // These are cycles that exist independently of the s-t flow
1002 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1003 {
1004 Node *start = it.get_curr();
1005
1006 // Try to find a cycle starting from this node
1007 while (true)
1008 {
1009 // Find an outgoing arc with positive remaining flow
1010 Arc *first_arc = nullptr;
1011 for (typename Net::Node_Arc_Iterator ait(start); ait.has_curr(); ait.next_ne())
1012 {
1013 Arc *arc = ait.get_curr();
1014 if (net.get_src_node(arc) == start and remaining_flow[arc] > Flow_Type{0})
1015 {
1016 first_arc = arc;
1017 break;
1018 }
1019 }
1020
1021 if (first_arc == nullptr)
1022 break; // No outgoing flow from this node
1023
1024 // Follow the flow to find a cycle
1029
1030 path_nodes.append(start);
1031 visited[start] = 0;
1032
1033 Node *curr = net.get_tgt_node(first_arc);
1035 path_nodes.append(curr);
1036
1037 bool found_cycle = false;
1038
1039 while (not found_cycle)
1040 {
1041 if (visited.contains(curr))
1042 {
1043 // Found a cycle starting at 'curr'
1044 const size_t cycle_start = visited[curr];
1045 cycle.flow = std::numeric_limits<Flow_Type>::max();
1046
1047 // Skip to cycle start in path
1048 auto node_it = path_nodes.get_it();
1049 auto arc_it = path_arcs.get_it();
1050 for (size_t i = 0; i < cycle_start; ++i)
1051 {
1052 node_it.next_ne();
1053 arc_it.next_ne();
1054 }
1055
1056 // Collect cycle and find minimum flow
1057 while (arc_it.has_curr())
1058 {
1059 cycle.nodes.append(node_it.get_curr());
1060 cycle.arcs.append(arc_it.get_curr());
1061 cycle.flow = std::min(cycle.flow, remaining_flow[arc_it.get_curr()]);
1062 node_it.next_ne();
1063 arc_it.next_ne();
1064 }
1065 // Add the closing node to complete the cycle (as Phase 1 does)
1066 cycle.nodes.append(curr);
1067
1068 found_cycle = true;
1069 break;
1070 }
1071
1072 visited[curr] = path_nodes.size() - 1;
1073
1074 // Find next arc with positive flow
1075 Arc *next_arc = nullptr;
1076 for (typename Net::Node_Arc_Iterator ait(curr); ait.has_curr(); ait.next_ne())
1077 {
1078 Arc *arc = ait.get_curr();
1079 if (net.get_src_node(arc) == curr and remaining_flow[arc] > Flow_Type{0})
1080 {
1081 next_arc = arc;
1082 break;
1083 }
1084 }
1085
1086 if (next_arc == nullptr)
1087 break; // Dead end, no cycle from this path
1088
1090 curr = net.get_tgt_node(next_arc);
1091 path_nodes.append(curr);
1092 }
1093
1094 if (found_cycle and cycle.flow > Flow_Type{0})
1095 {
1096 // Subtract cycle flow from all cycle arcs
1097 for (auto cit = cycle.arcs.get_it(); cit.has_curr(); cit.next_ne())
1098 remaining_flow[cit.get_curr()] -= cycle.flow;
1099 result.cycles.append(std::move(cycle));
1100 }
1101 else
1102 break; // No cycle found from this start node
1103 }
1104 }
1105
1106 return result;
1107 }
1108
1112 template <class Net>
1114 {
1130 {
1131 return decompose_flow(net);
1132 }
1133 };
1134
1135
1136 //==============================================================================
1137 // HIGHEST LABEL PREFLOW-PUSH (HLPP)
1138 //==============================================================================
1139
1144 template <typename Flow_Type>
1146 {
1147 long height = 0;
1149 };
1150
1152 template <class Net>
1153 inline long &hlpp_height(typename Net::Node *p) noexcept
1154 {
1155 return static_cast<HLPP_Node_Info<typename Net::Flow_Type> *>
1156 (NODE_COOKIE(p))->height;
1157 }
1158
1160 template <class Net>
1161 inline typename Net::Flow_Type &hlpp_excess(typename Net::Node *p) noexcept
1162 {
1163 return static_cast<HLPP_Node_Info<typename Net::Flow_Type> *>
1164 (NODE_COOKIE(p))->excess;
1165 }
1166
1182 template <class Net>
1184 {
1185 using Node = typename Net::Node;
1186 using Arc = typename Net::Arc;
1187 using Flow_Type = typename Net::Flow_Type;
1188
1190 << "Network must have single source and single sink";
1191
1192 Node *source = net.get_source();
1193 Node *sink = net.get_sink();
1194 const size_t n_nodes = net.vsize();
1195 const long n = static_cast<long>(n_nodes);
1196
1197 // Allocate node info first so it outlives cookie_saver.
1198 // C++ destroys locals in reverse order: cookie_saver restores
1199 // original cookies before node_info storage is freed.
1200 auto node_info = Array<HLPP_Node_Info<Flow_Type>>::create(n_nodes);
1201 Cookie_Saver<Net> cookie_saver(net, true, false); // save nodes only
1202 {
1203 size_t idx = 0;
1204 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne(), ++idx)
1205 NODE_COOKIE(it.get_curr()) = &node_info[idx];
1206 }
1207
1208 // --- Initialize heights via reverse BFS from sink ---
1209 // All heights start at n (unreachable); BFS sets exact distances
1210 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1211 hlpp_height<Net>(it.get_curr()) = n;
1212
1213 hlpp_height<Net>(sink) = 0;
1214
1215 {
1217 queue.put(sink);
1218 while (not queue.is_empty())
1219 {
1220 Node *curr = queue.get();
1221 for (typename Net::Node_Arc_Iterator ait(curr);
1222 ait.has_curr(); ait.next_ne())
1223 {
1224 auto arc = ait.get_curr();
1225 auto next = net.get_connected_node(arc, curr);
1226
1227 if (next == source or
1228 hlpp_height<Net>(next) != n)
1229 continue;
1230
1231 // In the residual graph (flow = 0 initially), there is a
1232 // residual arc next→curr iff the original arc goes
1233 // next→curr with cap > 0.
1234 if (net.get_src_node(arc) == next and
1235 arc->cap > Flow_Type{0})
1236 {
1238 queue.put(next);
1239 }
1240 }
1241 }
1242 }
1243
1244 // Source always has height = n
1245 hlpp_height<Net>(source) = n;
1246
1247 // --- Saturate all arcs from source ---
1248 for (typename Net::Node_Arc_Iterator it(source); it.has_curr(); it.next_ne())
1249 if (Arc *arc = it.get_curr(); net.get_src_node(arc) == source)
1250 {
1251 Flow_Type delta = arc->cap - arc->flow;
1252 arc->flow = arc->cap;
1253 hlpp_excess<Net>(net.get_tgt_node(arc)) += delta;
1254 hlpp_excess<Net>(source) -= delta;
1255 }
1256
1257 // --- Initialize bucket structure ---
1258 const size_t bucket_count = static_cast<size_t>(2 * n + 1);
1260 long max_height = 0;
1261
1262 // Add active nodes to buckets
1263 for (Node_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1264 {
1265 auto p = it.get_curr();
1266 if (p != source and p != sink and
1268 {
1269 long h = hlpp_height<Net>(p);
1270 buckets[h].append(p);
1271 max_height = std::max(max_height, h);
1272 }
1273 }
1274
1275 // --- Main push/relabel loop ---
1276 while (max_height >= 0)
1277 {
1278 if (buckets[max_height].is_empty())
1279 {
1280 --max_height;
1281 continue;
1282 }
1283
1285
1286 // Push/Relabel
1287 bool pushed = false;
1288 long min_height = 2 * n;
1289
1290 for (typename Net::Node_Arc_Iterator it(u);
1292 it.next_ne())
1293 {
1294 Arc *arc = it.get_curr();
1295 Node *v = net.get_connected_node(arc, u);
1296
1297 Flow_Type residual;
1298 const bool is_forward = (net.get_src_node(arc) == u);
1299 if (is_forward)
1300 residual = arc->cap - arc->flow;
1301 else
1302 residual = arc->flow;
1303
1304 if (residual > Flow_Type{0})
1305 {
1306 if (hlpp_height<Net>(u) == hlpp_height<Net>(v) + 1)
1307 {
1308 // Push
1309 Flow_Type delta = std::min(hlpp_excess<Net>(u), residual);
1310 if (is_forward)
1311 arc->flow += delta;
1312 else
1313 arc->flow -= delta;
1314
1315 const bool was_inactive =
1316 (v != source and v != sink and
1317 hlpp_excess<Net>(v) <= Flow_Type{0});
1318
1319 hlpp_excess<Net>(u) -= delta;
1320 hlpp_excess<Net>(v) += delta;
1321
1323 {
1325 max_height = std::max(max_height, hlpp_height<Net>(v));
1326 }
1327
1328 pushed = true;
1329 }
1330 else
1331 min_height = std::min(min_height, hlpp_height<Net>(v));
1332 }
1333 }
1334
1335 if (hlpp_excess<Net>(u) > Flow_Type{0})
1336 {
1337 if (not pushed)
1338 {
1339 if (min_height >= 2 * n)
1340 continue; // no admissible neighbour; drop the node
1342 }
1344 max_height = std::max(max_height, hlpp_height<Net>(u));
1345 }
1346 }
1347
1348 return hlpp_excess<Net>(sink);
1349 }
1350
1354 template <class Net>
1356 {
1371 typename Net::Flow_Type operator()(Net & net) const
1372 {
1373 return hlpp_maximum_flow(net);
1374 }
1375 };
1376
1377
1378 //==============================================================================
1379 // FLOW STATISTICS
1380 //==============================================================================
1381
1385 template <typename Flow_Type>
1387 {
1391 size_t num_empty_arcs{0};
1393 double utilization{0.0};
1394
1405 [[nodiscard]] std::string to_string() const
1406 {
1407 std::ostringstream oss;
1408 oss << "=== Flow Statistics ===\n"
1409 << "Total flow: " << total_flow << "\n"
1410 << "Total capacity: " << total_capacity << "\n"
1411 << "Saturated arcs: " << num_saturated_arcs << "\n"
1412 << "Empty arcs: " << num_empty_arcs << "\n"
1413 << "Partial arcs: " << num_partial_arcs << "\n"
1414 << "Utilization: " << std::fixed << std::setprecision(2)
1415 << (utilization * 100) << "%\n";
1416 return oss.str();
1417 }
1418
1428 void print() const
1429 {
1430 std::cout << to_string();
1431 }
1432 };
1433
1446 template <class Net>
1448 {
1449 using Flow_Type = typename Net::Flow_Type;
1450
1452
1453 for (Arc_Iterator<Net> it(net); it.has_curr(); it.next_ne())
1454 {
1455 auto arc = it.get_curr();
1456 stats.total_capacity += arc->cap;
1457
1458 if (arc->flow == arc->cap)
1459 ++stats.num_saturated_arcs;
1460 else if (arc->flow == Flow_Type{0})
1461 ++stats.num_empty_arcs;
1462 else
1463 ++stats.num_partial_arcs;
1464 }
1465
1466 if (net.is_single_source())
1467 stats.total_flow = net.flow_value();
1468
1469 if (stats.total_capacity > Flow_Type{0})
1470 stats.utilization = static_cast<double>(stats.total_flow) /
1471 static_cast<double>(stats.total_capacity);
1472
1473 return stats;
1474 }
1475} // namespace Aleph
1476
1477#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:522
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:219
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:191
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.
Dynamic singly linked list with functional programming support.
Definition htlist.H:1423
T & append(const T &item)
Append a new item by copy.
Definition htlist.H:1562
Generic key-value map implemented on top of a binary search tree.
bool has(const Key &key) const noexcept
void next_ne() noexcept
Advances the iterator to the next filtered element (noexcept version).
constexpr bool is_empty() const noexcept
Return true if list is empty.
Definition htlist.H:523
size_t size() const noexcept
Count the number of elements of the list.
Definition htlist.H:1319
Filtered iterator on the nodes of a graph.
Definition tpl_graph.H:1206
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
constexpr size_t vsize() const noexcept
Definition graph-dry.H:698
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
RAII guards for graph node/arc cookies.
#define NODE_COOKIE(p)
Return the node cookie
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.
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:175
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.
DynList< T > maps(const C &c, Op op)
Classic map operation.
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:1164
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:822
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.