ruvector_mincut/cluster/
hierarchy.rs

1//! Three-Level Cluster Hierarchy for Dynamic Minimum Cut
2//!
3//! Implementation of the 3-level decomposition from:
4//! "Deterministic and Exact Fully-dynamic Minimum Cut of Superpolylogarithmic
5//! Size in Subpolynomial Time" (arXiv:2512.13105)
6//!
7//! # Hierarchy Structure
8//!
9//! Level 0: **Expanders** - φ-expander subgraphs with guaranteed edge expansion
10//! Level 1: **Preclusters** - Groups of expanders with bounded boundaries
11//! Level 2: **Clusters** - Final clustering with mirror cut maintenance
12//!
13//! # Key Features
14//!
15//! - Expanders guarantee no sparse internal cuts
16//! - Preclusters enable local cut computation
17//! - Clusters maintain mirror cuts for cross-boundary tracking
18//! - Incremental updates propagate through hierarchy
19
20use crate::graph::{VertexId, Weight};
21use std::collections::{HashMap, HashSet, VecDeque};
22
23/// Expansion parameter type
24pub type Phi = f64;
25
26/// Configuration for the 3-level hierarchy
27#[derive(Debug, Clone)]
28pub struct HierarchyConfig {
29    /// Expansion parameter φ for expander detection
30    pub phi: Phi,
31    /// Maximum expander size before decomposition
32    pub max_expander_size: usize,
33    /// Minimum expander size (don't decompose smaller)
34    pub min_expander_size: usize,
35    /// Target precluster size
36    pub target_precluster_size: usize,
37    /// Maximum boundary ratio for preclusters
38    pub max_boundary_ratio: f64,
39    /// Enable mirror cut tracking
40    pub track_mirror_cuts: bool,
41}
42
43impl Default for HierarchyConfig {
44    fn default() -> Self {
45        Self {
46            phi: 0.1,
47            max_expander_size: 500,
48            min_expander_size: 5,
49            target_precluster_size: 100,
50            max_boundary_ratio: 0.3,
51            track_mirror_cuts: true,
52        }
53    }
54}
55
56/// Level in the hierarchy
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum HierarchyLevel {
59    /// Level 0: Expander (φ-expander subgraph)
60    Expander,
61    /// Level 1: Precluster (group of expanders)
62    Precluster,
63    /// Level 2: Cluster (top-level grouping)
64    Cluster,
65}
66
67/// An expander at level 0
68#[derive(Debug, Clone)]
69pub struct Expander {
70    /// Unique expander ID
71    pub id: u64,
72    /// Vertices in this expander
73    pub vertices: HashSet<VertexId>,
74    /// Internal edges (both endpoints in expander)
75    pub internal_edges: Vec<(VertexId, VertexId)>,
76    /// Boundary edges (one endpoint outside)
77    pub boundary_edges: Vec<(VertexId, VertexId)>,
78    /// Volume (sum of degrees)
79    pub volume: usize,
80    /// Verified expansion ratio
81    pub expansion_ratio: f64,
82    /// Parent precluster ID
83    pub precluster_id: Option<u64>,
84}
85
86impl Expander {
87    /// Create new expander
88    pub fn new(id: u64, vertices: HashSet<VertexId>) -> Self {
89        Self {
90            id,
91            vertices,
92            internal_edges: Vec::new(),
93            boundary_edges: Vec::new(),
94            volume: 0,
95            expansion_ratio: 0.0,
96            precluster_id: None,
97        }
98    }
99
100    /// Get size (number of vertices)
101    pub fn size(&self) -> usize {
102        self.vertices.len()
103    }
104
105    /// Check if vertex is in this expander
106    pub fn contains(&self, v: VertexId) -> bool {
107        self.vertices.contains(&v)
108    }
109
110    /// Compute boundary sparsity
111    pub fn boundary_sparsity(&self) -> f64 {
112        if self.volume == 0 {
113            return 0.0;
114        }
115        self.boundary_edges.len() as f64 / self.volume as f64
116    }
117}
118
119/// A precluster at level 1 (group of expanders)
120#[derive(Debug, Clone)]
121pub struct Precluster {
122    /// Unique precluster ID
123    pub id: u64,
124    /// Expander IDs in this precluster
125    pub expanders: Vec<u64>,
126    /// All vertices (union of expanders)
127    pub vertices: HashSet<VertexId>,
128    /// Boundary edges to other preclusters
129    pub boundary_edges: Vec<(VertexId, VertexId)>,
130    /// Volume of this precluster
131    pub volume: usize,
132    /// Parent cluster ID
133    pub cluster_id: Option<u64>,
134}
135
136impl Precluster {
137    /// Create new precluster
138    pub fn new(id: u64) -> Self {
139        Self {
140            id,
141            expanders: Vec::new(),
142            vertices: HashSet::new(),
143            boundary_edges: Vec::new(),
144            volume: 0,
145            cluster_id: None,
146        }
147    }
148
149    /// Add an expander to this precluster
150    pub fn add_expander(&mut self, expander: &Expander) {
151        self.expanders.push(expander.id);
152        self.vertices.extend(&expander.vertices);
153        self.volume += expander.volume;
154    }
155
156    /// Get size
157    pub fn size(&self) -> usize {
158        self.vertices.len()
159    }
160
161    /// Compute boundary ratio
162    pub fn boundary_ratio(&self) -> f64 {
163        if self.volume == 0 {
164            return 0.0;
165        }
166        self.boundary_edges.len() as f64 / self.volume as f64
167    }
168}
169
170/// A mirror cut for tracking cross-expander minimum cuts
171#[derive(Debug, Clone)]
172pub struct MirrorCut {
173    /// Source expander ID
174    pub source_expander: u64,
175    /// Target expander ID
176    pub target_expander: u64,
177    /// Cut value between the expanders
178    pub cut_value: f64,
179    /// Edges in the cut
180    pub cut_edges: Vec<(VertexId, VertexId)>,
181    /// Is this cut certified (verified via LocalKCut)?
182    pub certified: bool,
183}
184
185/// A cluster at level 2 (top-level grouping)
186#[derive(Debug, Clone)]
187pub struct HierarchyCluster {
188    /// Unique cluster ID
189    pub id: u64,
190    /// Precluster IDs in this cluster
191    pub preclusters: Vec<u64>,
192    /// All vertices
193    pub vertices: HashSet<VertexId>,
194    /// Boundary edges to other clusters
195    pub boundary_edges: Vec<(VertexId, VertexId)>,
196    /// Mirror cuts tracked for this cluster
197    pub mirror_cuts: Vec<MirrorCut>,
198    /// Minimum cut within this cluster
199    pub internal_min_cut: f64,
200}
201
202impl HierarchyCluster {
203    /// Create new cluster
204    pub fn new(id: u64) -> Self {
205        Self {
206            id,
207            preclusters: Vec::new(),
208            vertices: HashSet::new(),
209            boundary_edges: Vec::new(),
210            mirror_cuts: Vec::new(),
211            internal_min_cut: f64::INFINITY,
212        }
213    }
214
215    /// Add a precluster
216    pub fn add_precluster(&mut self, precluster: &Precluster) {
217        self.preclusters.push(precluster.id);
218        self.vertices.extend(&precluster.vertices);
219    }
220
221    /// Get size
222    pub fn size(&self) -> usize {
223        self.vertices.len()
224    }
225}
226
227/// The three-level hierarchy data structure
228#[derive(Debug)]
229pub struct ThreeLevelHierarchy {
230    /// Configuration
231    config: HierarchyConfig,
232    /// All expanders (level 0)
233    expanders: HashMap<u64, Expander>,
234    /// All preclusters (level 1)
235    preclusters: HashMap<u64, Precluster>,
236    /// All clusters (level 2)
237    clusters: HashMap<u64, HierarchyCluster>,
238    /// Vertex to expander mapping
239    vertex_expander: HashMap<VertexId, u64>,
240    /// Next ID counter
241    next_id: u64,
242    /// Graph adjacency
243    adjacency: HashMap<VertexId, HashMap<VertexId, Weight>>,
244    /// Global minimum cut estimate
245    pub global_min_cut: f64,
246}
247
248impl ThreeLevelHierarchy {
249    /// Create new hierarchy
250    pub fn new(config: HierarchyConfig) -> Self {
251        Self {
252            config,
253            expanders: HashMap::new(),
254            preclusters: HashMap::new(),
255            clusters: HashMap::new(),
256            vertex_expander: HashMap::new(),
257            next_id: 1,
258            adjacency: HashMap::new(),
259            global_min_cut: f64::INFINITY,
260        }
261    }
262
263    /// Create with default config
264    pub fn with_defaults() -> Self {
265        Self::new(HierarchyConfig::default())
266    }
267
268    /// Insert an edge
269    pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) {
270        self.adjacency.entry(u).or_default().insert(v, weight);
271        self.adjacency.entry(v).or_default().insert(u, weight);
272    }
273
274    /// Delete an edge
275    pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
276        if let Some(neighbors) = self.adjacency.get_mut(&u) {
277            neighbors.remove(&v);
278        }
279        if let Some(neighbors) = self.adjacency.get_mut(&v) {
280            neighbors.remove(&u);
281        }
282    }
283
284    /// Get all vertices
285    pub fn vertices(&self) -> Vec<VertexId> {
286        self.adjacency.keys().copied().collect()
287    }
288
289    /// Get neighbors
290    pub fn neighbors(&self, v: VertexId) -> Vec<(VertexId, Weight)> {
291        self.adjacency
292            .get(&v)
293            .map(|n| n.iter().map(|(&v, &w)| (v, w)).collect())
294            .unwrap_or_default()
295    }
296
297    /// Get degree
298    pub fn degree(&self, v: VertexId) -> usize {
299        self.adjacency.get(&v).map_or(0, |n| n.len())
300    }
301
302    /// Build the complete 3-level hierarchy
303    pub fn build(&mut self) {
304        let vertices: HashSet<_> = self.vertices().into_iter().collect();
305        if vertices.is_empty() {
306            return;
307        }
308
309        // Step 1: Create initial expanders via greedy decomposition
310        self.build_expanders(&vertices);
311
312        // Step 2: Group expanders into preclusters
313        self.build_preclusters();
314
315        // Step 3: Group preclusters into clusters
316        self.build_clusters();
317
318        // Step 4: Compute mirror cuts if enabled
319        if self.config.track_mirror_cuts {
320            self.compute_mirror_cuts();
321        }
322
323        // Step 5: Compute global min cut estimate
324        self.update_global_min_cut();
325    }
326
327    /// Build expanders using greedy expansion detection
328    fn build_expanders(&mut self, vertices: &HashSet<VertexId>) {
329        self.expanders.clear();
330        self.vertex_expander.clear();
331
332        let mut remaining: HashSet<_> = vertices.iter().copied().collect();
333
334        while !remaining.is_empty() {
335            // Pick a random starting vertex
336            let start = *remaining.iter().next().unwrap();
337
338            // Grow an expander from this vertex
339            let expander_vertices = self.grow_expander(start, &remaining);
340
341            if expander_vertices.is_empty() {
342                remaining.remove(&start);
343                continue;
344            }
345
346            // Create expander
347            let id = self.next_id;
348            self.next_id += 1;
349
350            let mut expander = Expander::new(id, expander_vertices.clone());
351            self.compute_expander_properties(&mut expander);
352
353            // Update mappings
354            for &v in &expander_vertices {
355                self.vertex_expander.insert(v, id);
356                remaining.remove(&v);
357            }
358
359            self.expanders.insert(id, expander);
360        }
361    }
362
363    /// Grow an expander from a starting vertex using BFS
364    fn grow_expander(&self, start: VertexId, available: &HashSet<VertexId>) -> HashSet<VertexId> {
365        let mut expander = HashSet::new();
366        let mut queue = VecDeque::new();
367        let mut volume = 0usize;
368
369        queue.push_back(start);
370        expander.insert(start);
371        volume += self.degree(start);
372
373        while let Some(v) = queue.pop_front() {
374            if expander.len() >= self.config.max_expander_size {
375                break;
376            }
377
378            for (neighbor, _) in self.neighbors(v) {
379                if !available.contains(&neighbor) || expander.contains(&neighbor) {
380                    continue;
381                }
382
383                // Check if adding this vertex maintains expansion
384                let new_volume = volume + self.degree(neighbor);
385                let boundary_after = self.count_boundary(&expander, &[neighbor]);
386
387                let expansion = if new_volume > 0 {
388                    boundary_after as f64 / (new_volume.min(expander.len() * 2)) as f64
389                } else {
390                    0.0
391                };
392
393                // Only add if it doesn't violate expansion too much
394                if expansion >= self.config.phi * 0.5
395                    || expander.len() < self.config.min_expander_size
396                {
397                    expander.insert(neighbor);
398                    volume = new_volume;
399                    queue.push_back(neighbor);
400                }
401            }
402        }
403
404        expander
405    }
406
407    /// Count boundary edges for a set of vertices
408    fn count_boundary(&self, vertices: &HashSet<VertexId>, additional: &[VertexId]) -> usize {
409        let mut full_set = vertices.clone();
410        for &v in additional {
411            full_set.insert(v);
412        }
413
414        let mut boundary = 0;
415        for &v in &full_set {
416            for (neighbor, _) in self.neighbors(v) {
417                if !full_set.contains(&neighbor) {
418                    boundary += 1;
419                }
420            }
421        }
422        boundary
423    }
424
425    /// Compute expander properties (edges, volume, expansion ratio)
426    fn compute_expander_properties(&self, expander: &mut Expander) {
427        let mut internal = Vec::new();
428        let mut boundary = Vec::new();
429        let mut volume = 0;
430
431        for &v in &expander.vertices {
432            let neighbors = self.neighbors(v);
433            volume += neighbors.len();
434
435            for (neighbor, _) in neighbors {
436                if expander.vertices.contains(&neighbor) {
437                    if v < neighbor {
438                        internal.push((v, neighbor));
439                    }
440                } else {
441                    boundary.push((v, neighbor));
442                }
443            }
444        }
445
446        expander.internal_edges = internal;
447        expander.boundary_edges = boundary;
448        expander.volume = volume;
449
450        // Compute expansion ratio
451        let min_vol = expander.volume.min(expander.vertices.len() * 2);
452        expander.expansion_ratio = if min_vol > 0 {
453            expander.boundary_edges.len() as f64 / min_vol as f64
454        } else {
455            0.0
456        };
457    }
458
459    /// Build preclusters by grouping nearby expanders
460    fn build_preclusters(&mut self) {
461        self.preclusters.clear();
462
463        let expander_ids: Vec<_> = self.expanders.keys().copied().collect();
464        let mut assigned = HashSet::new();
465
466        for &exp_id in &expander_ids {
467            if assigned.contains(&exp_id) {
468                continue;
469            }
470
471            let id = self.next_id;
472            self.next_id += 1;
473
474            let mut precluster = Precluster::new(id);
475
476            // Add this expander
477            if let Some(expander) = self.expanders.get_mut(&exp_id) {
478                precluster.add_expander(expander);
479                expander.precluster_id = Some(id);
480                assigned.insert(exp_id);
481            }
482
483            // Try to add neighboring expanders
484            for &other_id in &expander_ids {
485                if assigned.contains(&other_id) {
486                    continue;
487                }
488
489                if precluster.size() >= self.config.target_precluster_size {
490                    break;
491                }
492
493                // Check if expanders are adjacent
494                if self.expanders_adjacent(exp_id, other_id) {
495                    if let Some(expander) = self.expanders.get_mut(&other_id) {
496                        precluster.add_expander(expander);
497                        expander.precluster_id = Some(id);
498                        assigned.insert(other_id);
499                    }
500                }
501            }
502
503            // Compute precluster boundary
504            self.compute_precluster_boundary(&mut precluster);
505
506            self.preclusters.insert(id, precluster);
507        }
508    }
509
510    /// Check if two expanders share an edge
511    fn expanders_adjacent(&self, exp1: u64, exp2: u64) -> bool {
512        let e1 = match self.expanders.get(&exp1) {
513            Some(e) => e,
514            None => return false,
515        };
516        let e2 = match self.expanders.get(&exp2) {
517            Some(e) => e,
518            None => return false,
519        };
520
521        // Check if any vertex in e1 has a neighbor in e2
522        for &v in &e1.vertices {
523            for (neighbor, _) in self.neighbors(v) {
524                if e2.vertices.contains(&neighbor) {
525                    return true;
526                }
527            }
528        }
529        false
530    }
531
532    /// Compute boundary for a precluster
533    fn compute_precluster_boundary(&self, precluster: &mut Precluster) {
534        precluster.boundary_edges.clear();
535
536        for &v in &precluster.vertices {
537            for (neighbor, _) in self.neighbors(v) {
538                if !precluster.vertices.contains(&neighbor) {
539                    precluster.boundary_edges.push((v, neighbor));
540                }
541            }
542        }
543    }
544
545    /// Build top-level clusters from preclusters
546    fn build_clusters(&mut self) {
547        self.clusters.clear();
548
549        let precluster_ids: Vec<_> = self.preclusters.keys().copied().collect();
550        let mut assigned = HashSet::new();
551
552        for &pre_id in &precluster_ids {
553            if assigned.contains(&pre_id) {
554                continue;
555            }
556
557            let id = self.next_id;
558            self.next_id += 1;
559
560            let mut cluster = HierarchyCluster::new(id);
561
562            // Add this precluster
563            if let Some(precluster) = self.preclusters.get_mut(&pre_id) {
564                cluster.add_precluster(precluster);
565                precluster.cluster_id = Some(id);
566                assigned.insert(pre_id);
567            }
568
569            // Try to add adjacent preclusters (greedy)
570            for &other_id in &precluster_ids {
571                if assigned.contains(&other_id) {
572                    continue;
573                }
574
575                if self.preclusters_adjacent(pre_id, other_id) {
576                    if let Some(precluster) = self.preclusters.get_mut(&other_id) {
577                        cluster.add_precluster(precluster);
578                        precluster.cluster_id = Some(id);
579                        assigned.insert(other_id);
580                    }
581                }
582            }
583
584            // Compute cluster boundary
585            self.compute_cluster_boundary(&mut cluster);
586
587            self.clusters.insert(id, cluster);
588        }
589    }
590
591    /// Check if two preclusters share an edge
592    fn preclusters_adjacent(&self, pre1: u64, pre2: u64) -> bool {
593        let p1 = match self.preclusters.get(&pre1) {
594            Some(p) => p,
595            None => return false,
596        };
597        let p2 = match self.preclusters.get(&pre2) {
598            Some(p) => p,
599            None => return false,
600        };
601
602        for &v in &p1.vertices {
603            for (neighbor, _) in self.neighbors(v) {
604                if p2.vertices.contains(&neighbor) {
605                    return true;
606                }
607            }
608        }
609        false
610    }
611
612    /// Compute boundary for a cluster
613    fn compute_cluster_boundary(&self, cluster: &mut HierarchyCluster) {
614        cluster.boundary_edges.clear();
615
616        for &v in &cluster.vertices {
617            for (neighbor, _) in self.neighbors(v) {
618                if !cluster.vertices.contains(&neighbor) {
619                    cluster.boundary_edges.push((v, neighbor));
620                }
621            }
622        }
623    }
624
625    /// Compute mirror cuts between adjacent expanders
626    fn compute_mirror_cuts(&mut self) {
627        let expander_ids: Vec<_> = self.expanders.keys().copied().collect();
628
629        for cluster in self.clusters.values_mut() {
630            cluster.mirror_cuts.clear();
631        }
632
633        // For each pair of adjacent expanders
634        for (i, &exp1) in expander_ids.iter().enumerate() {
635            for &exp2 in expander_ids.iter().skip(i + 1) {
636                if !self.expanders_adjacent(exp1, exp2) {
637                    continue;
638                }
639
640                // Compute cut between them
641                let (cut_value, cut_edges) = self.compute_expander_cut(exp1, exp2);
642
643                let mirror = MirrorCut {
644                    source_expander: exp1,
645                    target_expander: exp2,
646                    cut_value,
647                    cut_edges,
648                    certified: false,
649                };
650
651                // Add to the cluster containing both expanders
652                if let Some(pre_id) = self.expanders.get(&exp1).and_then(|e| e.precluster_id) {
653                    if let Some(cluster_id) =
654                        self.preclusters.get(&pre_id).and_then(|p| p.cluster_id)
655                    {
656                        if let Some(cluster) = self.clusters.get_mut(&cluster_id) {
657                            cluster.mirror_cuts.push(mirror);
658                        }
659                    }
660                }
661            }
662        }
663
664        // Update internal min cuts
665        for cluster in self.clusters.values_mut() {
666            if let Some(min_mirror) = cluster
667                .mirror_cuts
668                .iter()
669                .map(|m| m.cut_value)
670                .min_by(|a, b| a.partial_cmp(b).unwrap())
671            {
672                cluster.internal_min_cut = cluster.internal_min_cut.min(min_mirror);
673            }
674        }
675    }
676
677    /// Compute cut between two expanders
678    fn compute_expander_cut(&self, exp1: u64, exp2: u64) -> (f64, Vec<(VertexId, VertexId)>) {
679        let e1 = match self.expanders.get(&exp1) {
680            Some(e) => e,
681            None => return (0.0, Vec::new()),
682        };
683        let e2 = match self.expanders.get(&exp2) {
684            Some(e) => e,
685            None => return (0.0, Vec::new()),
686        };
687
688        let mut cut_edges = Vec::new();
689        let mut cut_value = 0.0;
690
691        for &v in &e1.vertices {
692            for (neighbor, weight) in self.neighbors(v) {
693                if e2.vertices.contains(&neighbor) {
694                    cut_edges.push((v, neighbor));
695                    cut_value += weight;
696                }
697            }
698        }
699
700        (cut_value, cut_edges)
701    }
702
703    /// Update global minimum cut estimate
704    fn update_global_min_cut(&mut self) {
705        let mut min_cut = f64::INFINITY;
706
707        // Check cluster boundaries
708        for cluster in self.clusters.values() {
709            let boundary_cut: f64 = cluster
710                .boundary_edges
711                .iter()
712                .map(|&(u, v)| {
713                    self.adjacency
714                        .get(&u)
715                        .and_then(|n| n.get(&v))
716                        .copied()
717                        .unwrap_or(1.0)
718                })
719                .sum();
720
721            min_cut = min_cut.min(boundary_cut);
722            min_cut = min_cut.min(cluster.internal_min_cut);
723        }
724
725        self.global_min_cut = min_cut;
726    }
727
728    // === Incremental Updates ===
729
730    /// Handle edge insertion with incremental updates
731    ///
732    /// Instead of rebuilding, propagate changes through hierarchy:
733    /// 1. Update expander containing endpoints
734    /// 2. Propagate to precluster
735    /// 3. Propagate to cluster
736    /// 4. Update affected mirror cuts
737    pub fn handle_edge_insert(&mut self, u: VertexId, v: VertexId, weight: Weight) {
738        // First, add the edge to the graph
739        self.insert_edge(u, v, weight);
740
741        // If hierarchy not built yet, nothing to update
742        if self.expanders.is_empty() {
743            return;
744        }
745
746        // Find expanders containing u and v
747        let exp_u = self.vertex_expander.get(&u).copied();
748        let exp_v = self.vertex_expander.get(&v).copied();
749
750        match (exp_u, exp_v) {
751            (Some(eu), Some(ev)) if eu == ev => {
752                // Both in same expander - update internal edges
753                self.update_expander_edges(eu);
754            }
755            (Some(eu), Some(ev)) => {
756                // Different expanders - update both and their connection
757                self.update_expander_edges(eu);
758                self.update_expander_edges(ev);
759
760                // Update mirror cut between them
761                self.update_mirror_cut_between(eu, ev);
762            }
763            (Some(eu), None) => {
764                // Only u is in an expander - add v to same expander or create new
765                self.try_add_vertex_to_expander(v, eu);
766                self.update_expander_edges(eu);
767            }
768            (None, Some(ev)) => {
769                // Only v is in an expander - add u to same expander or create new
770                self.try_add_vertex_to_expander(u, ev);
771                self.update_expander_edges(ev);
772            }
773            (None, None) => {
774                // Neither in an expander - create new expander for both
775                self.create_expander_for_new_vertices(&[u, v]);
776            }
777        }
778
779        // Propagate changes up the hierarchy
780        self.propagate_updates(&[u, v]);
781
782        // Update global min cut
783        self.update_global_min_cut();
784    }
785
786    /// Handle edge deletion with incremental updates
787    pub fn handle_edge_delete(&mut self, u: VertexId, v: VertexId) {
788        // Remove the edge from the graph
789        self.delete_edge(u, v);
790
791        // If hierarchy not built yet, nothing to update
792        if self.expanders.is_empty() {
793            return;
794        }
795
796        // Find expanders containing u and v
797        let exp_u = self.vertex_expander.get(&u).copied();
798        let exp_v = self.vertex_expander.get(&v).copied();
799
800        if let (Some(eu), Some(ev)) = (exp_u, exp_v) {
801            if eu == ev {
802                // Same expander - may need to split if expansion violated
803                self.check_and_split_expander(eu);
804            } else {
805                // Different expanders - update boundary
806                self.update_expander_edges(eu);
807                self.update_expander_edges(ev);
808
809                // Update mirror cut
810                self.update_mirror_cut_between(eu, ev);
811            }
812        }
813
814        // Propagate changes up the hierarchy
815        self.propagate_updates(&[u, v]);
816
817        // Update global min cut
818        self.update_global_min_cut();
819    }
820
821    /// Update edges for an expander
822    fn update_expander_edges(&mut self, exp_id: u64) {
823        // First collect vertices
824        let vertices = match self.expanders.get(&exp_id) {
825            Some(e) => e.vertices.clone(),
826            None => return,
827        };
828
829        let mut internal = Vec::new();
830        let mut boundary = Vec::new();
831        let mut volume = 0;
832
833        for &v in &vertices {
834            let neighbors = self.neighbors(v);
835            volume += neighbors.len();
836
837            for (neighbor, _) in neighbors {
838                if vertices.contains(&neighbor) {
839                    if v < neighbor {
840                        internal.push((v, neighbor));
841                    }
842                } else {
843                    boundary.push((v, neighbor));
844                }
845            }
846        }
847
848        // Now update the expander
849        if let Some(expander) = self.expanders.get_mut(&exp_id) {
850            expander.internal_edges = internal;
851            expander.boundary_edges = boundary;
852            expander.volume = volume;
853
854            let min_vol = volume.min(vertices.len() * 2);
855            expander.expansion_ratio = if min_vol > 0 {
856                expander.boundary_edges.len() as f64 / min_vol as f64
857            } else {
858                0.0
859            };
860        }
861    }
862
863    /// Try to add a new vertex to an existing expander
864    fn try_add_vertex_to_expander(&mut self, v: VertexId, exp_id: u64) {
865        // Check if adding v would violate expansion
866        if let Some(expander) = self.expanders.get(&exp_id) {
867            let new_volume = expander.volume + self.degree(v);
868            let new_boundary = self.count_boundary_with_vertex(exp_id, v);
869
870            let expansion = if new_volume > 0 {
871                new_boundary as f64 / new_volume as f64
872            } else {
873                0.0
874            };
875
876            if expansion >= self.config.phi * 0.5 {
877                // OK to add
878                if let Some(exp) = self.expanders.get_mut(&exp_id) {
879                    exp.vertices.insert(v);
880                    self.vertex_expander.insert(v, exp_id);
881                }
882            } else {
883                // Create new expander for this vertex
884                self.create_expander_for_new_vertices(&[v]);
885            }
886        }
887    }
888
889    /// Count boundary if we add a vertex to an expander
890    fn count_boundary_with_vertex(&self, exp_id: u64, v: VertexId) -> usize {
891        let expander = match self.expanders.get(&exp_id) {
892            Some(e) => e,
893            None => return 0,
894        };
895
896        let mut extended = expander.vertices.clone();
897        extended.insert(v);
898
899        let mut boundary = 0;
900        for &u in &extended {
901            for (neighbor, _) in self.neighbors(u) {
902                if !extended.contains(&neighbor) {
903                    boundary += 1;
904                }
905            }
906        }
907        boundary
908    }
909
910    /// Create a new expander for new vertices
911    fn create_expander_for_new_vertices(&mut self, vertices: &[VertexId]) {
912        let id = self.next_id;
913        self.next_id += 1;
914
915        let vertex_set: HashSet<_> = vertices.iter().copied().collect();
916        let mut expander = Expander::new(id, vertex_set);
917        self.compute_expander_properties(&mut expander);
918
919        for &v in vertices {
920            self.vertex_expander.insert(v, id);
921        }
922
923        self.expanders.insert(id, expander);
924
925        // Add to existing precluster if possible
926        self.assign_expander_to_precluster(id);
927    }
928
929    /// Assign a new expander to a precluster
930    fn assign_expander_to_precluster(&mut self, exp_id: u64) {
931        // Find adjacent precluster
932        for (pre_id, precluster) in &self.preclusters {
933            // Check if any expander in precluster is adjacent
934            for &other_exp in &precluster.expanders {
935                if self.expanders_adjacent(exp_id, other_exp) {
936                    if let Some(exp) = self.expanders.get_mut(&exp_id) {
937                        exp.precluster_id = Some(*pre_id);
938                    }
939                    return;
940                }
941            }
942        }
943
944        // Create new precluster if not adjacent to any
945        let id = self.next_id;
946        self.next_id += 1;
947
948        let mut precluster = Precluster::new(id);
949        if let Some(expander) = self.expanders.get_mut(&exp_id) {
950            precluster.add_expander(expander);
951            expander.precluster_id = Some(id);
952        }
953        self.compute_precluster_boundary(&mut precluster);
954        self.preclusters.insert(id, precluster);
955
956        // Assign to cluster
957        self.assign_precluster_to_cluster(id);
958    }
959
960    /// Assign a new precluster to a cluster
961    fn assign_precluster_to_cluster(&mut self, pre_id: u64) {
962        // Find adjacent cluster
963        for (cluster_id, cluster) in &self.clusters {
964            for &other_pre in &cluster.preclusters {
965                if self.preclusters_adjacent(pre_id, other_pre) {
966                    if let Some(pre) = self.preclusters.get_mut(&pre_id) {
967                        pre.cluster_id = Some(*cluster_id);
968                    }
969                    return;
970                }
971            }
972        }
973
974        // Create new cluster
975        let id = self.next_id;
976        self.next_id += 1;
977
978        let mut cluster = HierarchyCluster::new(id);
979        if let Some(precluster) = self.preclusters.get_mut(&pre_id) {
980            cluster.add_precluster(precluster);
981            precluster.cluster_id = Some(id);
982        }
983        self.compute_cluster_boundary(&mut cluster);
984        self.clusters.insert(id, cluster);
985    }
986
987    /// Check if an expander needs splitting after edge deletion
988    fn check_and_split_expander(&mut self, exp_id: u64) {
989        if let Some(expander) = self.expanders.get(&exp_id) {
990            // Check if expansion is now violated
991            if expander.expansion_ratio < self.config.phi * 0.3 {
992                // Need to potentially split - for now just update
993                self.update_expander_edges(exp_id);
994            }
995        }
996    }
997
998    /// Update mirror cut between two expanders
999    fn update_mirror_cut_between(&mut self, exp1: u64, exp2: u64) {
1000        let (cut_value, cut_edges) = self.compute_expander_cut(exp1, exp2);
1001
1002        // Find cluster containing these expanders
1003        let cluster_id = self
1004            .expanders
1005            .get(&exp1)
1006            .and_then(|e| e.precluster_id)
1007            .and_then(|pid| self.preclusters.get(&pid))
1008            .and_then(|p| p.cluster_id);
1009
1010        if let Some(cid) = cluster_id {
1011            if let Some(cluster) = self.clusters.get_mut(&cid) {
1012                // Update or add mirror cut
1013                let found = cluster.mirror_cuts.iter_mut().find(|m| {
1014                    (m.source_expander == exp1 && m.target_expander == exp2)
1015                        || (m.source_expander == exp2 && m.target_expander == exp1)
1016                });
1017
1018                if let Some(mirror) = found {
1019                    mirror.cut_value = cut_value;
1020                    mirror.cut_edges = cut_edges;
1021                } else if !cut_edges.is_empty() {
1022                    cluster.mirror_cuts.push(MirrorCut {
1023                        source_expander: exp1,
1024                        target_expander: exp2,
1025                        cut_value,
1026                        cut_edges,
1027                        certified: false,
1028                    });
1029                }
1030
1031                // Update internal min cut
1032                if let Some(min_mirror) = cluster
1033                    .mirror_cuts
1034                    .iter()
1035                    .map(|m| m.cut_value)
1036                    .min_by(|a, b| a.partial_cmp(b).unwrap())
1037                {
1038                    cluster.internal_min_cut = min_mirror;
1039                }
1040            }
1041        }
1042    }
1043
1044    /// Propagate updates up through the hierarchy
1045    fn propagate_updates(&mut self, vertices: &[VertexId]) {
1046        // Collect affected expanders
1047        let mut affected_expanders = HashSet::new();
1048        for &v in vertices {
1049            if let Some(&exp_id) = self.vertex_expander.get(&v) {
1050                affected_expanders.insert(exp_id);
1051            }
1052        }
1053
1054        // Collect affected preclusters
1055        let mut affected_preclusters = HashSet::new();
1056        for &exp_id in &affected_expanders {
1057            if let Some(expander) = self.expanders.get(&exp_id) {
1058                if let Some(pre_id) = expander.precluster_id {
1059                    affected_preclusters.insert(pre_id);
1060                }
1061            }
1062        }
1063
1064        // Update precluster boundaries
1065        for &pre_id in &affected_preclusters {
1066            if let Some(precluster) = self.preclusters.get_mut(&pre_id) {
1067                // Recollect vertices from expanders
1068                precluster.vertices.clear();
1069                precluster.volume = 0;
1070
1071                for &exp_id in &precluster.expanders {
1072                    if let Some(expander) = self.expanders.get(&exp_id) {
1073                        precluster.vertices.extend(&expander.vertices);
1074                        precluster.volume += expander.volume;
1075                    }
1076                }
1077            }
1078
1079            // Recompute boundary
1080            let precluster = self.preclusters.get(&pre_id).cloned();
1081            if let Some(mut pre) = precluster {
1082                self.compute_precluster_boundary(&mut pre);
1083                self.preclusters.insert(pre_id, pre);
1084            }
1085        }
1086
1087        // Collect affected clusters
1088        let mut affected_clusters = HashSet::new();
1089        for &pre_id in &affected_preclusters {
1090            if let Some(precluster) = self.preclusters.get(&pre_id) {
1091                if let Some(cluster_id) = precluster.cluster_id {
1092                    affected_clusters.insert(cluster_id);
1093                }
1094            }
1095        }
1096
1097        // Update cluster boundaries
1098        for &cluster_id in &affected_clusters {
1099            if let Some(cluster) = self.clusters.get_mut(&cluster_id) {
1100                // Recollect vertices from preclusters
1101                cluster.vertices.clear();
1102
1103                for &pre_id in &cluster.preclusters {
1104                    if let Some(precluster) = self.preclusters.get(&pre_id) {
1105                        cluster.vertices.extend(&precluster.vertices);
1106                    }
1107                }
1108            }
1109
1110            // Recompute boundary
1111            let cluster = self.clusters.get(&cluster_id).cloned();
1112            if let Some(mut c) = cluster {
1113                self.compute_cluster_boundary(&mut c);
1114                self.clusters.insert(cluster_id, c);
1115            }
1116        }
1117    }
1118
1119    // === Mirror Cut Certification ===
1120
1121    /// Certify mirror cuts using LocalKCut verification
1122    ///
1123    /// This verifies that the tracked mirror cuts are accurate by
1124    /// running LocalKCut queries on the expander boundaries.
1125    pub fn certify_mirror_cuts(&mut self) {
1126        use crate::localkcut::deterministic::DeterministicLocalKCut;
1127
1128        // First, collect all the information we need from immutable self
1129        let mut certifications: Vec<(u64, usize, bool)> = Vec::new();
1130
1131        for (cluster_id, cluster) in &self.clusters {
1132            for (mirror_idx, mirror) in cluster.mirror_cuts.iter().enumerate() {
1133                // Get expander vertices
1134                let exp1 = match self.expanders.get(&mirror.source_expander) {
1135                    Some(e) => e.vertices.clone(),
1136                    None => continue,
1137                };
1138                let exp2 = match self.expanders.get(&mirror.target_expander) {
1139                    Some(e) => e.vertices.clone(),
1140                    None => continue,
1141                };
1142
1143                // Build LocalKCut for the combined subgraph
1144                let combined: HashSet<_> = exp1.union(&exp2).copied().collect();
1145                let lambda_max = (mirror.cut_value.ceil() as u64).max(1) * 2;
1146                let volume = combined.len() * 10; // Generous volume bound
1147
1148                let mut lkc = DeterministicLocalKCut::new(lambda_max, volume, 2);
1149
1150                // Add edges in the combined region
1151                for &u in &combined {
1152                    for (v, w) in self.neighbors(u) {
1153                        if combined.contains(&v) && u < v {
1154                            lkc.insert_edge(u, v, w);
1155                        }
1156                    }
1157                }
1158
1159                // Query from boundary vertices
1160                let mut min_verified_cut = f64::INFINITY;
1161                let boundary_verts: Vec<_> = mirror.cut_edges.iter().map(|(u, _)| *u).collect();
1162
1163                for v in boundary_verts {
1164                    let cuts = lkc.query(v);
1165                    for cut in cuts {
1166                        // Check if this cut separates exp1 from exp2
1167                        let cut_separates = {
1168                            let in_exp1 = cut.vertices.iter().any(|&u| exp1.contains(&u));
1169                            let in_exp2 = cut.vertices.iter().any(|&u| exp2.contains(&u));
1170                            let not_in_exp1 = exp1.iter().any(|&u| !cut.vertices.contains(&u));
1171                            let not_in_exp2 = exp2.iter().any(|&u| !cut.vertices.contains(&u));
1172                            (in_exp1 && not_in_exp2) || (in_exp2 && not_in_exp1)
1173                        };
1174
1175                        if cut_separates {
1176                            min_verified_cut = min_verified_cut.min(cut.cut_value);
1177                        }
1178                    }
1179                }
1180
1181                // Determine certification status
1182                let certified = if min_verified_cut < f64::INFINITY {
1183                    // LocalKCut found a separating cut - verify it matches
1184                    let diff = (mirror.cut_value - min_verified_cut).abs();
1185                    diff < 0.001 || min_verified_cut <= mirror.cut_value
1186                } else {
1187                    // No separating cut found by LocalKCut - trust the boundary computation
1188                    true
1189                };
1190
1191                certifications.push((*cluster_id, mirror_idx, certified));
1192            }
1193        }
1194
1195        // Now apply the certifications with mutable access
1196        for (cluster_id, mirror_idx, certified) in certifications {
1197            if let Some(cluster) = self.clusters.get_mut(&cluster_id) {
1198                if let Some(mirror) = cluster.mirror_cuts.get_mut(mirror_idx) {
1199                    mirror.certified = certified;
1200                }
1201            }
1202        }
1203    }
1204
1205    /// Get number of certified mirror cuts
1206    pub fn num_certified_mirror_cuts(&self) -> usize {
1207        self.clusters
1208            .values()
1209            .flat_map(|c| &c.mirror_cuts)
1210            .filter(|m| m.certified)
1211            .count()
1212    }
1213
1214    /// Get number of total mirror cuts
1215    pub fn num_mirror_cuts(&self) -> usize {
1216        self.clusters.values().map(|c| c.mirror_cuts.len()).sum()
1217    }
1218
1219    // === Getters ===
1220
1221    /// Get expander containing vertex
1222    pub fn get_vertex_expander(&self, v: VertexId) -> Option<&Expander> {
1223        self.vertex_expander
1224            .get(&v)
1225            .and_then(|&id| self.expanders.get(&id))
1226    }
1227
1228    /// Get all expanders
1229    pub fn get_expanders(&self) -> &HashMap<u64, Expander> {
1230        &self.expanders
1231    }
1232
1233    /// Get all preclusters
1234    pub fn get_preclusters(&self) -> &HashMap<u64, Precluster> {
1235        &self.preclusters
1236    }
1237
1238    /// Get all clusters
1239    pub fn get_clusters(&self) -> &HashMap<u64, HierarchyCluster> {
1240        &self.clusters
1241    }
1242
1243    /// Get hierarchy statistics
1244    pub fn stats(&self) -> HierarchyStats {
1245        HierarchyStats {
1246            num_expanders: self.expanders.len(),
1247            num_preclusters: self.preclusters.len(),
1248            num_clusters: self.clusters.len(),
1249            num_vertices: self.adjacency.len(),
1250            num_edges: self.adjacency.values().map(|n| n.len()).sum::<usize>() / 2,
1251            global_min_cut: self.global_min_cut,
1252            avg_expander_size: if self.expanders.is_empty() {
1253                0.0
1254            } else {
1255                self.expanders.values().map(|e| e.size()).sum::<usize>() as f64
1256                    / self.expanders.len() as f64
1257            },
1258        }
1259    }
1260}
1261
1262impl Default for ThreeLevelHierarchy {
1263    fn default() -> Self {
1264        Self::with_defaults()
1265    }
1266}
1267
1268/// Statistics about the hierarchy
1269#[derive(Debug, Clone)]
1270pub struct HierarchyStats {
1271    /// Number of expanders (level 0)
1272    pub num_expanders: usize,
1273    /// Number of preclusters (level 1)
1274    pub num_preclusters: usize,
1275    /// Number of clusters (level 2)
1276    pub num_clusters: usize,
1277    /// Total vertices
1278    pub num_vertices: usize,
1279    /// Total edges
1280    pub num_edges: usize,
1281    /// Global minimum cut estimate
1282    pub global_min_cut: f64,
1283    /// Average expander size
1284    pub avg_expander_size: f64,
1285}
1286
1287#[cfg(test)]
1288mod tests {
1289    use super::*;
1290
1291    fn build_path(h: &mut ThreeLevelHierarchy, n: usize) {
1292        for i in 0..n - 1 {
1293            h.insert_edge(i as u64, (i + 1) as u64, 1.0);
1294        }
1295    }
1296
1297    fn build_clique(h: &mut ThreeLevelHierarchy, vertices: &[u64]) {
1298        for i in 0..vertices.len() {
1299            for j in i + 1..vertices.len() {
1300                h.insert_edge(vertices[i], vertices[j], 1.0);
1301            }
1302        }
1303    }
1304
1305    #[test]
1306    fn test_hierarchy_empty() {
1307        let mut h = ThreeLevelHierarchy::with_defaults();
1308        h.build();
1309        assert_eq!(h.expanders.len(), 0);
1310        assert_eq!(h.preclusters.len(), 0);
1311        assert_eq!(h.clusters.len(), 0);
1312    }
1313
1314    #[test]
1315    fn test_hierarchy_path() {
1316        let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
1317            min_expander_size: 2,
1318            max_expander_size: 5,
1319            ..Default::default()
1320        });
1321        build_path(&mut h, 10);
1322        h.build();
1323
1324        assert!(h.expanders.len() >= 1);
1325        assert!(h.preclusters.len() >= 1);
1326        assert!(h.clusters.len() >= 1);
1327
1328        let stats = h.stats();
1329        assert_eq!(stats.num_vertices, 10);
1330        assert_eq!(stats.num_edges, 9);
1331    }
1332
1333    #[test]
1334    fn test_hierarchy_clique() {
1335        let mut h = ThreeLevelHierarchy::with_defaults();
1336        build_clique(&mut h, &[1, 2, 3, 4, 5]);
1337        h.build();
1338
1339        // Clique should be a single expander
1340        assert!(h.expanders.len() >= 1);
1341
1342        // Check vertex assignment
1343        for v in 1..=5 {
1344            assert!(h.get_vertex_expander(v).is_some());
1345        }
1346    }
1347
1348    #[test]
1349    fn test_hierarchy_two_cliques() {
1350        let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
1351            min_expander_size: 2,
1352            ..Default::default()
1353        });
1354
1355        // Two cliques connected by a single edge
1356        build_clique(&mut h, &[1, 2, 3, 4]);
1357        build_clique(&mut h, &[5, 6, 7, 8]);
1358        h.insert_edge(4, 5, 1.0); // Bridge
1359
1360        h.build();
1361
1362        // Should have at least 2 expanders
1363        assert!(h.expanders.len() >= 1);
1364
1365        // Global min cut should be 1 (the bridge)
1366        assert!(h.global_min_cut <= 2.0);
1367    }
1368
1369    #[test]
1370    fn test_mirror_cuts() {
1371        let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
1372            track_mirror_cuts: true,
1373            min_expander_size: 2,
1374            ..Default::default()
1375        });
1376
1377        build_clique(&mut h, &[1, 2, 3]);
1378        build_clique(&mut h, &[4, 5, 6]);
1379        h.insert_edge(3, 4, 2.0);
1380        h.insert_edge(2, 5, 1.0);
1381
1382        h.build();
1383
1384        // Should track mirror cuts if multiple expanders
1385        let stats = h.stats();
1386        assert!(stats.num_expanders >= 1);
1387    }
1388
1389    #[test]
1390    fn test_expander_properties() {
1391        let mut h = ThreeLevelHierarchy::with_defaults();
1392        build_clique(&mut h, &[1, 2, 3, 4]);
1393        h.build();
1394
1395        for expander in h.expanders.values() {
1396            // Clique should have good expansion
1397            assert!(expander.size() > 0);
1398            assert!(expander.volume > 0);
1399        }
1400    }
1401
1402    #[test]
1403    fn test_stats() {
1404        let mut h = ThreeLevelHierarchy::with_defaults();
1405        build_path(&mut h, 5);
1406        h.build();
1407
1408        let stats = h.stats();
1409        assert_eq!(stats.num_vertices, 5);
1410        assert_eq!(stats.num_edges, 4);
1411        assert!(stats.avg_expander_size > 0.0);
1412    }
1413
1414    #[test]
1415    fn test_incremental_insert() {
1416        let mut h = ThreeLevelHierarchy::with_defaults();
1417        build_clique(&mut h, &[1, 2, 3, 4]);
1418        h.build();
1419
1420        let initial_expanders = h.expanders.len();
1421
1422        // Insert an edge to a new vertex
1423        h.handle_edge_insert(4, 5, 1.0);
1424
1425        // Should have updated the hierarchy
1426        assert!(h.expanders.len() >= initial_expanders);
1427
1428        let stats = h.stats();
1429        assert!(stats.num_vertices >= 5);
1430        assert!(stats.num_edges >= 7);
1431    }
1432
1433    #[test]
1434    fn test_incremental_delete() {
1435        let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
1436            min_expander_size: 2,
1437            ..Default::default()
1438        });
1439
1440        build_clique(&mut h, &[1, 2, 3]);
1441        build_clique(&mut h, &[4, 5, 6]);
1442        h.insert_edge(3, 4, 1.0); // Bridge
1443        h.build();
1444
1445        let initial_min_cut = h.global_min_cut;
1446
1447        // Delete the bridge
1448        h.handle_edge_delete(3, 4);
1449
1450        // Min cut should change (possibly to 0 if disconnected)
1451        assert!(h.global_min_cut <= initial_min_cut || h.global_min_cut.is_infinite());
1452    }
1453
1454    #[test]
1455    fn test_incremental_within_expander() {
1456        let mut h = ThreeLevelHierarchy::with_defaults();
1457        build_clique(&mut h, &[1, 2, 3, 4]);
1458        h.build();
1459
1460        let initial_expanders = h.expanders.len();
1461
1462        // Insert edge within existing vertices
1463        h.handle_edge_insert(1, 4, 2.0);
1464
1465        // Should still have same expanders
1466        assert_eq!(h.expanders.len(), initial_expanders);
1467    }
1468
1469    #[test]
1470    fn test_propagate_updates() {
1471        let mut h = ThreeLevelHierarchy::new(HierarchyConfig {
1472            min_expander_size: 2,
1473            ..Default::default()
1474        });
1475
1476        build_clique(&mut h, &[1, 2, 3]);
1477        h.build();
1478
1479        // Add edge to new vertex
1480        h.handle_edge_insert(3, 4, 1.0);
1481        h.handle_edge_insert(4, 5, 1.0);
1482
1483        // Hierarchy should have been updated
1484        assert!(h.expanders.len() >= 1);
1485        assert!(h.preclusters.len() >= 1);
1486        assert!(h.clusters.len() >= 1);
1487    }
1488}