1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
//! Shared machinery for the maximum s-t flow algorithms.
//!
//! Every concrete max-flow algorithm in this crate (for example [`Dinic`] and
//! [`EdmondsKarp`]) computes the same object: a maximum flow value, a feasible
//! per-arc flow, and a minimum cut on a directed, capacitated graph. The result
//! and error types and the residual-graph construction are therefore factored
//! out here, so each algorithm only has to supply its own augmentation loop.
//!
//! Entry `(i, j)` of the matrix is the capacity of the directed arc `i -> j`.
//! Capacities must be finite and non-negative, and self-loops are ignored. The
//! matrix is read forward only (its rows are the out-arcs), and the reverse
//! residual arcs are created internally with zero capacity, so antiparallel
//! input arcs `i -> j` and `j -> i` stay independent.
//!
//! [`Dinic`]: crate::traits::algorithms::Dinic
//! [`EdmondsKarp`]: crate::traits::algorithms::EdmondsKarp
use alloc::{collections::VecDeque, vec::Vec};
use num_traits::{AsPrimitive, Zero};
use crate::traits::{Finite, Number, SparseValuedMatrix2D, SquareMatrix};
/// Error enumeration shared by the maximum-flow algorithms.
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
pub enum MaxFlowError {
/// The source node index is not smaller than the number of nodes.
#[error("The source node {source_id} is out of range for a graph with {order} nodes.")]
SourceOutOfRange {
/// The offending source index.
source_id: usize,
/// The number of nodes in the graph.
order: usize,
},
/// The sink node index is not smaller than the number of nodes.
#[error("The sink node {sink} is out of range for a graph with {order} nodes.")]
SinkOutOfRange {
/// The offending sink index.
sink: usize,
/// The number of nodes in the graph.
order: usize,
},
/// The source and the sink coincide, so the flow is undefined.
#[error("The source and the sink must differ, but both are {node}.")]
SourceEqualsSink {
/// The shared source and sink index.
node: usize,
},
/// An arc carries a non-finite capacity.
#[error("Found a non-finite capacity on arc ({source_id}, {destination_id}).")]
NonFiniteCapacity {
/// Source node identifier of the offending arc.
source_id: usize,
/// Destination node identifier of the offending arc.
destination_id: usize,
},
/// An arc carries a negative capacity.
#[error("Found a negative capacity on arc ({source_id}, {destination_id}).")]
NegativeCapacity {
/// Source node identifier of the offending arc.
source_id: usize,
/// Destination node identifier of the offending arc.
destination_id: usize,
},
}
/// Result of a maximum s-t flow computation.
#[derive(Debug, Clone, PartialEq)]
pub struct MaxFlowResult<V> {
/// The value of the maximum flow from the source to the sink.
max_flow: V,
/// Per-arc flow `(source, destination, flow)` for every original arc that
/// carries a strictly positive flow.
flows: Vec<(usize, usize, V)>,
/// The arcs `(source, destination)` of a minimum cut: the original arcs
/// that leave the source side of the residual partition. Their
/// capacities sum to the maximum flow.
min_cut: Vec<(usize, usize)>,
/// The source side of the minimum cut: `source_side[node]` is `true` when
/// `node` is still reachable from the source in the residual graph.
source_side: Vec<bool>,
}
impl<V: Copy> MaxFlowResult<V> {
/// The value of the maximum flow from the source to the sink.
#[must_use]
pub fn max_flow(&self) -> V {
self.max_flow
}
/// The per-arc flow on every original arc that carries positive flow, as
/// `(source, destination, flow)` triples.
#[must_use]
pub fn flows(&self) -> &[(usize, usize, V)] {
&self.flows
}
/// The arcs crossing a minimum cut, as `(source, destination)` pairs. Their
/// capacities sum to [`MaxFlowResult::max_flow`].
#[must_use]
pub fn min_cut(&self) -> &[(usize, usize)] {
&self.min_cut
}
/// The source side of the minimum cut: `source_side()[node]` is `true` when
/// `node` remains reachable from the source in the residual graph.
#[must_use]
pub fn source_side(&self) -> &[bool] {
&self.source_side
}
}
/// Residual graph in paired-arc form shared by the max-flow algorithms.
///
/// Arc `e` has reverse arc `e ^ 1`, and `arc_to[e ^ 1]` is the tail of `e`.
/// Forward arcs start at their capacity, reverse arcs at zero. Algorithms push
/// flow by mutating [`Residual::arc_cap`] in place, then call
/// [`Residual::finish`] to read out the flow and the minimum cut.
pub(super) struct Residual<V> {
/// Per-node lists of incident residual arc indices.
pub(super) adjacency: Vec<Vec<usize>>,
/// Head node of each residual arc.
pub(super) arc_to: Vec<usize>,
/// Remaining residual capacity of each arc.
pub(super) arc_cap: Vec<V>,
/// Original forward arcs as `(tail, head, arc_index, original_capacity)`.
forward_arcs: Vec<(usize, usize, usize, V)>,
/// The source node index.
source: usize,
}
impl<V: Number> Residual<V> {
/// Reads out the flow and the minimum cut after an algorithm has pushed a
/// maximum flow of value `max_flow` into [`Residual::arc_cap`].
pub(super) fn finish(self, max_flow: V) -> MaxFlowResult<V> {
let zero = V::zero();
let order = self.adjacency.len();
// Per-arc flow is the capacity consumed on each original forward arc.
let mut flows: Vec<(usize, usize, V)> = Vec::new();
for &(tail, head, forward, capacity) in &self.forward_arcs {
let flow = capacity - self.arc_cap[forward];
if flow > zero {
flows.push((tail, head, flow));
}
}
// The source side is everything still reachable from the source in the
// residual graph. Original arcs leaving it form the minimum cut.
let mut source_side = alloc::vec![false; order];
source_side[self.source] = true;
let mut queue = VecDeque::new();
queue.push_back(self.source);
while let Some(node) = queue.pop_front() {
for &arc in &self.adjacency[node] {
let next = self.arc_to[arc];
if self.arc_cap[arc] > zero && !source_side[next] {
source_side[next] = true;
queue.push_back(next);
}
}
}
let mut min_cut: Vec<(usize, usize)> = Vec::new();
for &(tail, head, _forward, _capacity) in &self.forward_arcs {
if source_side[tail] && !source_side[head] {
min_cut.push((tail, head));
}
}
MaxFlowResult { max_flow, flows, min_cut, source_side }
}
}
/// Builds the residual graph from a square capacity matrix, validating the
/// source, the sink, and every capacity.
///
/// # Errors
///
/// Returns an error when `source` or `sink` is out of range, when they
/// coincide, or when a non-self-loop arc carries a non-finite or negative
/// capacity.
pub(super) fn build_residual<M>(
matrix: &M,
source: usize,
sink: usize,
) -> Result<Residual<M::Value>, MaxFlowError>
where
M: SparseValuedMatrix2D + SquareMatrix + ?Sized,
M::Value: Number + Finite,
{
let order: usize = matrix.order().as_();
if source >= order {
return Err(MaxFlowError::SourceOutOfRange { source_id: source, order });
}
if sink >= order {
return Err(MaxFlowError::SinkOutOfRange { sink, order });
}
if source == sink {
return Err(MaxFlowError::SourceEqualsSink { node: source });
}
let zero = M::Value::zero();
// Map each node index to its matrix row handle so a row can be addressed by
// `usize`.
let handles: Vec<M::Index> = matrix.row_indices().collect();
let mut adjacency: Vec<Vec<usize>> = alloc::vec![Vec::new(); order];
let mut arc_to: Vec<usize> = Vec::new();
let mut arc_cap: Vec<M::Value> = Vec::new();
let mut forward_arcs: Vec<(usize, usize, usize, M::Value)> = Vec::new();
for (tail, &row) in handles.iter().enumerate() {
for (column, capacity) in matrix.sparse_row(row).zip(matrix.sparse_row_values(row)) {
let head = column.as_();
if tail == head {
// Self-loops cannot carry s-t flow and are ignored.
continue;
}
if !capacity.is_finite() {
return Err(MaxFlowError::NonFiniteCapacity {
source_id: tail,
destination_id: head,
});
}
if capacity < zero {
return Err(MaxFlowError::NegativeCapacity {
source_id: tail,
destination_id: head,
});
}
if capacity == zero {
continue;
}
let forward = arc_to.len();
adjacency[tail].push(forward);
arc_to.push(head);
arc_cap.push(capacity);
adjacency[head].push(forward + 1);
arc_to.push(tail);
arc_cap.push(zero);
forward_arcs.push((tail, head, forward, capacity));
}
}
Ok(Residual { adjacency, arc_to, arc_cap, forward_arcs, source })
}