1pub mod replacement;
14pub mod approximate;
15
16pub use replacement::{ReplacementEdgeIndex, ReplacementIndexStats};
17
18use std::sync::Arc;
19use std::time::Instant;
20use parking_lot::RwLock;
21use crate::graph::{DynamicGraph, VertexId, EdgeId, Weight, Edge};
22use crate::tree::HierarchicalDecomposition;
23use crate::linkcut::LinkCutTree;
24use crate::euler::EulerTourTree;
25use crate::error::{MinCutError, Result};
26
27#[derive(Debug, Clone)]
29pub struct MinCutConfig {
30 pub max_exact_cut_size: usize,
32 pub epsilon: f64,
34 pub approximate: bool,
36 pub parallel: bool,
38 pub cache_size: usize,
40}
41
42impl Default for MinCutConfig {
43 fn default() -> Self {
44 Self {
45 max_exact_cut_size: 1000,
46 epsilon: 0.1,
47 approximate: false,
48 parallel: true,
49 cache_size: 10000,
50 }
51 }
52}
53
54#[derive(Debug, Clone)]
56pub struct MinCutResult {
57 pub value: f64,
59 pub cut_edges: Option<Vec<Edge>>,
61 pub partition: Option<(Vec<VertexId>, Vec<VertexId>)>,
63 pub is_exact: bool,
65 pub approximation_ratio: f64,
67}
68
69#[derive(Debug, Clone, Default)]
71pub struct AlgorithmStats {
72 pub insertions: u64,
74 pub deletions: u64,
76 pub queries: u64,
78 pub avg_update_time_us: f64,
80 pub avg_query_time_us: f64,
82 pub restructures: u64,
84}
85
86pub struct DynamicMinCut {
88 graph: Arc<RwLock<DynamicGraph>>,
90 decomposition: HierarchicalDecomposition,
92 link_cut_tree: LinkCutTree,
94 spanning_forest: EulerTourTree,
96 current_min_cut: f64,
98 config: MinCutConfig,
100 stats: Arc<RwLock<AlgorithmStats>>,
102 tree_edges: Arc<RwLock<std::collections::HashSet<(VertexId, VertexId)>>>,
104}
105
106impl DynamicMinCut {
107 pub fn new(config: MinCutConfig) -> Self {
109 let empty_graph = Arc::new(DynamicGraph::new());
110 Self {
111 graph: Arc::new(RwLock::new(DynamicGraph::new())),
112 decomposition: HierarchicalDecomposition::build(empty_graph).unwrap(),
113 link_cut_tree: LinkCutTree::new(),
114 spanning_forest: EulerTourTree::new(),
115 current_min_cut: f64::INFINITY,
116 config,
117 stats: Arc::new(RwLock::new(AlgorithmStats::default())),
118 tree_edges: Arc::new(RwLock::new(std::collections::HashSet::new())),
119 }
120 }
121
122 pub fn from_graph(graph: DynamicGraph, config: MinCutConfig) -> Result<Self> {
124 let graph_shared = Arc::new(RwLock::new(graph.clone()));
126
127 let mut link_cut_tree = LinkCutTree::new();
129 let mut spanning_forest = EulerTourTree::new();
130
131 let vertices = graph.vertices();
132 for &v in &vertices {
133 link_cut_tree.make_tree(v, 0.0);
134 spanning_forest.make_tree(v)?;
135 }
136
137 let mut visited = std::collections::HashSet::new();
139 let mut tree_edges = std::collections::HashSet::new();
140
141 for &start_vertex in &vertices {
142 if visited.contains(&start_vertex) {
143 continue;
144 }
145
146 let mut stack = vec![start_vertex];
148 visited.insert(start_vertex);
149
150 while let Some(u) = stack.pop() {
151 let neighbors = graph.neighbors(u);
152 for (v, _) in neighbors {
153 if !visited.contains(&v) {
154 visited.insert(v);
155 stack.push(v);
156
157 link_cut_tree.link(u, v)?;
159 spanning_forest.link(u, v)?;
160
161 let key = if u < v { (u, v) } else { (v, u) };
163 tree_edges.insert(key);
164 }
165 }
166 }
167 }
168
169 let graph_for_decomp = Arc::new(graph);
171 let decomposition = HierarchicalDecomposition::build(graph_for_decomp)?;
172
173 let mut mincut = Self {
175 graph: graph_shared,
176 decomposition,
177 link_cut_tree,
178 spanning_forest,
179 current_min_cut: f64::INFINITY,
180 config,
181 stats: Arc::new(RwLock::new(AlgorithmStats::default())),
182 tree_edges: Arc::new(RwLock::new(tree_edges)),
183 };
184
185 mincut.recompute_min_cut();
187
188 Ok(mincut)
189 }
190
191 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> Result<f64> {
193 let start_time = Instant::now();
194
195 {
197 let graph = self.graph.write();
198 graph.insert_edge(u, v, weight)?;
199 }
200
201 let u_exists = self.link_cut_tree.len() > 0 &&
204 self.link_cut_tree.find_root(u).is_ok();
205 let v_exists = self.link_cut_tree.len() > 0 &&
206 self.link_cut_tree.find_root(v).is_ok();
207
208 if !u_exists {
209 self.link_cut_tree.make_tree(u, 0.0);
210 self.spanning_forest.make_tree(u)?;
211 }
212 if !v_exists {
213 self.link_cut_tree.make_tree(v, 0.0);
214 self.spanning_forest.make_tree(v)?;
215 }
216
217 let connected = self.link_cut_tree.connected(u, v);
219
220 if !connected {
221 self.handle_bridge_edge(u, v, weight)?;
223 } else {
224 self.handle_cycle_edge(u, v, weight)?;
226 }
227
228 self.rebuild_decomposition();
230
231 self.recompute_min_cut();
233
234 let elapsed = start_time.elapsed().as_micros() as f64;
236 let mut stats = self.stats.write();
237 stats.insertions += 1;
238 let n = stats.insertions as f64;
239 stats.avg_update_time_us = (stats.avg_update_time_us * (n - 1.0) + elapsed) / n;
240 drop(stats);
241
242 Ok(self.current_min_cut)
243 }
244
245 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<f64> {
247 let start_time = Instant::now();
248
249 {
251 let graph = self.graph.write();
252 graph.delete_edge(u, v)?;
253 }
254
255 let key = if u < v { (u, v) } else { (v, u) };
257 let is_tree_edge = self.tree_edges.read().contains(&key);
258
259 if is_tree_edge {
260 self.handle_tree_edge_deletion(u, v)?;
261 } else {
262 self.handle_non_tree_edge_deletion(u, v)?;
263 }
264
265 self.rebuild_decomposition();
267
268 self.recompute_min_cut();
270
271 let elapsed = start_time.elapsed().as_micros() as f64;
273 let mut stats = self.stats.write();
274 stats.deletions += 1;
275 let n = stats.deletions as f64;
276 stats.avg_update_time_us = (stats.avg_update_time_us * (n - 1.0) + elapsed) / n;
277 drop(stats);
278
279 Ok(self.current_min_cut)
280 }
281
282 pub fn min_cut_value(&self) -> f64 {
284 let start_time = Instant::now();
285
286 let value = self.current_min_cut;
287
288 let elapsed = start_time.elapsed().as_micros() as f64;
290 let mut stats = self.stats.write();
291 stats.queries += 1;
292 let n = stats.queries as f64;
293 stats.avg_query_time_us = (stats.avg_query_time_us * (n - 1.0) + elapsed) / n;
294
295 value
296 }
297
298 pub fn min_cut(&self) -> MinCutResult {
300 let value = self.min_cut_value();
301 let (partition_s, partition_t) = self.partition();
302 let edges = self.cut_edges();
303
304 MinCutResult {
305 value,
306 cut_edges: Some(edges),
307 partition: Some((partition_s, partition_t)),
308 is_exact: !self.config.approximate,
309 approximation_ratio: if self.config.approximate {
310 1.0 + self.config.epsilon
311 } else {
312 1.0
313 },
314 }
315 }
316
317 pub fn partition(&self) -> (Vec<VertexId>, Vec<VertexId>) {
319 let (set_a, set_b) = self.decomposition.min_cut_partition();
321 (set_a.into_iter().collect(), set_b.into_iter().collect())
322 }
323
324 pub fn cut_edges(&self) -> Vec<Edge> {
326 let (partition_s, partition_t) = self.partition();
327 let partition_t_set: std::collections::HashSet<_> = partition_t.iter().copied().collect();
328
329 let graph = self.graph.read();
330 let mut cut_edges = Vec::new();
331
332 for &u in &partition_s {
333 let neighbors = graph.neighbors(u);
334 for (v, _) in neighbors {
335 if partition_t_set.contains(&v) {
336 if let Some(edge) = graph.get_edge(u, v) {
337 cut_edges.push(edge);
338 }
339 }
340 }
341 }
342
343 cut_edges
344 }
345
346 pub fn is_connected(&self) -> bool {
348 let graph = self.graph.read();
349 graph.is_connected()
350 }
351
352 pub fn stats(&self) -> AlgorithmStats {
354 self.stats.read().clone()
355 }
356
357 pub fn reset_stats(&mut self) {
359 *self.stats.write() = AlgorithmStats::default();
360 }
361
362 pub fn config(&self) -> &MinCutConfig {
364 &self.config
365 }
366
367 pub fn graph(&self) -> Arc<RwLock<DynamicGraph>> {
369 Arc::clone(&self.graph)
370 }
371
372 pub fn num_vertices(&self) -> usize {
374 self.graph.read().num_vertices()
375 }
376
377 pub fn num_edges(&self) -> usize {
379 self.graph.read().num_edges()
380 }
381
382 fn handle_cycle_edge(&mut self, _u: VertexId, _v: VertexId, _weight: Weight) -> Result<()> {
386 Ok(())
389 }
390
391 fn handle_bridge_edge(&mut self, u: VertexId, v: VertexId, _weight: Weight) -> Result<()> {
393 self.link_cut_tree.link(u, v)?;
395 self.spanning_forest.link(u, v)?;
396
397 let key = if u < v { (u, v) } else { (v, u) };
399 self.tree_edges.write().insert(key);
400
401 Ok(())
402 }
403
404 fn handle_tree_edge_deletion(&mut self, u: VertexId, v: VertexId) -> Result<()> {
406 let key = if u < v { (u, v) } else { (v, u) };
408 self.tree_edges.write().remove(&key);
409
410 if self.link_cut_tree.connected(u, v) {
413 if self.link_cut_tree.cut(u).is_err() {
416 let _ = self.link_cut_tree.cut(v); }
418 }
419
420 if let Some((x, y)) = self.find_replacement_edge(u, v) {
422 let already_connected = self.link_cut_tree.connected(x, y);
424
425 if !already_connected {
426 self.link_cut_tree.link(x, y)?;
428
429 if !self.spanning_forest.connected(x, y) {
431 let _ = self.spanning_forest.link(x, y); }
433
434 let key = if x < y { (x, y) } else { (y, x) };
436 self.tree_edges.write().insert(key);
437 }
438 } else {
439 self.stats.write().restructures += 1;
442 }
443
444 Ok(())
445 }
446
447 fn handle_non_tree_edge_deletion(&mut self, _u: VertexId, _v: VertexId) -> Result<()> {
449 Ok(())
452 }
453
454 fn find_replacement_edge(&mut self, u: VertexId, v: VertexId) -> Option<(VertexId, VertexId)> {
456 let graph = self.graph.read();
459
460 let mut comp_u = std::collections::HashSet::new();
461 let mut queue = std::collections::VecDeque::new();
462
463 queue.push_back(u);
464 comp_u.insert(u);
465
466 while let Some(x) = queue.pop_front() {
467 let neighbors = graph.neighbors(x);
468 for (y, _) in neighbors {
469 if (x == u && y == v) || (x == v && y == u) {
471 continue;
472 }
473
474 let key = if x < y { (x, y) } else { (y, x) };
476 if !self.tree_edges.read().contains(&key) {
477 continue;
478 }
479
480 if comp_u.insert(y) {
481 queue.push_back(y);
482 }
483 }
484 }
485
486 for &x in &comp_u {
488 let neighbors = graph.neighbors(x);
489 for (y, _) in neighbors {
490 if !comp_u.contains(&y) {
491 return Some((x, y));
493 }
494 }
495 }
496
497 None
498 }
499
500 fn rebuild_decomposition(&mut self) {
502 let graph = self.graph.read();
503 let graph_clone = graph.clone();
504 drop(graph);
505
506 let graph_for_decomp = Arc::new(graph_clone);
507 self.decomposition = HierarchicalDecomposition::build(graph_for_decomp)
508 .unwrap_or_else(|_| {
509 let empty = Arc::new(DynamicGraph::new());
511 HierarchicalDecomposition::build(empty).unwrap()
512 });
513 }
514
515 fn recompute_min_cut(&mut self) {
517 let graph = self.graph.read();
518
519 if !graph.is_connected() {
521 self.current_min_cut = 0.0;
522 drop(graph);
523 return;
524 }
525
526 let mut min_cut = self.decomposition.min_cut_value();
528
529 let tree_edges: Vec<_> = self.tree_edges.read().iter().copied().collect();
532
533 for (u, v) in tree_edges {
534 let cut_value = self.compute_tree_edge_cut(&graph, u, v);
535 if cut_value < min_cut {
536 min_cut = cut_value;
537 }
538 }
539
540 drop(graph);
541 self.current_min_cut = min_cut;
542 }
543
544 fn compute_tree_edge_cut(&self, graph: &DynamicGraph, u: VertexId, v: VertexId) -> f64 {
546 let mut component_u = std::collections::HashSet::new();
548 let mut queue = std::collections::VecDeque::new();
549
550 queue.push_back(u);
551 component_u.insert(u);
552
553 while let Some(x) = queue.pop_front() {
554 for (y, _) in graph.neighbors(x) {
555 if (x == u && y == v) || (x == v && y == u) {
557 continue;
558 }
559
560 let key = if x < y { (x, y) } else { (y, x) };
562 if !self.tree_edges.read().contains(&key) {
563 continue;
564 }
565
566 if component_u.insert(y) {
567 queue.push_back(y);
568 }
569 }
570 }
571
572 let mut cut_weight = 0.0;
574 for &x in &component_u {
575 for (y, _) in graph.neighbors(x) {
576 if !component_u.contains(&y) {
577 if let Some(weight) = graph.edge_weight(x, y) {
578 cut_weight += weight;
579 }
580 }
581 }
582 }
583
584 cut_weight
585 }
586}
587
588pub struct MinCutBuilder {
590 config: MinCutConfig,
591 initial_edges: Vec<(VertexId, VertexId, Weight)>,
592}
593
594impl MinCutBuilder {
595 pub fn new() -> Self {
597 Self {
598 config: MinCutConfig::default(),
599 initial_edges: Vec::new(),
600 }
601 }
602
603 pub fn exact(mut self) -> Self {
605 self.config.approximate = false;
606 self
607 }
608
609 pub fn approximate(mut self, epsilon: f64) -> Self {
611 assert!(epsilon > 0.0 && epsilon <= 1.0, "Epsilon must be in (0, 1]");
612 self.config.approximate = true;
613 self.config.epsilon = epsilon;
614 self
615 }
616
617 pub fn max_cut_size(mut self, size: usize) -> Self {
619 self.config.max_exact_cut_size = size;
620 self
621 }
622
623 pub fn parallel(mut self, enabled: bool) -> Self {
625 self.config.parallel = enabled;
626 self
627 }
628
629 pub fn with_edges(mut self, edges: Vec<(VertexId, VertexId, Weight)>) -> Self {
631 self.initial_edges = edges;
632 self
633 }
634
635 pub fn build(self) -> Result<DynamicMinCut> {
637 if self.initial_edges.is_empty() {
638 Ok(DynamicMinCut::new(self.config))
639 } else {
640 let graph = DynamicGraph::new();
642 for (u, v, weight) in &self.initial_edges {
643 graph.insert_edge(*u, *v, *weight)?;
644 }
645
646 DynamicMinCut::from_graph(graph, self.config)
647 }
648 }
649}
650
651impl Default for MinCutBuilder {
652 fn default() -> Self {
653 Self::new()
654 }
655}
656
657#[cfg(test)]
658mod tests {
659 use super::*;
660
661 #[test]
662 fn test_empty_graph() {
663 let mincut = DynamicMinCut::new(MinCutConfig::default());
664 assert_eq!(mincut.min_cut_value(), f64::INFINITY);
665 assert_eq!(mincut.num_vertices(), 0);
666 assert_eq!(mincut.num_edges(), 0);
667 }
668
669 #[test]
670 fn test_single_edge() {
671 let mincut = MinCutBuilder::new()
672 .with_edges(vec![(1, 2, 1.0)])
673 .build()
674 .unwrap();
675
676 assert_eq!(mincut.num_vertices(), 2);
677 assert_eq!(mincut.num_edges(), 1);
678 assert_eq!(mincut.min_cut_value(), 1.0);
679 assert!(mincut.is_connected());
680 }
681
682 #[test]
683 fn test_triangle() {
684 let edges = vec![
685 (1, 2, 1.0),
686 (2, 3, 1.0),
687 (3, 1, 1.0),
688 ];
689
690 let mincut = MinCutBuilder::new()
691 .with_edges(edges)
692 .build()
693 .unwrap();
694
695 assert_eq!(mincut.num_vertices(), 3);
696 assert_eq!(mincut.num_edges(), 3);
697 assert_eq!(mincut.min_cut_value(), 2.0); assert!(mincut.is_connected());
699 }
700
701 #[test]
702 fn test_insert_edge() {
703 let mut mincut = MinCutBuilder::new()
704 .with_edges(vec![(1, 2, 1.0)])
705 .build()
706 .unwrap();
707
708 let cut_value = mincut.insert_edge(2, 3, 1.0).unwrap();
709 assert_eq!(mincut.num_edges(), 2);
710 assert_eq!(cut_value, 1.0);
711 }
712
713 #[test]
714 fn test_delete_edge() {
715 let mut mincut = MinCutBuilder::new()
716 .with_edges(vec![
717 (1, 2, 1.0),
718 (2, 3, 1.0),
719 ])
720 .build()
721 .unwrap();
722
723 assert!(mincut.is_connected());
724
725 let cut_value = mincut.delete_edge(1, 2).unwrap();
726 assert_eq!(mincut.num_edges(), 1);
727 assert_eq!(cut_value, 0.0);
729 }
730
731 #[test]
732 fn test_disconnected_graph() {
733 let mincut = MinCutBuilder::new()
734 .with_edges(vec![
735 (1, 2, 1.0),
736 (3, 4, 1.0),
737 ])
738 .build()
739 .unwrap();
740
741 assert!(!mincut.is_connected());
742 assert_eq!(mincut.min_cut_value(), 0.0);
743 }
744
745 #[test]
746 fn test_weighted_edges() {
747 let edges = vec![
748 (1, 2, 2.0),
749 (2, 3, 3.0),
750 (3, 1, 1.0),
751 ];
752
753 let mincut = MinCutBuilder::new()
754 .with_edges(edges)
755 .build()
756 .unwrap();
757
758 assert_eq!(mincut.min_cut_value(), 3.0);
760 }
761
762 #[test]
763 fn test_partition() {
764 let edges = vec![
765 (1, 2, 1.0),
766 (2, 3, 1.0),
767 (3, 4, 1.0),
768 ];
769
770 let mincut = MinCutBuilder::new()
771 .with_edges(edges)
772 .build()
773 .unwrap();
774
775 let (s, t) = mincut.partition();
776 assert!(!s.is_empty());
777 assert!(!t.is_empty());
778 assert_eq!(s.len() + t.len(), 4);
779 }
780
781 #[test]
782 fn test_cut_edges() {
783 let edges = vec![
784 (1, 2, 1.0),
785 (2, 3, 1.0),
786 ];
787
788 let mincut = MinCutBuilder::new()
789 .with_edges(edges)
790 .build()
791 .unwrap();
792
793 let cut = mincut.cut_edges();
794 assert!(!cut.is_empty());
795 assert!(cut.len() <= 2);
796 }
797
798 #[test]
799 fn test_min_cut_result() {
800 let edges = vec![
801 (1, 2, 1.0),
802 (2, 3, 1.0),
803 (3, 1, 1.0),
804 ];
805
806 let mincut = MinCutBuilder::new()
807 .exact()
808 .with_edges(edges)
809 .build()
810 .unwrap();
811
812 let result = mincut.min_cut();
813 assert!(result.is_exact);
814 assert_eq!(result.approximation_ratio, 1.0);
815 assert!(result.cut_edges.is_some());
816 assert!(result.partition.is_some());
817 }
818
819 #[test]
820 fn test_approximate_mode() {
821 let mincut = MinCutBuilder::new()
822 .approximate(0.1)
823 .build()
824 .unwrap();
825
826 let result = mincut.min_cut();
827 assert!(!result.is_exact);
828 assert_eq!(result.approximation_ratio, 1.1);
829 }
830
831 #[test]
832 fn test_statistics() {
833 let mut mincut = MinCutBuilder::new()
834 .with_edges(vec![(1, 2, 1.0)])
835 .build()
836 .unwrap();
837
838 mincut.insert_edge(2, 3, 1.0).unwrap();
839 mincut.delete_edge(1, 2).unwrap();
840 let _ = mincut.min_cut_value();
841
842 let stats = mincut.stats();
843 assert_eq!(stats.insertions, 1);
844 assert_eq!(stats.deletions, 1);
845 assert_eq!(stats.queries, 1);
846 assert!(stats.avg_update_time_us > 0.0);
847 }
848
849 #[test]
850 fn test_reset_stats() {
851 let mut mincut = MinCutBuilder::new()
852 .with_edges(vec![(1, 2, 1.0)])
853 .build()
854 .unwrap();
855
856 mincut.insert_edge(2, 3, 1.0).unwrap();
857 assert_eq!(mincut.stats().insertions, 1);
858
859 mincut.reset_stats();
860 assert_eq!(mincut.stats().insertions, 0);
861 }
862
863 #[test]
864 fn test_builder_pattern() {
865 let mincut = MinCutBuilder::new()
866 .exact()
867 .max_cut_size(500)
868 .parallel(true)
869 .with_edges(vec![(1, 2, 1.0)])
870 .build()
871 .unwrap();
872
873 assert!(!mincut.config().approximate);
874 assert_eq!(mincut.config().max_exact_cut_size, 500);
875 assert!(mincut.config().parallel);
876 }
877
878 #[test]
879 fn test_large_graph() {
880 let mut edges = Vec::new();
881
882 for i in 0..99 {
884 edges.push((i, i + 1, 1.0));
885 }
886
887 let mincut = MinCutBuilder::new()
888 .with_edges(edges)
889 .build()
890 .unwrap();
891
892 assert_eq!(mincut.num_vertices(), 100);
893 assert_eq!(mincut.num_edges(), 99);
894 assert_eq!(mincut.min_cut_value(), 1.0); assert!(mincut.is_connected());
896 }
897
898 #[test]
899 fn test_tree_edge_deletion_with_replacement() {
900 let mut mincut = MinCutBuilder::new()
901 .with_edges(vec![
902 (1, 2, 1.0),
903 (2, 3, 1.0),
904 (1, 3, 1.0), ])
906 .build()
907 .unwrap();
908
909 assert!(mincut.is_connected());
910
911 mincut.delete_edge(1, 2).unwrap();
913
914 assert_eq!(mincut.num_edges(), 2);
916 }
917
918 #[test]
919 fn test_multiple_components() {
920 let edges = vec![
921 (1, 2, 1.0),
922 (3, 4, 1.0),
923 (5, 6, 1.0),
924 ];
925
926 let mincut = MinCutBuilder::new()
927 .with_edges(edges)
928 .build()
929 .unwrap();
930
931 assert!(!mincut.is_connected());
932 assert_eq!(mincut.min_cut_value(), 0.0);
933 }
934
935 #[test]
936 fn test_dynamic_updates() {
937 let mut mincut = MinCutBuilder::new().build().unwrap();
938
939 assert_eq!(mincut.min_cut_value(), f64::INFINITY);
941
942 mincut.insert_edge(1, 2, 2.0).unwrap();
944 assert_eq!(mincut.min_cut_value(), 2.0);
945
946 mincut.insert_edge(2, 3, 3.0).unwrap();
948 mincut.insert_edge(3, 1, 1.0).unwrap();
949 assert_eq!(mincut.min_cut_value(), 3.0); mincut.delete_edge(2, 3).unwrap();
953 assert_eq!(mincut.min_cut_value(), 1.0); }
955
956 #[test]
957 fn test_config_access() {
958 let mincut = MinCutBuilder::new()
959 .approximate(0.2)
960 .max_cut_size(2000)
961 .build()
962 .unwrap();
963
964 let config = mincut.config();
965 assert_eq!(config.epsilon, 0.2);
966 assert_eq!(config.max_exact_cut_size, 2000);
967 assert!(config.approximate);
968 }
969
970 #[test]
971 fn test_graph_access() {
972 let mincut = MinCutBuilder::new()
973 .with_edges(vec![(1, 2, 1.0)])
974 .build()
975 .unwrap();
976
977 let graph = mincut.graph();
978 let g = graph.read();
979 assert_eq!(g.num_vertices(), 2);
980 assert_eq!(g.num_edges(), 1);
981 }
982
983 #[test]
984 fn test_bridge_graph() {
985 let edges = vec![
987 (1, 2, 2.0),
989 (2, 3, 2.0),
990 (3, 1, 2.0),
991 (3, 4, 1.0),
993 (4, 5, 2.0),
995 (5, 6, 2.0),
996 (6, 4, 2.0),
997 ];
998
999 let mincut = MinCutBuilder::new()
1000 .with_edges(edges)
1001 .build()
1002 .unwrap();
1003
1004 assert_eq!(mincut.min_cut_value(), 1.0);
1006 assert!(mincut.is_connected());
1007 }
1008
1009 #[test]
1010 fn test_complete_graph_k4() {
1011 let mut edges = Vec::new();
1013 for i in 1..=4 {
1014 for j in (i + 1)..=4 {
1015 edges.push((i, j, 1.0));
1016 }
1017 }
1018
1019 let mincut = MinCutBuilder::new()
1020 .with_edges(edges)
1021 .build()
1022 .unwrap();
1023
1024 assert_eq!(mincut.num_vertices(), 4);
1025 assert_eq!(mincut.num_edges(), 6);
1026 assert_eq!(mincut.min_cut_value(), 3.0);
1028 }
1029
1030 #[test]
1031 fn test_sequential_insertions() {
1032 let mut mincut = MinCutBuilder::new().build().unwrap();
1033
1034 mincut.insert_edge(1, 2, 1.0).unwrap();
1036 assert_eq!(mincut.min_cut_value(), 1.0);
1037
1038 mincut.insert_edge(2, 3, 1.0).unwrap();
1039 assert_eq!(mincut.min_cut_value(), 1.0);
1040
1041 mincut.insert_edge(3, 4, 1.0).unwrap();
1042 assert_eq!(mincut.min_cut_value(), 1.0);
1043
1044 mincut.insert_edge(4, 1, 1.0).unwrap();
1046 assert_eq!(mincut.min_cut_value(), 2.0);
1047 }
1048
1049 #[test]
1050 fn test_sequential_deletions() {
1051 let mut mincut = MinCutBuilder::new()
1052 .with_edges(vec![
1053 (1, 2, 1.0),
1054 (2, 3, 1.0),
1055 (3, 1, 1.0),
1056 ])
1057 .build()
1058 .unwrap();
1059
1060 assert_eq!(mincut.min_cut_value(), 2.0);
1061
1062 mincut.delete_edge(1, 2).unwrap();
1064 assert_eq!(mincut.min_cut_value(), 1.0);
1065
1066 mincut.delete_edge(2, 3).unwrap();
1068 assert_eq!(mincut.min_cut_value(), 0.0);
1069 }
1070}