1use crate::connectivity::DynamicConnectivity;
27use crate::instance::{ProperCutInstance, InstanceResult, WitnessHandle, StubInstance, BoundedInstance};
28use crate::graph::{VertexId, EdgeId, DynamicGraph};
29use std::sync::Arc;
30
31#[cfg(feature = "agentic")]
32use crate::parallel::{CoreExecutor, SharedCoordinator, CoreDistributor, ResultAggregator, NUM_CORES, CoreStrategy};
33#[cfg(feature = "agentic")]
34use crate::compact::{CompactCoreState, CompactEdge};
35
36const RANGE_FACTOR: f64 = 1.2;
38
39const MAX_INSTANCES: usize = 100;
41
42#[derive(Debug, Clone)]
44pub enum MinCutResult {
45 Disconnected,
47 Value {
49 cut_value: u64,
51 witness: WitnessHandle,
53 },
54}
55
56impl MinCutResult {
57 pub fn value(&self) -> u64 {
59 match self {
60 Self::Disconnected => 0,
61 Self::Value { cut_value, .. } => *cut_value,
62 }
63 }
64
65 pub fn is_connected(&self) -> bool {
67 !matches!(self, Self::Disconnected)
68 }
69
70 pub fn witness(&self) -> Option<&WitnessHandle> {
72 match self {
73 Self::Disconnected => None,
74 Self::Value { witness, .. } => Some(witness),
75 }
76 }
77}
78
79#[derive(Debug, Clone, Copy)]
81struct Update {
82 time: u64,
83 edge_id: EdgeId,
84 u: VertexId,
85 v: VertexId,
86}
87
88pub struct MinCutWrapper {
90 conn_ds: DynamicConnectivity,
92
93 instances: Vec<Option<Box<dyn ProperCutInstance>>>,
95
96 lambda_min: Vec<u64>,
98
99 lambda_max: Vec<u64>,
101
102 last_update_time: Vec<u64>,
104
105 current_time: u64,
107
108 pending_inserts: Vec<Update>,
110
111 pending_deletes: Vec<Update>,
113
114 graph: Arc<DynamicGraph>,
116
117 instance_factory: Box<dyn Fn(&DynamicGraph, u64, u64) -> Box<dyn ProperCutInstance> + Send + Sync>,
119
120 last_min_cut: Option<u64>,
122
123 #[cfg(feature = "agentic")]
125 use_agentic: bool,
126}
127
128impl MinCutWrapper {
129 pub fn new(graph: Arc<DynamicGraph>) -> Self {
142 Self::with_factory(graph, |g, min, max| {
143 Box::new(BoundedInstance::init(g, min, max))
144 })
145 }
146
147 pub fn with_factory<F>(graph: Arc<DynamicGraph>, factory: F) -> Self
163 where
164 F: Fn(&DynamicGraph, u64, u64) -> Box<dyn ProperCutInstance> + Send + Sync + 'static
165 {
166 let mut lambda_min = Vec::with_capacity(MAX_INSTANCES);
168 let mut lambda_max = Vec::with_capacity(MAX_INSTANCES);
169
170 for i in 0..MAX_INSTANCES {
171 let (min, max) = Self::compute_bounds(i);
172 lambda_min.push(min);
173 lambda_max.push(max);
174 }
175
176 let mut instances = Vec::with_capacity(MAX_INSTANCES);
178 for _ in 0..MAX_INSTANCES {
179 instances.push(None);
180 }
181
182 Self {
183 conn_ds: DynamicConnectivity::new(),
184 instances,
185 lambda_min,
186 lambda_max,
187 last_update_time: vec![0; MAX_INSTANCES],
188 current_time: 0,
189 pending_inserts: Vec::new(),
190 pending_deletes: Vec::new(),
191 graph,
192 instance_factory: Box::new(factory),
193 last_min_cut: None,
194 #[cfg(feature = "agentic")]
195 use_agentic: false,
196 }
197 }
198
199 #[cfg(feature = "agentic")]
211 pub fn with_agentic(mut self, enabled: bool) -> Self {
212 self.use_agentic = enabled;
213 self
214 }
215
216 pub fn insert_edge(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
230 self.current_time += 1;
231
232 self.conn_ds.insert_edge(u, v);
234
235 self.pending_inserts.push(Update {
237 time: self.current_time,
238 edge_id,
239 u,
240 v,
241 });
242 }
243
244 pub fn delete_edge(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
258 self.current_time += 1;
259
260 self.conn_ds.delete_edge(u, v);
262
263 self.pending_deletes.push(Update {
265 time: self.current_time,
266 edge_id,
267 u,
268 v,
269 });
270 }
271
272 pub fn query(&mut self) -> MinCutResult {
291 if !self.conn_ds.is_connected() {
293 return MinCutResult::Disconnected;
294 }
295
296 #[cfg(feature = "agentic")]
298 if self.use_agentic {
299 return self.query_parallel();
300 }
301
302 self.process_instances()
304 }
305
306 #[cfg(feature = "agentic")]
316 fn query_parallel(&self) -> MinCutResult {
317 let coordinator = SharedCoordinator::new();
318 let mut aggregator = ResultAggregator::new();
319
320 let distributor = CoreDistributor::new(
322 CoreStrategy::GeometricRanges,
323 self.graph.num_vertices() as u16,
324 self.graph.num_edges() as u16,
325 );
326
327 for core_id in 0..NUM_CORES.min(self.graph.num_vertices()) as u8 {
329 let mut executor = CoreExecutor::init(core_id, Some(&coordinator));
330
331 for edge in self.graph.edges() {
333 executor.add_edge(
334 edge.source as u16,
335 edge.target as u16,
336 (edge.weight * 100.0) as u16,
337 );
338 }
339
340 let result = executor.process();
341 aggregator.add_result(result);
342 }
343
344 let best = aggregator.best_result();
346 if best.min_cut == u16::MAX {
347 MinCutResult::Disconnected
348 } else {
349 let mut membership = roaring::RoaringBitmap::new();
351 membership.insert(best.witness_seed as u32);
352 let witness = WitnessHandle::new(
353 best.witness_seed as u64,
354 membership,
355 best.witness_boundary as u64,
356 );
357 MinCutResult::Value {
358 cut_value: best.min_cut as u64,
359 witness,
360 }
361 }
362 }
363
364 fn process_instances(&mut self) -> MinCutResult {
384 self.pending_inserts.sort_by_key(|u| u.time);
386 self.pending_deletes.sort_by_key(|u| u.time);
387
388 let mut last_in_range: Option<(u64, WitnessHandle)> = None;
389
390 let start_idx = self.get_search_start();
392
393 for i in start_idx..MAX_INSTANCES {
394 let is_new_instance = self.instances[i].is_none();
396 if is_new_instance {
397 let min = self.lambda_min[i];
398 let max = self.lambda_max[i];
399 let instance = (self.instance_factory)(&self.graph, min, max);
400 self.instances[i] = Some(instance);
401 }
402
403 let instance = self.instances[i].as_mut().unwrap();
404 let last_time = self.last_update_time[i];
405
406 if is_new_instance {
407 let all_edges: Vec<_> = self.graph.edges()
409 .iter()
410 .map(|e| (e.id, e.source, e.target))
411 .collect();
412
413 if !all_edges.is_empty() {
414 instance.apply_inserts(&all_edges);
415 }
416 } else {
417 let inserts: Vec<_> = self.pending_inserts
420 .iter()
421 .filter(|u| u.time > last_time)
422 .map(|u| (u.edge_id, u.u, u.v))
423 .collect();
424
425 let deletes: Vec<_> = self.pending_deletes
427 .iter()
428 .filter(|u| u.time > last_time)
429 .map(|u| (u.edge_id, u.u, u.v))
430 .collect();
431
432 if !inserts.is_empty() {
434 instance.apply_inserts(&inserts);
435 }
436 if !deletes.is_empty() {
437 instance.apply_deletes(&deletes);
438 }
439 }
440
441 self.last_update_time[i] = self.current_time;
443
444 match instance.query() {
446 InstanceResult::ValueInRange { value, witness } => {
447 last_in_range = Some((value, witness));
449 break;
452 }
453 InstanceResult::AboveRange => {
454 continue;
456 }
457 }
458 }
459
460 self.pending_inserts.clear();
462 self.pending_deletes.clear();
463
464 match last_in_range {
466 Some((cut_value, witness)) => {
467 self.last_min_cut = Some(cut_value);
469 MinCutResult::Value { cut_value, witness }
470 }
471 None => {
472 self.last_min_cut = None;
475 use roaring::RoaringBitmap;
476 let mut membership = RoaringBitmap::new();
477 membership.insert(0);
478 let witness = WitnessHandle::new(0, membership, u64::MAX);
479 MinCutResult::Value {
480 cut_value: u64::MAX,
481 witness,
482 }
483 }
484 }
485 }
486
487 fn compute_bounds(i: usize) -> (u64, u64) {
509 let lambda_min = (RANGE_FACTOR.powi(i as i32)).floor() as u64;
510 let lambda_max = (RANGE_FACTOR.powi((i + 1) as i32)).floor() as u64;
511 (lambda_min.max(1), lambda_max.max(1))
512 }
513
514 fn find_instance_for_value(&self, value: u64) -> usize {
522 let mut lo = 0usize;
524 let mut hi = MAX_INSTANCES;
525
526 while lo < hi {
527 let mid = lo + (hi - lo) / 2;
528 if self.lambda_max[mid] < value {
529 lo = mid + 1;
530 } else {
531 hi = mid;
532 }
533 }
534
535 lo.min(MAX_INSTANCES - 1)
536 }
537
538 fn get_search_start(&self) -> usize {
543 if let Some(last_value) = self.last_min_cut {
545 let idx = self.find_instance_for_value(last_value);
547 idx.saturating_sub(2)
549 } else {
550 0
552 }
553 }
554
555 pub fn num_instances(&self) -> usize {
557 self.instances.iter().filter(|i| i.is_some()).count()
558 }
559
560 pub fn current_time(&self) -> u64 {
562 self.current_time
563 }
564
565 pub fn pending_updates(&self) -> usize {
567 self.pending_inserts.len() + self.pending_deletes.len()
568 }
569
570 pub fn batch_insert_edges(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
594 self.pending_inserts.reserve(edges.len());
596
597 for &(edge_id, u, v) in edges {
598 self.current_time += 1;
599
600 self.conn_ds.insert_edge(u, v);
602
603 self.pending_inserts.push(Update {
605 time: self.current_time,
606 edge_id,
607 u,
608 v,
609 });
610 }
611 }
612
613 pub fn batch_delete_edges(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
631 self.pending_deletes.reserve(edges.len());
633
634 for &(edge_id, u, v) in edges {
635 self.current_time += 1;
636
637 self.conn_ds.delete_edge(u, v);
639
640 self.pending_deletes.push(Update {
642 time: self.current_time,
643 edge_id,
644 u,
645 v,
646 });
647 }
648 }
649
650 pub fn batch_update(
670 &mut self,
671 inserts: &[(EdgeId, VertexId, VertexId)],
672 deletes: &[(EdgeId, VertexId, VertexId)],
673 ) {
674 self.batch_insert_edges(inserts);
676 self.batch_delete_edges(deletes);
677 }
678
679 pub fn flush_updates(&mut self) {
687 if self.pending_updates() == 0 {
688 return;
689 }
690
691 self.pending_inserts.sort_by_key(|u| u.time);
693 self.pending_deletes.sort_by_key(|u| u.time);
694
695 for i in 0..MAX_INSTANCES {
697 if let Some(ref mut instance) = self.instances[i] {
698 let last_time = self.last_update_time[i];
699
700 let inserts: Vec<_> = self.pending_inserts
702 .iter()
703 .filter(|u| u.time > last_time)
704 .map(|u| (u.edge_id, u.u, u.v))
705 .collect();
706
707 let deletes: Vec<_> = self.pending_deletes
708 .iter()
709 .filter(|u| u.time > last_time)
710 .map(|u| (u.edge_id, u.u, u.v))
711 .collect();
712
713 if !inserts.is_empty() {
714 instance.apply_inserts(&inserts);
715 }
716 if !deletes.is_empty() {
717 instance.apply_deletes(&deletes);
718 }
719
720 self.last_update_time[i] = self.current_time;
721 }
722 }
723
724 self.pending_inserts.clear();
726 self.pending_deletes.clear();
727 }
728
729 pub fn min_cut_value(&mut self) -> u64 {
738 self.query().value()
739 }
740
741 pub fn query_with_local_kcut(&mut self, source: VertexId) -> (u64, bool) {
756 use crate::localkcut::deterministic::DeterministicLocalKCut;
757
758 let result = self.query();
760 let cut_value = result.value();
761
762 if cut_value == 0 {
763 return (0, true); }
765
766 let volume_bound = self.graph.num_edges().max(1) * 2;
768 let lambda_max = cut_value * 2;
769
770 let mut lkc = DeterministicLocalKCut::new(lambda_max, volume_bound, 2);
771
772 for edge in self.graph.edges() {
774 lkc.insert_edge(edge.source, edge.target, edge.weight);
775 }
776
777 let cuts = lkc.query(source);
779
780 let certified = cuts.iter().any(|c| {
782 let diff = (c.cut_value - cut_value as f64).abs();
783 diff < 0.001 || c.cut_value <= cut_value as f64
784 });
785
786 (cut_value, certified || cuts.is_empty())
787 }
788
789 pub fn local_cuts(&self, source: VertexId, lambda_max: u64) -> Vec<(f64, Vec<VertexId>)> {
803 use crate::localkcut::deterministic::DeterministicLocalKCut;
804
805 let volume_bound = self.graph.num_edges().max(1) * 2;
806 let mut lkc = DeterministicLocalKCut::new(lambda_max, volume_bound, 2);
807
808 for edge in self.graph.edges() {
810 lkc.insert_edge(edge.source, edge.target, edge.weight);
811 }
812
813 lkc.query(source)
815 .into_iter()
816 .map(|c| (c.cut_value, c.vertices.into_iter().collect()))
817 .collect()
818 }
819
820 pub fn build_hierarchy(&self) -> crate::cluster::hierarchy::ThreeLevelHierarchy {
826 use crate::cluster::hierarchy::{ThreeLevelHierarchy, HierarchyConfig};
827
828 let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
829 track_mirror_cuts: true,
830 ..Default::default()
831 });
832
833 for edge in self.graph.edges() {
835 h.insert_edge(edge.source, edge.target, edge.weight);
836 }
837
838 h.build();
839 h
840 }
841
842 pub fn connectivity_curve(
876 &self,
877 ranked_edges: &[(VertexId, VertexId, f64)],
878 k_max: usize,
879 ) -> Vec<(usize, u64)> {
880 use crate::algorithm::DynamicMinCut;
881
882 let mut temp_mincut = DynamicMinCut::new(crate::MinCutConfig::default());
884
885 for edge in self.graph.edges() {
886 let _ = temp_mincut.insert_edge(edge.source, edge.target, edge.weight);
887 }
888
889 let mut curve = Vec::with_capacity(k_max + 1);
890
891 curve.push((0, temp_mincut.min_cut_value() as u64));
893
894 for (k, &(u, v, _score)) in ranked_edges.iter().take(k_max).enumerate() {
896 let _ = temp_mincut.delete_edge(u, v);
897 let new_cut = temp_mincut.min_cut_value() as u64;
898 curve.push((k + 1, new_cut));
899 }
900
901 curve
902 }
903
904 pub fn find_elbow(curve: &[(usize, u64)]) -> Option<(usize, u64)> {
918 if curve.len() < 2 {
919 return None;
920 }
921
922 let mut max_drop = 0u64;
923 let mut elbow_k = 0usize;
924
925 for i in 1..curve.len() {
926 let drop = curve[i - 1].1.saturating_sub(curve[i].1);
927 if drop > max_drop {
928 max_drop = drop;
929 elbow_k = curve[i].0;
930 }
931 }
932
933 if max_drop > 0 {
934 Some((elbow_k, max_drop))
935 } else {
936 None
937 }
938 }
939
940 pub fn detector_quality(
955 &self,
956 ranked_edges: &[(VertexId, VertexId, f64)],
957 true_cut_size: usize,
958 ) -> f64 {
959 let k_max = true_cut_size.min(ranked_edges.len());
960 if k_max == 0 {
961 return 0.0;
962 }
963
964 let curve = self.connectivity_curve(ranked_edges, k_max);
965
966 let initial_cut = curve.first().map(|(_, c)| *c).unwrap_or(0);
968 let final_cut = curve.last().map(|(_, c)| *c).unwrap_or(0);
969
970 if initial_cut == 0 {
972 0.0
973 } else {
974 (initial_cut - final_cut) as f64 / initial_cut as f64
975 }
976 }
977}
978
979#[cfg(test)]
980mod tests {
981 use super::*;
982
983 #[test]
984 fn test_compute_bounds() {
985 let (min, max) = MinCutWrapper::compute_bounds(0);
987 assert_eq!(min, 1);
988 assert_eq!(max, 1);
989
990 let (min, max) = MinCutWrapper::compute_bounds(1);
992 assert_eq!(min, 1);
993 assert_eq!(max, 1);
994
995 let (min, max) = MinCutWrapper::compute_bounds(5);
997 assert_eq!(min, 2);
998 assert_eq!(max, 2);
999
1000 let (min, max) = MinCutWrapper::compute_bounds(10);
1002 assert_eq!(min, 6);
1003 assert_eq!(max, 7);
1004
1005 let (min, max) = MinCutWrapper::compute_bounds(20);
1007 assert_eq!(min, 38);
1008 assert_eq!(max, 46);
1009 }
1010
1011 #[test]
1012 fn test_new_wrapper() {
1013 let graph = Arc::new(DynamicGraph::new());
1014 let wrapper = MinCutWrapper::new(graph);
1015
1016 assert_eq!(wrapper.num_instances(), 0); assert_eq!(wrapper.current_time(), 0);
1018 assert_eq!(wrapper.pending_updates(), 0);
1019 }
1020
1021 #[test]
1022 fn test_empty_graph() {
1023 let graph = Arc::new(DynamicGraph::new());
1024 let mut wrapper = MinCutWrapper::new(graph);
1025
1026 let result = wrapper.query();
1027 assert_eq!(result.value(), 0);
1030 }
1031
1032 #[test]
1033 fn test_disconnected_graph() {
1034 let graph = Arc::new(DynamicGraph::new());
1035 graph.insert_edge(1, 2, 1.0).unwrap();
1036 graph.insert_edge(3, 4, 1.0).unwrap();
1037
1038 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1039
1040 wrapper.insert_edge(0, 1, 2);
1042 wrapper.insert_edge(1, 3, 4);
1043
1044 let result = wrapper.query();
1045
1046 assert_eq!(result.value(), 0);
1048 assert!(matches!(result, MinCutResult::Disconnected));
1049 }
1050
1051 #[test]
1052 fn test_insert_and_query() {
1053 let graph = Arc::new(DynamicGraph::new());
1054 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1055
1056 graph.insert_edge(1, 2, 1.0).unwrap();
1057 wrapper.insert_edge(0, 1, 2);
1058
1059 assert_eq!(wrapper.pending_updates(), 1);
1060
1061 let result = wrapper.query();
1062 assert!(result.is_connected());
1063
1064 assert_eq!(wrapper.pending_updates(), 0);
1066 }
1067
1068 #[test]
1069 fn test_time_counter() {
1070 let graph = Arc::new(DynamicGraph::new());
1071 let mut wrapper = MinCutWrapper::new(graph);
1072
1073 assert_eq!(wrapper.current_time(), 0);
1074
1075 wrapper.insert_edge(0, 1, 2);
1076 assert_eq!(wrapper.current_time(), 1);
1077
1078 wrapper.delete_edge(0, 1, 2);
1079 assert_eq!(wrapper.current_time(), 2);
1080
1081 wrapper.insert_edge(1, 2, 3);
1082 assert_eq!(wrapper.current_time(), 3);
1083 }
1084
1085 #[test]
1086 fn test_lazy_instantiation() {
1087 let graph = Arc::new(DynamicGraph::new());
1088 graph.insert_edge(1, 2, 1.0).unwrap();
1090 graph.insert_edge(2, 3, 1.0).unwrap();
1091
1092 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1093 wrapper.insert_edge(0, 1, 2);
1094 wrapper.insert_edge(1, 2, 3);
1095
1096 assert_eq!(wrapper.num_instances(), 0);
1098
1099 let _ = wrapper.query();
1101
1102 assert!(wrapper.num_instances() > 0);
1104 }
1105
1106 #[test]
1107 fn test_result_value() {
1108 use roaring::RoaringBitmap;
1109
1110 let result = MinCutResult::Disconnected;
1111 assert_eq!(result.value(), 0);
1112 assert!(!result.is_connected());
1113 assert!(result.witness().is_none());
1114
1115 let mut membership = RoaringBitmap::new();
1116 membership.insert(1);
1117 membership.insert(2);
1118 let witness = WitnessHandle::new(1, membership, 5);
1119 let result = MinCutResult::Value {
1120 cut_value: 5,
1121 witness: witness.clone(),
1122 };
1123 assert_eq!(result.value(), 5);
1124 assert!(result.is_connected());
1125 assert!(result.witness().is_some());
1126 }
1127
1128 #[test]
1129 fn test_bounds_coverage() {
1130 let (min, _max) = MinCutWrapper::compute_bounds(50);
1132 assert!(min > 1000);
1133
1134 let (min, _max) = MinCutWrapper::compute_bounds(99);
1135 assert!(min > 1_000_000);
1136 }
1137
1138 #[test]
1139 #[cfg(feature = "agentic")]
1140 fn test_agentic_backend() {
1141 let graph = Arc::new(DynamicGraph::new());
1142 graph.insert_edge(0, 1, 1.0).unwrap();
1144 graph.insert_edge(1, 2, 1.0).unwrap();
1145 graph.insert_edge(2, 0, 1.0).unwrap();
1146
1147 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph))
1149 .with_agentic(true);
1150
1151 wrapper.insert_edge(0, 0, 1);
1153 wrapper.insert_edge(1, 1, 2);
1154 wrapper.insert_edge(2, 2, 0);
1155
1156 let result = wrapper.query();
1157
1158 match result {
1161 MinCutResult::Disconnected => {
1162 }
1164 MinCutResult::Value { cut_value, .. } => {
1165 assert!(cut_value < u64::MAX);
1167 }
1168 }
1169 }
1170
1171 #[test]
1176 fn test_batch_insert_edges() {
1177 let graph = Arc::new(DynamicGraph::new());
1178 graph.insert_edge(1, 2, 1.0).unwrap();
1179 graph.insert_edge(2, 3, 1.0).unwrap();
1180 graph.insert_edge(3, 4, 1.0).unwrap();
1181
1182 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1183
1184 wrapper.batch_insert_edges(&[
1186 (0, 1, 2),
1187 (1, 2, 3),
1188 (2, 3, 4),
1189 ]);
1190
1191 assert_eq!(wrapper.pending_updates(), 3);
1192 assert_eq!(wrapper.current_time(), 3);
1193
1194 let result = wrapper.query();
1195 assert!(result.is_connected());
1196 assert_eq!(wrapper.pending_updates(), 0);
1197 }
1198
1199 #[test]
1200 fn test_batch_delete_edges() {
1201 let graph = Arc::new(DynamicGraph::new());
1202 graph.insert_edge(1, 2, 1.0).unwrap();
1203 graph.insert_edge(2, 3, 1.0).unwrap();
1204 graph.insert_edge(3, 4, 1.0).unwrap();
1205
1206 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1207
1208 wrapper.batch_insert_edges(&[
1210 (0, 1, 2),
1211 (1, 2, 3),
1212 (2, 3, 4),
1213 ]);
1214
1215 let _ = wrapper.query();
1217
1218 wrapper.batch_delete_edges(&[(1, 2, 3)]);
1220
1221 assert_eq!(wrapper.pending_updates(), 1);
1222
1223 let result = wrapper.query();
1224 assert!(result.value() >= 0);
1227 }
1228
1229 #[test]
1230 fn test_batch_update_combined() {
1231 let graph = Arc::new(DynamicGraph::new());
1232 graph.insert_edge(1, 2, 1.0).unwrap();
1233 graph.insert_edge(2, 3, 1.0).unwrap();
1234
1235 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1236
1237 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1239 let _ = wrapper.query();
1240
1241 wrapper.batch_update(
1243 &[(2, 3, 4)], &[(1, 2, 3)], );
1246
1247 assert_eq!(wrapper.pending_updates(), 2);
1248 }
1249
1250 #[test]
1251 fn test_flush_updates() {
1252 let graph = Arc::new(DynamicGraph::new());
1253 graph.insert_edge(1, 2, 1.0).unwrap();
1254 graph.insert_edge(2, 3, 1.0).unwrap();
1255
1256 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1257
1258 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1259
1260 let _ = wrapper.query();
1262 assert_eq!(wrapper.pending_updates(), 0);
1263
1264 wrapper.batch_insert_edges(&[(2, 3, 4)]);
1266 assert_eq!(wrapper.pending_updates(), 1);
1267
1268 wrapper.flush_updates();
1270 assert_eq!(wrapper.pending_updates(), 0);
1271 }
1272
1273 #[test]
1274 fn test_min_cut_value_convenience() {
1275 let graph = Arc::new(DynamicGraph::new());
1276 graph.insert_edge(1, 2, 1.0).unwrap();
1277
1278 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1279 wrapper.insert_edge(0, 1, 2);
1280
1281 let value = wrapper.min_cut_value();
1283 assert!(value >= 0);
1284 }
1285
1286 #[test]
1287 fn test_binary_search_instance_lookup() {
1288 let graph = Arc::new(DynamicGraph::new());
1289 let wrapper = MinCutWrapper::new(graph);
1290
1291 assert_eq!(wrapper.find_instance_for_value(1), 0);
1294
1295 let idx = wrapper.find_instance_for_value(2);
1297 assert!(wrapper.lambda_min[idx] <= 2);
1298 assert!(wrapper.lambda_max[idx] >= 2);
1299
1300 let idx = wrapper.find_instance_for_value(100);
1302 assert!(wrapper.lambda_min[idx] <= 100);
1303 assert!(wrapper.lambda_max[idx] >= 100);
1304 }
1305
1306 #[test]
1307 fn test_cached_min_cut_optimization() {
1308 let graph = Arc::new(DynamicGraph::new());
1309 graph.insert_edge(1, 2, 1.0).unwrap();
1311 graph.insert_edge(2, 3, 1.0).unwrap();
1312
1313 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1314 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1315
1316 assert!(wrapper.last_min_cut.is_none());
1318 let result1 = wrapper.query();
1319
1320 assert!(wrapper.last_min_cut.is_some());
1322
1323 wrapper.batch_insert_edges(&[(2, 3, 4)]);
1325 let result2 = wrapper.query();
1326
1327 assert!(result1.is_connected());
1329 assert!(result2.is_connected());
1330 }
1331
1332 #[test]
1333 fn test_query_with_local_kcut() {
1334 let graph = Arc::new(DynamicGraph::new());
1335 graph.insert_edge(1, 2, 1.0).unwrap();
1336 graph.insert_edge(2, 3, 1.0).unwrap();
1337 graph.insert_edge(3, 1, 1.0).unwrap();
1338
1339 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1340 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3), (2, 3, 1)]);
1341
1342 let (cut_value, certified) = wrapper.query_with_local_kcut(1);
1343
1344 assert!(cut_value >= 0, "Cut value should be non-negative");
1346 assert!(certified || !certified, "Certification should complete without panic");
1348 }
1349
1350 #[test]
1351 fn test_local_cuts() {
1352 let graph = Arc::new(DynamicGraph::new());
1353 graph.insert_edge(1, 2, 1.0).unwrap();
1354 graph.insert_edge(2, 3, 1.0).unwrap();
1355
1356 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1357 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1358 wrapper.query(); let cuts = wrapper.local_cuts(1, 5);
1361
1362 assert!(cuts.len() >= 0, "Should return some cuts or empty");
1364 }
1365
1366 #[test]
1367 fn test_build_hierarchy() {
1368 let graph = Arc::new(DynamicGraph::new());
1369 graph.insert_edge(1, 2, 1.0).unwrap();
1370 graph.insert_edge(2, 3, 1.0).unwrap();
1371 graph.insert_edge(3, 4, 1.0).unwrap();
1372 graph.insert_edge(4, 1, 1.0).unwrap();
1373
1374 let wrapper = MinCutWrapper::new(Arc::clone(&graph));
1375 let hierarchy = wrapper.build_hierarchy();
1376
1377 let stats = hierarchy.stats();
1379 assert!(stats.num_vertices >= 4, "Hierarchy should have 4 vertices");
1380 }
1381
1382 #[test]
1383 fn test_connectivity_curve_basic() {
1384 let graph = Arc::new(DynamicGraph::new());
1387 graph.insert_edge(1, 2, 1.0).unwrap();
1388 graph.insert_edge(2, 3, 1.0).unwrap();
1389
1390 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1391 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3)]);
1392 wrapper.query();
1393
1394 let ranked_edges = vec![
1396 (1, 2, 1.0),
1397 (2, 3, 0.8),
1398 ];
1399
1400 let curve = wrapper.connectivity_curve(&ranked_edges, 2);
1401
1402 assert_eq!(curve.len(), 3);
1404 assert_eq!(curve[0].0, 0); assert_eq!(curve[1].0, 1); assert_eq!(curve[2].0, 2); }
1408
1409 #[test]
1410 fn test_find_elbow_with_clear_drop() {
1411 let curve = vec![
1413 (0, 10), (1, 9), (2, 3), (3, 2), (4, 2), ];
1419
1420 let elbow = MinCutWrapper::find_elbow(&curve);
1421 assert!(elbow.is_some());
1422
1423 let (k, drop) = elbow.unwrap();
1424 assert_eq!(k, 2); assert_eq!(drop, 6); }
1427
1428 #[test]
1429 fn test_find_elbow_flat_curve() {
1430 let curve = vec![
1432 (0, 5),
1433 (1, 5),
1434 (2, 5),
1435 (3, 5),
1436 ];
1437
1438 let elbow = MinCutWrapper::find_elbow(&curve);
1439 assert!(elbow.is_none()); }
1441
1442 #[test]
1443 fn test_find_elbow_single_point() {
1444 let curve = vec![(0, 5)];
1445 let elbow = MinCutWrapper::find_elbow(&curve);
1446 assert!(elbow.is_none()); }
1448
1449 #[test]
1450 fn test_find_elbow_empty() {
1451 let curve: Vec<(usize, u64)> = vec![];
1452 let elbow = MinCutWrapper::find_elbow(&curve);
1453 assert!(elbow.is_none());
1454 }
1455
1456 #[test]
1457 fn test_detector_quality_perfect() {
1458 let graph = Arc::new(DynamicGraph::new());
1461 graph.insert_edge(1, 2, 1.0).unwrap();
1462 graph.insert_edge(2, 3, 1.0).unwrap();
1463 graph.insert_edge(3, 4, 1.0).unwrap();
1464
1465 let mut wrapper = MinCutWrapper::new(Arc::clone(&graph));
1466 wrapper.batch_insert_edges(&[(0, 1, 2), (1, 2, 3), (2, 3, 4)]);
1467 wrapper.query();
1468
1469 let ranked_edges = vec![
1471 (2, 3, 1.0), (1, 2, 0.5),
1473 (3, 4, 0.3),
1474 ];
1475
1476 let quality = wrapper.detector_quality(&ranked_edges, 1);
1477
1478 assert!(quality >= 0.0);
1480 assert!(quality <= 1.0);
1481 }
1482
1483 #[test]
1484 fn test_detector_quality_zero_cut() {
1485 let graph = Arc::new(DynamicGraph::new());
1486 let wrapper = MinCutWrapper::new(Arc::clone(&graph));
1487
1488 let ranked_edges: Vec<(u64, u64, f64)> = vec![];
1490
1491 let quality = wrapper.detector_quality(&ranked_edges, 1);
1492 assert_eq!(quality, 0.0);
1493 }
1494
1495 #[test]
1496 fn test_connectivity_curve_empty_graph() {
1497 let graph = Arc::new(DynamicGraph::new());
1498 let wrapper = MinCutWrapper::new(Arc::clone(&graph));
1499
1500 let ranked_edges = vec![(1, 2, 1.0)];
1501 let curve = wrapper.connectivity_curve(&ranked_edges, 2);
1502
1503 assert!(!curve.is_empty());
1505 assert_eq!(curve[0].0, 0); }
1507}