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(
115        &self,
116        start: VertexId,
117        visited: &mut HashSet<VertexId>,
118    ) -> HashSet<VertexId> {
119        let mut component = HashSet::new();
120        let mut queue = VecDeque::new();
121
122        queue.push_back(start);
123        visited.insert(start);
124        component.insert(start);
125
126        while let Some(current) = queue.pop_front() {
127            for (neighbor, _edge_id) in self.graph.neighbors(current) {
128                if visited.insert(neighbor) {
129                    queue.push_back(neighbor);
130                    component.insert(neighbor);
131                }
132            }
133        }
134
135        component
136    }
137
138    /// Create a fragment from a set of vertices
139    fn create_fragment(&mut self, vertices: HashSet<VertexId>) {
140        let fragment_id = self.next_id;
141        self.next_id += 1;
142
143        // Find edges within this fragment
144        let edges: Vec<EdgeId> = self
145            .graph
146            .edges()
147            .into_iter()
148            .filter(|e| vertices.contains(&e.source) && vertices.contains(&e.target))
149            .map(|e| e.id)
150            .collect();
151
152        // Compute minimum cut within fragment
153        let (min_cut, witness) = self.compute_fragment_min_cut(&vertices);
154
155        let fragment = Fragment {
156            id: fragment_id,
157            vertices: vertices.clone(),
158            edges,
159            min_cut,
160            witness,
161        };
162
163        // Update vertex mapping
164        for &v in &vertices {
165            self.vertex_fragment.insert(v, fragment_id);
166        }
167
168        self.fragments.push(fragment);
169    }
170
171    /// Compute minimum cut within a fragment
172    fn compute_fragment_min_cut(
173        &self,
174        vertices: &HashSet<VertexId>,
175    ) -> (u64, Option<WitnessHandle>) {
176        if vertices.len() <= 1 {
177            return (u64::MAX, None);
178        }
179
180        // For small fragments, use brute force
181        if vertices.len() < 20 {
182            return self.brute_force_min_cut(vertices);
183        }
184
185        // For larger fragments, use heuristic
186        self.heuristic_min_cut(vertices)
187    }
188
189    /// Brute force minimum cut for small fragments
190    fn brute_force_min_cut(&self, vertices: &HashSet<VertexId>) -> (u64, Option<WitnessHandle>) {
191        let vertex_vec: Vec<_> = vertices.iter().copied().collect();
192        let n = vertex_vec.len();
193
194        if n >= 20 {
195            return (u64::MAX, None);
196        }
197
198        let mut min_cut = u64::MAX;
199        let mut best_set = HashSet::new();
200
201        // Enumerate all non-empty proper subsets
202        let max_mask = 1u64 << n;
203        for mask in 1..max_mask - 1 {
204            let mut subset: HashSet<VertexId> = HashSet::new();
205            for (i, &v) in vertex_vec.iter().enumerate() {
206                if mask & (1 << i) != 0 {
207                    subset.insert(v);
208                }
209            }
210
211            // Check if subset is connected within fragment
212            if !self.is_connected_within(&subset, vertices) {
213                continue;
214            }
215
216            // Compute boundary within fragment
217            let boundary = self.compute_boundary_within(&subset, vertices);
218
219            if boundary < min_cut {
220                min_cut = boundary;
221                best_set = subset;
222            }
223        }
224
225        if min_cut == u64::MAX || best_set.is_empty() {
226            return (u64::MAX, None);
227        }
228
229        // Create witness
230        let membership: RoaringBitmap = best_set.iter().map(|&v| v as u32).collect();
231        let seed = *best_set.iter().next().unwrap();
232        let witness = WitnessHandle::new(seed, membership, min_cut);
233
234        (min_cut, Some(witness))
235    }
236
237    /// Heuristic minimum cut for larger fragments
238    fn heuristic_min_cut(&self, vertices: &HashSet<VertexId>) -> (u64, Option<WitnessHandle>) {
239        // Use minimum degree as upper bound
240        let mut min_degree = u64::MAX;
241        let mut min_vertex = None;
242
243        for &v in vertices {
244            let degree = self
245                .graph
246                .neighbors(v)
247                .into_iter()
248                .filter(|(n, _)| vertices.contains(n))
249                .count() as u64;
250
251            if degree < min_degree {
252                min_degree = degree;
253                min_vertex = Some(v);
254            }
255        }
256
257        if let Some(v) = min_vertex {
258            let mut membership = RoaringBitmap::new();
259            membership.insert(v as u32);
260            let witness = WitnessHandle::new(v, membership, min_degree);
261            return (min_degree, Some(witness));
262        }
263
264        (u64::MAX, None)
265    }
266
267    /// Check if subset is connected within fragment
268    fn is_connected_within(
269        &self,
270        subset: &HashSet<VertexId>,
271        fragment: &HashSet<VertexId>,
272    ) -> bool {
273        if subset.is_empty() {
274            return true;
275        }
276
277        let start = *subset.iter().next().unwrap();
278        let mut visited = HashSet::new();
279        let mut queue = VecDeque::new();
280
281        queue.push_back(start);
282        visited.insert(start);
283
284        while let Some(current) = queue.pop_front() {
285            for (neighbor, _edge_id) in self.graph.neighbors(current) {
286                if subset.contains(&neighbor)
287                    && fragment.contains(&neighbor)
288                    && visited.insert(neighbor)
289                {
290                    queue.push_back(neighbor);
291                }
292            }
293        }
294
295        visited.len() == subset.len()
296    }
297
298    /// Compute boundary of subset within fragment
299    fn compute_boundary_within(
300        &self,
301        subset: &HashSet<VertexId>,
302        fragment: &HashSet<VertexId>,
303    ) -> u64 {
304        let mut boundary = 0u64;
305
306        for edge in self.graph.edges().into_iter() {
307            // Only count edges within the fragment
308            if !fragment.contains(&edge.source) || !fragment.contains(&edge.target) {
309                continue;
310            }
311
312            let src_in = subset.contains(&edge.source);
313            let tgt_in = subset.contains(&edge.target);
314
315            if src_in != tgt_in {
316                boundary += 1;
317            }
318        }
319
320        boundary
321    }
322
323    /// Handle edge insertion
324    pub fn insert_edge(&mut self, _edge_id: EdgeId, u: VertexId, v: VertexId) {
325        self.connectivity.insert_edge(u, v);
326
327        // Check if this merges two fragments
328        let u_frag = self.vertex_fragment.get(&u).copied();
329        let v_frag = self.vertex_fragment.get(&v).copied();
330
331        match (u_frag, v_frag) {
332            (Some(uf), Some(vf)) if uf != vf => {
333                // Merge fragments
334                self.merge_fragments(uf, vf);
335            }
336            (None, Some(_)) | (Some(_), None) | (None, None) => {
337                // New vertex or edge - rebuild
338                self.rebuild();
339            }
340            _ => {
341                // Same fragment - update min cut
342                self.update_fragment_containing(u);
343            }
344        }
345    }
346
347    /// Handle edge deletion
348    pub fn delete_edge(&mut self, _edge_id: EdgeId, u: VertexId, v: VertexId) {
349        self.connectivity.delete_edge(u, v);
350
351        // Check if this splits a fragment
352        if !self.connectivity.connected(u, v) {
353            // Fragment split - rebuild
354            self.rebuild();
355        } else {
356            // Same fragment - update min cut
357            self.update_fragment_containing(u);
358        }
359    }
360
361    /// Merge two fragments
362    fn merge_fragments(&mut self, _frag1_id: u64, _frag2_id: u64) {
363        // Simple approach: rebuild
364        // Optimization: could merge in place
365        self.rebuild();
366    }
367
368    /// Update fragment containing vertex
369    fn update_fragment_containing(&mut self, v: VertexId) {
370        if let Some(&frag_id) = self.vertex_fragment.get(&v) {
371            // Find the fragment and get its vertices
372            let vertices = self
373                .fragments
374                .iter()
375                .find(|f| f.id == frag_id)
376                .map(|f| f.vertices.clone());
377
378            if let Some(vertices) = vertices {
379                // Compute min cut outside of borrow
380                let (min_cut, witness) = self.compute_fragment_min_cut(&vertices);
381
382                // Now update the fragment
383                if let Some(frag) = self.fragments.iter_mut().find(|f| f.id == frag_id) {
384                    frag.min_cut = min_cut;
385                    frag.witness = witness;
386                }
387            }
388        }
389    }
390
391    /// Query the current result
392    pub fn query(&self) -> FragmentResult {
393        if self.fragments.len() <= 1 {
394            if let Some(frag) = self.fragments.first() {
395                if let Some(ref witness) = frag.witness {
396                    return FragmentResult::Connected {
397                        min_cut: frag.min_cut,
398                        witness: witness.clone(),
399                    };
400                }
401            }
402            // Empty or single vertex graph
403            let mut membership = RoaringBitmap::new();
404            membership.insert(0);
405            return FragmentResult::Connected {
406                min_cut: u64::MAX,
407                witness: WitnessHandle::new(0, membership, u64::MAX),
408            };
409        }
410
411        FragmentResult::Disconnected {
412            fragments: self.fragments.clone(),
413            global_min_cut: 0,
414        }
415    }
416
417    /// Get number of fragments
418    pub fn num_fragments(&self) -> usize {
419        self.fragments.len()
420    }
421
422    /// Is graph connected?
423    pub fn is_connected(&self) -> bool {
424        self.fragments.len() <= 1
425    }
426}
427
428#[cfg(test)]
429mod tests {
430    use super::*;
431
432    #[test]
433    fn test_empty_graph() {
434        let graph = Arc::new(DynamicGraph::new());
435        let alg = FragmentingAlgorithm::new(graph);
436        assert_eq!(alg.num_fragments(), 0);
437    }
438
439    #[test]
440    fn test_connected_graph() {
441        let graph = Arc::new(DynamicGraph::new());
442        graph.insert_edge(0, 1, 1.0).unwrap();
443        graph.insert_edge(1, 2, 1.0).unwrap();
444
445        let alg = FragmentingAlgorithm::new(graph);
446        assert_eq!(alg.num_fragments(), 1);
447        assert!(alg.is_connected());
448    }
449
450    #[test]
451    fn test_disconnected_graph() {
452        let graph = Arc::new(DynamicGraph::new());
453        graph.insert_edge(0, 1, 1.0).unwrap();
454        graph.insert_edge(2, 3, 1.0).unwrap();
455
456        let alg = FragmentingAlgorithm::new(graph);
457        assert_eq!(alg.num_fragments(), 2);
458        assert!(!alg.is_connected());
459    }
460
461    #[test]
462    fn test_dynamic_split() {
463        let graph = Arc::new(DynamicGraph::new());
464        let _e1 = graph.insert_edge(0, 1, 1.0).unwrap();
465        let e2 = graph.insert_edge(1, 2, 1.0).unwrap();
466
467        let mut alg = FragmentingAlgorithm::new(Arc::clone(&graph));
468        assert!(alg.is_connected());
469
470        // Delete middle edge to split
471        graph.delete_edge(1, 2).unwrap();
472        alg.delete_edge(e2, 1, 2);
473
474        assert!(!alg.is_connected());
475        assert_eq!(alg.num_fragments(), 2);
476    }
477
478    #[test]
479    fn test_dynamic_merge() {
480        let graph = Arc::new(DynamicGraph::new());
481        graph.insert_edge(0, 1, 1.0).unwrap();
482        graph.insert_edge(2, 3, 1.0).unwrap();
483
484        let mut alg = FragmentingAlgorithm::new(Arc::clone(&graph));
485        assert!(!alg.is_connected());
486
487        // Add bridge edge
488        let bridge = graph.insert_edge(1, 2, 1.0).unwrap();
489        alg.insert_edge(bridge, 1, 2);
490
491        assert!(alg.is_connected());
492        assert_eq!(alg.num_fragments(), 1);
493    }
494
495    #[test]
496    fn test_query_connected() {
497        let graph = Arc::new(DynamicGraph::new());
498        graph.insert_edge(0, 1, 1.0).unwrap();
499        graph.insert_edge(1, 2, 1.0).unwrap();
500        graph.insert_edge(2, 0, 1.0).unwrap();
501
502        let alg = FragmentingAlgorithm::new(graph);
503
504        match alg.query() {
505            FragmentResult::Connected { min_cut, .. } => {
506                assert_eq!(min_cut, 2); // Cycle has min cut 2
507            }
508            _ => panic!("Expected connected result"),
509        }
510    }
511
512    #[test]
513    fn test_query_disconnected() {
514        let graph = Arc::new(DynamicGraph::new());
515        graph.insert_edge(0, 1, 1.0).unwrap();
516        graph.insert_edge(2, 3, 1.0).unwrap();
517
518        let alg = FragmentingAlgorithm::new(graph);
519
520        match alg.query() {
521            FragmentResult::Disconnected { global_min_cut, .. } => {
522                assert_eq!(global_min_cut, 0);
523            }
524            _ => panic!("Expected disconnected result"),
525        }
526    }
527}