Skip to main content

weavatrix_graph/algo/flow/
core.rs

1use super::cut::{indexed_nodes, residual_reachable};
2use crate::{GraphError, IndexGraphView, Result};
3use std::collections::VecDeque;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6pub struct MaxFlow<Node, Edge> {
7    value: u64,
8    edge_flows: Vec<(Edge, u64)>,
9    source_side: Vec<Node>,
10}
11
12impl<Node, Edge> MaxFlow<Node, Edge> {
13    #[must_use]
14    pub const fn value(&self) -> u64 {
15        self.value
16    }
17
18    #[must_use]
19    pub fn edge_flows(&self) -> &[(Edge, u64)] {
20        &self.edge_flows
21    }
22
23    #[must_use]
24    pub fn source_side(&self) -> &[Node] {
25        &self.source_side
26    }
27}
28
29/// Computes a directed maximum flow with Dinic's blocking-flow algorithm.
30///
31/// The returned source-side residual partition also describes a minimum cut.
32///
33/// # Errors
34///
35/// Returns an error if the total flow is larger than `u64::MAX`.
36pub fn maximum_flow<G, F>(
37    graph: &G,
38    source: G::Node,
39    sink: G::Node,
40    mut edge_capacity: F,
41) -> Result<Option<MaxFlow<G::Node, G::Edge>>>
42where
43    G: IndexGraphView,
44    F: FnMut(G::Edge) -> u64,
45{
46    if !graph.contains_node(source) || !graph.contains_node(sink) {
47        return Ok(None);
48    }
49    let mut capacities = vec![0_u64; graph.edge_bound()];
50    let mut edge_order = Vec::with_capacity(graph.edge_count());
51    for edge in graph.edge_indices() {
52        capacities[G::edge_slot(edge)] = edge_capacity(edge);
53        edge_order.push(edge);
54    }
55    if source == sink {
56        return Ok(Some(MaxFlow {
57            value: 0,
58            edge_flows: edge_order.into_iter().map(|edge| (edge, 0)).collect(),
59            source_side: vec![source],
60        }));
61    }
62
63    let mut flows = vec![0_u64; graph.edge_bound()];
64    let mut levels = vec![-1_i32; graph.node_bound()];
65    let mut level_edges = vec![Vec::new(); graph.node_bound()];
66    let mut total = 0_u64;
67    while build_levels(
68        graph,
69        source,
70        sink,
71        &capacities,
72        &flows,
73        &mut levels,
74        &mut level_edges,
75    ) {
76        let mut next = vec![0_usize; graph.node_bound()];
77        loop {
78            let pushed = augment_level_path::<G>(
79                G::node_slot(source),
80                G::node_slot(sink),
81                &level_edges,
82                &mut next,
83                &capacities,
84                &mut flows,
85            );
86            if pushed == 0 {
87                break;
88            }
89            total = total
90                .checked_add(pushed)
91                .ok_or(GraphError::ArithmeticOverflow {
92                    operation: "maximum flow",
93                })?;
94        }
95    }
96
97    let edge_flows = edge_order
98        .into_iter()
99        .map(|edge| (edge, flows[G::edge_slot(edge)]))
100        .collect();
101    let reachable = residual_reachable(graph, source, &capacities, &flows);
102    let source_side = graph
103        .node_indices()
104        .filter(|node| reachable[G::node_slot(*node)])
105        .collect();
106    Ok(Some(MaxFlow {
107        value: total,
108        edge_flows,
109        source_side,
110    }))
111}
112
113#[derive(Clone, Copy)]
114struct ResidualEdge<Edge> {
115    edge: Edge,
116    target: usize,
117    forward: bool,
118}
119
120fn build_levels<G>(
121    graph: &G,
122    source: G::Node,
123    sink: G::Node,
124    capacities: &[u64],
125    flows: &[u64],
126    levels: &mut [i32],
127    level_edges: &mut [Vec<ResidualEdge<G::Edge>>],
128) -> bool
129where
130    G: IndexGraphView,
131{
132    levels.fill(-1);
133    for edges in &mut *level_edges {
134        edges.clear();
135    }
136    let source = G::node_slot(source);
137    let sink = G::node_slot(sink);
138    levels[source] = 0;
139    let mut queue = VecDeque::from([source]);
140    let nodes = indexed_nodes(graph);
141    while let Some(node) = queue.pop_front() {
142        let Some(node_key) = nodes[node] else {
143            continue;
144        };
145        for edge in graph.outgoing_edges(node_key) {
146            let slot = G::edge_slot(edge);
147            if capacities[slot] > flows[slot] {
148                let target = graph
149                    .edge_endpoints(edge)
150                    .map(|endpoints| G::node_slot(endpoints.target()));
151                add_level_edge(edge, target, true, node, levels, level_edges, &mut queue);
152            }
153        }
154        for edge in graph.incoming_edges(node_key) {
155            let slot = G::edge_slot(edge);
156            if flows[slot] > 0 {
157                let target = graph
158                    .edge_endpoints(edge)
159                    .map(|endpoints| G::node_slot(endpoints.source()));
160                add_level_edge(edge, target, false, node, levels, level_edges, &mut queue);
161            }
162        }
163    }
164    levels[sink] >= 0
165}
166
167fn add_level_edge<Edge: Copy>(
168    edge: Edge,
169    target: Option<usize>,
170    forward: bool,
171    source: usize,
172    levels: &mut [i32],
173    level_edges: &mut [Vec<ResidualEdge<Edge>>],
174    queue: &mut VecDeque<usize>,
175) {
176    let Some(target) = target else {
177        return;
178    };
179    if levels[target] < 0 {
180        levels[target] = levels[source] + 1;
181        queue.push_back(target);
182    }
183    if levels[target] == levels[source] + 1 {
184        level_edges[source].push(ResidualEdge {
185            edge,
186            target,
187            forward,
188        });
189    }
190}
191
192fn augment_level_path<G>(
193    source: usize,
194    sink: usize,
195    level_edges: &[Vec<ResidualEdge<G::Edge>>],
196    next: &mut [usize],
197    capacities: &[u64],
198    flows: &mut [u64],
199) -> u64
200where
201    G: IndexGraphView,
202{
203    let mut nodes = vec![source];
204    let mut path: Vec<ResidualEdge<G::Edge>> = Vec::new();
205    loop {
206        let node = *nodes.last().unwrap_or(&source);
207        if node == sink {
208            let amount = path
209                .iter()
210                .map(|step| residual::<G>(*step, capacities, flows))
211                .min()
212                .unwrap_or(0);
213            for step in &path {
214                let slot = G::edge_slot(step.edge);
215                if step.forward {
216                    flows[slot] += amount;
217                } else {
218                    flows[slot] -= amount;
219                }
220            }
221            return amount;
222        }
223        let Some(&step) = level_edges[node].get(next[node]) else {
224            if node == source {
225                return 0;
226            }
227            nodes.pop();
228            path.pop();
229            let parent = *nodes.last().unwrap_or(&source);
230            next[parent] += 1;
231            continue;
232        };
233        if residual::<G>(step, capacities, flows) > 0 {
234            nodes.push(step.target);
235            path.push(step);
236        } else {
237            next[node] += 1;
238        }
239    }
240}
241
242fn residual<G>(step: ResidualEdge<G::Edge>, capacities: &[u64], flows: &[u64]) -> u64
243where
244    G: IndexGraphView,
245{
246    let slot = G::edge_slot(step.edge);
247    if step.forward {
248        capacities[slot] - flows[slot]
249    } else {
250        flows[slot]
251    }
252}