1use crate::error::{GraphalgError, GraphalgResult};
42use crate::max_flow::edmonds_karp::FlowNetwork;
43use crate::max_flow::min_cut::min_cut_from_max_flow;
44
45#[derive(Debug, Clone)]
51pub struct GomoryHuTree {
52 pub n: usize,
54 pub parent: Vec<usize>,
56 pub weight: Vec<f64>,
58}
59
60impl GomoryHuTree {
61 pub fn tree_edges(&self) -> Vec<(usize, usize, f64)> {
63 let mut edges = Vec::with_capacity(self.n.saturating_sub(1));
64 for i in 1..self.n {
65 let u = i.min(self.parent[i]);
66 let v = i.max(self.parent[i]);
67 edges.push((u, v, self.weight[i]));
68 }
69 edges
70 }
71
72 pub fn min_cut(&self, s: usize, t: usize) -> GraphalgResult<f64> {
82 if s >= self.n {
83 return Err(GraphalgError::IndexOutOfBounds {
84 index: s,
85 len: self.n,
86 });
87 }
88 if t >= self.n {
89 return Err(GraphalgError::IndexOutOfBounds {
90 index: t,
91 len: self.n,
92 });
93 }
94 if s == t {
95 return Ok(0.0);
96 }
97
98 let depth = self.depths();
101 let (mut a, mut b) = (s, t);
102 let mut best = f64::INFINITY;
103
104 while depth[a] > depth[b] {
106 best = best.min(self.weight[a]);
107 a = self.parent[a];
108 }
109 while depth[b] > depth[a] {
110 best = best.min(self.weight[b]);
111 b = self.parent[b];
112 }
113 while a != b {
115 best = best.min(self.weight[a]);
116 a = self.parent[a];
117 best = best.min(self.weight[b]);
118 b = self.parent[b];
119 }
120 Ok(best)
121 }
122
123 fn depths(&self) -> Vec<usize> {
125 let mut depth = vec![0usize; self.n];
126 let mut resolved = vec![false; self.n];
130 resolved[0] = true;
131 for start in 1..self.n {
132 let mut chain = Vec::new();
134 let mut cur = start;
135 while !resolved[cur] {
136 chain.push(cur);
137 cur = self.parent[cur];
138 }
139 let mut d = depth[cur];
140 for &node in chain.iter().rev() {
141 d += 1;
142 depth[node] = d;
143 resolved[node] = true;
144 }
145 }
146 depth
147 }
148}
149
150fn build_undirected_network(
155 n: usize,
156 edges: &[(usize, usize, f64)],
157) -> GraphalgResult<FlowNetwork> {
158 let mut net = FlowNetwork::new(n);
159 for &(u, v, w) in edges {
160 if u >= n || v >= n {
161 return Err(GraphalgError::IndexOutOfBounds {
162 index: u.max(v),
163 len: n,
164 });
165 }
166 if !w.is_finite() {
167 return Err(GraphalgError::InvalidEdgeWeight(format!(
168 "edge ({u},{v}) weight not finite: {w}"
169 )));
170 }
171 if w < 0.0 {
172 return Err(GraphalgError::NegativeWeight {
173 edge: (u, v),
174 weight: w,
175 });
176 }
177 if u == v {
178 continue;
180 }
181 net.add_edge(u, v, w)?;
182 net.add_edge(v, u, w)?;
183 }
184 Ok(net)
185}
186
187pub fn gomory_hu_tree(n: usize, edges: &[(usize, usize, f64)]) -> GraphalgResult<GomoryHuTree> {
199 if n == 0 {
200 return Ok(GomoryHuTree {
201 n: 0,
202 parent: Vec::new(),
203 weight: Vec::new(),
204 });
205 }
206 if n == 1 {
207 return Ok(GomoryHuTree {
208 n: 1,
209 parent: vec![0],
210 weight: vec![0.0],
211 });
212 }
213
214 let net = build_undirected_network(n, edges)?;
215
216 let mut parent = vec![0usize; n];
218 let mut weight = vec![0.0f64; n];
219
220 for s in 1..n {
221 let t = parent[s];
222 let cut = min_cut_from_max_flow(&net, s, t)?;
223 let f = cut.value;
224 weight[s] = f;
225
226 let mut in_source = vec![false; n];
228 for &v in &cut.source_side {
229 in_source[v] = true;
230 }
231
232 for v in 0..n {
234 if v != s && parent[v] == t && in_source[v] {
235 parent[v] = s;
236 }
237 }
238
239 let pt = parent[t];
241 if in_source[pt] {
242 parent[s] = pt;
243 parent[t] = s;
244 weight[s] = weight[t];
245 weight[t] = f;
246 }
247 }
248
249 Ok(GomoryHuTree { n, parent, weight })
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255 use crate::max_flow::edmonds_karp::FlowNetwork;
256 use crate::max_flow::min_cut::min_cut_from_max_flow;
257
258 fn direct_min_cut(n: usize, edges: &[(usize, usize, f64)], s: usize, t: usize) -> f64 {
260 let mut net = FlowNetwork::new(n);
261 for &(u, v, w) in edges {
262 if u == v {
263 continue;
264 }
265 net.add_edge(u, v, w).expect("add ok");
266 net.add_edge(v, u, w).expect("add ok");
267 }
268 min_cut_from_max_flow(&net, s, t).expect("cut ok").value
269 }
270
271 fn assert_all_pairs(n: usize, edges: &[(usize, usize, f64)]) {
273 let tree = gomory_hu_tree(n, edges).expect("tree ok");
274 for s in 0..n {
275 for t in 0..n {
276 if s == t {
277 continue;
278 }
279 let tree_cut = tree.min_cut(s, t).expect("min_cut ok");
280 let oracle = direct_min_cut(n, edges, s, t);
281 assert!(
282 (tree_cut - oracle).abs() < 1e-6,
283 "pair ({s},{t}): tree={tree_cut} oracle={oracle}"
284 );
285 }
286 }
287 }
288
289 #[test]
290 fn all_pairs_match_on_weighted_graph() {
291 let edges = [
293 (0, 1, 1.0),
294 (0, 2, 7.0),
295 (1, 2, 1.0),
296 (1, 3, 3.0),
297 (1, 4, 2.0),
298 (2, 4, 4.0),
299 (3, 4, 1.0),
300 (3, 5, 6.0),
301 (4, 5, 2.0),
302 ];
303 assert_all_pairs(6, &edges);
304 }
305
306 #[test]
307 fn all_pairs_match_on_dense_small() {
308 let edges = [
310 (0, 1, 3.0),
311 (0, 2, 1.0),
312 (0, 3, 5.0),
313 (1, 2, 4.0),
314 (1, 3, 2.0),
315 (2, 3, 6.0),
316 ];
317 assert_all_pairs(4, &edges);
318 }
319
320 #[test]
321 fn tree_has_n_minus_one_edges_and_is_connected() {
322 let edges = [
323 (0, 1, 2.0),
324 (1, 2, 3.0),
325 (2, 3, 4.0),
326 (3, 0, 5.0),
327 (0, 2, 1.0),
328 ];
329 let n = 4;
330 let tree = gomory_hu_tree(n, &edges).expect("tree ok");
331 let tree_edges = tree.tree_edges();
332 assert_eq!(tree_edges.len(), n - 1);
333
334 let mut comp: Vec<usize> = (0..n).collect();
336 fn find(comp: &mut [usize], x: usize) -> usize {
337 let mut r = x;
338 while comp[r] != r {
339 r = comp[r];
340 }
341 let mut c = x;
342 while comp[c] != c {
343 let nxt = comp[c];
344 comp[c] = r;
345 c = nxt;
346 }
347 r
348 }
349 for &(u, v, _) in &tree_edges {
350 let (ru, rv) = (find(&mut comp, u), find(&mut comp, v));
351 comp[ru] = rv;
352 }
353 let root = find(&mut comp, 0);
354 for v in 0..n {
355 assert_eq!(find(&mut comp, v), root, "vertex {v} not connected in tree");
356 }
357 }
358
359 #[test]
360 fn simple_cycle_all_pairs() {
361 let edges = [(0, 1, 1.0), (1, 2, 1.0), (2, 3, 1.0), (3, 0, 1.0)];
363 let n = 4;
364 assert_all_pairs(n, &edges);
365 let tree = gomory_hu_tree(n, &edges).expect("tree ok");
366 for s in 0..n {
367 for t in 0..n {
368 if s != t {
369 assert!((tree.min_cut(s, t).expect("ok") - 2.0).abs() < 1e-9);
370 }
371 }
372 }
373 }
374
375 #[test]
376 fn disconnected_components_give_zero_cut() {
377 let edges = [(0, 1, 5.0), (2, 3, 7.0)];
379 let n = 4;
380 let tree = gomory_hu_tree(n, &edges).expect("tree ok");
381 assert!((tree.min_cut(0, 2).expect("ok")).abs() < 1e-9);
383 assert!((tree.min_cut(1, 3).expect("ok")).abs() < 1e-9);
384 assert!((tree.min_cut(0, 1).expect("ok") - 5.0).abs() < 1e-9);
386 assert!((tree.min_cut(2, 3).expect("ok") - 7.0).abs() < 1e-9);
387 assert_all_pairs(n, &edges);
389 }
390
391 #[test]
392 fn cut_is_symmetric() {
393 let edges = [
394 (0, 1, 2.0),
395 (1, 2, 5.0),
396 (2, 3, 1.0),
397 (0, 3, 4.0),
398 (1, 3, 3.0),
399 ];
400 let n = 4;
401 let tree = gomory_hu_tree(n, &edges).expect("tree ok");
402 for s in 0..n {
403 for t in 0..n {
404 let st = tree.min_cut(s, t).expect("ok");
405 let ts = tree.min_cut(t, s).expect("ok");
406 assert!((st - ts).abs() < 1e-12, "asymmetry ({s},{t}): {st} vs {ts}");
407 }
408 }
409 }
410
411 #[test]
412 fn single_edge_graph() {
413 let edges = [(0, 1, 9.0)];
414 let tree = gomory_hu_tree(2, &edges).expect("tree ok");
415 assert_eq!(tree.tree_edges().len(), 1);
416 assert!((tree.min_cut(0, 1).expect("ok") - 9.0).abs() < 1e-9);
417 assert!((tree.min_cut(1, 0).expect("ok") - 9.0).abs() < 1e-9);
418 }
419
420 #[test]
421 fn tiny_two_node_no_edges() {
422 let tree = gomory_hu_tree(2, &[]).expect("tree ok");
423 assert!((tree.min_cut(0, 1).expect("ok")).abs() < 1e-9);
425 assert_eq!(tree.tree_edges().len(), 1);
426 }
427
428 #[test]
429 fn single_node_graph() {
430 let tree = gomory_hu_tree(1, &[]).expect("tree ok");
431 assert_eq!(tree.n, 1);
432 assert_eq!(tree.tree_edges().len(), 0);
433 assert!((tree.min_cut(0, 0).expect("ok")).abs() < 1e-9);
434 }
435
436 #[test]
437 fn empty_graph() {
438 let tree = gomory_hu_tree(0, &[]).expect("tree ok");
439 assert_eq!(tree.n, 0);
440 assert_eq!(tree.tree_edges().len(), 0);
441 }
442
443 #[test]
444 fn same_vertex_is_zero() {
445 let edges = [(0, 1, 3.0), (1, 2, 4.0)];
446 let tree = gomory_hu_tree(3, &edges).expect("tree ok");
447 assert!((tree.min_cut(1, 1).expect("ok")).abs() < 1e-12);
448 }
449
450 #[test]
451 fn parallel_edges_sum() {
452 let edges = [(0, 1, 2.0), (0, 1, 3.0)];
454 let tree = gomory_hu_tree(2, &edges).expect("tree ok");
455 assert!((tree.min_cut(0, 1).expect("ok") - 5.0).abs() < 1e-9);
456 }
457
458 #[test]
459 fn self_loops_ignored() {
460 let edges = [(0, 0, 100.0), (0, 1, 4.0), (1, 1, 50.0)];
461 let tree = gomory_hu_tree(2, &edges).expect("tree ok");
462 assert!((tree.min_cut(0, 1).expect("ok") - 4.0).abs() < 1e-9);
463 }
464
465 #[test]
466 fn rejects_negative_weight() {
467 let edges = [(0, 1, -1.0)];
468 assert!(matches!(
469 gomory_hu_tree(2, &edges),
470 Err(GraphalgError::NegativeWeight { .. })
471 ));
472 }
473
474 #[test]
475 fn rejects_oob_endpoint() {
476 let edges = [(0, 5, 1.0)];
477 assert!(matches!(
478 gomory_hu_tree(3, &edges),
479 Err(GraphalgError::IndexOutOfBounds { .. })
480 ));
481 }
482
483 #[test]
484 fn min_cut_rejects_oob_query() {
485 let tree = gomory_hu_tree(3, &[(0, 1, 1.0), (1, 2, 1.0)]).expect("ok");
486 assert!(tree.min_cut(0, 9).is_err());
487 assert!(tree.min_cut(9, 0).is_err());
488 }
489
490 #[test]
491 fn star_graph_all_pairs() {
492 let edges = [(0, 1, 1.0), (0, 2, 2.0), (0, 3, 3.0)];
495 let n = 4;
496 assert_all_pairs(n, &edges);
497 }
498
499 #[test]
500 fn path_graph_all_pairs() {
501 let edges = [(0, 1, 4.0), (1, 2, 2.0), (2, 3, 6.0), (3, 4, 5.0)];
502 assert_all_pairs(5, &edges);
503 }
504}