1pub mod paper_impl;
31pub mod deterministic;
32
33pub use paper_impl::{
35 LocalKCutQuery, LocalKCutResult, LocalKCutOracle,
36 DeterministicLocalKCut, DeterministicFamilyGenerator,
37};
38
39use crate::graph::{DynamicGraph, VertexId, EdgeId, Weight};
40use crate::{MinCutError, Result};
41use std::collections::{HashMap, HashSet, VecDeque};
42use std::sync::Arc;
43
44#[derive(Debug, Clone)]
46pub struct LocalCutResult {
47 pub cut_value: Weight,
49 pub cut_set: HashSet<VertexId>,
51 pub cut_edges: Vec<(VertexId, VertexId)>,
53 pub is_minimum: bool,
55 pub iterations: usize,
57}
58
59impl LocalCutResult {
60 pub fn new(
62 cut_value: Weight,
63 cut_set: HashSet<VertexId>,
64 cut_edges: Vec<(VertexId, VertexId)>,
65 is_minimum: bool,
66 iterations: usize,
67 ) -> Self {
68 Self {
69 cut_value,
70 cut_set,
71 cut_edges,
72 is_minimum,
73 iterations,
74 }
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
80pub enum EdgeColor {
81 Red,
83 Blue,
85 Green,
87 Yellow,
89}
90
91impl EdgeColor {
92 pub fn from_index(index: usize) -> Self {
94 match index % 4 {
95 0 => EdgeColor::Red,
96 1 => EdgeColor::Blue,
97 2 => EdgeColor::Green,
98 _ => EdgeColor::Yellow,
99 }
100 }
101
102 pub fn to_index(self) -> usize {
104 match self {
105 EdgeColor::Red => 0,
106 EdgeColor::Blue => 1,
107 EdgeColor::Green => 2,
108 EdgeColor::Yellow => 3,
109 }
110 }
111
112 pub fn all() -> [EdgeColor; 4] {
114 [EdgeColor::Red, EdgeColor::Blue, EdgeColor::Green, EdgeColor::Yellow]
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
120pub struct ColorMask(u8);
121
122impl ColorMask {
123 pub fn empty() -> Self {
125 Self(0)
126 }
127
128 pub fn all() -> Self {
130 Self(0b1111)
131 }
132
133 pub fn from_colors(colors: &[EdgeColor]) -> Self {
135 let mut mask = 0u8;
136 for color in colors {
137 mask |= 1 << color.to_index();
138 }
139 Self(mask)
140 }
141
142 pub fn contains(self, color: EdgeColor) -> bool {
144 (self.0 & (1 << color.to_index())) != 0
145 }
146
147 pub fn insert(&mut self, color: EdgeColor) {
149 self.0 |= 1 << color.to_index();
150 }
151
152 pub fn colors(self) -> Vec<EdgeColor> {
154 let mut result = Vec::new();
155 for color in EdgeColor::all() {
156 if self.contains(color) {
157 result.push(color);
158 }
159 }
160 result
161 }
162
163 pub fn count(self) -> usize {
165 self.0.count_ones() as usize
166 }
167}
168
169pub struct LocalKCut {
171 k: usize,
173 graph: Arc<DynamicGraph>,
175 edge_colors: HashMap<EdgeId, EdgeColor>,
177 radius: usize,
179}
180
181impl LocalKCut {
182 pub fn new(graph: Arc<DynamicGraph>, k: usize) -> Self {
191 let radius = Self::compute_radius(k);
192 let mut finder = Self {
193 k,
194 graph,
195 edge_colors: HashMap::new(),
196 radius,
197 };
198 finder.assign_colors();
199 finder
200 }
201
202 fn compute_radius(k: usize) -> usize {
205 if k <= 1 {
206 1
207 } else {
208 let log_k = (k as f64).log2() / 2.0;
210 log_k.ceil() as usize + 1
211 }
212 }
213
214 pub fn find_cut(&self, v: VertexId) -> Option<LocalCutResult> {
228 if !self.graph.has_vertex(v) {
229 return None;
230 }
231
232 let mut best_cut: Option<LocalCutResult> = None;
233 let mut iterations = 0;
234
235 for depth in 1..=self.radius {
238 let num_masks = 1 << 4; for mask_bits in 1..num_masks {
242 iterations += 1;
243 let mask = ColorMask(mask_bits as u8);
244
245 let reachable = self.color_constrained_bfs(v, mask, depth);
247
248 if reachable.is_empty() || reachable.len() >= self.graph.num_vertices() {
249 continue;
250 }
251
252 if let Some(cut) = self.check_cut(&reachable) {
254 let should_update = match &best_cut {
256 None => true,
257 Some(prev) => cut.cut_value < prev.cut_value,
258 };
259
260 if should_update {
261 let mut cut = cut;
262 cut.iterations = iterations;
263 best_cut = Some(cut);
264 }
265 }
266 }
267
268 if let Some(ref cut) = best_cut {
270 if cut.cut_value <= 1.0 {
271 break;
272 }
273 }
274 }
275
276 best_cut
277 }
278
279 fn color_constrained_bfs(
292 &self,
293 start: VertexId,
294 mask: ColorMask,
295 max_depth: usize,
296 ) -> HashSet<VertexId> {
297 let mut visited = HashSet::new();
298 let mut queue = VecDeque::new();
299
300 queue.push_back((start, 0));
301 visited.insert(start);
302
303 while let Some((v, depth)) = queue.pop_front() {
304 if depth >= max_depth {
305 continue;
306 }
307
308 for (neighbor, edge_id) in self.graph.neighbors(v) {
310 if visited.contains(&neighbor) {
311 continue;
312 }
313
314 if let Some(&color) = self.edge_colors.get(&edge_id) {
316 if mask.contains(color) {
317 visited.insert(neighbor);
318 queue.push_back((neighbor, depth + 1));
319 }
320 }
321 }
322 }
323
324 visited
325 }
326
327 fn assign_colors(&mut self) {
334 self.edge_colors.clear();
335
336 for edge in self.graph.edges() {
337 let color = EdgeColor::from_index(edge.id as usize);
339 self.edge_colors.insert(edge.id, color);
340 }
341 }
342
343 fn check_cut(&self, vertices: &HashSet<VertexId>) -> Option<LocalCutResult> {
351 if vertices.is_empty() || vertices.len() >= self.graph.num_vertices() {
352 return None;
353 }
354
355 let mut cut_edges = Vec::new();
356 let mut cut_value = 0.0;
357
358 for &v in vertices {
360 for (neighbor, edge_id) in self.graph.neighbors(v) {
361 if !vertices.contains(&neighbor) {
362 if let Some(edge) = self.graph.edges().iter().find(|e| e.id == edge_id) {
364 cut_edges.push((v, neighbor));
365 cut_value += edge.weight;
366 }
367 }
368 }
369 }
370
371 if cut_value <= self.k as f64 {
373 Some(LocalCutResult::new(
374 cut_value,
375 vertices.clone(),
376 cut_edges,
377 false, 0, ))
380 } else {
381 None
382 }
383 }
384
385 pub fn enumerate_paths(&self, v: VertexId, depth: usize) -> Vec<HashSet<VertexId>> {
397 let mut results = Vec::new();
398
399 for mask_bits in 1..16u8 {
401 let mask = ColorMask(mask_bits);
402 let reachable = self.color_constrained_bfs(v, mask, depth);
403
404 if !reachable.is_empty() {
405 results.push(reachable);
406 }
407 }
408
409 results
410 }
411
412 pub fn edge_color(&self, edge_id: EdgeId) -> Option<EdgeColor> {
414 self.edge_colors.get(&edge_id).copied()
415 }
416
417 pub fn radius(&self) -> usize {
419 self.radius
420 }
421
422 pub fn max_cut_size(&self) -> usize {
424 self.k
425 }
426}
427
428pub struct ForestPacking {
439 num_forests: usize,
441 forests: Vec<HashSet<(VertexId, VertexId)>>,
443}
444
445impl ForestPacking {
446 pub fn greedy_packing(
465 graph: &DynamicGraph,
466 lambda_max: usize,
467 epsilon: f64,
468 ) -> Self {
469 let m = graph.num_edges();
470 let n = graph.num_vertices();
471
472 if m == 0 || n == 0 {
473 return Self {
474 num_forests: 0,
475 forests: Vec::new(),
476 };
477 }
478
479 let log_m = (m as f64).ln();
481 let num_forests = ((lambda_max as f64 * log_m) / (epsilon * epsilon)).ceil() as usize;
482 let num_forests = num_forests.max(1);
483
484 let mut forests = Vec::with_capacity(num_forests);
485 let edges = graph.edges();
486
487 for _ in 0..num_forests {
489 let mut forest = HashSet::new();
490 let mut components = UnionFind::new(n);
491
492 for edge in &edges {
493 let (u, v) = (edge.source, edge.target);
494
495 if components.find(u) != components.find(v) {
497 forest.insert((u.min(v), u.max(v)));
498 components.union(u, v);
499 }
500 }
501
502 forests.push(forest);
503 }
504
505 Self {
506 num_forests,
507 forests,
508 }
509 }
510
511 pub fn witnesses_cut(&self, cut_edges: &[(VertexId, VertexId)]) -> bool {
524 if self.forests.is_empty() {
525 return true;
526 }
527
528 let normalized_cut: HashSet<_> = cut_edges
530 .iter()
531 .map(|(u, v)| ((*u).min(*v), (*u).max(*v)))
532 .collect();
533
534 for forest in &self.forests {
536 let has_witness = forest.iter().any(|edge| normalized_cut.contains(edge));
538
539 if !has_witness {
540 return false;
541 }
542 }
543
544 true
545 }
546
547 pub fn num_forests(&self) -> usize {
549 self.num_forests
550 }
551
552 pub fn forest(&self, index: usize) -> Option<&HashSet<(VertexId, VertexId)>> {
554 self.forests.get(index)
555 }
556}
557
558struct UnionFind {
560 parent: Vec<usize>,
561 rank: Vec<usize>,
562}
563
564impl UnionFind {
565 fn new(n: usize) -> Self {
566 Self {
567 parent: (0..n).collect(),
568 rank: vec![0; n],
569 }
570 }
571
572 fn find(&mut self, x: VertexId) -> VertexId {
573 let x_idx = x as usize % self.parent.len();
574 let mut idx = x_idx;
575
576 while self.parent[idx] != idx {
578 let parent = self.parent[idx];
579 self.parent[idx] = self.parent[parent];
580 idx = parent;
581 }
582
583 idx as VertexId
584 }
585
586 fn union(&mut self, x: VertexId, y: VertexId) {
587 let root_x = self.find(x);
588 let root_y = self.find(y);
589
590 if root_x == root_y {
591 return;
592 }
593
594 let rx = root_x as usize % self.parent.len();
595 let ry = root_y as usize % self.parent.len();
596
597 if self.rank[rx] < self.rank[ry] {
599 self.parent[rx] = ry;
600 } else if self.rank[rx] > self.rank[ry] {
601 self.parent[ry] = rx;
602 } else {
603 self.parent[ry] = rx;
604 self.rank[rx] += 1;
605 }
606 }
607}
608
609#[cfg(test)]
610mod tests {
611 use super::*;
612
613 fn create_test_graph() -> Arc<DynamicGraph> {
614 let graph = DynamicGraph::new();
615
616 graph.insert_edge(1, 2, 1.0).unwrap();
622 graph.insert_edge(2, 3, 1.0).unwrap();
623 graph.insert_edge(1, 4, 1.0).unwrap();
624 graph.insert_edge(2, 5, 1.0).unwrap();
625 graph.insert_edge(3, 6, 1.0).unwrap();
626 graph.insert_edge(4, 5, 1.0).unwrap();
627 graph.insert_edge(5, 6, 1.0).unwrap();
628
629 Arc::new(graph)
630 }
631
632 #[test]
633 fn test_edge_color_conversion() {
634 assert_eq!(EdgeColor::from_index(0), EdgeColor::Red);
635 assert_eq!(EdgeColor::from_index(1), EdgeColor::Blue);
636 assert_eq!(EdgeColor::from_index(2), EdgeColor::Green);
637 assert_eq!(EdgeColor::from_index(3), EdgeColor::Yellow);
638 assert_eq!(EdgeColor::from_index(4), EdgeColor::Red); assert_eq!(EdgeColor::Red.to_index(), 0);
641 assert_eq!(EdgeColor::Blue.to_index(), 1);
642 assert_eq!(EdgeColor::Green.to_index(), 2);
643 assert_eq!(EdgeColor::Yellow.to_index(), 3);
644 }
645
646 #[test]
647 fn test_color_mask() {
648 let mut mask = ColorMask::empty();
649 assert_eq!(mask.count(), 0);
650
651 mask.insert(EdgeColor::Red);
652 assert!(mask.contains(EdgeColor::Red));
653 assert!(!mask.contains(EdgeColor::Blue));
654 assert_eq!(mask.count(), 1);
655
656 mask.insert(EdgeColor::Blue);
657 assert_eq!(mask.count(), 2);
658
659 let all_mask = ColorMask::all();
660 assert_eq!(all_mask.count(), 4);
661 assert!(all_mask.contains(EdgeColor::Red));
662 assert!(all_mask.contains(EdgeColor::Blue));
663 assert!(all_mask.contains(EdgeColor::Green));
664 assert!(all_mask.contains(EdgeColor::Yellow));
665 }
666
667 #[test]
668 fn test_color_mask_from_colors() {
669 let colors = vec![EdgeColor::Red, EdgeColor::Green];
670 let mask = ColorMask::from_colors(&colors);
671
672 assert!(mask.contains(EdgeColor::Red));
673 assert!(!mask.contains(EdgeColor::Blue));
674 assert!(mask.contains(EdgeColor::Green));
675 assert!(!mask.contains(EdgeColor::Yellow));
676 assert_eq!(mask.count(), 2);
677 }
678
679 #[test]
680 fn test_local_kcut_new() {
681 let graph = create_test_graph();
682 let local_kcut = LocalKCut::new(graph.clone(), 3);
683
684 assert_eq!(local_kcut.max_cut_size(), 3);
685 assert!(local_kcut.radius() > 0);
686 assert_eq!(local_kcut.edge_colors.len(), graph.num_edges());
687 }
688
689 #[test]
690 fn test_compute_radius() {
691 assert_eq!(LocalKCut::compute_radius(1), 1);
692 assert_eq!(LocalKCut::compute_radius(4), 2);
693 assert_eq!(LocalKCut::compute_radius(16), 3);
694 assert_eq!(LocalKCut::compute_radius(64), 4);
695 }
696
697 #[test]
698 fn test_assign_colors() {
699 let graph = create_test_graph();
700 let local_kcut = LocalKCut::new(graph.clone(), 3);
701
702 for edge in graph.edges() {
704 assert!(local_kcut.edge_color(edge.id).is_some());
705 }
706 }
707
708 #[test]
709 fn test_color_constrained_bfs() {
710 let graph = create_test_graph();
711 let local_kcut = LocalKCut::new(graph.clone(), 3);
712
713 let all_mask = ColorMask::all();
715 let reachable = local_kcut.color_constrained_bfs(1, all_mask, 10);
716
717 assert!(reachable.contains(&1));
718 assert!(reachable.len() > 1);
719 }
720
721 #[test]
722 fn test_color_constrained_bfs_limited() {
723 let graph = create_test_graph();
724 let local_kcut = LocalKCut::new(graph.clone(), 3);
725
726 let all_mask = ColorMask::all();
728 let reachable = local_kcut.color_constrained_bfs(1, all_mask, 0);
729
730 assert_eq!(reachable.len(), 1);
731 assert!(reachable.contains(&1));
732 }
733
734 #[test]
735 fn test_find_cut_simple() {
736 let graph = Arc::new(DynamicGraph::new());
737
738 graph.insert_edge(1, 2, 2.0).unwrap();
741 graph.insert_edge(2, 3, 1.0).unwrap();
742
743 let local_kcut = LocalKCut::new(graph.clone(), 5);
744 let result = local_kcut.find_cut(1);
745
746 assert!(result.is_some());
747 if let Some(cut) = result {
748 assert!(cut.cut_value <= 5.0);
749 assert!(!cut.cut_set.is_empty());
750 }
751 }
752
753 #[test]
754 fn test_check_cut() {
755 let graph = create_test_graph();
756 let local_kcut = LocalKCut::new(graph.clone(), 10);
757
758 let mut cut_set = HashSet::new();
760 cut_set.insert(1);
761 cut_set.insert(2);
762
763 let result = local_kcut.check_cut(&cut_set);
764 assert!(result.is_some());
765
766 if let Some(cut) = result {
767 assert!(cut.cut_value > 0.0);
768 assert!(!cut.cut_edges.is_empty());
769 }
770 }
771
772 #[test]
773 fn test_check_cut_invalid() {
774 let graph = create_test_graph();
775 let local_kcut = LocalKCut::new(graph.clone(), 3);
776
777 let empty_set = HashSet::new();
779 assert!(local_kcut.check_cut(&empty_set).is_none());
780
781 let all_vertices: HashSet<_> = graph.vertices().into_iter().collect();
783 assert!(local_kcut.check_cut(&all_vertices).is_none());
784 }
785
786 #[test]
787 fn test_enumerate_paths() {
788 let graph = create_test_graph();
789 let local_kcut = LocalKCut::new(graph.clone(), 3);
790
791 let paths = local_kcut.enumerate_paths(1, 2);
792
793 assert!(!paths.is_empty());
795
796 for path in &paths {
798 assert!(path.contains(&1));
799 }
800 }
801
802 #[test]
803 fn test_forest_packing_empty_graph() {
804 let graph = DynamicGraph::new();
805 let packing = ForestPacking::greedy_packing(&graph, 10, 0.1);
806
807 assert_eq!(packing.num_forests(), 0);
808 }
809
810 #[test]
811 fn test_forest_packing_simple() {
812 let graph = create_test_graph();
813 let packing = ForestPacking::greedy_packing(&*graph, 10, 0.1);
814
815 assert!(packing.num_forests() > 0);
816
817 for i in 0..packing.num_forests() {
819 if let Some(forest) = packing.forest(i) {
820 assert!(!forest.is_empty());
821 }
822 }
823 }
824
825 #[test]
826 fn test_forest_witnesses_cut() {
827 let graph = create_test_graph();
828 let packing = ForestPacking::greedy_packing(&*graph, 5, 0.1);
829
830 let cut_edges = vec![(1, 2)];
832
833 let witnesses = packing.witnesses_cut(&cut_edges);
835
836 let _ = witnesses;
839
840 assert!(packing.num_forests() >= 0);
842 }
843
844 #[test]
845 fn test_union_find() {
846 let mut uf = UnionFind::new(5);
847
848 assert_eq!(uf.find(0), 0);
849 assert_eq!(uf.find(1), 1);
850
851 uf.union(0, 1);
852 assert_eq!(uf.find(0), uf.find(1));
853
854 uf.union(2, 3);
855 assert_eq!(uf.find(2), uf.find(3));
856 assert_ne!(uf.find(0), uf.find(2));
857
858 uf.union(1, 2);
859 assert_eq!(uf.find(0), uf.find(3));
860 }
861
862 #[test]
863 fn test_local_cut_result() {
864 let mut cut_set = HashSet::new();
865 cut_set.insert(1);
866 cut_set.insert(2);
867
868 let cut_edges = vec![(1, 3), (2, 4)];
869
870 let result = LocalCutResult::new(
871 2.5,
872 cut_set.clone(),
873 cut_edges.clone(),
874 true,
875 10,
876 );
877
878 assert_eq!(result.cut_value, 2.5);
879 assert_eq!(result.cut_set.len(), 2);
880 assert_eq!(result.cut_edges.len(), 2);
881 assert!(result.is_minimum);
882 assert_eq!(result.iterations, 10);
883 }
884
885 #[test]
886 fn test_deterministic_coloring() {
887 let graph = create_test_graph();
888
889 let lk1 = LocalKCut::new(graph.clone(), 3);
891 let lk2 = LocalKCut::new(graph.clone(), 3);
892
893 for edge in graph.edges() {
895 assert_eq!(lk1.edge_color(edge.id), lk2.edge_color(edge.id));
896 }
897 }
898
899 #[test]
900 fn test_complete_workflow() {
901 let graph = Arc::new(DynamicGraph::new());
903
904 graph.insert_edge(1, 2, 1.0).unwrap();
907 graph.insert_edge(2, 3, 1.0).unwrap();
908 graph.insert_edge(3, 1, 1.0).unwrap();
909
910 graph.insert_edge(3, 4, 1.0).unwrap();
912
913 graph.insert_edge(4, 5, 1.0).unwrap();
915 graph.insert_edge(5, 6, 1.0).unwrap();
916 graph.insert_edge(6, 4, 1.0).unwrap();
917
918 let local_kcut = LocalKCut::new(graph.clone(), 3);
920 let result = local_kcut.find_cut(1);
921
922 assert!(result.is_some());
923 if let Some(cut) = result {
924 assert!(cut.cut_value <= 3.0);
926 assert!(cut.iterations > 0);
927 }
928
929 let packing = ForestPacking::greedy_packing(&*graph, 3, 0.1);
931 assert!(packing.num_forests() > 0);
932 }
933}