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 Ok(best.expect("n >= 2 guarantees at least one iteration"))
124}
125
126#[cfg(test)]
127#[allow(clippy::float_cmp)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn mincut_empty_graph() {
133 let g = Graph::new(0, false).unwrap();
134 let mc = mincut(&g, None).unwrap();
135 assert_eq!(mc.value, f64::INFINITY);
136 assert!(mc.cut.is_empty());
137 }
138
139 #[test]
140 fn mincut_single_vertex() {
141 let g = Graph::new(1, false).unwrap();
142 let mc = mincut(&g, None).unwrap();
143 assert_eq!(mc.value, f64::INFINITY);
144 assert_eq!(mc.partition, vec![0]);
145 }
146
147 #[test]
148 fn mincut_disconnected() {
149 let g = Graph::new(3, false).unwrap();
150 let mc = mincut(&g, None).unwrap();
151 assert!((mc.value - 0.0).abs() < 1e-12);
152 assert!(mc.cut.is_empty());
153 }
154
155 #[test]
156 fn mincut_path() {
157 let mut g = Graph::new(3, false).unwrap();
158 g.add_edge(0, 1).unwrap();
159 g.add_edge(1, 2).unwrap();
160 let mc = mincut(&g, None).unwrap();
161 assert!((mc.value - 1.0).abs() < 1e-12);
162 assert_eq!(mc.cut.len(), 1);
163 assert_eq!(mc.partition.len() + mc.partition2.len(), 3);
164 }
165
166 #[test]
167 fn mincut_ring() {
168 let mut g = Graph::new(5, false).unwrap();
169 for (u, v) in [(0u32, 1u32), (1, 2), (2, 3), (3, 4), (4, 0)] {
170 g.add_edge(u, v).unwrap();
171 }
172 let mc = mincut(&g, None).unwrap();
173 assert!((mc.value - 2.0).abs() < 1e-12);
174 assert_eq!(mc.cut.len(), 2);
175 }
176
177 #[test]
178 fn mincut_weighted() {
179 let mut g = Graph::new(3, false).unwrap();
181 g.add_edge(0, 1).unwrap();
182 g.add_edge(1, 2).unwrap();
183 g.add_edge(2, 0).unwrap();
184 let caps = vec![10.0, 1.0, 10.0];
185 let mc = mincut(&g, Some(&caps)).unwrap();
186 assert!((mc.value - 11.0).abs() < 1e-12);
195 }
196
197 #[test]
198 fn mincut_directed_ring() {
199 let mut g = Graph::new(3, true).unwrap();
201 g.add_edge(0, 1).unwrap();
202 g.add_edge(1, 2).unwrap();
203 g.add_edge(2, 0).unwrap();
204 let mc = mincut(&g, None).unwrap();
205 assert!((mc.value - 1.0).abs() < 1e-12);
206 assert_eq!(mc.cut.len(), 1);
207 }
208
209 #[test]
210 fn mincut_k4() {
211 let mut g = Graph::new(4, false).unwrap();
213 g.add_edge(0, 1).unwrap();
214 g.add_edge(0, 2).unwrap();
215 g.add_edge(0, 3).unwrap();
216 g.add_edge(1, 2).unwrap();
217 g.add_edge(1, 3).unwrap();
218 g.add_edge(2, 3).unwrap();
219 let mc = mincut(&g, None).unwrap();
220 assert!((mc.value - 3.0).abs() < 1e-12);
221 assert_eq!(mc.cut.len(), 3);
222 }
223
224 #[test]
225 fn mincut_invalid_capacity_len() {
226 let mut g = Graph::new(2, false).unwrap();
227 g.add_edge(0, 1).unwrap();
228 assert!(mincut(&g, Some(&[1.0, 2.0])).is_err());
229 }
230}