ruvector_mincut/fragment/
mod.rs

1//! Fragmenting Algorithm for Dynamic Minimum Cut
2//!
3//! Handles graph fragmentation after edge deletions.
4//! Recursively processes disconnected components.
5
6use crate::connectivity::DynamicConnectivity;
7use crate::graph::{DynamicGraph, EdgeId, VertexId};
8use crate::instance::WitnessHandle;
9use roaring::RoaringBitmap;
10use std::collections::{HashMap, HashSet, VecDeque};
11use std::sync::Arc;
12
13/// A fragment (connected component) of the graph
14#[derive(Debug, Clone)]
15pub struct Fragment {
16    /// Fragment ID
17    pub id: u64,
18    /// Vertices in this fragment
19    pub vertices: HashSet<VertexId>,
20    /// Edges within this fragment
21    pub edges: Vec<EdgeId>,
22    /// Minimum cut value within this fragment
23    pub min_cut: u64,
24    /// Witness for the minimum cut
25    pub witness: Option<WitnessHandle>,
26}
27
28/// Result of fragmenting algorithm
29#[derive(Debug, Clone)]
30pub enum FragmentResult {
31    /// Graph is connected, single fragment
32    Connected {
33        /// Minimum cut value
34        min_cut: u64,
35        /// Witness for the minimum cut
36        witness: WitnessHandle,
37    },
38    /// Graph has multiple fragments (disconnected)
39    Disconnected {
40        /// All fragments in the graph
41        fragments: Vec<Fragment>,
42        /// Global min cut is 0 when disconnected
43        global_min_cut: u64,
44    },
45}
46
47/// Fragmenting algorithm for handling graph decomposition
48pub struct FragmentingAlgorithm {
49    /// Reference to the graph
50    graph: Arc<DynamicGraph>,
51    /// Current fragments
52    fragments: Vec<Fragment>,
53    /// Vertex to fragment mapping
54    vertex_fragment: HashMap<VertexId, u64>,
55    /// Next fragment ID
56    next_id: u64,
57    /// Connectivity checker
58    connectivity: DynamicConnectivity,
59}
60
61impl FragmentingAlgorithm {
62    /// Create a new fragmenting algorithm instance
63    pub fn new(graph: Arc<DynamicGraph>) -> Self {
64        let mut alg = Self {
65            graph,
66            fragments: Vec::new(),
67            vertex_fragment: HashMap::new(),
68            next_id: 0,
69            connectivity: DynamicConnectivity::new(),
70        };
71        alg.rebuild();
72        alg
73    }
74
75    /// Rebuild fragment structure from scratch
76    pub fn rebuild(&mut self) {
77        self.fragments.clear();
78        self.vertex_fragment.clear();
79        self.next_id = 0;
80        self.connectivity = DynamicConnectivity::new();
81
82        // Build connectivity structure
83        for edge in self.graph.edges() {
84            self.connectivity.insert_edge(edge.source, edge.target);
85        }
86
87        // Find connected components using BFS
88        let components = self.find_connected_components();
89
90        // Create fragments for each component
91        for component in components {
92            self.create_fragment(component);
93        }
94    }
95
96    /// Find all connected components
97    fn find_connected_components(&self) -> Vec<HashSet<VertexId>> {
98        let mut visited = HashSet::new();
99        let mut components = Vec::new();
100
101        for vertex in self.graph.vertices() {
102            if !visited.contains(&vertex) {
103                let component = self.bfs_component(vertex, &mut visited);
104                if !component.is_empty() {
105                    components.push(component);
106                }
107            }
108        }
109
110        components
111    }
112
113    /// BFS to find a single connected component
114    fn bfs_component(&self, start: VertexId, visited: &mut HashSet<VertexId>) -> HashSet<VertexId> {
115        let mut component = HashSet::new();
116        let mut queue = VecDeque::new();
117
118        queue.push_back(start);
119        visited.insert(start);
120        component.insert(start);
121
122        while let Some(current) = queue.pop_front() {
123            for (neighbor, _edge_id) in self.graph.neighbors(current) {
124                if visited.insert(neighbor) {
125                    queue.push_back(neighbor);
126                    component.insert(neighbor);
127                }
128            }
129        }
130
131        component
132    }
133
134    /// Create a fragment from a set of vertices
135    fn create_fragment(&mut self, vertices: HashSet<VertexId>) {
136        let fragment_id = self.next_id;
137        self.next_id += 1;
138
139        // Find edges within this fragment
140        let edges: Vec<EdgeId> = self
141            .graph
142            .edges()
143            .into_iter()
144            .filter(|e| vertices.contains(&e.source) && vertices.contains(&e.target))
145            .map(|e| e.id)
146            .collect();
147
148        // Compute minimum cut within fragment
149        let (min_cut, witness) = self.compute_fragment_min_cut(&vertices);
150
151        let fragment = Fragment {
152            id: fragment_id,
153            vertices: vertices.clone(),
154            edges,
155            min_cut,
156            witness,
157        };
158
159        // Update vertex mapping
160        for &v in &vertices {
161            self.vertex_fragment.insert(v, fragment_id);
162        }
163
164        self.fragments.push(fragment);
165    }
166
167    /// Compute minimum cut within a fragment
168    fn compute_fragment_min_cut(
169        &self,
170        vertices: &HashSet<VertexId>,
171    ) -> (u64, Option<WitnessHandle>) {
172        if vertices.len() <= 1 {
173            return (u64::MAX, None);
174        }
175
176        // For small fragments, use brute force
177        if vertices.len() < 20 {
178            return self.brute_force_min_cut(vertices);
179        }
180
181        // For larger fragments, use heuristic
182        self.heuristic_min_cut(vertices)
183    }
184
185    /// Brute force minimum cut for small fragments
186    fn brute_force_min_cut(&self, vertices: &HashSet<VertexId>) -> (u64, Option<WitnessHandle>) {
187        let vertex_vec: Vec<_> = vertices.iter().copied().collect();
188        let n = vertex_vec.len();
189
190        if n >= 20 {
191            return (u64::MAX, None);
192        }
193
194        let mut min_cut = u64::MAX;
195        let mut best_set = HashSet::new();
196
197        // Enumerate all non-empty proper subsets
198        let max_mask = 1u64 << n;
199        for mask in 1..max_mask - 1 {
200            let mut subset: HashSet<VertexId> = HashSet::new();
201            for (i, &v) in vertex_vec.iter().enumerate() {
202                if mask & (1 << i) != 0 {
203                    subset.insert(v);
204                }
205            }
206
207            // Check if subset is connected within fragment
208            if !self.is_connected_within(&subset, vertices) {
209                continue;
210            }
211
212            // Compute boundary within fragment
213            let boundary = self.compute_boundary_within(&subset, vertices);
214
215            if boundary < min_cut {
216                min_cut = boundary;
217                best_set = subset;
218            }
219        }
220
221        if min_cut == u64::MAX || best_set.is_empty() {
222            return (u64::MAX, None);
223        }
224
225        // Create witness
226        let membership: RoaringBitmap = best_set.iter().map(|&v| v as u32).collect();
227        let seed = *best_set.iter().next().unwrap();
228        let witness = WitnessHandle::new(seed, membership, min_cut);
229
230        (min_cut, Some(witness))
231    }
232
233    /// Heuristic minimum cut for larger fragments
234    fn heuristic_min_cut(&self, vertices: &HashSet<VertexId>) -> (u64, Option<WitnessHandle>) {
235        // Use minimum degree as upper bound
236        let mut min_degree = u64::MAX;
237        let mut min_vertex = None;
238
239        for &v in vertices {
240            let degree = self
241                .graph
242                .neighbors(v)
243                .into_iter()
244                .filter(|(n, _)| vertices.contains(n))
245                .count() as u64;
246
247            if degree < min_degree {
248                min_degree = degree;
249                min_vertex = Some(v);
250            }
251        }
252
253        if let Some(v) = min_vertex {
254            let mut membership = RoaringBitmap::new();
255            membership.insert(v as u32);
256            let witness = WitnessHandle::new(v, membership, min_degree);
257            return (min_degree, Some(witness));
258        }
259
260        (u64::MAX, None)
261    }
262
263    /// Check if subset is connected within fragment
264    fn is_connected_within(
265        &self,
266        subset: &HashSet<VertexId>,
267        fragment: &HashSet<VertexId>,
268    ) -> bool {
269        if subset.is_empty() {
270            return true;
271        }
272
273        let start = *subset.iter().next().unwrap();
274        let mut visited = HashSet::new();
275        let mut queue = VecDeque::new();
276
277        queue.push_back(start);
278        visited.insert(start);
279
280        while let Some(current) = queue.pop_front() {
281            for (neighbor, _edge_id) in self.graph.neighbors(current) {
282                if subset.contains(&neighbor)
283                    && fragment.contains(&neighbor)
284                    && visited.insert(neighbor)
285                {
286                    queue.push_back(neighbor);
287                }
288            }
289        }
290
291        visited.len() == subset.len()
292    }
293
294    /// Compute boundary of subset within fragment
295    fn compute_boundary_within(
296        &self,
297        subset: &HashSet<VertexId>,
298        fragment: &HashSet<VertexId>,
299    ) -> u64 {
300        let mut boundary = 0u64;
301
302        for edge in self.graph.edges().into_iter() {
303            // Only count edges within the fragment
304            if !fragment.contains(&edge.source) || !fragment.contains(&edge.target) {
305                continue;
306            }
307
308            let src_in = subset.contains(&edge.source);
309            let tgt_in = subset.contains(&edge.target);
310
311            if src_in != tgt_in {
312                boundary += 1;
313            }
314        }
315
316        boundary
317    }
318
319    /// Handle edge insertion
320    pub fn insert_edge(&mut self, _edge_id: EdgeId, u: VertexId, v: VertexId) {
321        self.connectivity.insert_edge(u, v);
322
323        // Check if this merges two fragments
324        let u_frag = self.vertex_fragment.get(&u).copied();
325        let v_frag = self.vertex_fragment.get(&v).copied();
326
327        match (u_frag, v_frag) {
328            (Some(uf), Some(vf)) if uf != vf => {
329                // Merge fragments
330                self.merge_fragments(uf, vf);
331            }
332            (None, Some(_)) | (Some(_), None) | (None, None) => {
333                // New vertex or edge - rebuild
334                self.rebuild();
335            }
336            _ => {
337                // Same fragment - update min cut
338                self.update_fragment_containing(u);
339            }
340        }
341    }
342
343    /// Handle edge deletion
344    pub fn delete_edge(&mut self, _edge_id: EdgeId, u: VertexId, v: VertexId) {
345        self.connectivity.delete_edge(u, v);
346
347        // Check if this splits a fragment
348        if !self.connectivity.connected(u, v) {
349            // Fragment split - rebuild
350            self.rebuild();
351        } else {
352            // Same fragment - update min cut
353            self.update_fragment_containing(u);
354        }
355    }
356
357    /// Merge two fragments
358    fn merge_fragments(&mut self, _frag1_id: u64, _frag2_id: u64) {
359        // Simple approach: rebuild
360        // Optimization: could merge in place
361        self.rebuild();
362    }
363
364    /// Update fragment containing vertex
365    fn update_fragment_containing(&mut self, v: VertexId) {
366        if let Some(&frag_id) = self.vertex_fragment.get(&v) {
367            // Find the fragment and get its vertices
368            let vertices = self
369                .fragments
370                .iter()
371                .find(|f| f.id == frag_id)
372                .map(|f| f.vertices.clone());
373
374            if let Some(vertices) = vertices {
375                // Compute min cut outside of borrow
376                let (min_cut, witness) = self.compute_fragment_min_cut(&vertices);
377
378                // Now update the fragment
379                if let Some(frag) = self.fragments.iter_mut().find(|f| f.id == frag_id) {
380                    frag.min_cut = min_cut;
381                    frag.witness = witness;
382                }
383            }
384        }
385    }
386
387    /// Query the current result
388    pub fn query(&self) -> FragmentResult {
389        if self.fragments.len() <= 1 {
390            if let Some(frag) = self.fragments.first() {
391                if let Some(ref witness) = frag.witness {
392                    return FragmentResult::Connected {
393                        min_cut: frag.min_cut,
394                        witness: witness.clone(),
395                    };
396                }
397            }
398            // Empty or single vertex graph
399            let mut membership = RoaringBitmap::new();
400            membership.insert(0);
401            return FragmentResult::Connected {
402                min_cut: u64::MAX,
403                witness: WitnessHandle::new(0, membership, u64::MAX),
404            };
405        }
406
407        FragmentResult::Disconnected {
408            fragments: self.fragments.clone(),
409            global_min_cut: 0,
410        }
411    }
412
413    /// Get number of fragments
414    pub fn num_fragments(&self) -> usize {
415        self.fragments.len()
416    }
417
418    /// Is graph connected?
419    pub fn is_connected(&self) -> bool {
420        self.fragments.len() <= 1
421    }
422}
423
424#[cfg(test)]
425mod tests {
426    use super::*;
427
428    #[test]
429    fn test_empty_graph() {
430        let graph = Arc::new(DynamicGraph::new());
431        let alg = FragmentingAlgorithm::new(graph);
432        assert_eq!(alg.num_fragments(), 0);
433    }
434
435    #[test]
436    fn test_connected_graph() {
437        let graph = Arc::new(DynamicGraph::new());
438        graph.insert_edge(0, 1, 1.0).unwrap();
439        graph.insert_edge(1, 2, 1.0).unwrap();
440
441        let alg = FragmentingAlgorithm::new(graph);
442        assert_eq!(alg.num_fragments(), 1);
443        assert!(alg.is_connected());
444    }
445
446    #[test]
447    fn test_disconnected_graph() {
448        let graph = Arc::new(DynamicGraph::new());
449        graph.insert_edge(0, 1, 1.0).unwrap();
450        graph.insert_edge(2, 3, 1.0).unwrap();
451
452        let alg = FragmentingAlgorithm::new(graph);
453        assert_eq!(alg.num_fragments(), 2);
454        assert!(!alg.is_connected());
455    }
456
457    #[test]
458    fn test_dynamic_split() {
459        let graph = Arc::new(DynamicGraph::new());
460        let _e1 = graph.insert_edge(0, 1, 1.0).unwrap();
461        let e2 = graph.insert_edge(1, 2, 1.0).unwrap();
462
463        let mut alg = FragmentingAlgorithm::new(Arc::clone(&graph));
464        assert!(alg.is_connected());
465
466        // Delete middle edge to split
467        graph.delete_edge(1, 2).unwrap();
468        alg.delete_edge(e2, 1, 2);
469
470        assert!(!alg.is_connected());
471        assert_eq!(alg.num_fragments(), 2);
472    }
473
474    #[test]
475    fn test_dynamic_merge() {
476        let graph = Arc::new(DynamicGraph::new());
477        graph.insert_edge(0, 1, 1.0).unwrap();
478        graph.insert_edge(2, 3, 1.0).unwrap();
479
480        let mut alg = FragmentingAlgorithm::new(Arc::clone(&graph));
481        assert!(!alg.is_connected());
482
483        // Add bridge edge
484        let bridge = graph.insert_edge(1, 2, 1.0).unwrap();
485        alg.insert_edge(bridge, 1, 2);
486
487        assert!(alg.is_connected());
488        assert_eq!(alg.num_fragments(), 1);
489    }
490
491    #[test]
492    fn test_query_connected() {
493        let graph = Arc::new(DynamicGraph::new());
494        graph.insert_edge(0, 1, 1.0).unwrap();
495        graph.insert_edge(1, 2, 1.0).unwrap();
496        graph.insert_edge(2, 0, 1.0).unwrap();
497
498        let alg = FragmentingAlgorithm::new(graph);
499
500        match alg.query() {
501            FragmentResult::Connected { min_cut, .. } => {
502                assert_eq!(min_cut, 2); // Cycle has min cut 2
503            }
504            _ => panic!("Expected connected result"),
505        }
506    }
507
508    #[test]
509    fn test_query_disconnected() {
510        let graph = Arc::new(DynamicGraph::new());
511        graph.insert_edge(0, 1, 1.0).unwrap();
512        graph.insert_edge(2, 3, 1.0).unwrap();
513
514        let alg = FragmentingAlgorithm::new(graph);
515
516        match alg.query() {
517            FragmentResult::Disconnected { global_min_cut, .. } => {
518                assert_eq!(global_min_cut, 0);
519            }
520            _ => panic!("Expected disconnected result"),
521        }
522    }
523}