weavatrix_graph/algo/flow/
push_relabel.rs1use super::MaxFlow;
2use super::common::{finish, prepare, zero};
3use super::cut::indexed_nodes;
4use crate::{GraphError, IndexGraphView, Result};
5use alloc::collections::VecDeque;
6
7pub fn push_relabel<G, F>(
13 graph: &G,
14 source: G::Node,
15 sink: G::Node,
16 edge_capacity: F,
17) -> Result<Option<MaxFlow<G::Node, G::Edge>>>
18where
19 G: IndexGraphView,
20 F: FnMut(G::Edge) -> u64,
21{
22 if !graph.contains_node(source) || !graph.contains_node(sink) {
23 return Ok(None);
24 }
25 let input = prepare(graph, edge_capacity);
26 if source == sink {
27 return Ok(Some(zero(graph, source, input)));
28 }
29 let nodes = indexed_nodes(graph);
30 let source_slot = G::node_slot(source);
31 let sink_slot = G::node_slot(sink);
32 let mut flows = vec![0_u64; graph.edge_bound()];
33 let mut excess = vec![0_u128; graph.node_bound()];
34 let mut heights = vec![0_usize; graph.node_bound()];
35 let mut active = vec![false; graph.node_bound()];
36 let mut queue = VecDeque::new();
37 heights[source_slot] = graph.node_count();
38 if let Some(source_node) = nodes[source_slot] {
39 for edge in graph.outgoing_edges(source_node) {
40 let slot = G::edge_slot(edge);
41 let amount = input.capacities[slot];
42 let Some(target) = graph
43 .edge_endpoints(edge)
44 .map(|ends| G::node_slot(ends.target()))
45 else {
46 continue;
47 };
48 flows[slot] = amount;
49 excess[target] += u128::from(amount);
50 activate(
51 target,
52 source_slot,
53 sink_slot,
54 &excess,
55 &mut active,
56 &mut queue,
57 );
58 }
59 }
60 while let Some(node) = queue.pop_front() {
61 active[node] = false;
62 discharge::<G>(
63 graph,
64 &nodes,
65 node,
66 source_slot,
67 sink_slot,
68 &input.capacities,
69 &mut flows,
70 &mut excess,
71 &mut heights,
72 &mut active,
73 &mut queue,
74 );
75 activate(
76 node,
77 source_slot,
78 sink_slot,
79 &excess,
80 &mut active,
81 &mut queue,
82 );
83 }
84 let value = u64::try_from(excess[sink_slot]).map_err(|_| GraphError::ArithmeticOverflow {
85 operation: "push-relabel maximum flow",
86 })?;
87 Ok(Some(finish(graph, source, value, input, &flows)))
88}
89
90#[allow(clippy::too_many_arguments)]
91fn discharge<G>(
92 graph: &G,
93 nodes: &[Option<G::Node>],
94 node: usize,
95 source: usize,
96 sink: usize,
97 capacities: &[u64],
98 flows: &mut [u64],
99 excess: &mut [u128],
100 heights: &mut [usize],
101 active: &mut [bool],
102 queue: &mut VecDeque<usize>,
103) where
104 G: IndexGraphView,
105{
106 while excess[node] > 0 {
107 let Some(node_key) = nodes[node] else {
108 break;
109 };
110 let mut pushed = false;
111 for edge in graph.outgoing_edges(node_key) {
112 let slot = G::edge_slot(edge);
113 let Some(target) = graph
114 .edge_endpoints(edge)
115 .map(|ends| G::node_slot(ends.target()))
116 else {
117 continue;
118 };
119 let residual = capacities[slot] - flows[slot];
120 if residual > 0 && heights[node] == heights[target] + 1 {
121 let amount = residual.min(u64::try_from(excess[node]).unwrap_or(u64::MAX));
122 flows[slot] += amount;
123 transfer(node, target, amount, source, sink, excess, active, queue);
124 pushed = true;
125 if excess[node] == 0 {
126 break;
127 }
128 }
129 }
130 if excess[node] == 0 {
131 break;
132 }
133 for edge in graph.incoming_edges(node_key) {
134 let slot = G::edge_slot(edge);
135 let Some(target) = graph
136 .edge_endpoints(edge)
137 .map(|ends| G::node_slot(ends.source()))
138 else {
139 continue;
140 };
141 if flows[slot] > 0 && heights[node] == heights[target] + 1 {
142 let amount = flows[slot].min(u64::try_from(excess[node]).unwrap_or(u64::MAX));
143 flows[slot] -= amount;
144 transfer(node, target, amount, source, sink, excess, active, queue);
145 pushed = true;
146 if excess[node] == 0 {
147 break;
148 }
149 }
150 }
151 if excess[node] > 0 && !pushed {
152 heights[node] =
153 minimum_residual_height::<G>(graph, node_key, capacities, flows, heights)
154 .map_or(graph.node_bound().saturating_mul(2), |height| height + 1);
155 }
156 }
157}
158
159fn minimum_residual_height<G>(
160 graph: &G,
161 node: G::Node,
162 capacities: &[u64],
163 flows: &[u64],
164 heights: &[usize],
165) -> Option<usize>
166where
167 G: IndexGraphView,
168{
169 let outgoing = graph.outgoing_edges(node).filter_map(|edge| {
170 let slot = G::edge_slot(edge);
171 (capacities[slot] > flows[slot])
172 .then(|| {
173 graph
174 .edge_endpoints(edge)
175 .map(|ends| heights[G::node_slot(ends.target())])
176 })
177 .flatten()
178 });
179 let incoming = graph.incoming_edges(node).filter_map(|edge| {
180 (flows[G::edge_slot(edge)] > 0)
181 .then(|| {
182 graph
183 .edge_endpoints(edge)
184 .map(|ends| heights[G::node_slot(ends.source())])
185 })
186 .flatten()
187 });
188 outgoing.chain(incoming).min()
189}
190
191#[allow(clippy::too_many_arguments)]
192fn transfer(
193 source: usize,
194 target: usize,
195 amount: u64,
196 flow_source: usize,
197 sink: usize,
198 excess: &mut [u128],
199 active: &mut [bool],
200 queue: &mut VecDeque<usize>,
201) {
202 excess[source] -= u128::from(amount);
203 excess[target] += u128::from(amount);
204 activate(target, flow_source, sink, excess, active, queue);
205}
206
207fn activate(
208 node: usize,
209 source: usize,
210 sink: usize,
211 excess: &[u128],
212 active: &mut [bool],
213 queue: &mut VecDeque<usize>,
214) {
215 if node != source && node != sink && excess[node] > 0 && !active[node] {
216 active[node] = true;
217 queue.push_back(node);
218 }
219}