1use std::collections::{HashMap, HashSet, VecDeque};
43use std::time::Instant;
44
45use crate::cluster::hierarchy::{
46 Expander, HierarchyCluster, HierarchyConfig, Precluster, ThreeLevelHierarchy,
47};
48use crate::error::{MinCutError, Result};
49use crate::expander::{ExpanderComponent, ExpanderDecomposition};
50use crate::fragmentation::{Fragmentation, FragmentationConfig, TrimResult};
51use crate::graph::{DynamicGraph, EdgeId, VertexId, Weight};
52use crate::localkcut::deterministic::{DeterministicLocalKCut, LocalCut as DetLocalCut};
53use crate::witness::{LazyWitnessTree, WitnessTree};
54
55#[derive(Debug, Clone)]
57pub struct SubpolyConfig {
58 pub phi: f64,
61 pub lambda_max: u64,
64 pub epsilon: f64,
66 pub target_levels: usize,
68 pub track_recourse: bool,
70 pub certify_cuts: bool,
72 pub parallel: bool,
74}
75
76impl Default for SubpolyConfig {
77 fn default() -> Self {
78 Self {
79 phi: 0.01,
80 lambda_max: 1000,
81 epsilon: 0.1,
82 target_levels: 4, track_recourse: true,
84 certify_cuts: true,
85 parallel: true,
86 }
87 }
88}
89
90impl SubpolyConfig {
91 pub fn for_size(n: usize) -> Self {
93 let log_n = (n.max(2) as f64).ln();
94
95 let phi = 2.0_f64.powf(-log_n.powf(0.75) / 4.0);
97
98 let lambda_max = 2.0_f64.powf(log_n.powf(0.65)).min(1e9) as u64;
100
101 let target_levels = (log_n.powf(0.25).ceil() as usize).max(2).min(10);
103
104 Self {
105 phi,
106 lambda_max,
107 epsilon: 0.1,
108 target_levels,
109 track_recourse: true,
110 certify_cuts: true,
111 parallel: true,
112 }
113 }
114}
115
116#[derive(Debug, Clone, Default)]
118pub struct RecourseStats {
119 pub total_recourse: u64,
121 pub num_updates: u64,
123 pub max_single_recourse: u64,
125 pub recourse_per_level: Vec<u64>,
127 pub avg_update_time_us: f64,
129 pub theoretical_bound: f64,
131}
132
133impl RecourseStats {
134 pub fn is_subpolynomial(&self, n: usize) -> bool {
136 if n < 2 || self.num_updates == 0 {
137 return true;
138 }
139
140 let log_n = (n as f64).ln();
141 let bound = 2.0_f64.powf(log_n.powf(0.9));
143
144 (self.total_recourse as f64 / self.num_updates as f64) <= bound
145 }
146
147 pub fn amortized_recourse(&self) -> f64 {
149 if self.num_updates == 0 {
150 return 0.0;
151 }
152 self.total_recourse as f64 / self.num_updates as f64
153 }
154}
155
156#[derive(Debug)]
158pub struct HierarchyLevel {
159 pub level: usize,
161 pub expanders: HashMap<u64, LevelExpander>,
163 pub vertex_expander: HashMap<VertexId, u64>,
165 next_id: u64,
167 pub recourse: u64,
169 phi: f64,
171}
172
173#[derive(Debug, Clone)]
175pub struct LevelExpander {
176 pub id: u64,
178 pub vertices: HashSet<VertexId>,
180 pub boundary_size: usize,
182 pub volume: usize,
184 pub internal_min_cut: f64,
186 pub is_valid_expander: bool,
188 pub parent_id: Option<u64>,
190 pub children_ids: Vec<u64>,
192}
193
194#[derive(Debug)]
196pub struct SubpolynomialMinCut {
197 config: SubpolyConfig,
199 adjacency: HashMap<VertexId, HashMap<VertexId, Weight>>,
201 edges: HashSet<(VertexId, VertexId)>,
203 levels: Vec<HierarchyLevel>,
205 local_kcut: Option<DeterministicLocalKCut>,
207 current_min_cut: f64,
209 recourse_stats: RecourseStats,
211 num_vertices: usize,
213 num_edges: usize,
215 next_id: u64,
217 hierarchy_built: bool,
219}
220
221impl SubpolynomialMinCut {
222 pub fn new(config: SubpolyConfig) -> Self {
224 let num_levels = config.target_levels;
225 let levels = (0..num_levels)
226 .map(|i| HierarchyLevel {
227 level: i,
228 expanders: HashMap::new(),
229 vertex_expander: HashMap::new(),
230 next_id: 1,
231 recourse: 0,
232 phi: config.phi * (1.0 + i as f64 * 0.1), })
234 .collect();
235
236 Self {
237 config,
238 adjacency: HashMap::new(),
239 edges: HashSet::new(),
240 levels,
241 local_kcut: None,
242 current_min_cut: f64::INFINITY,
243 recourse_stats: RecourseStats::default(),
244 num_vertices: 0,
245 num_edges: 0,
246 next_id: 1,
247 hierarchy_built: false,
248 }
249 }
250
251 pub fn for_size(expected_n: usize) -> Self {
253 Self::new(SubpolyConfig::for_size(expected_n))
254 }
255
256 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> Result<f64> {
258 let start = Instant::now();
259
260 let key = Self::edge_key(u, v);
261 if self.edges.contains(&key) {
262 return Err(MinCutError::EdgeExists(u, v));
263 }
264
265 self.edges.insert(key);
267 let is_new_u = !self.adjacency.contains_key(&u);
268 let is_new_v = !self.adjacency.contains_key(&v);
269
270 self.adjacency.entry(u).or_default().insert(v, weight);
271 self.adjacency.entry(v).or_default().insert(u, weight);
272
273 if is_new_u {
274 self.num_vertices += 1;
275 }
276 if is_new_v && u != v {
277 self.num_vertices += 1;
278 }
279 self.num_edges += 1;
280
281 if self.hierarchy_built {
283 let recourse = self.handle_edge_insert(u, v, weight);
284 self.update_recourse_stats(recourse, start.elapsed().as_micros() as f64);
285 }
286
287 if let Some(ref mut lkc) = self.local_kcut {
289 lkc.insert_edge(u, v, weight);
290 }
291
292 Ok(self.current_min_cut)
293 }
294
295 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<f64> {
297 let start = Instant::now();
298
299 let key = Self::edge_key(u, v);
300 if !self.edges.remove(&key) {
301 return Err(MinCutError::EdgeNotFound(u, v));
302 }
303
304 if let Some(neighbors) = self.adjacency.get_mut(&u) {
306 neighbors.remove(&v);
307 }
308 if let Some(neighbors) = self.adjacency.get_mut(&v) {
309 neighbors.remove(&u);
310 }
311 self.num_edges -= 1;
312
313 if self.hierarchy_built {
315 let recourse = self.handle_edge_delete(u, v);
316 self.update_recourse_stats(recourse, start.elapsed().as_micros() as f64);
317 }
318
319 if let Some(ref mut lkc) = self.local_kcut {
321 lkc.delete_edge(u, v);
322 }
323
324 Ok(self.current_min_cut)
325 }
326
327 pub fn build(&mut self) {
332 if self.adjacency.is_empty() {
333 return;
334 }
335
336 let n = self.num_vertices;
338 let log_n = (n.max(2) as f64).ln();
339 let optimal_levels = (log_n.powf(0.25).ceil() as usize).max(2).min(10);
340
341 while self.levels.len() < optimal_levels {
343 let i = self.levels.len();
344 self.levels.push(HierarchyLevel {
345 level: i,
346 expanders: HashMap::new(),
347 vertex_expander: HashMap::new(),
348 next_id: 1,
349 recourse: 0,
350 phi: self.config.phi * (1.0 + i as f64 * 0.1),
351 });
352 }
353
354 self.build_base_level();
356
357 for level in 1..self.levels.len() {
359 self.build_level(level);
360 }
361
362 self.local_kcut = Some(DeterministicLocalKCut::new(
364 self.config.lambda_max,
365 self.num_vertices * 10,
366 2,
367 ));
368
369 let edge_data: Vec<(VertexId, VertexId, Weight)> = self
371 .edges
372 .iter()
373 .map(|&(u, v)| (u, v, self.get_weight(u, v).unwrap_or(1.0)))
374 .collect();
375
376 if let Some(ref mut lkc) = self.local_kcut {
378 for (u, v, weight) in edge_data {
379 lkc.insert_edge(u, v, weight);
380 }
381 }
382
383 self.recompute_min_cut();
385
386 self.hierarchy_built = true;
387
388 self.recourse_stats.theoretical_bound = 2.0_f64.powf(log_n.powf(0.9));
390 }
391
392 fn build_base_level(&mut self) {
394 let vertices: HashSet<_> = self.adjacency.keys().copied().collect();
395 if vertices.is_empty() {
396 return;
397 }
398
399 let phi = self.levels[0].phi;
401
402 let mut remaining = vertices.clone();
404 let mut expander_sets: Vec<HashSet<VertexId>> = Vec::new();
405
406 while !remaining.is_empty() {
407 let start = *remaining.iter().next().unwrap();
408 let expander_vertices = self.grow_expander(&remaining, start, phi);
409
410 if expander_vertices.is_empty() {
411 remaining.remove(&start);
412 continue;
413 }
414
415 for &v in &expander_vertices {
416 remaining.remove(&v);
417 }
418
419 expander_sets.push(expander_vertices);
420 }
421
422 let mut expanders_to_create: Vec<LevelExpander> = Vec::new();
424 let mut vertex_mappings: Vec<(VertexId, u64)> = Vec::new();
425 let mut next_id = self.levels[0].next_id;
426
427 for expander_vertices in &expander_sets {
428 let id = next_id;
429 next_id += 1;
430
431 let volume = expander_vertices.iter().map(|&v| self.degree(v)).sum();
432
433 let boundary_size = self.count_boundary(expander_vertices);
434
435 let expander = LevelExpander {
436 id,
437 vertices: expander_vertices.clone(),
438 boundary_size,
439 volume,
440 internal_min_cut: f64::INFINITY,
441 is_valid_expander: true,
442 parent_id: None,
443 children_ids: Vec::new(),
444 };
445
446 for &v in expander_vertices {
447 vertex_mappings.push((v, id));
448 }
449
450 expanders_to_create.push(expander);
451 }
452
453 let level = &mut self.levels[0];
455 level.expanders.clear();
456 level.vertex_expander.clear();
457 level.next_id = next_id;
458
459 for expander in expanders_to_create {
460 level.expanders.insert(expander.id, expander);
461 }
462
463 for (v, id) in vertex_mappings {
464 level.vertex_expander.insert(v, id);
465 }
466 }
467
468 fn build_level(&mut self, level_idx: usize) {
470 if level_idx == 0 || level_idx >= self.levels.len() {
471 return;
472 }
473
474 let prev_expander_ids: Vec<u64> = self.levels[level_idx - 1]
476 .expanders
477 .keys()
478 .copied()
479 .collect();
480
481 let mut groups: Vec<Vec<u64>> = Vec::new();
483 let mut assigned: HashSet<u64> = HashSet::new();
484
485 for &exp_id in &prev_expander_ids {
486 if assigned.contains(&exp_id) {
487 continue;
488 }
489
490 let mut group = vec![exp_id];
491 assigned.insert(exp_id);
492
493 for &other_id in &prev_expander_ids {
495 if assigned.contains(&other_id) {
496 continue;
497 }
498
499 if self.expanders_adjacent_at_level(level_idx - 1, exp_id, other_id) {
500 group.push(other_id);
501 assigned.insert(other_id);
502
503 if group.len() >= 4 {
505 break;
506 }
507 }
508 }
509
510 groups.push(group);
511 }
512
513 let mut group_vertices: Vec<(Vec<u64>, HashSet<VertexId>)> = Vec::new();
515 for group in &groups {
516 let mut vertices = HashSet::new();
517 for &child_id in group {
518 if let Some(child) = self.levels[level_idx - 1].expanders.get(&child_id) {
519 vertices.extend(&child.vertices);
520 }
521 }
522 group_vertices.push((group.clone(), vertices));
523 }
524
525 let mut expanders_to_create: Vec<(u64, LevelExpander, HashMap<VertexId, u64>)> = Vec::new();
527 let mut next_id = self.levels[level_idx].next_id;
528
529 for (group, vertices) in &group_vertices {
530 let id = next_id;
531 next_id += 1;
532
533 let volume = vertices.iter().map(|&v| self.degree(v)).sum();
534
535 let boundary_size = self.count_boundary(vertices);
536
537 let expander = LevelExpander {
538 id,
539 vertices: vertices.clone(),
540 boundary_size,
541 volume,
542 internal_min_cut: f64::INFINITY,
543 is_valid_expander: true,
544 parent_id: None,
545 children_ids: group.clone(),
546 };
547
548 let mut vertex_map = HashMap::new();
549 for &v in vertices {
550 vertex_map.insert(v, id);
551 }
552
553 expanders_to_create.push((id, expander, vertex_map));
554 }
555
556 let level = &mut self.levels[level_idx];
558 level.expanders.clear();
559 level.vertex_expander.clear();
560 level.next_id = next_id;
561
562 for (id, expander, vertex_map) in expanders_to_create {
563 level.expanders.insert(id, expander);
564 level.vertex_expander.extend(vertex_map);
565 }
566
567 for (group, _) in &group_vertices {
569 let parent_id = self.levels[level_idx]
571 .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(
677 &mut self,
678 level_idx: usize,
679 u: VertexId,
680 v: VertexId,
681 _weight: Weight,
682 ) -> u64 {
683 if level_idx >= self.levels.len() {
684 return 0;
685 }
686
687 let mut recourse = 0u64;
688
689 let exp_u = self.levels[level_idx].vertex_expander.get(&u).copied();
691 let exp_v = self.levels[level_idx].vertex_expander.get(&v).copied();
692
693 match (exp_u, exp_v) {
694 (Some(eu), Some(ev)) if eu == ev => {
695 recourse += 1;
697 self.update_expander_properties(level_idx, eu);
698 }
699 (Some(eu), Some(ev)) => {
700 recourse += 2;
702 self.update_expander_properties(level_idx, eu);
703 self.update_expander_properties(level_idx, ev);
704 }
705 (Some(eu), None) => {
706 recourse += self.try_add_to_expander(level_idx, v, eu);
708 }
709 (None, Some(ev)) => {
710 recourse += self.try_add_to_expander(level_idx, u, ev);
712 }
713 (None, None) => {
714 recourse += self.create_new_expander(level_idx, &[u, v]);
716 }
717 }
718
719 recourse
720 }
721
722 fn update_level_for_delete(&mut self, level_idx: usize, u: VertexId, v: VertexId) -> u64 {
724 if level_idx >= self.levels.len() {
725 return 0;
726 }
727
728 let mut recourse = 0u64;
729
730 let exp_u = self.levels[level_idx].vertex_expander.get(&u).copied();
732 let exp_v = self.levels[level_idx].vertex_expander.get(&v).copied();
733
734 if let (Some(eu), Some(ev)) = (exp_u, exp_v) {
735 if eu == ev {
736 recourse += self.check_and_split_expander(level_idx, eu);
738 } else {
739 recourse += 2;
741 self.update_expander_properties(level_idx, eu);
742 self.update_expander_properties(level_idx, ev);
743 }
744 }
745
746 recourse
747 }
748
749 fn update_expander_properties(&mut self, level_idx: usize, exp_id: u64) {
751 if level_idx >= self.levels.len() {
752 return;
753 }
754
755 let (vertices, phi) = match self.levels[level_idx].expanders.get(&exp_id) {
757 Some(e) => (e.vertices.clone(), self.levels[level_idx].phi),
758 None => return,
759 };
760
761 let volume: usize = vertices.iter().map(|&v| self.degree(v)).sum();
762 let boundary_size = self.count_boundary(&vertices);
763
764 let expansion = if volume > 0 {
766 boundary_size as f64 / volume as f64
767 } else {
768 0.0
769 };
770 let is_valid = expansion >= phi * 0.3;
771
772 if let Some(expander) = self.levels[level_idx].expanders.get_mut(&exp_id) {
773 expander.volume = volume;
774 expander.boundary_size = boundary_size;
775 expander.is_valid_expander = is_valid;
776 }
777 }
778
779 fn try_add_to_expander(&mut self, level_idx: usize, v: VertexId, exp_id: u64) -> u64 {
781 if level_idx >= self.levels.len() {
782 return 0;
783 }
784
785 let (can_add, volume, boundary) = {
787 let level = &self.levels[level_idx];
788 if let Some(expander) = level.expanders.get(&exp_id) {
789 let mut test_vertices = expander.vertices.clone();
790 test_vertices.insert(v);
791
792 let volume: usize = test_vertices.iter().map(|&u| self.degree(u)).sum();
793 let boundary = self.count_boundary(&test_vertices);
794
795 let expansion = if volume > 0 {
796 boundary as f64 / volume as f64
797 } else {
798 0.0
799 };
800
801 (expansion >= level.phi * 0.3, volume, boundary)
802 } else {
803 (false, 0, 0)
804 }
805 };
806
807 if can_add {
808 let level = &mut self.levels[level_idx];
809 if let Some(expander) = level.expanders.get_mut(&exp_id) {
810 expander.vertices.insert(v);
811 expander.volume = volume;
812 expander.boundary_size = boundary;
813 }
814 level.vertex_expander.insert(v, exp_id);
815 1
816 } else {
817 self.create_new_expander(level_idx, &[v])
818 }
819 }
820
821 fn create_new_expander(&mut self, level_idx: usize, vertices: &[VertexId]) -> u64 {
823 if level_idx >= self.levels.len() {
824 return 0;
825 }
826
827 let vertex_set: HashSet<_> = vertices.iter().copied().collect();
828 let volume: usize = vertex_set.iter().map(|&v| self.degree(v)).sum();
829 let boundary_size = self.count_boundary(&vertex_set);
830
831 let level = &mut self.levels[level_idx];
832 let id = level.next_id;
833 level.next_id += 1;
834
835 let expander = LevelExpander {
836 id,
837 vertices: vertex_set.clone(),
838 boundary_size,
839 volume,
840 internal_min_cut: f64::INFINITY,
841 is_valid_expander: true,
842 parent_id: None,
843 children_ids: Vec::new(),
844 };
845
846 for &v in &vertex_set {
847 level.vertex_expander.insert(v, id);
848 }
849
850 level.expanders.insert(id, expander);
851
852 vertices.len() as u64
853 }
854
855 fn check_and_split_expander(&mut self, level_idx: usize, exp_id: u64) -> u64 {
857 if level_idx >= self.levels.len() {
858 return 0;
859 }
860
861 let needs_split = {
863 let level = &self.levels[level_idx];
864 if let Some(expander) = level.expanders.get(&exp_id) {
865 let expansion = if expander.volume > 0 {
866 expander.boundary_size as f64 / expander.volume as f64
867 } else {
868 0.0
869 };
870 expansion < level.phi * 0.2
871 } else {
872 false
873 }
874 };
875
876 if needs_split {
877 self.update_expander_properties(level_idx, exp_id);
880 2
881 } else {
882 self.update_expander_properties(level_idx, exp_id);
883 1
884 }
885 }
886
887 fn update_min_cut_incremental(&mut self, u: VertexId, v: VertexId, is_insert: bool) {
889 if let Some(ref lkc) = self.local_kcut {
891 let cuts_u = lkc.query(u);
892 let cuts_v = lkc.query(v);
893
894 let mut min_local = f64::INFINITY;
895
896 for cut in cuts_u.iter().chain(cuts_v.iter()) {
897 if cut.cut_value < min_local {
898 min_local = cut.cut_value;
899 }
900 }
901
902 if is_insert {
903 self.current_min_cut = self.current_min_cut.min(min_local);
906 } else {
907 if min_local < self.current_min_cut * 1.5 {
909 self.recompute_min_cut();
911 }
912 }
913 } else {
914 self.recompute_min_cut();
916 }
917 }
918
919 fn recompute_min_cut(&mut self) {
921 if self.edges.is_empty() {
922 self.current_min_cut = f64::INFINITY;
923 return;
924 }
925
926 let mut min_cut = f64::INFINITY;
927
928 for level in &self.levels {
930 for expander in level.expanders.values() {
931 let boundary_cut = expander.boundary_size as f64;
933 min_cut = min_cut.min(boundary_cut);
934
935 min_cut = min_cut.min(expander.internal_min_cut);
937 }
938 }
939
940 if let Some(ref lkc) = self.local_kcut {
942 for v in self.adjacency.keys().take(10) {
943 let cuts = lkc.query(*v);
944 for cut in cuts {
945 min_cut = min_cut.min(cut.cut_value);
946 }
947 }
948 }
949
950 self.current_min_cut = min_cut;
951 }
952
953 fn update_recourse_stats(&mut self, recourse: u64, time_us: f64) {
955 self.recourse_stats.total_recourse += recourse;
956 self.recourse_stats.num_updates += 1;
957 self.recourse_stats.max_single_recourse =
958 self.recourse_stats.max_single_recourse.max(recourse);
959
960 let n = self.recourse_stats.num_updates as f64;
962 self.recourse_stats.avg_update_time_us =
963 (self.recourse_stats.avg_update_time_us * (n - 1.0) + time_us) / n;
964
965 self.recourse_stats.recourse_per_level = self.levels.iter().map(|l| l.recourse).collect();
967 }
968
969 fn edge_key(u: VertexId, v: VertexId) -> (VertexId, VertexId) {
972 if u < v {
973 (u, v)
974 } else {
975 (v, u)
976 }
977 }
978
979 fn get_weight(&self, u: VertexId, v: VertexId) -> Option<Weight> {
980 self.adjacency.get(&u).and_then(|n| n.get(&v).copied())
981 }
982
983 fn degree(&self, v: VertexId) -> usize {
984 self.adjacency.get(&v).map_or(0, |n| n.len())
985 }
986
987 fn neighbors(&self, v: VertexId) -> Vec<(VertexId, Weight)> {
988 self.adjacency
989 .get(&v)
990 .map(|n| n.iter().map(|(&v, &w)| (v, w)).collect())
991 .unwrap_or_default()
992 }
993
994 fn count_boundary(&self, vertices: &HashSet<VertexId>) -> usize {
995 let mut boundary = 0;
996 for &v in vertices {
997 for (neighbor, _) in self.neighbors(v) {
998 if !vertices.contains(&neighbor) {
999 boundary += 1;
1000 }
1001 }
1002 }
1003 boundary
1004 }
1005
1006 fn expanders_adjacent_at_level(&self, level_idx: usize, exp1: u64, exp2: u64) -> bool {
1007 if level_idx >= self.levels.len() {
1008 return false;
1009 }
1010
1011 let level = &self.levels[level_idx];
1012
1013 let e1 = match level.expanders.get(&exp1) {
1014 Some(e) => e,
1015 None => return false,
1016 };
1017 let e2 = match level.expanders.get(&exp2) {
1018 Some(e) => e,
1019 None => return false,
1020 };
1021
1022 for &v in &e1.vertices {
1024 for (neighbor, _) in self.neighbors(v) {
1025 if e2.vertices.contains(&neighbor) {
1026 return true;
1027 }
1028 }
1029 }
1030 false
1031 }
1032
1033 pub fn min_cut_value(&self) -> f64 {
1037 self.current_min_cut
1038 }
1039
1040 pub fn min_cut(&self) -> MinCutQueryResult {
1042 MinCutQueryResult {
1043 value: self.current_min_cut,
1044 cut_edges: None, partition: None,
1046 is_exact: true,
1047 complexity_verified: self.recourse_stats.is_subpolynomial(self.num_vertices),
1048 }
1049 }
1050
1051 pub fn config(&self) -> &SubpolyConfig {
1053 &self.config
1054 }
1055
1056 pub fn num_vertices(&self) -> usize {
1058 self.num_vertices
1059 }
1060
1061 pub fn num_edges(&self) -> usize {
1063 self.num_edges
1064 }
1065
1066 pub fn num_levels(&self) -> usize {
1068 self.levels.len()
1069 }
1070
1071 pub fn recourse_stats(&self) -> &RecourseStats {
1073 &self.recourse_stats
1074 }
1075
1076 pub fn hierarchy_stats(&self) -> HierarchyStatistics {
1078 HierarchyStatistics {
1079 num_levels: self.levels.len(),
1080 expanders_per_level: self.levels.iter().map(|l| l.expanders.len()).collect(),
1081 total_expanders: self.levels.iter().map(|l| l.expanders.len()).sum(),
1082 avg_expander_size: if self.levels[0].expanders.is_empty() {
1083 0.0
1084 } else {
1085 self.levels[0]
1086 .expanders
1087 .values()
1088 .map(|e| e.vertices.len())
1089 .sum::<usize>() as f64
1090 / self.levels[0].expanders.len() as f64
1091 },
1092 }
1093 }
1094
1095 pub fn is_subpolynomial(&self) -> bool {
1097 self.recourse_stats.is_subpolynomial(self.num_vertices)
1098 }
1099
1100 pub fn certify_cuts(&mut self) {
1102 let mut expander_data: Vec<(usize, u64, HashSet<VertexId>)> = Vec::new();
1104 for level_idx in 0..self.levels.len() {
1105 for (&exp_id, expander) in &self.levels[level_idx].expanders {
1106 expander_data.push((level_idx, exp_id, expander.vertices.clone()));
1107 }
1108 }
1109
1110 let mut updates: Vec<(usize, u64, f64)> = Vec::new();
1112
1113 if let Some(ref lkc) = self.local_kcut {
1114 for (level_idx, exp_id, vertices) in &expander_data {
1115 let boundary_verts: Vec<_> = vertices
1117 .iter()
1118 .filter(|&&v| self.neighbors(v).iter().any(|(n, _)| !vertices.contains(n)))
1119 .take(5)
1120 .copied()
1121 .collect();
1122
1123 let mut min_internal_cut = f64::INFINITY;
1124
1125 for v in boundary_verts {
1126 let cuts = lkc.query(v);
1127 for cut in cuts {
1128 let is_internal = cut.vertices.iter().all(|u| vertices.contains(u));
1130
1131 if is_internal {
1132 min_internal_cut = min_internal_cut.min(cut.cut_value);
1133 }
1134 }
1135 }
1136
1137 updates.push((*level_idx, *exp_id, min_internal_cut));
1138 }
1139 }
1140
1141 for (level_idx, exp_id, min_cut) in updates {
1143 if let Some(expander) = self.levels[level_idx].expanders.get_mut(&exp_id) {
1144 expander.internal_min_cut = min_cut;
1145 }
1146 }
1147 }
1148}
1149
1150#[derive(Debug, Clone)]
1152pub struct MinCutQueryResult {
1153 pub value: f64,
1155 pub cut_edges: Option<Vec<(VertexId, VertexId)>>,
1157 pub partition: Option<(Vec<VertexId>, Vec<VertexId>)>,
1159 pub is_exact: bool,
1161 pub complexity_verified: bool,
1163}
1164
1165#[derive(Debug, Clone)]
1167pub struct HierarchyStatistics {
1168 pub num_levels: usize,
1170 pub expanders_per_level: Vec<usize>,
1172 pub total_expanders: usize,
1174 pub avg_expander_size: f64,
1176}
1177
1178#[cfg(test)]
1179mod tests {
1180 use super::*;
1181
1182 #[test]
1183 fn test_subpoly_config_default() {
1184 let config = SubpolyConfig::default();
1185 assert!(config.phi > 0.0);
1186 assert!(config.lambda_max > 0);
1187 assert!(config.target_levels > 0);
1188 }
1189
1190 #[test]
1191 fn test_subpoly_config_for_size() {
1192 let config = SubpolyConfig::for_size(1_000_000);
1193 assert!(config.phi < 0.1);
1194 assert!(config.lambda_max > 100);
1195 assert!(config.target_levels >= 2);
1196 }
1197
1198 #[test]
1199 fn test_create_empty() {
1200 let mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1201 assert_eq!(mincut.num_vertices(), 0);
1202 assert_eq!(mincut.num_edges(), 0);
1203 assert_eq!(mincut.min_cut_value(), f64::INFINITY);
1204 }
1205
1206 #[test]
1207 fn test_insert_edges() {
1208 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1209
1210 mincut.insert_edge(1, 2, 1.0).unwrap();
1211 mincut.insert_edge(2, 3, 1.0).unwrap();
1212 mincut.insert_edge(3, 1, 1.0).unwrap();
1213
1214 assert_eq!(mincut.num_vertices(), 3);
1215 assert_eq!(mincut.num_edges(), 3);
1216 }
1217
1218 #[test]
1219 fn test_build_hierarchy() {
1220 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1221
1222 for i in 0..10 {
1224 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1225 }
1226
1227 mincut.build();
1228
1229 assert!(mincut.num_levels() >= 2);
1230 let stats = mincut.hierarchy_stats();
1231 assert!(stats.total_expanders > 0);
1232 }
1233
1234 #[test]
1235 fn test_min_cut_triangle() {
1236 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1237
1238 mincut.insert_edge(1, 2, 1.0).unwrap();
1239 mincut.insert_edge(2, 3, 1.0).unwrap();
1240 mincut.insert_edge(3, 1, 1.0).unwrap();
1241
1242 mincut.build();
1243
1244 assert!(mincut.min_cut_value() <= 2.0);
1245 }
1246
1247 #[test]
1248 fn test_min_cut_bridge() {
1249 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1250
1251 mincut.insert_edge(1, 2, 1.0).unwrap();
1253 mincut.insert_edge(2, 3, 1.0).unwrap();
1254 mincut.insert_edge(3, 1, 1.0).unwrap();
1255
1256 mincut.insert_edge(3, 4, 1.0).unwrap(); mincut.insert_edge(4, 5, 1.0).unwrap();
1259 mincut.insert_edge(5, 6, 1.0).unwrap();
1260 mincut.insert_edge(6, 4, 1.0).unwrap();
1261
1262 mincut.build();
1263
1264 assert!(mincut.min_cut_value() <= 2.0);
1266 }
1267
1268 #[test]
1269 fn test_incremental_updates() {
1270 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1271
1272 mincut.insert_edge(1, 2, 1.0).unwrap();
1274 mincut.insert_edge(2, 3, 1.0).unwrap();
1275 mincut.insert_edge(3, 1, 1.0).unwrap();
1276
1277 mincut.build();
1278
1279 let initial_cut = mincut.min_cut_value();
1280
1281 mincut.insert_edge(3, 4, 1.0).unwrap();
1283 mincut.insert_edge(4, 5, 1.0).unwrap();
1284
1285 assert!(mincut.min_cut_value() <= initial_cut * 2.0);
1287
1288 let stats = mincut.recourse_stats();
1290 assert!(stats.num_updates > 0);
1291 }
1292
1293 #[test]
1294 fn test_delete_edge() {
1295 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1296
1297 mincut.insert_edge(1, 2, 1.0).unwrap();
1298 mincut.insert_edge(2, 3, 1.0).unwrap();
1299 mincut.insert_edge(3, 1, 1.0).unwrap();
1300
1301 mincut.build();
1302
1303 mincut.delete_edge(1, 2).unwrap();
1304
1305 assert_eq!(mincut.num_edges(), 2);
1306 }
1307
1308 #[test]
1309 fn test_recourse_stats() {
1310 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1311
1312 for i in 0..20 {
1314 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1315 }
1316
1317 mincut.build();
1318
1319 mincut.insert_edge(0, 10, 1.0).unwrap();
1321 mincut.insert_edge(5, 15, 1.0).unwrap();
1322
1323 let stats = mincut.recourse_stats();
1324 assert!(stats.num_updates >= 2);
1325 assert!(stats.amortized_recourse() >= 0.0);
1326 }
1327
1328 #[test]
1329 fn test_is_subpolynomial() {
1330 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1331
1332 for i in 0..10 {
1334 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1335 }
1336
1337 mincut.build();
1338
1339 mincut.insert_edge(0, 5, 1.0).unwrap();
1341
1342 assert!(mincut.is_subpolynomial());
1344 }
1345
1346 #[test]
1347 fn test_certify_cuts() {
1348 let mut mincut = SubpolynomialMinCut::new(SubpolyConfig::default());
1349
1350 mincut.insert_edge(1, 2, 1.0).unwrap();
1352 mincut.insert_edge(2, 3, 1.0).unwrap();
1353 mincut.insert_edge(3, 1, 1.0).unwrap();
1354
1355 mincut.build();
1356 mincut.certify_cuts();
1357
1358 }
1360
1361 #[test]
1362 fn test_large_graph() {
1363 let mut mincut = SubpolynomialMinCut::for_size(1000);
1364
1365 for i in 0..100 {
1367 mincut.insert_edge(i, i + 1, 1.0).unwrap();
1368 }
1369 for i in 0..10 {
1371 mincut.insert_edge(i * 10, i * 10 + 50, 1.0).unwrap();
1372 }
1373
1374 mincut.build();
1375
1376 let stats = mincut.hierarchy_stats();
1377 assert!(stats.num_levels >= 2);
1378
1379 mincut.insert_edge(25, 75, 1.0).unwrap();
1381
1382 assert!(mincut.recourse_stats().num_updates > 0);
1383 }
1384}