1use std::collections::{HashMap, HashSet, VecDeque};
43use std::time::Instant;
44
45use crate::graph::{DynamicGraph, VertexId, EdgeId, Weight};
46use crate::localkcut::deterministic::{DeterministicLocalKCut, LocalCut as DetLocalCut};
47use crate::cluster::hierarchy::{ThreeLevelHierarchy, HierarchyConfig, Expander, Precluster, HierarchyCluster};
48use crate::fragmentation::{Fragmentation, FragmentationConfig, TrimResult};
49use crate::witness::{WitnessTree, LazyWitnessTree};
50use crate::expander::{ExpanderDecomposition, ExpanderComponent};
51use crate::error::{MinCutError, Result};
52
53#[derive(Debug, Clone)]
55pub struct SubpolyConfig {
56 pub phi: f64,
59 pub lambda_max: u64,
62 pub epsilon: f64,
64 pub target_levels: usize,
66 pub track_recourse: bool,
68 pub certify_cuts: bool,
70 pub parallel: bool,
72}
73
74impl Default for SubpolyConfig {
75 fn default() -> Self {
76 Self {
77 phi: 0.01,
78 lambda_max: 1000,
79 epsilon: 0.1,
80 target_levels: 4, track_recourse: true,
82 certify_cuts: true,
83 parallel: true,
84 }
85 }
86}
87
88impl SubpolyConfig {
89 pub fn for_size(n: usize) -> Self {
91 let log_n = (n.max(2) as f64).ln();
92
93 let phi = 2.0_f64.powf(-log_n.powf(0.75) / 4.0);
95
96 let lambda_max = 2.0_f64.powf(log_n.powf(0.65)).min(1e9) as u64;
98
99 let target_levels = (log_n.powf(0.25).ceil() as usize).max(2).min(10);
101
102 Self {
103 phi,
104 lambda_max,
105 epsilon: 0.1,
106 target_levels,
107 track_recourse: true,
108 certify_cuts: true,
109 parallel: true,
110 }
111 }
112}
113
114#[derive(Debug, Clone, Default)]
116pub struct RecourseStats {
117 pub total_recourse: u64,
119 pub num_updates: u64,
121 pub max_single_recourse: u64,
123 pub recourse_per_level: Vec<u64>,
125 pub avg_update_time_us: f64,
127 pub theoretical_bound: f64,
129}
130
131impl RecourseStats {
132 pub fn is_subpolynomial(&self, n: usize) -> bool {
134 if n < 2 || self.num_updates == 0 {
135 return true;
136 }
137
138 let log_n = (n as f64).ln();
139 let bound = 2.0_f64.powf(log_n.powf(0.9));
141
142 (self.total_recourse as f64 / self.num_updates as f64) <= bound
143 }
144
145 pub fn amortized_recourse(&self) -> f64 {
147 if self.num_updates == 0 {
148 return 0.0;
149 }
150 self.total_recourse as f64 / self.num_updates as f64
151 }
152}
153
154#[derive(Debug)]
156pub struct HierarchyLevel {
157 pub level: usize,
159 pub expanders: HashMap<u64, LevelExpander>,
161 pub vertex_expander: HashMap<VertexId, u64>,
163 next_id: u64,
165 pub recourse: u64,
167 phi: f64,
169}
170
171#[derive(Debug, Clone)]
173pub struct LevelExpander {
174 pub id: u64,
176 pub vertices: HashSet<VertexId>,
178 pub boundary_size: usize,
180 pub volume: usize,
182 pub internal_min_cut: f64,
184 pub is_valid_expander: bool,
186 pub parent_id: Option<u64>,
188 pub children_ids: Vec<u64>,
190}
191
192#[derive(Debug)]
194pub struct SubpolynomialMinCut {
195 config: SubpolyConfig,
197 adjacency: HashMap<VertexId, HashMap<VertexId, Weight>>,
199 edges: HashSet<(VertexId, VertexId)>,
201 levels: Vec<HierarchyLevel>,
203 local_kcut: Option<DeterministicLocalKCut>,
205 current_min_cut: f64,
207 recourse_stats: RecourseStats,
209 num_vertices: usize,
211 num_edges: usize,
213 next_id: u64,
215 hierarchy_built: bool,
217}
218
219impl SubpolynomialMinCut {
220 pub fn new(config: SubpolyConfig) -> Self {
222 let num_levels = config.target_levels;
223 let levels = (0..num_levels)
224 .map(|i| HierarchyLevel {
225 level: i,
226 expanders: HashMap::new(),
227 vertex_expander: HashMap::new(),
228 next_id: 1,
229 recourse: 0,
230 phi: config.phi * (1.0 + i as f64 * 0.1), })
232 .collect();
233
234 Self {
235 config,
236 adjacency: HashMap::new(),
237 edges: HashSet::new(),
238 levels,
239 local_kcut: None,
240 current_min_cut: f64::INFINITY,
241 recourse_stats: RecourseStats::default(),
242 num_vertices: 0,
243 num_edges: 0,
244 next_id: 1,
245 hierarchy_built: false,
246 }
247 }
248
249 pub fn for_size(expected_n: usize) -> Self {
251 Self::new(SubpolyConfig::for_size(expected_n))
252 }
253
254 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> Result<f64> {
256 let start = Instant::now();
257
258 let key = Self::edge_key(u, v);
259 if self.edges.contains(&key) {
260 return Err(MinCutError::EdgeExists(u, v));
261 }
262
263 self.edges.insert(key);
265 let is_new_u = !self.adjacency.contains_key(&u);
266 let is_new_v = !self.adjacency.contains_key(&v);
267
268 self.adjacency.entry(u).or_default().insert(v, weight);
269 self.adjacency.entry(v).or_default().insert(u, weight);
270
271 if is_new_u {
272 self.num_vertices += 1;
273 }
274 if is_new_v && u != v {
275 self.num_vertices += 1;
276 }
277 self.num_edges += 1;
278
279 if self.hierarchy_built {
281 let recourse = self.handle_edge_insert(u, v, weight);
282 self.update_recourse_stats(recourse, start.elapsed().as_micros() as f64);
283 }
284
285 if let Some(ref mut lkc) = self.local_kcut {
287 lkc.insert_edge(u, v, weight);
288 }
289
290 Ok(self.current_min_cut)
291 }
292
293 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<f64> {
295 let start = Instant::now();
296
297 let key = Self::edge_key(u, v);
298 if !self.edges.remove(&key) {
299 return Err(MinCutError::EdgeNotFound(u, v));
300 }
301
302 if let Some(neighbors) = self.adjacency.get_mut(&u) {
304 neighbors.remove(&v);
305 }
306 if let Some(neighbors) = self.adjacency.get_mut(&v) {
307 neighbors.remove(&u);
308 }
309 self.num_edges -= 1;
310
311 if self.hierarchy_built {
313 let recourse = self.handle_edge_delete(u, v);
314 self.update_recourse_stats(recourse, start.elapsed().as_micros() as f64);
315 }
316
317 if let Some(ref mut lkc) = self.local_kcut {
319 lkc.delete_edge(u, v);
320 }
321
322 Ok(self.current_min_cut)
323 }
324
325 pub fn build(&mut self) {
330 if self.adjacency.is_empty() {
331 return;
332 }
333
334 let n = self.num_vertices;
336 let log_n = (n.max(2) as f64).ln();
337 let optimal_levels = (log_n.powf(0.25).ceil() as usize).max(2).min(10);
338
339 while self.levels.len() < optimal_levels {
341 let i = self.levels.len();
342 self.levels.push(HierarchyLevel {
343 level: i,
344 expanders: HashMap::new(),
345 vertex_expander: HashMap::new(),
346 next_id: 1,
347 recourse: 0,
348 phi: self.config.phi * (1.0 + i as f64 * 0.1),
349 });
350 }
351
352 self.build_base_level();
354
355 for level in 1..self.levels.len() {
357 self.build_level(level);
358 }
359
360 self.local_kcut = Some(DeterministicLocalKCut::new(
362 self.config.lambda_max,
363 self.num_vertices * 10,
364 2,
365 ));
366
367 let edge_data: Vec<(VertexId, VertexId, Weight)> = self.edges.iter()
369 .map(|&(u, v)| (u, v, self.get_weight(u, v).unwrap_or(1.0)))
370 .collect();
371
372 if let Some(ref mut lkc) = self.local_kcut {
374 for (u, v, weight) in edge_data {
375 lkc.insert_edge(u, v, weight);
376 }
377 }
378
379 self.recompute_min_cut();
381
382 self.hierarchy_built = true;
383
384 self.recourse_stats.theoretical_bound =
386 2.0_f64.powf(log_n.powf(0.9));
387 }
388
389 fn build_base_level(&mut self) {
391 let vertices: HashSet<_> = self.adjacency.keys().copied().collect();
392 if vertices.is_empty() {
393 return;
394 }
395
396 let phi = self.levels[0].phi;
398
399 let mut remaining = vertices.clone();
401 let mut expander_sets: Vec<HashSet<VertexId>> = Vec::new();
402
403 while !remaining.is_empty() {
404 let start = *remaining.iter().next().unwrap();
405 let expander_vertices = self.grow_expander(&remaining, start, phi);
406
407 if expander_vertices.is_empty() {
408 remaining.remove(&start);
409 continue;
410 }
411
412 for &v in &expander_vertices {
413 remaining.remove(&v);
414 }
415
416 expander_sets.push(expander_vertices);
417 }
418
419 let mut expanders_to_create: Vec<LevelExpander> = Vec::new();
421 let mut vertex_mappings: Vec<(VertexId, u64)> = Vec::new();
422 let mut next_id = self.levels[0].next_id;
423
424 for expander_vertices in &expander_sets {
425 let id = next_id;
426 next_id += 1;
427
428 let volume = expander_vertices.iter()
429 .map(|&v| self.degree(v))
430 .sum();
431
432 let boundary_size = self.count_boundary(expander_vertices);
433
434 let expander = LevelExpander {
435 id,
436 vertices: expander_vertices.clone(),
437 boundary_size,
438 volume,
439 internal_min_cut: f64::INFINITY,
440 is_valid_expander: true,
441 parent_id: None,
442 children_ids: Vec::new(),
443 };
444
445 for &v in expander_vertices {
446 vertex_mappings.push((v, id));
447 }
448
449 expanders_to_create.push(expander);
450 }
451
452 let level = &mut self.levels[0];
454 level.expanders.clear();
455 level.vertex_expander.clear();
456 level.next_id = next_id;
457
458 for expander in expanders_to_create {
459 level.expanders.insert(expander.id, expander);
460 }
461
462 for (v, id) in vertex_mappings {
463 level.vertex_expander.insert(v, id);
464 }
465 }
466
467 fn build_level(&mut self, level_idx: usize) {
469 if level_idx == 0 || level_idx >= self.levels.len() {
470 return;
471 }
472
473 let prev_expander_ids: Vec<u64> = self.levels[level_idx - 1]
475 .expanders
476 .keys()
477 .copied()
478 .collect();
479
480 let mut groups: Vec<Vec<u64>> = Vec::new();
482 let mut assigned: HashSet<u64> = HashSet::new();
483
484 for &exp_id in &prev_expander_ids {
485 if assigned.contains(&exp_id) {
486 continue;
487 }
488
489 let mut group = vec![exp_id];
490 assigned.insert(exp_id);
491
492 for &other_id in &prev_expander_ids {
494 if assigned.contains(&other_id) {
495 continue;
496 }
497
498 if self.expanders_adjacent_at_level(level_idx - 1, exp_id, other_id) {
499 group.push(other_id);
500 assigned.insert(other_id);
501
502 if group.len() >= 4 {
504 break;
505 }
506 }
507 }
508
509 groups.push(group);
510 }
511
512 let mut group_vertices: Vec<(Vec<u64>, HashSet<VertexId>)> = Vec::new();
514 for group in &groups {
515 let mut vertices = HashSet::new();
516 for &child_id in group {
517 if let Some(child) = self.levels[level_idx - 1].expanders.get(&child_id) {
518 vertices.extend(&child.vertices);
519 }
520 }
521 group_vertices.push((group.clone(), vertices));
522 }
523
524 let mut expanders_to_create: Vec<(u64, LevelExpander, HashMap<VertexId, u64>)> = Vec::new();
526 let mut next_id = self.levels[level_idx].next_id;
527
528 for (group, vertices) in &group_vertices {
529 let id = next_id;
530 next_id += 1;
531
532 let volume = vertices.iter()
533 .map(|&v| self.degree(v))
534 .sum();
535
536 let boundary_size = self.count_boundary(vertices);
537
538 let expander = LevelExpander {
539 id,
540 vertices: vertices.clone(),
541 boundary_size,
542 volume,
543 internal_min_cut: f64::INFINITY,
544 is_valid_expander: true,
545 parent_id: None,
546 children_ids: group.clone(),
547 };
548
549 let mut vertex_map = HashMap::new();
550 for &v in vertices {
551 vertex_map.insert(v, id);
552 }
553
554 expanders_to_create.push((id, expander, vertex_map));
555 }
556
557 let level = &mut self.levels[level_idx];
559 level.expanders.clear();
560 level.vertex_expander.clear();
561 level.next_id = next_id;
562
563 for (id, expander, vertex_map) in expanders_to_create {
564 level.expanders.insert(id, expander);
565 level.vertex_expander.extend(vertex_map);
566 }
567
568 for (group, _) in &group_vertices {
570 let parent_id = self.levels[level_idx].expanders
572 .values()
573 .find(|e| &e.children_ids == group)
574 .map(|e| e.id);
575
576 if let Some(pid) = parent_id {
577 for &child_id in group {
578 if let Some(child) = self.levels[level_idx - 1].expanders.get_mut(&child_id) {
579 child.parent_id = Some(pid);
580 }
581 }
582 }
583 }
584 }
585
586 fn grow_expander(
588 &self,
589 available: &HashSet<VertexId>,
590 start: VertexId,
591 phi: f64,
592 ) -> HashSet<VertexId> {
593 let mut expander = HashSet::new();
594 let mut queue = VecDeque::new();
595
596 queue.push_back(start);
597 expander.insert(start);
598
599 let max_size = (self.num_vertices / 4).max(10);
600
601 while let Some(v) = queue.pop_front() {
602 if expander.len() >= max_size {
603 break;
604 }
605
606 for (neighbor, _) in self.neighbors(v) {
607 if !available.contains(&neighbor) || expander.contains(&neighbor) {
608 continue;
609 }
610
611 let mut test_set = expander.clone();
613 test_set.insert(neighbor);
614
615 let volume: usize = test_set.iter().map(|&u| self.degree(u)).sum();
616 let boundary = self.count_boundary(&test_set);
617
618 let expansion = if volume > 0 {
619 boundary as f64 / volume as f64
620 } else {
621 0.0
622 };
623
624 if expansion >= phi * 0.5 || expander.len() < 3 {
626 expander.insert(neighbor);
627 queue.push_back(neighbor);
628 }
629 }
630 }
631
632 expander
633 }
634
635 fn handle_edge_insert(&mut self, u: VertexId, v: VertexId, weight: Weight) -> u64 {
637 let mut total_recourse = 0u64;
638
639 for level_idx in 0..self.levels.len() {
641 let recourse = self.update_level_for_insert(level_idx, u, v, weight);
642 total_recourse += recourse;
643
644 if level_idx < self.levels.len() {
645 self.levels[level_idx].recourse += recourse;
646 }
647 }
648
649 self.update_min_cut_incremental(u, v, true);
651
652 total_recourse
653 }
654
655 fn handle_edge_delete(&mut self, u: VertexId, v: VertexId) -> u64 {
657 let mut total_recourse = 0u64;
658
659 for level_idx in 0..self.levels.len() {
661 let recourse = self.update_level_for_delete(level_idx, u, v);
662 total_recourse += recourse;
663
664 if level_idx < self.levels.len() {
665 self.levels[level_idx].recourse += recourse;
666 }
667 }
668
669 self.update_min_cut_incremental(u, v, false);
671
672 total_recourse
673 }
674
675 fn update_level_for_insert(&mut self, level_idx: usize, u: VertexId, v: VertexId, _weight: Weight) -> u64 {
677 if level_idx >= self.levels.len() {
678 return 0;
679 }
680
681 let mut recourse = 0u64;
682
683 let exp_u = self.levels[level_idx].vertex_expander.get(&u).copied();
685 let exp_v = self.levels[level_idx].vertex_expander.get(&v).copied();
686
687 match (exp_u, exp_v) {
688 (Some(eu), Some(ev)) if eu == ev => {
689 recourse += 1;
691 self.update_expander_properties(level_idx, eu);
692 }
693 (Some(eu), Some(ev)) => {
694 recourse += 2;
696 self.update_expander_properties(level_idx, eu);
697 self.update_expander_properties(level_idx, ev);
698 }
699 (Some(eu), None) => {
700 recourse += self.try_add_to_expander(level_idx, v, eu);
702 }
703 (None, Some(ev)) => {
704 recourse += self.try_add_to_expander(level_idx, u, ev);
706 }
707 (None, None) => {
708 recourse += self.create_new_expander(level_idx, &[u, v]);
710 }
711 }
712
713 recourse
714 }
715
716 fn update_level_for_delete(&mut self, level_idx: usize, u: VertexId, v: VertexId) -> u64 {
718 if level_idx >= self.levels.len() {
719 return 0;
720 }
721
722 let mut recourse = 0u64;
723
724 let exp_u = self.levels[level_idx].vertex_expander.get(&u).copied();
726 let exp_v = self.levels[level_idx].vertex_expander.get(&v).copied();
727
728 if let (Some(eu), Some(ev)) = (exp_u, exp_v) {
729 if eu == ev {
730 recourse += self.check_and_split_expander(level_idx, eu);
732 } else {
733 recourse += 2;
735 self.update_expander_properties(level_idx, eu);
736 self.update_expander_properties(level_idx, ev);
737 }
738 }
739
740 recourse
741 }
742
743 fn update_expander_properties(&mut self, level_idx: usize, exp_id: u64) {
745 if level_idx >= self.levels.len() {
746 return;
747 }
748
749 let (vertices, phi) = match self.levels[level_idx].expanders.get(&exp_id) {
751 Some(e) => (e.vertices.clone(), self.levels[level_idx].phi),
752 None => return,
753 };
754
755 let volume: usize = vertices.iter().map(|&v| self.degree(v)).sum();
756 let boundary_size = self.count_boundary(&vertices);
757
758 let expansion = if volume > 0 {
760 boundary_size as f64 / volume as f64
761 } else {
762 0.0
763 };
764 let is_valid = expansion >= phi * 0.3;
765
766 if let Some(expander) = self.levels[level_idx].expanders.get_mut(&exp_id) {
767 expander.volume = volume;
768 expander.boundary_size = boundary_size;
769 expander.is_valid_expander = is_valid;
770 }
771 }
772
773 fn try_add_to_expander(&mut self, level_idx: usize, v: VertexId, exp_id: u64) -> u64 {
775 if level_idx >= self.levels.len() {
776 return 0;
777 }
778
779 let (can_add, volume, boundary) = {
781 let level = &self.levels[level_idx];
782 if let Some(expander) = level.expanders.get(&exp_id) {
783 let mut test_vertices = expander.vertices.clone();
784 test_vertices.insert(v);
785
786 let volume: usize = test_vertices.iter().map(|&u| self.degree(u)).sum();
787 let boundary = self.count_boundary(&test_vertices);
788
789 let expansion = if volume > 0 {
790 boundary as f64 / volume as f64
791 } else {
792 0.0
793 };
794
795 (expansion >= level.phi * 0.3, volume, boundary)
796 } else {
797 (false, 0, 0)
798 }
799 };
800
801 if can_add {
802 let level = &mut self.levels[level_idx];
803 if let Some(expander) = level.expanders.get_mut(&exp_id) {
804 expander.vertices.insert(v);
805 expander.volume = volume;
806 expander.boundary_size = boundary;
807 }
808 level.vertex_expander.insert(v, exp_id);
809 1
810 } else {
811 self.create_new_expander(level_idx, &[v])
812 }
813 }
814
815 fn create_new_expander(&mut self, level_idx: usize, vertices: &[VertexId]) -> u64 {
817 if level_idx >= self.levels.len() {
818 return 0;
819 }
820
821 let vertex_set: HashSet<_> = vertices.iter().copied().collect();
822 let volume: usize = vertex_set.iter().map(|&v| self.degree(v)).sum();
823 let boundary_size = self.count_boundary(&vertex_set);
824
825 let level = &mut self.levels[level_idx];
826 let id = level.next_id;
827 level.next_id += 1;
828
829 let expander = LevelExpander {
830 id,
831 vertices: vertex_set.clone(),
832 boundary_size,
833 volume,
834 internal_min_cut: f64::INFINITY,
835 is_valid_expander: true,
836 parent_id: None,
837 children_ids: Vec::new(),
838 };
839
840 for &v in &vertex_set {
841 level.vertex_expander.insert(v, id);
842 }
843
844 level.expanders.insert(id, expander);
845
846 vertices.len() as u64
847 }
848
849 fn check_and_split_expander(&mut self, level_idx: usize, exp_id: u64) -> u64 {
851 if level_idx >= self.levels.len() {
852 return 0;
853 }
854
855 let needs_split = {
857 let level = &self.levels[level_idx];
858 if let Some(expander) = level.expanders.get(&exp_id) {
859 let expansion = if expander.volume > 0 {
860 expander.boundary_size as f64 / expander.volume as f64
861 } else {
862 0.0
863 };
864 expansion < level.phi * 0.2
865 } else {
866 false
867 }
868 };
869
870 if needs_split {
871 self.update_expander_properties(level_idx, exp_id);
874 2
875 } else {
876 self.update_expander_properties(level_idx, exp_id);
877 1
878 }
879 }
880
881 fn update_min_cut_incremental(&mut self, u: VertexId, v: VertexId, is_insert: bool) {
883 if let Some(ref lkc) = self.local_kcut {
885 let cuts_u = lkc.query(u);
886 let cuts_v = lkc.query(v);
887
888 let mut min_local = f64::INFINITY;
889
890 for cut in cuts_u.iter().chain(cuts_v.iter()) {
891 if cut.cut_value < min_local {
892 min_local = cut.cut_value;
893 }
894 }
895
896 if is_insert {
897 self.current_min_cut = self.current_min_cut.min(min_local);
900 } else {
901 if min_local < self.current_min_cut * 1.5 {
903 self.recompute_min_cut();
905 }
906 }
907 } else {
908 self.recompute_min_cut();
910 }
911 }
912
913 fn recompute_min_cut(&mut self) {
915 if self.edges.is_empty() {
916 self.current_min_cut = f64::INFINITY;
917 return;
918 }
919
920 let mut min_cut = f64::INFINITY;
921
922 for level in &self.levels {
924 for expander in level.expanders.values() {
925 let boundary_cut = expander.boundary_size as f64;
927 min_cut = min_cut.min(boundary_cut);
928
929 min_cut = min_cut.min(expander.internal_min_cut);
931 }
932 }
933
934 if let Some(ref lkc) = self.local_kcut {
936 for v in self.adjacency.keys().take(10) {
937 let cuts = lkc.query(*v);
938 for cut in cuts {
939 min_cut = min_cut.min(cut.cut_value);
940 }
941 }
942 }
943
944 self.current_min_cut = min_cut;
945 }
946
947 fn update_recourse_stats(&mut self, recourse: u64, time_us: f64) {
949 self.recourse_stats.total_recourse += recourse;
950 self.recourse_stats.num_updates += 1;
951 self.recourse_stats.max_single_recourse =
952 self.recourse_stats.max_single_recourse.max(recourse);
953
954 let n = self.recourse_stats.num_updates as f64;
956 self.recourse_stats.avg_update_time_us =
957 (self.recourse_stats.avg_update_time_us * (n - 1.0) + time_us) / n;
958
959 self.recourse_stats.recourse_per_level =
961 self.levels.iter().map(|l| l.recourse).collect();
962 }
963
964 fn edge_key(u: VertexId, v: VertexId) -> (VertexId, VertexId) {
967 if u < v { (u, v) } else { (v, u) }
968 }
969
970 fn get_weight(&self, u: VertexId, v: VertexId) -> Option<Weight> {
971 self.adjacency.get(&u).and_then(|n| n.get(&v).copied())
972 }
973
974 fn degree(&self, v: VertexId) -> usize {
975 self.adjacency.get(&v).map_or(0, |n| n.len())
976 }
977
978 fn neighbors(&self, v: VertexId) -> Vec<(VertexId, Weight)> {
979 self.adjacency.get(&v)
980 .map(|n| n.iter().map(|(&v, &w)| (v, w)).collect())
981 .unwrap_or_default()
982 }
983
984 fn count_boundary(&self, vertices: &HashSet<VertexId>) -> usize {
985 let mut boundary = 0;
986 for &v in vertices {
987 for (neighbor, _) in self.neighbors(v) {
988 if !vertices.contains(&neighbor) {
989 boundary += 1;
990 }
991 }
992 }
993 boundary
994 }
995
996 fn expanders_adjacent_at_level(&self, level_idx: usize, exp1: u64, exp2: u64) -> bool {
997 if level_idx >= self.levels.len() {
998 return false;
999 }
1000
1001 let level = &self.levels[level_idx];
1002
1003 let e1 = match level.expanders.get(&exp1) {
1004 Some(e) => e,
1005 None => return false,
1006 };
1007 let e2 = match level.expanders.get(&exp2) {
1008 Some(e) => e,
1009 None => return false,
1010 };
1011
1012 for &v in &e1.vertices {
1014 for (neighbor, _) in self.neighbors(v) {
1015 if e2.vertices.contains(&neighbor) {
1016 return true;
1017 }
1018 }
1019 }
1020 false
1021 }
1022
1023 pub fn min_cut_value(&self) -> f64 {
1027 self.current_min_cut
1028 }
1029
1030 pub fn min_cut(&self) -> MinCutQueryResult {
1032 MinCutQueryResult {
1033 value: self.current_min_cut,
1034 cut_edges: None, partition: None,
1036 is_exact: true,
1037 complexity_verified: self.recourse_stats.is_subpolynomial(self.num_vertices),
1038 }
1039 }
1040
1041 pub fn config(&self) -> &SubpolyConfig {
1043 &self.config
1044 }
1045
1046 pub fn num_vertices(&self) -> usize {
1048 self.num_vertices
1049 }
1050
1051 pub fn num_edges(&self) -> usize {
1053 self.num_edges
1054 }
1055
1056 pub fn num_levels(&self) -> usize {
1058 self.levels.len()
1059 }
1060
1061 pub fn recourse_stats(&self) -> &RecourseStats {
1063 &self.recourse_stats
1064 }
1065
1066 pub fn hierarchy_stats(&self) -> HierarchyStatistics {
1068 HierarchyStatistics {
1069 num_levels: self.levels.len(),
1070 expanders_per_level: self.levels.iter()
1071 .map(|l| l.expanders.len())
1072 .collect(),
1073 total_expanders: self.levels.iter()
1074 .map(|l| l.expanders.len())
1075 .sum(),
1076 avg_expander_size: if self.levels[0].expanders.is_empty() {
1077 0.0
1078 } else {
1079 self.levels[0].expanders.values()
1080 .map(|e| e.vertices.len())
1081 .sum::<usize>() as f64 / self.levels[0].expanders.len() as f64
1082 },
1083 }
1084 }
1085
1086 pub fn is_subpolynomial(&self) -> bool {
1088 self.recourse_stats.is_subpolynomial(self.num_vertices)
1089 }
1090
1091 pub fn certify_cuts(&mut self) {
1093 let mut expander_data: Vec<(usize, u64, HashSet<VertexId>)> = Vec::new();
1095 for level_idx in 0..self.levels.len() {
1096 for (&exp_id, expander) in &self.levels[level_idx].expanders {
1097 expander_data.push((level_idx, exp_id, expander.vertices.clone()));
1098 }
1099 }
1100
1101 let mut updates: Vec<(usize, u64, f64)> = Vec::new();
1103
1104 if let Some(ref lkc) = self.local_kcut {
1105 for (level_idx, exp_id, vertices) in &expander_data {
1106 let boundary_verts: Vec<_> = vertices.iter()
1108 .filter(|&&v| {
1109 self.neighbors(v).iter()
1110 .any(|(n, _)| !vertices.contains(n))
1111 })
1112 .take(5)
1113 .copied()
1114 .collect();
1115
1116 let mut min_internal_cut = f64::INFINITY;
1117
1118 for v in boundary_verts {
1119 let cuts = lkc.query(v);
1120 for cut in cuts {
1121 let is_internal = cut.vertices.iter()
1123 .all(|u| vertices.contains(u));
1124
1125 if is_internal {
1126 min_internal_cut = min_internal_cut.min(cut.cut_value);
1127 }
1128 }
1129 }
1130
1131 updates.push((*level_idx, *exp_id, min_internal_cut));
1132 }
1133 }
1134
1135 for (level_idx, exp_id, min_cut) in updates {
1137 if let Some(expander) = self.levels[level_idx].expanders.get_mut(&exp_id) {
1138 expander.internal_min_cut = min_cut;
1139 }
1140 }
1141 }
1142}
1143
1144#[derive(Debug, Clone)]
1146pub struct MinCutQueryResult {
1147 pub value: f64,
1149 pub cut_edges: Option<Vec<(VertexId, VertexId)>>,
1151 pub partition: Option<(Vec<VertexId>, Vec<VertexId>)>,
1153 pub is_exact: bool,
1155 pub complexity_verified: bool,
1157}
1158
1159#[derive(Debug, Clone)]
1161pub struct HierarchyStatistics {
1162 pub num_levels: usize,
1164 pub expanders_per_level: Vec<usize>,
1166 pub total_expanders: usize,
1168 pub avg_expander_size: f64,
1170}
1171
1172#[cfg(test)]
1173mod tests {
1174 use super::*;
1175
1176 #[test]
1177 fn test_subpoly_config_default() {
1178 let config = SubpolyConfig::default();
1179 assert!(config.phi > 0.0);
1180 assert!(config.lambda_max > 0);
1181 assert!(config.target_levels > 0);
1182 }
1183
1184 #[test]
1185 fn test_subpoly_config_for_size() {
1186 let config = SubpolyConfig::for_size(1_000_000);
1187 assert!(config.phi < 0.1);
1188 assert!(config.lambda_max > 100);
1189 assert!(config.target_levels >= 2);
1190 }
1191
1192 #[test]
1193 fn test_create_empty() {
1194 let mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1195 assert_eq!(mincut.num_vertices(), 0);
1196 assert_eq!(mincut.num_edges(), 0);
1197 assert_eq!(mincut.min_cut_value(), f64::INFINITY);
1198 }
1199
1200 #[test]
1201 fn test_insert_edges() {
1202 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1203
1204 mincut.insert_edge(1, 2, 1.0).unwrap();
1205 mincut.insert_edge(2, 3, 1.0).unwrap();
1206 mincut.insert_edge(3, 1, 1.0).unwrap();
1207
1208 assert_eq!(mincut.num_vertices(), 3);
1209 assert_eq!(mincut.num_edges(), 3);
1210 }
1211
1212 #[test]
1213 fn test_build_hierarchy() {
1214 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1215
1216 for i in 0..10 {
1218 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1219 }
1220
1221 mincut.build();
1222
1223 assert!(mincut.num_levels() >= 2);
1224 let stats = mincut.hierarchy_stats();
1225 assert!(stats.total_expanders > 0);
1226 }
1227
1228 #[test]
1229 fn test_min_cut_triangle() {
1230 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1231
1232 mincut.insert_edge(1, 2, 1.0).unwrap();
1233 mincut.insert_edge(2, 3, 1.0).unwrap();
1234 mincut.insert_edge(3, 1, 1.0).unwrap();
1235
1236 mincut.build();
1237
1238 assert!(mincut.min_cut_value() <= 2.0);
1239 }
1240
1241 #[test]
1242 fn test_min_cut_bridge() {
1243 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1244
1245 mincut.insert_edge(1, 2, 1.0).unwrap();
1247 mincut.insert_edge(2, 3, 1.0).unwrap();
1248 mincut.insert_edge(3, 1, 1.0).unwrap();
1249
1250 mincut.insert_edge(3, 4, 1.0).unwrap(); mincut.insert_edge(4, 5, 1.0).unwrap();
1253 mincut.insert_edge(5, 6, 1.0).unwrap();
1254 mincut.insert_edge(6, 4, 1.0).unwrap();
1255
1256 mincut.build();
1257
1258 assert!(mincut.min_cut_value() <= 2.0);
1260 }
1261
1262 #[test]
1263 fn test_incremental_updates() {
1264 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1265
1266 mincut.insert_edge(1, 2, 1.0).unwrap();
1268 mincut.insert_edge(2, 3, 1.0).unwrap();
1269 mincut.insert_edge(3, 1, 1.0).unwrap();
1270
1271 mincut.build();
1272
1273 let initial_cut = mincut.min_cut_value();
1274
1275 mincut.insert_edge(3, 4, 1.0).unwrap();
1277 mincut.insert_edge(4, 5, 1.0).unwrap();
1278
1279 assert!(mincut.min_cut_value() <= initial_cut * 2.0);
1281
1282 let stats = mincut.recourse_stats();
1284 assert!(stats.num_updates > 0);
1285 }
1286
1287 #[test]
1288 fn test_delete_edge() {
1289 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1290
1291 mincut.insert_edge(1, 2, 1.0).unwrap();
1292 mincut.insert_edge(2, 3, 1.0).unwrap();
1293 mincut.insert_edge(3, 1, 1.0).unwrap();
1294
1295 mincut.build();
1296
1297 mincut.delete_edge(1, 2).unwrap();
1298
1299 assert_eq!(mincut.num_edges(), 2);
1300 }
1301
1302 #[test]
1303 fn test_recourse_stats() {
1304 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1305
1306 for i in 0..20 {
1308 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1309 }
1310
1311 mincut.build();
1312
1313 mincut.insert_edge(0, 10, 1.0).unwrap();
1315 mincut.insert_edge(5, 15, 1.0).unwrap();
1316
1317 let stats = mincut.recourse_stats();
1318 assert!(stats.num_updates >= 2);
1319 assert!(stats.amortized_recourse() >= 0.0);
1320 }
1321
1322 #[test]
1323 fn test_is_subpolynomial() {
1324 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1325
1326 for i in 0..10 {
1328 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1329 }
1330
1331 mincut.build();
1332
1333 mincut.insert_edge(0, 5, 1.0).unwrap();
1335
1336 assert!(mincut.is_subpolynomial());
1338 }
1339
1340 #[test]
1341 fn test_certify_cuts() {
1342 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1343
1344 mincut.insert_edge(1, 2, 1.0).unwrap();
1346 mincut.insert_edge(2, 3, 1.0).unwrap();
1347 mincut.insert_edge(3, 1, 1.0).unwrap();
1348
1349 mincut.build();
1350 mincut.certify_cuts();
1351
1352 }
1354
1355 #[test]
1356 fn test_large_graph() {
1357 let mut mincut = SubpolynomialMinCut::for_size(1000);
1358
1359 for i in 0..100 {
1361 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1362 }
1363 for i in 0..10 {
1365 mincut.insert_edge(i * 10, i * 10 + 50, 1.0).unwrap();
1366 }
1367
1368 mincut.build();
1369
1370 let stats = mincut.hierarchy_stats();
1371 assert!(stats.num_levels >= 2);
1372
1373 mincut.insert_edge(25, 75, 1.0).unwrap();
1375
1376 assert!(mincut.recourse_stats().num_updates > 0);
1377 }
1378}