1use crate::base::{DiGraph, EdgeWeight, Graph, IndexType, Node};
6use crate::error::{GraphError, Result};
7use std::collections::{HashMap, VecDeque};
8use std::hash::Hash;
9
10pub fn minimum_cut<N, E, Ix>(graph: &Graph<N, E, Ix>) -> Result<(f64, Vec<bool>)>
15where
16 N: Node + Clone + Hash + Eq,
17 E: EdgeWeight + Into<f64> + Clone,
18 Ix: IndexType,
19{
20 let nodes: Vec<N> = graph.nodes().into_iter().cloned().collect();
21 let n = nodes.len();
22
23 if n < 2 {
24 return Err(GraphError::InvalidGraph(
25 "Graph must have at least 2 nodes".to_string(),
26 ));
27 }
28
29 if n <= 10 {
31 let mut min_cut_value = f64::INFINITY;
32 let mut best_partition = vec![false; n];
33
34 for mask in 1..(1 << n) - 1 {
36 let mut partition = vec![false; n];
37 for (i, p) in partition.iter_mut().enumerate().take(n) {
38 *p = (mask & (1 << i)) != 0;
39 }
40
41 let cut_value = calculate_cut_value(graph, &nodes, &partition);
43
44 if cut_value < min_cut_value {
45 min_cut_value = cut_value;
46 best_partition = partition;
47 }
48 }
49
50 Ok((min_cut_value, best_partition))
51 } else {
52 minimum_cut_heuristic(graph, &nodes)
55 }
56}
57
58fn calculate_cut_value<N, E, Ix>(graph: &Graph<N, E, Ix>, nodes: &[N], partition: &[bool]) -> f64
59where
60 N: Node + Clone + Hash + Eq,
61 E: EdgeWeight + Into<f64>,
62 Ix: IndexType,
63{
64 let mut cut_value = 0.0;
65
66 for (i, node_i) in nodes.iter().enumerate() {
67 if let Ok(neighbors) = graph.neighbors(node_i) {
68 for neighbor in neighbors {
69 if let Some(j) = nodes.iter().position(|n| n == &neighbor) {
70 if partition[i] && !partition[j] {
72 if let Ok(weight) = graph.edge_weight(node_i, &neighbor) {
73 cut_value += weight.into();
74 }
75 }
76 }
77 }
78 }
79 }
80
81 cut_value
82}
83
84fn minimum_cut_heuristic<N, E, Ix>(graph: &Graph<N, E, Ix>, nodes: &[N]) -> Result<(f64, Vec<bool>)>
85where
86 N: Node + Clone + Hash + Eq,
87 E: EdgeWeight + Into<f64> + Clone,
88 Ix: IndexType,
89{
90 let n = nodes.len();
91 let mut best_cut_value = f64::INFINITY;
92 let mut best_partition = vec![false; n];
93
94 use rand::Rng;
96 let mut rng = rand::rng();
97
98 for _ in 0..10 {
99 let mut partition = vec![false; n];
100 let size_a = rng.random_range(1..n);
101
102 let mut indices: Vec<usize> = (0..n).collect();
104 use rand::seq::SliceRandom;
105 indices.shuffle(&mut rng);
106
107 for i in 0..size_a {
108 partition[indices[i]] = true;
109 }
110
111 let cut_value = calculate_cut_value(graph, nodes, &partition);
112
113 if cut_value < best_cut_value {
114 best_cut_value = cut_value;
115 best_partition = partition;
116 }
117 }
118
119 Ok((best_cut_value, best_partition))
120}
121
122pub fn dinic_max_flow<N, E, Ix>(graph: &DiGraph<N, E, Ix>, source: &N, sink: &N) -> Result<f64>
136where
137 N: Node + Clone + Hash + Eq,
138 E: EdgeWeight + Into<f64> + Clone + std::ops::Sub<Output = E> + num_traits::Zero + PartialOrd,
139 Ix: IndexType,
140{
141 if !graph.contains_node(source) {
142 return Err(GraphError::NodeNotFound);
143 }
144 if !graph.contains_node(sink) {
145 return Err(GraphError::NodeNotFound);
146 }
147 if source == sink {
148 return Err(GraphError::InvalidGraph(
149 "Source and sink cannot be the same".to_string(),
150 ));
151 }
152
153 let mut residual_graph = DinicResidualGraph::new(graph, source, sink)?;
155 let mut max_flow = 0.0f64;
156
157 loop {
159 if !residual_graph.build_level_graph() {
161 break; }
163
164 loop {
166 let flow = residual_graph.send_flow(f64::INFINITY);
167 if flow == 0.0 {
168 break;
169 }
170 max_flow += flow;
171 }
172 }
173
174 Ok(max_flow)
175}
176
177struct DinicResidualGraph<N: Node> {
179 adj: HashMap<N, Vec<(N, f64, usize)>>,
181 source: N,
182 sink: N,
183 level: HashMap<N, i32>,
185 current: HashMap<N, usize>,
187}
188
189impl<N: Node + Clone + Hash + Eq> DinicResidualGraph<N> {
190 fn new<E, Ix>(graph: &DiGraph<N, E, Ix>, source: &N, sink: &N) -> Result<Self>
191 where
192 E: EdgeWeight + Into<f64> + Clone,
193 Ix: IndexType,
194 {
195 let mut adj: HashMap<N, Vec<(N, f64, usize)>> = HashMap::new();
196
197 for node in graph.nodes() {
199 adj.insert(node.clone(), Vec::new());
200 }
201
202 for node in graph.nodes() {
204 if let Ok(successors) = graph.successors(node) {
205 for successor in successors {
206 if let Ok(weight) = graph.edge_weight(node, &successor) {
207 let capacity: f64 = weight.into();
208
209 let forward_idx = adj.get(&successor).map(|v| v.len()).unwrap_or(0);
211 adj.entry(node.clone()).or_default().push((
212 successor.clone(),
213 capacity,
214 forward_idx,
215 ));
216
217 let backward_idx = adj.get(node).map(|v| v.len() - 1).unwrap_or(0);
219 adj.entry(successor.clone()).or_default().push((
220 node.clone(),
221 0.0,
222 backward_idx,
223 ));
224 }
225 }
226 }
227 }
228
229 Ok(DinicResidualGraph {
230 adj,
231 source: source.clone(),
232 sink: sink.clone(),
233 level: HashMap::new(),
234 current: HashMap::new(),
235 })
236 }
237
238 fn build_level_graph(&mut self) -> bool {
240 self.level.clear();
241 self.current.clear();
242
243 for node in self.adj.keys() {
245 self.level.insert(node.clone(), -1);
246 self.current.insert(node.clone(), 0);
247 }
248
249 let mut queue = VecDeque::new();
251 queue.push_back(self.source.clone());
252 self.level.insert(self.source.clone(), 0);
253
254 while let Some(node) = queue.pop_front() {
255 if let Some(edges) = self.adj.get(&node) {
256 for (neighbor, capacity, _) in edges {
257 if *capacity > 0.0 && self.level.get(neighbor) == Some(&-1) {
258 self.level.insert(neighbor.clone(), self.level[&node] + 1);
259 queue.push_back(neighbor.clone());
260 }
261 }
262 }
263 }
264
265 self.level.get(&self.sink) != Some(&-1)
267 }
268
269 fn send_flow(&mut self, max_flow: f64) -> f64 {
271 self.dfs(&self.source.clone(), max_flow)
272 }
273
274 fn dfs(&mut self, node: &N, flow: f64) -> f64 {
275 if node == &self.sink {
276 return flow;
277 }
278
279 let current_idx = *self.current.get(node).unwrap_or(&0);
280 let node_level = *self.level.get(node).unwrap_or(&-1);
281
282 if let Some(edges) = self.adj.get(node).cloned() {
283 for i in current_idx..edges.len() {
284 let (neighbor, capacity, rev_idx) = &edges[i];
285 let neighbor_level = *self.level.get(neighbor).unwrap_or(&-1);
286
287 if *capacity > 0.0 && neighbor_level == node_level + 1 {
288 let bottleneck = flow.min(*capacity);
289 let pushed = self.dfs(neighbor, bottleneck);
290
291 if pushed > 0.0 {
292 if let Some(node_edges) = self.adj.get_mut(node) {
294 node_edges[i].1 -= pushed; }
296 if let Some(neighbor_edges) = self.adj.get_mut(neighbor) {
297 neighbor_edges[*rev_idx].1 += pushed; }
299 return pushed;
300 }
301 }
302
303 self.current.insert(node.clone(), i + 1);
305 }
306 }
307
308 0.0
309 }
310}
311
312pub fn push_relabel_max_flow<N, E, Ix>(
325 graph: &DiGraph<N, E, Ix>,
326 source: &N,
327 sink: &N,
328) -> Result<f64>
329where
330 N: Node + Clone + Hash + Eq,
331 E: EdgeWeight + Into<f64> + Clone,
332 Ix: IndexType,
333{
334 if !graph.contains_node(source) {
335 return Err(GraphError::NodeNotFound);
336 }
337 if !graph.contains_node(sink) {
338 return Err(GraphError::NodeNotFound);
339 }
340 if source == sink {
341 return Err(GraphError::InvalidGraph(
342 "Source and sink cannot be the same".to_string(),
343 ));
344 }
345
346 let nodes: Vec<N> = graph.nodes().into_iter().cloned().collect();
348 let n = nodes.len();
349 let mut capacity = vec![vec![0.0f64; n]; n];
350 let mut flow = vec![vec![0.0f64; n]; n];
351 let mut height = vec![0; n];
352 let mut excess = vec![0.0f64; n];
353
354 let node_to_idx: HashMap<N, usize> = nodes
356 .iter()
357 .enumerate()
358 .map(|(i, node)| (node.clone(), i))
359 .collect();
360
361 let source_idx = *node_to_idx.get(source).unwrap();
362 let sink_idx = *node_to_idx.get(sink).unwrap();
363
364 for node in &nodes {
366 if let Ok(successors) = graph.successors(node) {
367 let from_idx = node_to_idx[node];
368 for successor in successors {
369 let to_idx = node_to_idx[&successor];
370 if let Ok(weight) = graph.edge_weight(node, &successor) {
371 capacity[from_idx][to_idx] = weight.into();
372 }
373 }
374 }
375 }
376
377 height[source_idx] = n;
379 for i in 0..n {
380 if capacity[source_idx][i] > 0.0 {
381 flow[source_idx][i] = capacity[source_idx][i];
382 flow[i][source_idx] = -capacity[source_idx][i];
383 excess[i] = capacity[source_idx][i];
384 }
385 }
386
387 loop {
389 let mut active_node = None;
391 for (i, &excess_val) in excess.iter().enumerate().take(n) {
392 if i != source_idx && i != sink_idx && excess_val > 0.0 {
393 active_node = Some(i);
394 break;
395 }
396 }
397
398 if let Some(u) = active_node {
399 let mut pushed = false;
400
401 for v in 0..n {
403 if capacity[u][v] - flow[u][v] > 0.0 && height[u] == height[v] + 1 {
404 let delta = excess[u].min(capacity[u][v] - flow[u][v]);
405 flow[u][v] += delta;
406 flow[v][u] -= delta;
407 excess[u] -= delta;
408 excess[v] += delta;
409 pushed = true;
410
411 if excess[u] == 0.0 {
412 break;
413 }
414 }
415 }
416
417 if !pushed {
419 let mut min_height = usize::MAX;
420 for (v, &height_val) in height.iter().enumerate().take(n) {
421 if capacity[u][v] - flow[u][v] > 0.0 {
422 min_height = min_height.min(height_val);
423 }
424 }
425 if min_height < usize::MAX {
426 height[u] = min_height + 1;
427 }
428 }
429 } else {
430 break; }
432 }
433
434 let total_flow: f64 = (0..n).map(|i| flow[source_idx][i]).sum();
436 Ok(total_flow)
437}
438
439#[cfg(test)]
440mod tests {
441 use super::*;
442 use crate::error::Result as GraphResult;
443 use crate::generators::create_graph;
444
445 #[test]
446 fn test_minimum_cut_simple() -> GraphResult<()> {
447 let mut graph = create_graph::<&str, f64>();
448
449 graph.add_edge("A", "B", 10.0)?;
452 graph.add_edge("B", "C", 10.0)?;
453 graph.add_edge("C", "A", 10.0)?;
454
455 graph.add_edge("D", "E", 10.0)?;
457 graph.add_edge("E", "F", 10.0)?;
458 graph.add_edge("F", "D", 10.0)?;
459
460 graph.add_edge("C", "D", 1.0)?;
462
463 let (cut_value, partition) = minimum_cut(&graph)?;
464
465 assert!((cut_value - 1.0).abs() < 1e-6);
467
468 let nodes: Vec<&str> = graph.nodes().into_iter().cloned().collect();
470 let cluster1: Vec<bool> = nodes.iter().map(|n| ["A", "B", "C"].contains(n)).collect();
471 let cluster2: Vec<bool> = nodes.iter().map(|n| ["D", "E", "F"].contains(n)).collect();
472
473 let matches_cluster1 = partition.iter().zip(&cluster1).all(|(a, b)| a == b);
475 let matches_cluster2 = partition.iter().zip(&cluster2).all(|(a, b)| a == b);
476
477 assert!(matches_cluster1 || matches_cluster2);
478
479 Ok(())
480 }
481
482 #[test]
483 fn test_minimum_cut_single_edge() -> GraphResult<()> {
484 let mut graph = create_graph::<&str, f64>();
485
486 graph.add_edge("A", "B", 5.0)?;
488
489 let (cut_value, partition) = minimum_cut(&graph)?;
490
491 assert_eq!(cut_value, 5.0);
493
494 assert_eq!(partition.iter().filter(|&&x| x).count(), 1);
496 assert_eq!(partition.iter().filter(|&&x| !x).count(), 1);
497
498 Ok(())
499 }
500
501 #[test]
502 fn test_dinic_max_flow() -> GraphResult<()> {
503 let mut graph = crate::generators::create_digraph::<&str, f64>();
504
505 graph.add_edge("A", "B", 2.0)?;
512 graph.add_edge("A", "C", 1.0)?;
513 graph.add_edge("B", "D", 3.0)?;
514 graph.add_edge("B", "E", 1.0)?;
515 graph.add_edge("C", "E", 4.0)?;
516 graph.add_edge("E", "D", 2.0)?;
517
518 let max_flow = dinic_max_flow(&graph, &"A", &"D")?;
519
520 assert!((max_flow - 3.0).abs() < 1e-6);
524
525 Ok(())
526 }
527
528 #[test]
529 fn test_push_relabel_max_flow() -> GraphResult<()> {
530 let mut graph = crate::generators::create_digraph::<&str, f64>();
531
532 graph.add_edge("S", "A", 10.0)?;
534 graph.add_edge("S", "B", 10.0)?;
535 graph.add_edge("A", "T", 10.0)?;
536 graph.add_edge("B", "T", 10.0)?;
537 graph.add_edge("A", "B", 1.0)?;
538
539 let max_flow = push_relabel_max_flow(&graph, &"S", &"T")?;
540
541 assert!((max_flow - 20.0).abs() < 1e-6);
543
544 Ok(())
545 }
546
547 #[test]
548 fn test_dinic_single_path() -> GraphResult<()> {
549 let mut graph = crate::generators::create_digraph::<&str, f64>();
550
551 graph.add_edge("A", "B", 5.0)?;
553 graph.add_edge("B", "C", 3.0)?;
554
555 let max_flow = dinic_max_flow(&graph, &"A", &"C")?;
556
557 assert!((max_flow - 3.0).abs() < 1e-6);
559
560 Ok(())
561 }
562}