rust_igraph/algorithms/flow/
mincut.rs1use crate::core::{Graph, IgraphError, IgraphResult};
21
22use super::st_mincut::{StMincut, st_mincut};
23
24pub type Mincut = StMincut;
29
30pub fn mincut(graph: &Graph, capacity: Option<&[f64]>) -> IgraphResult<Mincut> {
69 let n = graph.vcount();
70
71 if let Some(c) = capacity {
72 let m = graph.ecount();
73 if c.len() != m {
74 return Err(IgraphError::InvalidArgument(format!(
75 "mincut: capacity length {} does not match edge count {}",
76 c.len(),
77 m
78 )));
79 }
80 for (i, &v) in c.iter().enumerate() {
81 if !v.is_finite() || v < 0.0 {
82 return Err(IgraphError::InvalidArgument(format!(
83 "mincut: capacity[{i}] = {v} is not a finite non-negative number"
84 )));
85 }
86 }
87 }
88
89 if n <= 1 {
90 return Ok(Mincut {
91 value: f64::INFINITY,
92 cut: Vec::new(),
93 partition: if n == 1 { vec![0] } else { Vec::new() },
94 partition2: Vec::new(),
95 });
96 }
97
98 let directed = graph.is_directed();
99 let mut best: Option<Mincut> = None;
100
101 for v in 1..n {
102 let result = st_mincut(graph, 0, v, capacity)?;
103 let is_better = best.as_ref().is_none_or(|b| result.value < b.value);
104 if is_better {
105 if result.value == 0.0 {
106 return Ok(result);
107 }
108 best = Some(result);
109 }
110
111 if directed {
112 let result2 = st_mincut(graph, v, 0, capacity)?;
113 let is_better2 = best.as_ref().is_none_or(|b| result2.value < b.value);
114 if is_better2 {
115 if result2.value == 0.0 {
116 return Ok(result2);
117 }
118 best = Some(result2);
119 }
120 }
121 }
122
123 best.ok_or(IgraphError::Internal(
124 "mincut: no iteration completed despite n >= 2",
125 ))
126}
127
128#[cfg(test)]
129#[allow(clippy::float_cmp)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn mincut_empty_graph() {
135 let g = Graph::new(0, false).unwrap();
136 let mc = mincut(&g, None).unwrap();
137 assert_eq!(mc.value, f64::INFINITY);
138 assert!(mc.cut.is_empty());
139 }
140
141 #[test]
142 fn mincut_single_vertex() {
143 let g = Graph::new(1, false).unwrap();
144 let mc = mincut(&g, None).unwrap();
145 assert_eq!(mc.value, f64::INFINITY);
146 assert_eq!(mc.partition, vec![0]);
147 }
148
149 #[test]
150 fn mincut_disconnected() {
151 let g = Graph::new(3, false).unwrap();
152 let mc = mincut(&g, None).unwrap();
153 assert!((mc.value - 0.0).abs() < 1e-12);
154 assert!(mc.cut.is_empty());
155 }
156
157 #[test]
158 fn mincut_path() {
159 let mut g = Graph::new(3, false).unwrap();
160 g.add_edge(0, 1).unwrap();
161 g.add_edge(1, 2).unwrap();
162 let mc = mincut(&g, None).unwrap();
163 assert!((mc.value - 1.0).abs() < 1e-12);
164 assert_eq!(mc.cut.len(), 1);
165 assert_eq!(mc.partition.len() + mc.partition2.len(), 3);
166 }
167
168 #[test]
169 fn mincut_ring() {
170 let mut g = Graph::new(5, false).unwrap();
171 for (u, v) in [(0u32, 1u32), (1, 2), (2, 3), (3, 4), (4, 0)] {
172 g.add_edge(u, v).unwrap();
173 }
174 let mc = mincut(&g, None).unwrap();
175 assert!((mc.value - 2.0).abs() < 1e-12);
176 assert_eq!(mc.cut.len(), 2);
177 }
178
179 #[test]
180 fn mincut_weighted() {
181 let mut g = Graph::new(3, false).unwrap();
183 g.add_edge(0, 1).unwrap();
184 g.add_edge(1, 2).unwrap();
185 g.add_edge(2, 0).unwrap();
186 let caps = vec![10.0, 1.0, 10.0];
187 let mc = mincut(&g, Some(&caps)).unwrap();
188 assert!((mc.value - 11.0).abs() < 1e-12);
197 }
198
199 #[test]
200 fn mincut_directed_ring() {
201 let mut g = Graph::new(3, true).unwrap();
203 g.add_edge(0, 1).unwrap();
204 g.add_edge(1, 2).unwrap();
205 g.add_edge(2, 0).unwrap();
206 let mc = mincut(&g, None).unwrap();
207 assert!((mc.value - 1.0).abs() < 1e-12);
208 assert_eq!(mc.cut.len(), 1);
209 }
210
211 #[test]
212 fn mincut_k4() {
213 let mut g = Graph::new(4, false).unwrap();
215 g.add_edge(0, 1).unwrap();
216 g.add_edge(0, 2).unwrap();
217 g.add_edge(0, 3).unwrap();
218 g.add_edge(1, 2).unwrap();
219 g.add_edge(1, 3).unwrap();
220 g.add_edge(2, 3).unwrap();
221 let mc = mincut(&g, None).unwrap();
222 assert!((mc.value - 3.0).abs() < 1e-12);
223 assert_eq!(mc.cut.len(), 3);
224 }
225
226 #[test]
227 fn mincut_invalid_capacity_len() {
228 let mut g = Graph::new(2, false).unwrap();
229 g.add_edge(0, 1).unwrap();
230 assert!(mincut(&g, Some(&[1.0, 2.0])).is_err());
231 }
232}