Skip to main content

weavatrix_graph/algo/flow/
push_relabel.rs

1use super::MaxFlow;
2use super::common::{finish, prepare, zero};
3use super::cut::indexed_nodes;
4use crate::{GraphError, IndexGraphView, Result};
5use alloc::collections::VecDeque;
6
7/// Computes maximum flow with a deterministic FIFO push-relabel algorithm.
8///
9/// # Errors
10///
11/// Returns an error when the maximum flow exceeds `u64::MAX`.
12pub 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}