1use crate::connectivity::DynamicConnectivity;
27use crate::graph::{DynamicGraph, EdgeId, VertexId};
28use crate::instance::{
29 BoundedInstance, InstanceResult, ProperCutInstance, StubInstance, WitnessHandle,
30};
31use std::sync::Arc;
32
33#[cfg(feature = "agentic")]
34use crate::compact::{CompactCoreState, CompactEdge};
35#[cfg(feature = "agentic")]
36use crate::parallel::{
37 CoreDistributor, CoreExecutor, CoreStrategy, ResultAggregator, SharedCoordinator, NUM_CORES,
38};
39
40const RANGE_FACTOR: f64 = 1.2;
42
43const MAX_INSTANCES: usize = 100;
45
46#[derive(Debug, Clone)]
48pub enum MinCutResult {
49 Disconnected,
51 Value {
53 cut_value: u64,
55 witness: WitnessHandle,
57 },
58}
59
60impl MinCutResult {
61 pub fn value(&self) -> u64 {
63 match self {
64 Self::Disconnected => 0,
65 Self::Value { cut_value, .. } => *cut_value,
66 }
67 }
68
69 pub fn is_connected(&self) -> bool {
71 !matches!(self, Self::Disconnected)
72 }
73
74 pub fn witness(&self) -> Option<&WitnessHandle> {
76 match self {
77 Self::Disconnected => None,
78 Self::Value { witness, .. } => Some(witness),
79 }
80 }
81}
82
83#[derive(Debug, Clone, Copy)]
85struct Update {
86 time: u64,
87 edge_id: EdgeId,
88 u: VertexId,
89 v: VertexId,
90}
91
92pub struct MinCutWrapper {
94 conn_ds: DynamicConnectivity,
96
97 instances: Vec<Option<Box<dyn ProperCutInstance>>>,
99
100 lambda_min: Vec<u64>,
102
103 lambda_max: Vec<u64>,
105
106 last_update_time: Vec<u64>,
108
109 current_time: u64,
111
112 pending_inserts: Vec<Update>,
114
115 pending_deletes: Vec<Update>,
117
118 graph: Arc<DynamicGraph>,
120
121 instance_factory:
123 Box<dyn Fn(&DynamicGraph, u64, u64) -> Box<dyn ProperCutInstance> + Send + Sync>,
124
125 last_min_cut: Option<u64>,
127
128 #[cfg(feature = "agentic")]
130 use_agentic: bool,
131}
132
133impl MinCutWrapper {
134 pub fn new(graph: Arc<DynamicGraph>) -> Self {
147 Self::with_factory(graph, |g, min, max| {
148 Box::new(BoundedInstance::init(g, min, max))
149 })
150 }
151
152 pub fn with_factory<F>(graph: Arc<DynamicGraph>, factory: F) -> Self
168 where
169 F: Fn(&DynamicGraph, u64, u64) -> Box<dyn ProperCutInstance> + Send + Sync + 'static,
170 {
171 let mut lambda_min = Vec::with_capacity(MAX_INSTANCES);
173 let mut lambda_max = Vec::with_capacity(MAX_INSTANCES);
174
175 for i in 0..MAX_INSTANCES {
176 let (min, max) = Self::compute_bounds(i);
177 lambda_min.push(min);
178 lambda_max.push(max);
179 }
180
181 let mut instances = Vec::with_capacity(MAX_INSTANCES);
183 for _ in 0..MAX_INSTANCES {
184 instances.push(None);
185 }
186
187 Self {
188 conn_ds: DynamicConnectivity::new(),
189 instances,
190 lambda_min,
191 lambda_max,
192 last_update_time: vec![0; MAX_INSTANCES],
193 current_time: 0,
194 pending_inserts: Vec::new(),
195 pending_deletes: Vec::new(),
196 graph,
197 instance_factory: Box::new(factory),
198 last_min_cut: None,
199 #[cfg(feature = "agentic")]
200 use_agentic: false,
201 }
202 }
203
204 #[cfg(feature = "agentic")]
216 pub fn with_agentic(mut self, enabled: bool) -> Self {
217 self.use_agentic = enabled;
218 self
219 }
220
221 pub fn insert_edge(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
235 self.current_time += 1;
236
237 self.conn_ds.insert_edge(u, v);
239
240 self.pending_inserts.push(Update {
242 time: self.current_time,
243 edge_id,
244 u,
245 v,
246 });
247 }
248
249 pub fn delete_edge(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
263 self.current_time += 1;
264
265 self.conn_ds.delete_edge(u, v);
267
268 self.pending_deletes.push(Update {
270 time: self.current_time,
271 edge_id,
272 u,
273 v,
274 });
275 }
276
277 pub fn query(&mut self) -> MinCutResult {
296 if !self.conn_ds.is_connected() {
298 return MinCutResult::Disconnected;
299 }
300
301 #[cfg(feature = "agentic")]
303 if self.use_agentic {
304 return self.query_parallel();
305 }
306
307 self.process_instances()
309 }
310
311 #[cfg(feature = "agentic")]
321 fn query_parallel(&self) -> MinCutResult {
322 let coordinator = SharedCoordinator::new();
323 let mut aggregator = ResultAggregator::new();
324
325 let distributor = CoreDistributor::new(
327 CoreStrategy::GeometricRanges,
328 self.graph.num_vertices() as u16,
329 self.graph.num_edges() as u16,
330 );
331
332 for core_id in 0..NUM_CORES.min(self.graph.num_vertices()) as u8 {
334 let mut executor = CoreExecutor::init(core_id, Some(&coordinator));
335
336 for edge in self.graph.edges() {
338 executor.add_edge(
339 edge.source as u16,
340 edge.target as u16,
341 (edge.weight * 100.0) as u16,
342 );
343 }
344
345 let result = executor.process();
346 aggregator.add_result(result);
347 }
348
349 let best = aggregator.best_result();
351 if best.min_cut == u16::MAX {
352 MinCutResult::Disconnected
353 } else {
354 let mut membership = roaring::RoaringBitmap::new();
356 membership.insert(best.witness_seed as u32);
357 let witness = WitnessHandle::new(
358 best.witness_seed as u64,
359 membership,
360 best.witness_boundary as u64,
361 );
362 MinCutResult::Value {
363 cut_value: best.min_cut as u64,
364 witness,
365 }
366 }
367 }
368
369 fn process_instances(&mut self) -> MinCutResult {
389 self.pending_inserts.sort_by_key(|u| u.time);
391 self.pending_deletes.sort_by_key(|u| u.time);
392
393 let mut last_in_range: Option<(u64, WitnessHandle)> = None;
394
395 let start_idx = self.get_search_start();
397
398 for i in start_idx..MAX_INSTANCES {
399 let is_new_instance = self.instances[i].is_none();
401 if is_new_instance {
402 let min = self.lambda_min[i];
403 let max = self.lambda_max[i];
404 let instance = (self.instance_factory)(&self.graph, min, max);
405 self.instances[i] = Some(instance);
406 }
407
408 let instance = self.instances[i].as_mut().unwrap();
409 let last_time = self.last_update_time[i];
410
411 if is_new_instance {
412 let all_edges: Vec<_> = self
414 .graph
415 .edges()
416 .iter()
417 .map(|e| (e.id, e.source, e.target))
418 .collect();
419
420 if !all_edges.is_empty() {
421 instance.apply_inserts(&all_edges);
422 }
423 } else {
424 let inserts: Vec<_> = self
427 .pending_inserts
428 .iter()
429 .filter(|u| u.time > last_time)
430 .map(|u| (u.edge_id, u.u, u.v))
431 .collect();
432
433 let deletes: Vec<_> = self
435 .pending_deletes
436 .iter()
437 .filter(|u| u.time > last_time)
438 .map(|u| (u.edge_id, u.u, u.v))
439 .collect();
440
441 if !inserts.is_empty() {
443 instance.apply_inserts(&inserts);
444 }
445 if !deletes.is_empty() {
446 instance.apply_deletes(&deletes);
447 }
448 }
449
450 self.last_update_time[i] = self.current_time;
452
453 match instance.query() {
455 InstanceResult::ValueInRange { value, witness } => {
456 last_in_range = Some((value, witness));
458 break;
461 }
462 InstanceResult::AboveRange => {
463 continue;
465 }
466 }
467 }
468
469 self.pending_inserts.clear();
471 self.pending_deletes.clear();
472
473 match last_in_range {
475 Some((cut_value, witness)) => {
476 self.last_min_cut = Some(cut_value);
478 MinCutResult::Value { cut_value, witness }
479 }
480 None => {
481 self.last_min_cut = None;
484 use roaring::RoaringBitmap;
485 let mut membership = RoaringBitmap::new();
486 membership.insert(0);
487 let witness = WitnessHandle::new(0, membership, u64::MAX);
488 MinCutResult::Value {
489 cut_value: u64::MAX,
490 witness,
491 }
492 }
493 }
494 }
495
496 fn compute_bounds(i: usize) -> (u64, u64) {
518 let lambda_min = (RANGE_FACTOR.powi(i as i32)).floor() as u64;
519 let lambda_max = (RANGE_FACTOR.powi((i + 1) as i32)).floor() as u64;
520 (lambda_min.max(1), lambda_max.max(1))
521 }
522
523 fn find_instance_for_value(&self, value: u64) -> usize {
531 let mut lo = 0usize;
533 let mut hi = MAX_INSTANCES;
534
535 while lo < hi {
536 let mid = lo + (hi - lo) / 2;
537 if self.lambda_max[mid] < value {
538 lo = mid + 1;
539 } else {
540 hi = mid;
541 }
542 }
543
544 lo.min(MAX_INSTANCES - 1)
545 }
546
547 fn get_search_start(&self) -> usize {
552 if let Some(last_value) = self.last_min_cut {
554 let idx = self.find_instance_for_value(last_value);
556 idx.saturating_sub(2)
558 } else {
559 0
561 }
562 }
563
564 pub fn num_instances(&self) -> usize {
566 self.instances.iter().filter(|i| i.is_some()).count()
567 }
568
569 pub fn current_time(&self) -> u64 {
571 self.current_time
572 }
573
574 pub fn pending_updates(&self) -> usize {
576 self.pending_inserts.len() + self.pending_deletes.len()
577 }
578
579 pub fn batch_insert_edges(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
603 self.pending_inserts.reserve(edges.len());
605
606 for &(edge_id, u, v) in edges {
607 self.current_time += 1;
608
609 self.conn_ds.insert_edge(u, v);
611
612 self.pending_inserts.push(Update {
614 time: self.current_time,
615 edge_id,
616 u,
617 v,
618 });
619 }
620 }
621
622 pub fn batch_delete_edges(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
640 self.pending_deletes.reserve(edges.len());
642
643 for &(edge_id, u, v) in edges {
644 self.current_time += 1;
645
646 self.conn_ds.delete_edge(u, v);
648
649 self.pending_deletes.push(Update {
651 time: self.current_time,
652 edge_id,
653 u,
654 v,
655 });
656 }
657 }
658
659 pub fn batch_update(
679 &mut self,
680 inserts: &[(EdgeId, VertexId, VertexId)],
681 deletes: &[(EdgeId, VertexId, VertexId)],
682 ) {
683 self.batch_insert_edges(inserts);
685 self.batch_delete_edges(deletes);
686 }
687
688 pub fn flush_updates(&mut self) {
696 if self.pending_updates() == 0 {
697 return;
698 }
699
700 self.pending_inserts.sort_by_key(|u| u.time);
702 self.pending_deletes.sort_by_key(|u| u.time);
703
704 for i in 0..MAX_INSTANCES {
706 if let Some(ref mut instance) = self.instances[i] {
707 let last_time = self.last_update_time[i];
708
709 let inserts: Vec<_> = self
711 .pending_inserts
712 .iter()
713 .filter(|u| u.time > last_time)
714 .map(|u| (u.edge_id, u.u, u.v))
715 .collect();
716
717 let deletes: Vec<_> = self
718 .pending_deletes
719 .iter()
720 .filter(|u| u.time > last_time)
721 .map(|u| (u.edge_id, u.u, u.v))
722 .collect();
723
724 if !inserts.is_empty() {
725 instance.apply_inserts(&inserts);
726 }
727 if !deletes.is_empty() {
728 instance.apply_deletes(&deletes);
729 }
730
731 self.last_update_time[i] = self.current_time;
732 }
733 }
734
735 self.pending_inserts.clear();
737 self.pending_deletes.clear();
738 }
739
740 pub fn min_cut_value(&mut self) -> u64 {
749 self.query().value()
750 }
751
752 pub fn query_with_local_kcut(&mut self, source: VertexId) -> (u64, bool) {
767 use crate::localkcut::deterministic::DeterministicLocalKCut;
768
769 let result = self.query();
771 let cut_value = result.value();
772
773 if cut_value == 0 {
774 return (0, true); }
776
777 let volume_bound = self.graph.num_edges().max(1) * 2;
779 let lambda_max = cut_value * 2;
780
781 let mut lkc = DeterministicLocalKCut::new(lambda_max, volume_bound, 2);
782
783 for edge in self.graph.edges() {
785 lkc.insert_edge(edge.source, edge.target, edge.weight);
786 }
787
788 let cuts = lkc.query(source);
790
791 let certified = cuts.iter().any(|c| {
793 let diff = (c.cut_value - cut_value as f64).abs();
794 diff < 0.001 || c.cut_value <= cut_value as f64
795 });
796
797 (cut_value, certified || cuts.is_empty())
798 }
799
800 pub fn local_cuts(&self, source: VertexId, lambda_max: u64) -> Vec<(f64, Vec<VertexId>)> {
814 use crate::localkcut::deterministic::DeterministicLocalKCut;
815
816 let volume_bound = self.graph.num_edges().max(1) * 2;
817 let mut lkc = DeterministicLocalKCut::new(lambda_max, volume_bound, 2);
818
819 for edge in self.graph.edges() {
821 lkc.insert_edge(edge.source, edge.target, edge.weight);
822 }
823
824 lkc.query(source)
826 .into_iter()
827 .map(|c| (c.cut_value, c.vertices.into_iter().collect()))
828 .collect()
829 }
830
831 pub fn build_hierarchy(&self) -> crate::cluster::hierarchy::ThreeLevelHierarchy {
837 use crate::cluster::hierarchy::{HierarchyConfig, ThreeLevelHierarchy};
838
839 let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
840 track_mirror_cuts: true,
841 ..Default::default()
842 });
843
844 for edge in self.graph.edges() {
846 h.insert_edge(edge.source, edge.target, edge.weight);
847 }
848
849 h.build();
850 h
851 }
852
853 pub fn connectivity_curve(
887 &self,
888 ranked_edges: &[(VertexId, VertexId, f64)],
889 k_max: usize,
890 ) -> Vec<(usize, u64)> {
891 use crate::algorithm::DynamicMinCut;
892
893 let mut temp_mincut = DynamicMinCut::new(crate::MinCutConfig::default());
895
896 for edge in self.graph.edges() {
897 let _ = temp_mincut.insert_edge(edge.source, edge.target, edge.weight);
898 }
899
900 let mut curve = Vec::with_capacity(k_max + 1);
901
902 curve.push((0, temp_mincut.min_cut_value() as u64));
904
905 for (k, &(u, v, _score)) in ranked_edges.iter().take(k_max).enumerate() {
907 let _ = temp_mincut.delete_edge(u, v);
908 let new_cut = temp_mincut.min_cut_value() as u64;
909 curve.push((k + 1, new_cut));
910 }
911
912 curve
913 }
914
915 pub fn find_elbow(curve: &[(usize, u64)]) -> Option<(usize, u64)> {
929 if curve.len() < 2 {
930 return None;
931 }
932
933 let mut max_drop = 0u64;
934 let mut elbow_k = 0usize;
935
936 for i in 1..curve.len() {
937 let drop = curve[i - 1].1.saturating_sub(curve[i].1);
938 if drop > max_drop {
939 max_drop = drop;
940 elbow_k = curve[i].0;
941 }
942 }
943
944 if max_drop > 0 {
945 Some((elbow_k, max_drop))
946 } else {
947 None
948 }
949 }
950
951 pub fn detector_quality(
966 &self,
967 ranked_edges: &[(VertexId, VertexId, f64)],
968 true_cut_size: usize,
969 ) -> f64 {
970 let k_max = true_cut_size.min(ranked_edges.len());
971 if k_max == 0 {
972 return 0.0;
973 }
974
975 let curve = self.connectivity_curve(ranked_edges, k_max);
976
977 let initial_cut = curve.first().map(|(_, c)| *c).unwrap_or(0);
979 let final_cut = curve.last().map(|(_, c)| *c).unwrap_or(0);
980
981 if initial_cut == 0 {
983 0.0
984 } else {
985 (initial_cut - final_cut) as f64 / initial_cut as f64
986 }
987 }
988}
989
990#[cfg(test)]
991mod tests {
992 use super::*;
993
994 #[test]
995 fn test_compute_bounds() {
996 let (min, max) = MinCutWrapper::compute_bounds(0);
998 assert_eq!(min, 1);
999 assert_eq!(max, 1);
1000
1001 let (min, max) = MinCutWrapper::compute_bounds(1);
1003 assert_eq!(min, 1);
1004 assert_eq!(max, 1);
1005
1006 let (min, max) = MinCutWrapper::compute_bounds(5);
1008 assert_eq!(min, 2);
1009 assert_eq!(max, 2);
1010
1011 let (min, max) = MinCutWrapper::compute_bounds(10);
1013 assert_eq!(min, 6);
1014 assert_eq!(max, 7);
1015
1016 let (min, max) = MinCutWrapper::compute_bounds(20);
1018 assert_eq!(min, 38);
1019 assert_eq!(max, 46);
1020 }
1021
1022 #[test]
1023 fn test_new_wrapper() {
1024 let graph = Arc::new(DynamicGraph::new());
1025 let wrapper = MinCutWrapper::new(graph);
1026
1027 assert_eq!(wrapper.num_instances(), 0); assert_eq!(wrapper.current_time(), 0);
1029 assert_eq!(wrapper.pending_updates(), 0);
1030 }
1031
1032 #[test]
1033 fn test_empty_graph() {
1034 let graph = Arc::new(DynamicGraph::new());
1035 let mut wrapper = MinCutWrapper::new(graph);
1036
1037 let result = wrapper.query();
1038 assert_eq!(result.value(), 0);
1041 }
1042
1043 #[test]
1044 fn test_disconnected_graph() {
1045 let graph = Arc::new(DynamicGraph::new());
1046 graph.insert_edge(1, 2, 1.0).unwrap();
1047 graph.insert_edge(3, 4, 1.0).unwrap();
1048
1049 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1050
1051 wrapper.insert_edge(0, 1, 2);
1053 wrapper.insert_edge(1, 3, 4);
1054
1055 let result = wrapper.query();
1056
1057 assert_eq!(result.value(), 0);
1059 assert!(matches!(result, MinCutResult::Disconnected));
1060 }
1061
1062 #[test]
1063 fn test_insert_and_query() {
1064 let graph = Arc::new(DynamicGraph::new());
1065 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1066
1067 graph.insert_edge(1, 2, 1.0).unwrap();
1068 wrapper.insert_edge(0, 1, 2);
1069
1070 assert_eq!(wrapper.pending_updates(), 1);
1071
1072 let result = wrapper.query();
1073 assert!(result.is_connected());
1074
1075 assert_eq!(wrapper.pending_updates(), 0);
1077 }
1078
1079 #[test]
1080 fn test_time_counter() {
1081 let graph = Arc::new(DynamicGraph::new());
1082 let mut wrapper = MinCutWrapper::new(graph);
1083
1084 assert_eq!(wrapper.current_time(), 0);
1085
1086 wrapper.insert_edge(0, 1, 2);
1087 assert_eq!(wrapper.current_time(), 1);
1088
1089 wrapper.delete_edge(0, 1, 2);
1090 assert_eq!(wrapper.current_time(), 2);
1091
1092 wrapper.insert_edge(1, 2, 3);
1093 assert_eq!(wrapper.current_time(), 3);
1094 }
1095
1096 #[test]
1097 fn test_lazy_instantiation() {
1098 let graph = Arc::new(DynamicGraph::new());
1099 graph.insert_edge(1, 2, 1.0).unwrap();
1101 graph.insert_edge(2, 3, 1.0).unwrap();
1102
1103 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1104 wrapper.insert_edge(0, 1, 2);
1105 wrapper.insert_edge(1, 2, 3);
1106
1107 assert_eq!(wrapper.num_instances(), 0);
1109
1110 let _ = wrapper.query();
1112
1113 assert!(wrapper.num_instances() > 0);
1115 }
1116
1117 #[test]
1118 fn test_result_value() {
1119 use roaring::RoaringBitmap;
1120
1121 let result = MinCutResult::Disconnected;
1122 assert_eq!(result.value(), 0);
1123 assert!(!result.is_connected());
1124 assert!(result.witness().is_none());
1125
1126 let mut membership = RoaringBitmap::new();
1127 membership.insert(1);
1128 membership.insert(2);
1129 let witness = WitnessHandle::new(1, membership, 5);
1130 let result = MinCutResult::Value {
1131 cut_value: 5,
1132 witness: witness.clone(),
1133 };
1134 assert_eq!(result.value(), 5);
1135 assert!(result.is_connected());
1136 assert!(result.witness().is_some());
1137 }
1138
1139 #[test]
1140 fn test_bounds_coverage() {
1141 let (min, _max) = MinCutWrapper::compute_bounds(50);
1143 assert!(min > 1000);
1144
1145 let (min, _max) = MinCutWrapper::compute_bounds(99);
1146 assert!(min > 1_000_000);
1147 }
1148
1149 #[test]
1150 #[cfg(feature = "agentic")]
1151 fn test_agentic_backend() {
1152 let graph = Arc::new(DynamicGraph::new());
1153 graph.insert_edge(0, 1, 1.0).unwrap();
1155 graph.insert_edge(1, 2, 1.0).unwrap();
1156 graph.insert_edge(2, 0, 1.0).unwrap();
1157
1158 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph)).with_agentic(true);
1160
1161 wrapper.insert_edge(0, 0, 1);
1163 wrapper.insert_edge(1, 1, 2);
1164 wrapper.insert_edge(2, 2, 0);
1165
1166 let result = wrapper.query();
1167
1168 match result {
1171 MinCutResult::Disconnected => {
1172 }
1174 MinCutResult::Value { cut_value, .. } => {
1175 assert!(cut_value < u64::MAX);
1177 }
1178 }
1179 }
1180
1181 #[test]
1186 fn test_batch_insert_edges() {
1187 let graph = Arc::new(DynamicGraph::new());
1188 graph.insert_edge(1, 2, 1.0).unwrap();
1189 graph.insert_edge(2, 3, 1.0).unwrap();
1190 graph.insert_edge(3, 4, 1.0).unwrap();
1191
1192 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1193
1194 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3), (2, 3, 4)]);
1196
1197 assert_eq!(wrapper.pending_updates(), 3);
1198 assert_eq!(wrapper.current_time(), 3);
1199
1200 let result = wrapper.query();
1201 assert!(result.is_connected());
1202 assert_eq!(wrapper.pending_updates(), 0);
1203 }
1204
1205 #[test]
1206 fn test_batch_delete_edges() {
1207 let graph = Arc::new(DynamicGraph::new());
1208 graph.insert_edge(1, 2, 1.0).unwrap();
1209 graph.insert_edge(2, 3, 1.0).unwrap();
1210 graph.insert_edge(3, 4, 1.0).unwrap();
1211
1212 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1213
1214 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3), (2, 3, 4)]);
1216
1217 let _ = wrapper.query();
1219
1220 wrapper.batch_delete_edges(&[(1, 2, 3)]);
1222
1223 assert_eq!(wrapper.pending_updates(), 1);
1224
1225 let result = wrapper.query();
1226 assert!(result.value() >= 0);
1229 }
1230
1231 #[test]
1232 fn test_batch_update_combined() {
1233 let graph = Arc::new(DynamicGraph::new());
1234 graph.insert_edge(1, 2, 1.0).unwrap();
1235 graph.insert_edge(2, 3, 1.0).unwrap();
1236
1237 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1238
1239 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1241 let _ = wrapper.query();
1242
1243 wrapper.batch_update(
1245 &[(2, 3, 4)], &[(1, 2, 3)], );
1248
1249 assert_eq!(wrapper.pending_updates(), 2);
1250 }
1251
1252 #[test]
1253 fn test_flush_updates() {
1254 let graph = Arc::new(DynamicGraph::new());
1255 graph.insert_edge(1, 2, 1.0).unwrap();
1256 graph.insert_edge(2, 3, 1.0).unwrap();
1257
1258 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1259
1260 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1261
1262 let _ = wrapper.query();
1264 assert_eq!(wrapper.pending_updates(), 0);
1265
1266 wrapper.batch_insert_edges(&[(2, 3, 4)]);
1268 assert_eq!(wrapper.pending_updates(), 1);
1269
1270 wrapper.flush_updates();
1272 assert_eq!(wrapper.pending_updates(), 0);
1273 }
1274
1275 #[test]
1276 fn test_min_cut_value_convenience() {
1277 let graph = Arc::new(DynamicGraph::new());
1278 graph.insert_edge(1, 2, 1.0).unwrap();
1279
1280 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1281 wrapper.insert_edge(0, 1, 2);
1282
1283 let value = wrapper.min_cut_value();
1285 assert!(value >= 0);
1286 }
1287
1288 #[test]
1289 fn test_binary_search_instance_lookup() {
1290 let graph = Arc::new(DynamicGraph::new());
1291 let wrapper = MinCutWrapper::new(graph);
1292
1293 assert_eq!(wrapper.find_instance_for_value(1), 0);
1296
1297 let idx = wrapper.find_instance_for_value(2);
1299 assert!(wrapper.lambda_min[idx] <= 2);
1300 assert!(wrapper.lambda_max[idx] >= 2);
1301
1302 let idx = wrapper.find_instance_for_value(100);
1304 assert!(wrapper.lambda_min[idx] <= 100);
1305 assert!(wrapper.lambda_max[idx] >= 100);
1306 }
1307
1308 #[test]
1309 fn test_cached_min_cut_optimization() {
1310 let graph = Arc::new(DynamicGraph::new());
1311 graph.insert_edge(1, 2, 1.0).unwrap();
1313 graph.insert_edge(2, 3, 1.0).unwrap();
1314
1315 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1316 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1317
1318 assert!(wrapper.last_min_cut.is_none());
1320 let result1 = wrapper.query();
1321
1322 assert!(wrapper.last_min_cut.is_some());
1324
1325 wrapper.batch_insert_edges(&[(2, 3, 4)]);
1327 let result2 = wrapper.query();
1328
1329 assert!(result1.is_connected());
1331 assert!(result2.is_connected());
1332 }
1333
1334 #[test]
1335 fn test_query_with_local_kcut() {
1336 let graph = Arc::new(DynamicGraph::new());
1337 graph.insert_edge(1, 2, 1.0).unwrap();
1338 graph.insert_edge(2, 3, 1.0).unwrap();
1339 graph.insert_edge(3, 1, 1.0).unwrap();
1340
1341 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1342 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3), (2, 3, 1)]);
1343
1344 let (cut_value, certified) = wrapper.query_with_local_kcut(1);
1345
1346 assert!(cut_value >= 0, "Cut value should be non-negative");
1348 assert!(
1350 certified || !certified,
1351 "Certification should complete without panic"
1352 );
1353 }
1354
1355 #[test]
1356 fn test_local_cuts() {
1357 let graph = Arc::new(DynamicGraph::new());
1358 graph.insert_edge(1, 2, 1.0).unwrap();
1359 graph.insert_edge(2, 3, 1.0).unwrap();
1360
1361 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1362 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1363 wrapper.query(); let cuts = wrapper.local_cuts(1, 5);
1366
1367 assert!(cuts.len() >= 0, "Should return some cuts or empty");
1369 }
1370
1371 #[test]
1372 fn test_build_hierarchy() {
1373 let graph = Arc::new(DynamicGraph::new());
1374 graph.insert_edge(1, 2, 1.0).unwrap();
1375 graph.insert_edge(2, 3, 1.0).unwrap();
1376 graph.insert_edge(3, 4, 1.0).unwrap();
1377 graph.insert_edge(4, 1, 1.0).unwrap();
1378
1379 let wrapper = MinCutWrapper::new(Arc::clone(&graph));
1380 let hierarchy = wrapper.build_hierarchy();
1381
1382 let stats = hierarchy.stats();
1384 assert!(stats.num_vertices >= 4, "Hierarchy should have 4 vertices");
1385 }
1386
1387 #[test]
1388 fn test_connectivity_curve_basic() {
1389 let graph = Arc::new(DynamicGraph::new());
1392 graph.insert_edge(1, 2, 1.0).unwrap();
1393 graph.insert_edge(2, 3, 1.0).unwrap();
1394
1395 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1396 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1397 wrapper.query();
1398
1399 let ranked_edges = vec![(1, 2, 1.0), (2, 3, 0.8)];
1401
1402 let curve = wrapper.connectivity_curve(&ranked_edges, 2);
1403
1404 assert_eq!(curve.len(), 3);
1406 assert_eq!(curve[0].0, 0); assert_eq!(curve[1].0, 1); assert_eq!(curve[2].0, 2); }
1410
1411 #[test]
1412 fn test_find_elbow_with_clear_drop() {
1413 let curve = vec![
1415 (0, 10), (1, 9), (2, 3), (3, 2), (4, 2), ];
1421
1422 let elbow = MinCutWrapper::find_elbow(&curve);
1423 assert!(elbow.is_some());
1424
1425 let (k, drop) = elbow.unwrap();
1426 assert_eq!(k, 2); assert_eq!(drop, 6); }
1429
1430 #[test]
1431 fn test_find_elbow_flat_curve() {
1432 let curve = vec![(0, 5), (1, 5), (2, 5), (3, 5)];
1434
1435 let elbow = MinCutWrapper::find_elbow(&curve);
1436 assert!(elbow.is_none()); }
1438
1439 #[test]
1440 fn test_find_elbow_single_point() {
1441 let curve = vec![(0, 5)];
1442 let elbow = MinCutWrapper::find_elbow(&curve);
1443 assert!(elbow.is_none()); }
1445
1446 #[test]
1447 fn test_find_elbow_empty() {
1448 let curve: Vec<(usize, u64)> = vec![];
1449 let elbow = MinCutWrapper::find_elbow(&curve);
1450 assert!(elbow.is_none());
1451 }
1452
1453 #[test]
1454 fn test_detector_quality_perfect() {
1455 let graph = Arc::new(DynamicGraph::new());
1458 graph.insert_edge(1, 2, 1.0).unwrap();
1459 graph.insert_edge(2, 3, 1.0).unwrap();
1460 graph.insert_edge(3, 4, 1.0).unwrap();
1461
1462 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1463 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3), (2, 3, 4)]);
1464 wrapper.query();
1465
1466 let ranked_edges = vec![
1468 (2, 3, 1.0), (1, 2, 0.5),
1470 (3, 4, 0.3),
1471 ];
1472
1473 let quality = wrapper.detector_quality(&ranked_edges, 1);
1474
1475 assert!(quality >= 0.0);
1477 assert!(quality <= 1.0);
1478 }
1479
1480 #[test]
1481 fn test_detector_quality_zero_cut() {
1482 let graph = Arc::new(DynamicGraph::new());
1483 let wrapper = MinCutWrapper::new(Arc::clone(&graph));
1484
1485 let ranked_edges: Vec<(u64, u64, f64)> = vec![];
1487
1488 let quality = wrapper.detector_quality(&ranked_edges, 1);
1489 assert_eq!(quality, 0.0);
1490 }
1491
1492 #[test]
1493 fn test_connectivity_curve_empty_graph() {
1494 let graph = Arc::new(DynamicGraph::new());
1495 let wrapper = MinCutWrapper::new(Arc::clone(&graph));
1496
1497 let ranked_edges = vec![(1, 2, 1.0)];
1498 let curve = wrapper.connectivity_curve(&ranked_edges, 2);
1499
1500 assert!(!curve.is_empty());
1502 assert_eq!(curve[0].0, 0); }
1504}