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