ruvector_mincut/connectivity/mod.rs
1//! Dynamic Connectivity for minimum cut wrapper
2//!
3//! Hybrid implementation using Euler Tour Trees with union-find fallback.
4//! Provides O(log n) operations for insertions and queries.
5//!
6//! # Overview
7//!
8//! This module provides dynamic connectivity data structures:
9//!
10//! - [`DynamicConnectivity`]: Euler Tour Tree backend with union-find fallback
11//! - Edge insertions in O(log n) time
12//! - Edge deletions via full rebuild in O(m·α(n)) time
13//! - Connectivity queries in O(log n) time
14//!
15//! - [`PolylogConnectivity`]: Polylogarithmic worst-case connectivity (arXiv:2510.08297)
16//! - Edge insertions in O(log³ n) expected worst-case
17//! - Edge deletions in O(log³ n) expected worst-case
18//! - Connectivity queries in O(log n) worst-case
19//!
20//! # Implementation
21//!
22//! The primary backend uses Euler Tour Trees for O(log n) operations.
23//! Falls back to union-find rebuild for deletions until full ETT cut is implemented.
24//!
25//! The polylog backend uses a hierarchy of O(log n) levels with edge sparsification
26//! via low-congestion shortcuts for guaranteed worst-case bounds.
27
28pub mod polylog;
29pub mod cache_opt;
30
31use std::collections::{HashMap, HashSet};
32use crate::graph::VertexId;
33use crate::euler::EulerTourTree;
34
35/// Dynamic connectivity data structure with Euler Tour Tree backend
36///
37/// Maintains connected components of an undirected graph with support for
38/// edge insertions and deletions. Uses Euler Tour Trees for O(log n) operations
39/// with union-find fallback for robustness.
40///
41/// # Examples
42///
43/// ```ignore
44/// let mut dc = DynamicConnectivity::new();
45/// dc.add_vertex(0);
46/// dc.add_vertex(1);
47/// dc.add_vertex(2);
48///
49/// dc.insert_edge(0, 1);
50/// assert!(dc.connected(0, 1));
51/// assert!(!dc.connected(0, 2));
52///
53/// dc.insert_edge(1, 2);
54/// assert!(dc.is_connected()); // All vertices connected
55///
56/// dc.delete_edge(1, 2);
57/// assert!(!dc.connected(0, 2));
58/// ```
59#[derive(Debug, Clone)]
60pub struct DynamicConnectivity {
61 /// Union-find parent array
62 parent: HashMap<VertexId, VertexId>,
63
64 /// Union-find rank array for union by rank
65 rank: HashMap<VertexId, usize>,
66
67 /// Current edge set for rebuild on deletions
68 /// Edges normalized so smaller vertex is always first
69 edges: HashSet<(VertexId, VertexId)>,
70
71 /// Number of vertices
72 vertex_count: usize,
73
74 /// Number of connected components
75 component_count: usize,
76
77 /// Euler Tour Tree for O(log n) operations
78 ett: EulerTourTree,
79
80 /// Whether ETT is in sync with union-find
81 ett_synced: bool,
82}
83
84impl DynamicConnectivity {
85 /// Creates a new empty dynamic connectivity structure
86 ///
87 /// # Examples
88 ///
89 /// ```ignore
90 /// let dc = DynamicConnectivity::new();
91 /// assert_eq!(dc.component_count(), 0);
92 /// ```
93 pub fn new() -> Self {
94 Self {
95 parent: HashMap::new(),
96 rank: HashMap::new(),
97 edges: HashSet::new(),
98 vertex_count: 0,
99 component_count: 0,
100 ett: EulerTourTree::new(),
101 ett_synced: true,
102 }
103 }
104
105 /// Adds a vertex to the connectivity structure
106 ///
107 /// If the vertex already exists, this is a no-op.
108 /// Each new vertex starts in its own component.
109 ///
110 /// # Arguments
111 ///
112 /// * `v` - The vertex ID to add
113 ///
114 /// # Examples
115 ///
116 /// ```ignore
117 /// let mut dc = DynamicConnectivity::new();
118 /// dc.add_vertex(0);
119 /// assert_eq!(dc.component_count(), 1);
120 /// ```
121 pub fn add_vertex(&mut self, v: VertexId) {
122 if !self.parent.contains_key(&v) {
123 self.parent.insert(v, v);
124 self.rank.insert(v, 0);
125 self.vertex_count += 1;
126 self.component_count += 1;
127
128 // Add to Euler Tour Tree (O(log n))
129 let _ = self.ett.make_tree(v);
130 }
131 }
132
133 /// Inserts an edge between two vertices
134 ///
135 /// Automatically adds vertices if they don't exist.
136 /// If vertices are already connected, updates internal state but
137 /// doesn't change connectivity.
138 ///
139 /// # Arguments
140 ///
141 /// * `u` - First vertex
142 /// * `v` - Second vertex
143 ///
144 /// # Time Complexity
145 ///
146 /// O(log n) via Euler Tour Tree link operation
147 ///
148 /// # Examples
149 ///
150 /// ```ignore
151 /// let mut dc = DynamicConnectivity::new();
152 /// dc.insert_edge(0, 1);
153 /// assert!(dc.connected(0, 1));
154 /// ```
155 pub fn insert_edge(&mut self, u: VertexId, v: VertexId) {
156 // Add vertices if they don't exist
157 self.add_vertex(u);
158 self.add_vertex(v);
159
160 // Normalize edge (smaller vertex first)
161 let edge = if u < v { (u, v) } else { (v, u) };
162
163 // Add to edge set
164 if self.edges.insert(edge) {
165 // New edge - perform union
166 let root_u = self.find(u);
167 let root_v = self.find(v);
168
169 if root_u != root_v {
170 self.union(root_u, root_v);
171
172 // Link in Euler Tour Tree (O(log n))
173 let _ = self.ett.link(u, v);
174 }
175 }
176 }
177
178 /// Deletes an edge between two vertices
179 ///
180 /// Triggers a full rebuild of the data structure from the remaining edges.
181 /// The ETT is also rebuilt to maintain O(log n) queries.
182 ///
183 /// # Arguments
184 ///
185 /// * `u` - First vertex
186 /// * `v` - Second vertex
187 ///
188 /// # Time Complexity
189 ///
190 /// O(m·α(n)) where m is the number of edges (includes ETT rebuild)
191 ///
192 /// # Examples
193 ///
194 /// ```ignore
195 /// let mut dc = DynamicConnectivity::new();
196 /// dc.insert_edge(0, 1);
197 /// dc.delete_edge(0, 1);
198 /// assert!(!dc.connected(0, 1));
199 /// ```
200 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
201 // Normalize edge
202 let edge = if u < v { (u, v) } else { (v, u) };
203
204 // Remove from edge set
205 if self.edges.remove(&edge) {
206 // Mark ETT as out of sync
207 self.ett_synced = false;
208
209 // Rebuild the entire structure (including ETT)
210 self.rebuild();
211 }
212 }
213
214 /// Checks if the entire graph is connected (single component)
215 ///
216 /// # Returns
217 ///
218 /// `true` if all vertices are in a single connected component,
219 /// `false` otherwise
220 ///
221 /// # Time Complexity
222 ///
223 /// O(1)
224 ///
225 /// # Examples
226 ///
227 /// ```ignore
228 /// let mut dc = DynamicConnectivity::new();
229 /// dc.add_vertex(0);
230 /// dc.add_vertex(1);
231 /// assert!(!dc.is_connected());
232 ///
233 /// dc.insert_edge(0, 1);
234 /// assert!(dc.is_connected());
235 /// ```
236 pub fn is_connected(&self) -> bool {
237 self.component_count == 1
238 }
239
240 /// Checks if two vertices are in the same connected component
241 ///
242 /// # Arguments
243 ///
244 /// * `u` - First vertex
245 /// * `v` - Second vertex
246 ///
247 /// # Returns
248 ///
249 /// `true` if vertices are connected, `false` otherwise.
250 /// Returns `false` if either vertex doesn't exist.
251 ///
252 /// # Time Complexity
253 ///
254 /// O(α(n)) amortized via union-find with path compression
255 ///
256 /// # Examples
257 ///
258 /// ```ignore
259 /// let mut dc = DynamicConnectivity::new();
260 /// dc.insert_edge(0, 1);
261 /// dc.insert_edge(1, 2);
262 /// assert!(dc.connected(0, 2));
263 /// ```
264 pub fn connected(&mut self, u: VertexId, v: VertexId) -> bool {
265 if !self.parent.contains_key(&u) || !self.parent.contains_key(&v) {
266 return false;
267 }
268
269 // Use union-find with path compression (effectively O(1) amortized)
270 // ETT is maintained for future subtree query optimizations
271 self.find(u) == self.find(v)
272 }
273
274 /// Fast connectivity check using Euler Tour Tree (O(log n))
275 ///
276 /// Returns None if ETT is out of sync and result is unreliable.
277 /// Use `connected()` for the reliable version.
278 #[inline]
279 pub fn connected_fast(&self, u: VertexId, v: VertexId) -> Option<bool> {
280 if !self.ett_synced {
281 return None;
282 }
283 Some(self.ett.connected(u, v))
284 }
285
286 /// Returns the number of connected components
287 ///
288 /// # Returns
289 ///
290 /// The current number of connected components
291 pub fn component_count(&self) -> usize {
292 self.component_count
293 }
294
295 /// Returns the number of vertices
296 ///
297 /// # Returns
298 ///
299 /// The current number of vertices
300 pub fn vertex_count(&self) -> usize {
301 self.vertex_count
302 }
303
304 /// Finds the root of a vertex's component with path compression
305 ///
306 /// # Arguments
307 ///
308 /// * `v` - The vertex to find the root for
309 ///
310 /// # Returns
311 ///
312 /// The root vertex of the component containing `v`
313 ///
314 /// # Panics
315 ///
316 /// Panics if the vertex doesn't exist in the structure
317 fn find(&mut self, v: VertexId) -> VertexId {
318 let parent = *self.parent.get(&v).expect("Vertex not found");
319
320 if parent != v {
321 // Path compression: make v point directly to root
322 let root = self.find(parent);
323 self.parent.insert(v, root);
324 root
325 } else {
326 v
327 }
328 }
329
330 /// Unions two components by rank
331 ///
332 /// # Arguments
333 ///
334 /// * `u` - Root of first component
335 /// * `v` - Root of second component
336 ///
337 /// # Notes
338 ///
339 /// This function assumes `u` and `v` are roots. It should only be
340 /// called after `find()` operations.
341 fn union(&mut self, u: VertexId, v: VertexId) {
342 if u == v {
343 return;
344 }
345
346 let rank_u = *self.rank.get(&u).unwrap_or(&0);
347 let rank_v = *self.rank.get(&v).unwrap_or(&0);
348
349 // Union by rank: attach smaller tree to larger tree
350 if rank_u < rank_v {
351 self.parent.insert(u, v);
352 } else if rank_u > rank_v {
353 self.parent.insert(v, u);
354 } else {
355 // Equal rank: arbitrary choice, increment rank
356 self.parent.insert(v, u);
357 self.rank.insert(u, rank_u + 1);
358 }
359
360 // Decrease component count
361 self.component_count -= 1;
362 }
363
364 /// Rebuilds the union-find structure from the current edge set
365 ///
366 /// Called after edge deletions to recompute connected components.
367 /// Resets all vertices to singleton components and re-applies all edges.
368 /// Also rebuilds the Euler Tour Tree for O(log n) queries.
369 ///
370 /// # Time Complexity
371 ///
372 /// O(m·α(n)) where m is the number of edges
373 fn rebuild(&mut self) {
374 // Collect all vertices
375 let vertices: Vec<VertexId> = self.parent.keys().copied().collect();
376
377 // Reset to singleton components
378 self.component_count = vertices.len();
379 for &v in &vertices {
380 self.parent.insert(v, v);
381 self.rank.insert(v, 0);
382 }
383
384 // Rebuild Euler Tour Tree
385 self.ett = EulerTourTree::new();
386 for &v in &vertices {
387 let _ = self.ett.make_tree(v);
388 }
389
390 // Re-apply all edges
391 let edges: Vec<(VertexId, VertexId)> = self.edges.iter().copied().collect();
392 for (u, v) in edges {
393 let root_u = self.find(u);
394 let root_v = self.find(v);
395
396 if root_u != root_v {
397 self.union(root_u, root_v);
398 // Link in ETT
399 let _ = self.ett.link(u, v);
400 }
401 }
402
403 // Mark ETT as synced
404 self.ett_synced = true;
405 }
406}
407
408impl Default for DynamicConnectivity {
409 fn default() -> Self {
410 Self::new()
411 }
412}
413
414#[cfg(test)]
415mod tests {
416 use super::*;
417
418 #[test]
419 fn test_new() {
420 let dc = DynamicConnectivity::new();
421 assert_eq!(dc.vertex_count(), 0);
422 assert_eq!(dc.component_count(), 0);
423 }
424
425 #[test]
426 fn test_add_vertex() {
427 let mut dc = DynamicConnectivity::new();
428
429 dc.add_vertex(0);
430 assert_eq!(dc.vertex_count(), 1);
431 assert_eq!(dc.component_count(), 1);
432
433 dc.add_vertex(1);
434 assert_eq!(dc.vertex_count(), 2);
435 assert_eq!(dc.component_count(), 2);
436
437 // Adding same vertex is no-op
438 dc.add_vertex(0);
439 assert_eq!(dc.vertex_count(), 2);
440 assert_eq!(dc.component_count(), 2);
441 }
442
443 #[test]
444 fn test_insert_edge_basic() {
445 let mut dc = DynamicConnectivity::new();
446
447 dc.insert_edge(0, 1);
448 assert_eq!(dc.vertex_count(), 2);
449 assert_eq!(dc.component_count(), 1);
450 assert!(dc.connected(0, 1));
451 }
452
453 #[test]
454 fn test_insert_edge_chain() {
455 let mut dc = DynamicConnectivity::new();
456
457 dc.insert_edge(0, 1);
458 dc.insert_edge(1, 2);
459 dc.insert_edge(2, 3);
460
461 assert_eq!(dc.vertex_count(), 4);
462 assert_eq!(dc.component_count(), 1);
463 assert!(dc.connected(0, 3));
464 }
465
466 #[test]
467 fn test_is_connected() {
468 let mut dc = DynamicConnectivity::new();
469
470 dc.add_vertex(0);
471 dc.add_vertex(1);
472 assert!(!dc.is_connected());
473
474 dc.insert_edge(0, 1);
475 assert!(dc.is_connected());
476
477 dc.add_vertex(2);
478 assert!(!dc.is_connected());
479
480 dc.insert_edge(1, 2);
481 assert!(dc.is_connected());
482 }
483
484 #[test]
485 fn test_delete_edge() {
486 let mut dc = DynamicConnectivity::new();
487
488 dc.insert_edge(0, 1);
489 dc.insert_edge(1, 2);
490 assert!(dc.connected(0, 2));
491
492 dc.delete_edge(1, 2);
493 assert!(dc.connected(0, 1));
494 assert!(!dc.connected(0, 2));
495 assert_eq!(dc.component_count(), 2);
496 }
497
498 #[test]
499 fn test_delete_edge_normalized() {
500 let mut dc = DynamicConnectivity::new();
501
502 dc.insert_edge(0, 1);
503 assert!(dc.connected(0, 1));
504
505 // Delete with reversed vertices
506 dc.delete_edge(1, 0);
507 assert!(!dc.connected(0, 1));
508 }
509
510 #[test]
511 fn test_multiple_components() {
512 let mut dc = DynamicConnectivity::new();
513
514 // Component 1: 0-1-2
515 dc.insert_edge(0, 1);
516 dc.insert_edge(1, 2);
517
518 // Component 2: 3-4
519 dc.insert_edge(3, 4);
520
521 // Isolated vertex
522 dc.add_vertex(5);
523
524 assert_eq!(dc.vertex_count(), 6);
525 assert_eq!(dc.component_count(), 3);
526
527 assert!(dc.connected(0, 2));
528 assert!(dc.connected(3, 4));
529 assert!(!dc.connected(0, 3));
530 assert!(!dc.connected(0, 5));
531 }
532
533 #[test]
534 fn test_path_compression() {
535 let mut dc = DynamicConnectivity::new();
536
537 // Create a long chain
538 for i in 0..10 {
539 dc.insert_edge(i, i + 1);
540 }
541
542 // Path compression should happen on find
543 assert!(dc.connected(0, 10));
544
545 // All vertices should now point closer to root
546 let root = dc.find(0);
547 for i in 0..=10 {
548 assert_eq!(dc.find(i), root);
549 }
550 }
551
552 #[test]
553 fn test_union_by_rank() {
554 let mut dc = DynamicConnectivity::new();
555
556 // Create two trees of different sizes
557 dc.insert_edge(0, 1);
558 dc.insert_edge(0, 2);
559 dc.insert_edge(0, 3);
560
561 dc.insert_edge(4, 5);
562
563 // Union them
564 dc.insert_edge(0, 4);
565
566 assert_eq!(dc.component_count(), 1);
567 assert!(dc.connected(1, 5));
568 }
569
570 #[test]
571 fn test_rebuild_after_multiple_deletions() {
572 let mut dc = DynamicConnectivity::new();
573
574 // Create a complete graph K4
575 dc.insert_edge(0, 1);
576 dc.insert_edge(0, 2);
577 dc.insert_edge(0, 3);
578 dc.insert_edge(1, 2);
579 dc.insert_edge(1, 3);
580 dc.insert_edge(2, 3);
581
582 assert!(dc.is_connected());
583
584 // Remove edges to disconnect
585 dc.delete_edge(0, 1);
586 dc.delete_edge(0, 2);
587 dc.delete_edge(0, 3);
588
589 assert!(!dc.is_connected());
590 assert_eq!(dc.component_count(), 2);
591 assert!(!dc.connected(0, 1));
592 assert!(dc.connected(1, 2));
593 assert!(dc.connected(1, 3));
594 }
595
596 #[test]
597 fn test_connected_nonexistent_vertex() {
598 let mut dc = DynamicConnectivity::new();
599
600 dc.add_vertex(0);
601 assert!(!dc.connected(0, 999));
602 assert!(!dc.connected(999, 0));
603 }
604
605 #[test]
606 fn test_self_loop() {
607 let mut dc = DynamicConnectivity::new();
608
609 dc.insert_edge(0, 0);
610 assert_eq!(dc.vertex_count(), 1);
611 assert_eq!(dc.component_count(), 1);
612 assert!(dc.connected(0, 0));
613 }
614
615 #[test]
616 fn test_duplicate_edges() {
617 let mut dc = DynamicConnectivity::new();
618
619 dc.insert_edge(0, 1);
620 dc.insert_edge(0, 1); // Duplicate
621 dc.insert_edge(1, 0); // Duplicate (reversed)
622
623 assert_eq!(dc.vertex_count(), 2);
624 assert_eq!(dc.component_count(), 1);
625 }
626}