1pub mod deterministic;
31pub mod paper_impl;
32
33pub use paper_impl::{
35 DeterministicFamilyGenerator, DeterministicLocalKCut, LocalKCutOracle, LocalKCutQuery,
36 LocalKCutResult,
37};
38
39use crate::graph::{DynamicGraph, EdgeId, VertexId, 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 [
115 EdgeColor::Red,
116 EdgeColor::Blue,
117 EdgeColor::Green,
118 EdgeColor::Yellow,
119 ]
120 }
121}
122
123#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
125pub struct ColorMask(u8);
126
127impl ColorMask {
128 pub fn empty() -> Self {
130 Self(0)
131 }
132
133 pub fn all() -> Self {
135 Self(0b1111)
136 }
137
138 pub fn from_colors(colors: &[EdgeColor]) -> Self {
140 let mut mask = 0u8;
141 for color in colors {
142 mask |= 1 << color.to_index();
143 }
144 Self(mask)
145 }
146
147 pub fn contains(self, color: EdgeColor) -> bool {
149 (self.0 & (1 << color.to_index())) != 0
150 }
151
152 pub fn insert(&mut self, color: EdgeColor) {
154 self.0 |= 1 << color.to_index();
155 }
156
157 pub fn colors(self) -> Vec<EdgeColor> {
159 let mut result = Vec::new();
160 for color in EdgeColor::all() {
161 if self.contains(color) {
162 result.push(color);
163 }
164 }
165 result
166 }
167
168 pub fn count(self) -> usize {
170 self.0.count_ones() as usize
171 }
172}
173
174pub struct LocalKCut {
176 k: usize,
178 graph: Arc<DynamicGraph>,
180 edge_colors: HashMap<EdgeId, EdgeColor>,
182 radius: usize,
184}
185
186impl LocalKCut {
187 pub fn new(graph: Arc<DynamicGraph>, k: usize) -> Self {
196 let radius = Self::compute_radius(k);
197 let mut finder = Self {
198 k,
199 graph,
200 edge_colors: HashMap::new(),
201 radius,
202 };
203 finder.assign_colors();
204 finder
205 }
206
207 fn compute_radius(k: usize) -> usize {
210 if k <= 1 {
211 1
212 } else {
213 let log_k = (k as f64).log2() / 2.0;
215 log_k.ceil() as usize + 1
216 }
217 }
218
219 pub fn find_cut(&self, v: VertexId) -> Option<LocalCutResult> {
233 if !self.graph.has_vertex(v) {
234 return None;
235 }
236
237 let mut best_cut: Option<LocalCutResult> = None;
238 let mut iterations = 0;
239
240 for depth in 1..=self.radius {
243 let num_masks = 1 << 4; for mask_bits in 1..num_masks {
247 iterations += 1;
248 let mask = ColorMask(mask_bits as u8);
249
250 let reachable = self.color_constrained_bfs(v, mask, depth);
252
253 if reachable.is_empty() || reachable.len() >= self.graph.num_vertices() {
254 continue;
255 }
256
257 if let Some(cut) = self.check_cut(&reachable) {
259 let should_update = match &best_cut {
261 None => true,
262 Some(prev) => cut.cut_value < prev.cut_value,
263 };
264
265 if should_update {
266 let mut cut = cut;
267 cut.iterations = iterations;
268 best_cut = Some(cut);
269 }
270 }
271 }
272
273 if let Some(ref cut) = best_cut {
275 if cut.cut_value <= 1.0 {
276 break;
277 }
278 }
279 }
280
281 best_cut
282 }
283
284 fn color_constrained_bfs(
297 &self,
298 start: VertexId,
299 mask: ColorMask,
300 max_depth: usize,
301 ) -> HashSet<VertexId> {
302 let mut visited = HashSet::new();
303 let mut queue = VecDeque::new();
304
305 queue.push_back((start, 0));
306 visited.insert(start);
307
308 while let Some((v, depth)) = queue.pop_front() {
309 if depth >= max_depth {
310 continue;
311 }
312
313 for (neighbor, edge_id) in self.graph.neighbors(v) {
315 if visited.contains(&neighbor) {
316 continue;
317 }
318
319 if let Some(&color) = self.edge_colors.get(&edge_id) {
321 if mask.contains(color) {
322 visited.insert(neighbor);
323 queue.push_back((neighbor, depth + 1));
324 }
325 }
326 }
327 }
328
329 visited
330 }
331
332 fn assign_colors(&mut self) {
339 self.edge_colors.clear();
340
341 for edge in self.graph.edges() {
342 let color = EdgeColor::from_index(edge.id as usize);
344 self.edge_colors.insert(edge.id, color);
345 }
346 }
347
348 fn check_cut(&self, vertices: &HashSet<VertexId>) -> Option<LocalCutResult> {
356 if vertices.is_empty() || vertices.len() >= self.graph.num_vertices() {
357 return None;
358 }
359
360 let mut cut_edges = Vec::new();
361 let mut cut_value = 0.0;
362
363 for &v in vertices {
365 for (neighbor, edge_id) in self.graph.neighbors(v) {
366 if !vertices.contains(&neighbor) {
367 if let Some(edge) = self.graph.edges().iter().find(|e| e.id == edge_id) {
369 cut_edges.push((v, neighbor));
370 cut_value += edge.weight;
371 }
372 }
373 }
374 }
375
376 if cut_value <= self.k as f64 {
378 Some(LocalCutResult::new(
379 cut_value,
380 vertices.clone(),
381 cut_edges,
382 false, 0, ))
385 } else {
386 None
387 }
388 }
389
390 pub fn enumerate_paths(&self, v: VertexId, depth: usize) -> Vec<HashSet<VertexId>> {
402 let mut results = Vec::new();
403
404 for mask_bits in 1..16u8 {
406 let mask = ColorMask(mask_bits);
407 let reachable = self.color_constrained_bfs(v, mask, depth);
408
409 if !reachable.is_empty() {
410 results.push(reachable);
411 }
412 }
413
414 results
415 }
416
417 pub fn edge_color(&self, edge_id: EdgeId) -> Option<EdgeColor> {
419 self.edge_colors.get(&edge_id).copied()
420 }
421
422 pub fn radius(&self) -> usize {
424 self.radius
425 }
426
427 pub fn max_cut_size(&self) -> usize {
429 self.k
430 }
431}
432
433pub struct ForestPacking {
444 num_forests: usize,
446 forests: Vec<HashSet<(VertexId, VertexId)>>,
448}
449
450impl ForestPacking {
451 pub fn greedy_packing(graph: &DynamicGraph, lambda_max: usize, epsilon: f64) -> Self {
470 let m = graph.num_edges();
471 let n = graph.num_vertices();
472
473 if m == 0 || n == 0 {
474 return Self {
475 num_forests: 0,
476 forests: Vec::new(),
477 };
478 }
479
480 let log_m = (m as f64).ln();
482 let num_forests = ((lambda_max as f64 * log_m) / (epsilon * epsilon)).ceil() as usize;
483 let num_forests = num_forests.max(1);
484
485 let mut forests = Vec::with_capacity(num_forests);
486 let edges = graph.edges();
487
488 for _ in 0..num_forests {
490 let mut forest = HashSet::new();
491 let mut components = UnionFind::new(n);
492
493 for edge in &edges {
494 let (u, v) = (edge.source, edge.target);
495
496 if components.find(u) != components.find(v) {
498 forest.insert((u.min(v), u.max(v)));
499 components.union(u, v);
500 }
501 }
502
503 forests.push(forest);
504 }
505
506 Self {
507 num_forests,
508 forests,
509 }
510 }
511
512 pub fn witnesses_cut(&self, cut_edges: &[(VertexId, VertexId)]) -> bool {
525 if self.forests.is_empty() {
526 return true;
527 }
528
529 let normalized_cut: HashSet<_> = cut_edges
531 .iter()
532 .map(|(u, v)| ((*u).min(*v), (*u).max(*v)))
533 .collect();
534
535 for forest in &self.forests {
537 let has_witness = forest.iter().any(|edge| normalized_cut.contains(edge));
539
540 if !has_witness {
541 return false;
542 }
543 }
544
545 true
546 }
547
548 pub fn num_forests(&self) -> usize {
550 self.num_forests
551 }
552
553 pub fn forest(&self, index: usize) -> Option<&HashSet<(VertexId, VertexId)>> {
555 self.forests.get(index)
556 }
557}
558
559struct UnionFind {
561 parent: Vec<usize>,
562 rank: Vec<usize>,
563}
564
565impl UnionFind {
566 fn new(n: usize) -> Self {
567 Self {
568 parent: (0..n).collect(),
569 rank: vec![0; n],
570 }
571 }
572
573 fn find(&mut self, x: VertexId) -> VertexId {
574 let x_idx = x as usize % self.parent.len();
575 let mut idx = x_idx;
576
577 while self.parent[idx] != idx {
579 let parent = self.parent[idx];
580 self.parent[idx] = self.parent[parent];
581 idx = parent;
582 }
583
584 idx as VertexId
585 }
586
587 fn union(&mut self, x: VertexId, y: VertexId) {
588 let root_x = self.find(x);
589 let root_y = self.find(y);
590
591 if root_x == root_y {
592 return;
593 }
594
595 let rx = root_x as usize % self.parent.len();
596 let ry = root_y as usize % self.parent.len();
597
598 if self.rank[rx] < self.rank[ry] {
600 self.parent[rx] = ry;
601 } else if self.rank[rx] > self.rank[ry] {
602 self.parent[ry] = rx;
603 } else {
604 self.parent[ry] = rx;
605 self.rank[rx] += 1;
606 }
607 }
608}
609
610#[cfg(test)]
611mod tests {
612 use super::*;
613
614 fn create_test_graph() -> Arc<DynamicGraph> {
615 let graph = DynamicGraph::new();
616
617 graph.insert_edge(1, 2, 1.0).unwrap();
623 graph.insert_edge(2, 3, 1.0).unwrap();
624 graph.insert_edge(1, 4, 1.0).unwrap();
625 graph.insert_edge(2, 5, 1.0).unwrap();
626 graph.insert_edge(3, 6, 1.0).unwrap();
627 graph.insert_edge(4, 5, 1.0).unwrap();
628 graph.insert_edge(5, 6, 1.0).unwrap();
629
630 Arc::new(graph)
631 }
632
633 #[test]
634 fn test_edge_color_conversion() {
635 assert_eq!(EdgeColor::from_index(0), EdgeColor::Red);
636 assert_eq!(EdgeColor::from_index(1), EdgeColor::Blue);
637 assert_eq!(EdgeColor::from_index(2), EdgeColor::Green);
638 assert_eq!(EdgeColor::from_index(3), EdgeColor::Yellow);
639 assert_eq!(EdgeColor::from_index(4), EdgeColor::Red); assert_eq!(EdgeColor::Red.to_index(), 0);
642 assert_eq!(EdgeColor::Blue.to_index(), 1);
643 assert_eq!(EdgeColor::Green.to_index(), 2);
644 assert_eq!(EdgeColor::Yellow.to_index(), 3);
645 }
646
647 #[test]
648 fn test_color_mask() {
649 let mut mask = ColorMask::empty();
650 assert_eq!(mask.count(), 0);
651
652 mask.insert(EdgeColor::Red);
653 assert!(mask.contains(EdgeColor::Red));
654 assert!(!mask.contains(EdgeColor::Blue));
655 assert_eq!(mask.count(), 1);
656
657 mask.insert(EdgeColor::Blue);
658 assert_eq!(mask.count(), 2);
659
660 let all_mask = ColorMask::all();
661 assert_eq!(all_mask.count(), 4);
662 assert!(all_mask.contains(EdgeColor::Red));
663 assert!(all_mask.contains(EdgeColor::Blue));
664 assert!(all_mask.contains(EdgeColor::Green));
665 assert!(all_mask.contains(EdgeColor::Yellow));
666 }
667
668 #[test]
669 fn test_color_mask_from_colors() {
670 let colors = vec![EdgeColor::Red, EdgeColor::Green];
671 let mask = ColorMask::from_colors(&colors);
672
673 assert!(mask.contains(EdgeColor::Red));
674 assert!(!mask.contains(EdgeColor::Blue));
675 assert!(mask.contains(EdgeColor::Green));
676 assert!(!mask.contains(EdgeColor::Yellow));
677 assert_eq!(mask.count(), 2);
678 }
679
680 #[test]
681 fn test_local_kcut_new() {
682 let graph = create_test_graph();
683 let local_kcut = LocalKCut::new(graph.clone(), 3);
684
685 assert_eq!(local_kcut.max_cut_size(), 3);
686 assert!(local_kcut.radius() > 0);
687 assert_eq!(local_kcut.edge_colors.len(), graph.num_edges());
688 }
689
690 #[test]
691 fn test_compute_radius() {
692 assert_eq!(LocalKCut::compute_radius(1), 1);
693 assert_eq!(LocalKCut::compute_radius(4), 2);
694 assert_eq!(LocalKCut::compute_radius(16), 3);
695 assert_eq!(LocalKCut::compute_radius(64), 4);
696 }
697
698 #[test]
699 fn test_assign_colors() {
700 let graph = create_test_graph();
701 let local_kcut = LocalKCut::new(graph.clone(), 3);
702
703 for edge in graph.edges() {
705 assert!(local_kcut.edge_color(edge.id).is_some());
706 }
707 }
708
709 #[test]
710 fn test_color_constrained_bfs() {
711 let graph = create_test_graph();
712 let local_kcut = LocalKCut::new(graph.clone(), 3);
713
714 let all_mask = ColorMask::all();
716 let reachable = local_kcut.color_constrained_bfs(1, all_mask, 10);
717
718 assert!(reachable.contains(&1));
719 assert!(reachable.len() > 1);
720 }
721
722 #[test]
723 fn test_color_constrained_bfs_limited() {
724 let graph = create_test_graph();
725 let local_kcut = LocalKCut::new(graph.clone(), 3);
726
727 let all_mask = ColorMask::all();
729 let reachable = local_kcut.color_constrained_bfs(1, all_mask, 0);
730
731 assert_eq!(reachable.len(), 1);
732 assert!(reachable.contains(&1));
733 }
734
735 #[test]
736 fn test_find_cut_simple() {
737 let graph = Arc::new(DynamicGraph::new());
738
739 graph.insert_edge(1, 2, 2.0).unwrap();
742 graph.insert_edge(2, 3, 1.0).unwrap();
743
744 let local_kcut = LocalKCut::new(graph.clone(), 5);
745 let result = local_kcut.find_cut(1);
746
747 assert!(result.is_some());
748 if let Some(cut) = result {
749 assert!(cut.cut_value <= 5.0);
750 assert!(!cut.cut_set.is_empty());
751 }
752 }
753
754 #[test]
755 fn test_check_cut() {
756 let graph = create_test_graph();
757 let local_kcut = LocalKCut::new(graph.clone(), 10);
758
759 let mut cut_set = HashSet::new();
761 cut_set.insert(1);
762 cut_set.insert(2);
763
764 let result = local_kcut.check_cut(&cut_set);
765 assert!(result.is_some());
766
767 if let Some(cut) = result {
768 assert!(cut.cut_value > 0.0);
769 assert!(!cut.cut_edges.is_empty());
770 }
771 }
772
773 #[test]
774 fn test_check_cut_invalid() {
775 let graph = create_test_graph();
776 let local_kcut = LocalKCut::new(graph.clone(), 3);
777
778 let empty_set = HashSet::new();
780 assert!(local_kcut.check_cut(&empty_set).is_none());
781
782 let all_vertices: HashSet<_> = graph.vertices().into_iter().collect();
784 assert!(local_kcut.check_cut(&all_vertices).is_none());
785 }
786
787 #[test]
788 fn test_enumerate_paths() {
789 let graph = create_test_graph();
790 let local_kcut = LocalKCut::new(graph.clone(), 3);
791
792 let paths = local_kcut.enumerate_paths(1, 2);
793
794 assert!(!paths.is_empty());
796
797 for path in &paths {
799 assert!(path.contains(&1));
800 }
801 }
802
803 #[test]
804 fn test_forest_packing_empty_graph() {
805 let graph = DynamicGraph::new();
806 let packing = ForestPacking::greedy_packing(&graph, 10, 0.1);
807
808 assert_eq!(packing.num_forests(), 0);
809 }
810
811 #[test]
812 fn test_forest_packing_simple() {
813 let graph = create_test_graph();
814 let packing = ForestPacking::greedy_packing(&*graph, 10, 0.1);
815
816 assert!(packing.num_forests() > 0);
817
818 for i in 0..packing.num_forests() {
820 if let Some(forest) = packing.forest(i) {
821 assert!(!forest.is_empty());
822 }
823 }
824 }
825
826 #[test]
827 fn test_forest_witnesses_cut() {
828 let graph = create_test_graph();
829 let packing = ForestPacking::greedy_packing(&*graph, 5, 0.1);
830
831 let cut_edges = vec![(1, 2)];
833
834 let witnesses = packing.witnesses_cut(&cut_edges);
836
837 let _ = witnesses;
840
841 assert!(packing.num_forests() >= 0);
843 }
844
845 #[test]
846 fn test_union_find() {
847 let mut uf = UnionFind::new(5);
848
849 assert_eq!(uf.find(0), 0);
850 assert_eq!(uf.find(1), 1);
851
852 uf.union(0, 1);
853 assert_eq!(uf.find(0), uf.find(1));
854
855 uf.union(2, 3);
856 assert_eq!(uf.find(2), uf.find(3));
857 assert_ne!(uf.find(0), uf.find(2));
858
859 uf.union(1, 2);
860 assert_eq!(uf.find(0), uf.find(3));
861 }
862
863 #[test]
864 fn test_local_cut_result() {
865 let mut cut_set = HashSet::new();
866 cut_set.insert(1);
867 cut_set.insert(2);
868
869 let cut_edges = vec![(1, 3), (2, 4)];
870
871 let result = LocalCutResult::new(2.5, cut_set.clone(), cut_edges.clone(), true, 10);
872
873 assert_eq!(result.cut_value, 2.5);
874 assert_eq!(result.cut_set.len(), 2);
875 assert_eq!(result.cut_edges.len(), 2);
876 assert!(result.is_minimum);
877 assert_eq!(result.iterations, 10);
878 }
879
880 #[test]
881 fn test_deterministic_coloring() {
882 let graph = create_test_graph();
883
884 let lk1 = LocalKCut::new(graph.clone(), 3);
886 let lk2 = LocalKCut::new(graph.clone(), 3);
887
888 for edge in graph.edges() {
890 assert_eq!(lk1.edge_color(edge.id), lk2.edge_color(edge.id));
891 }
892 }
893
894 #[test]
895 fn test_complete_workflow() {
896 let graph = Arc::new(DynamicGraph::new());
898
899 graph.insert_edge(1, 2, 1.0).unwrap();
902 graph.insert_edge(2, 3, 1.0).unwrap();
903 graph.insert_edge(3, 1, 1.0).unwrap();
904
905 graph.insert_edge(3, 4, 1.0).unwrap();
907
908 graph.insert_edge(4, 5, 1.0).unwrap();
910 graph.insert_edge(5, 6, 1.0).unwrap();
911 graph.insert_edge(6, 4, 1.0).unwrap();
912
913 let local_kcut = LocalKCut::new(graph.clone(), 3);
915 let result = local_kcut.find_cut(1);
916
917 assert!(result.is_some());
918 if let Some(cut) = result {
919 assert!(cut.cut_value <= 3.0);
921 assert!(cut.iterations > 0);
922 }
923
924 let packing = ForestPacking::greedy_packing(&*graph, 3, 0.1);
926 assert!(packing.num_forests() > 0);
927 }
928}