oxicuda_graphalg/dynamic/
incremental_scc.rs1use std::collections::VecDeque;
31
32use crate::connectivity::scc_tarjan::scc_tarjan;
33use crate::dynamic::dynamic_graph::DynamicGraph;
34use crate::error::{GraphalgError, GraphalgResult};
35
36#[derive(Debug, Clone)]
38pub struct IncrementalScc {
39 graph: DynamicGraph,
40 comp_of: Vec<usize>,
42 members: Vec<Vec<usize>>,
44}
45
46impl IncrementalScc {
47 pub fn new(graph: DynamicGraph) -> GraphalgResult<Self> {
49 let n = graph.num_nodes();
50 let mut me = Self {
51 graph,
52 comp_of: vec![0; n],
53 members: Vec::new(),
54 };
55 me.full_recompute()?;
56 Ok(me)
57 }
58
59 pub fn num_nodes(&self) -> usize {
61 self.graph.num_nodes()
62 }
63
64 pub fn num_components(&self) -> usize {
66 self.members.len()
67 }
68
69 pub fn component_of(&self, v: usize) -> GraphalgResult<usize> {
71 if v >= self.graph.num_nodes() {
72 return Err(GraphalgError::IndexOutOfBounds {
73 index: v,
74 len: self.graph.num_nodes(),
75 });
76 }
77 Ok(self.comp_of[v])
78 }
79
80 pub fn same_component(&self, u: usize, v: usize) -> GraphalgResult<bool> {
82 Ok(self.component_of(u)? == self.component_of(v)?)
83 }
84
85 pub fn components(&self) -> Vec<Vec<usize>> {
88 let mut out: Vec<Vec<usize>> = self
89 .members
90 .iter()
91 .map(|m| {
92 let mut v = m.clone();
93 v.sort_unstable();
94 v
95 })
96 .collect();
97 out.sort_by_key(|c| c.first().copied().unwrap_or(usize::MAX));
98 out
99 }
100
101 pub fn graph(&self) -> &DynamicGraph {
103 &self.graph
104 }
105
106 pub fn apply_insert(&mut self, u: usize, v: usize) -> GraphalgResult<()> {
108 self.graph.add_edge(u, v)?;
109 self.handle_insert(u, v)
110 }
111
112 pub fn apply_remove(&mut self, u: usize, v: usize) -> GraphalgResult<()> {
118 self.graph.remove_edge(u, v)?;
119 self.handle_remove(u, v)
120 }
121
122 pub fn apply_batch(&mut self, updates: &[(usize, usize, bool)]) -> GraphalgResult<()> {
125 for &(u, v, is_insert) in updates {
126 if is_insert {
127 self.apply_insert(u, v)?;
128 } else {
129 self.apply_remove(u, v)?;
130 }
131 }
132 Ok(())
133 }
134
135 fn full_recompute(&mut self) -> GraphalgResult<()> {
139 let g = self.graph.to_adjacency_list();
140 let sccs = scc_tarjan(&g)?;
141 self.install_partition(sccs);
142 Ok(())
143 }
144
145 fn install_partition(&mut self, sccs: Vec<Vec<usize>>) {
147 let n = self.graph.num_nodes();
148 let mut comp_of = vec![usize::MAX; n];
149 let mut members: Vec<Vec<usize>> = Vec::with_capacity(sccs.len());
150 for comp in sccs {
151 let id = members.len();
152 for &v in &comp {
153 comp_of[v] = id;
154 }
155 members.push(comp);
156 }
157 self.comp_of = comp_of;
159 self.members = members;
160 }
161
162 fn handle_insert(&mut self, u: usize, v: usize) -> GraphalgResult<()> {
164 let cu = self.comp_of[u];
165 let cv = self.comp_of[v];
166 if cu == cv {
167 return Ok(());
169 }
170 let merge = self.condensation_cycle_components(cv, cu);
174 if merge.len() <= 1 {
175 return Ok(());
176 }
177 self.merge_components(&merge);
178 Ok(())
179 }
180
181 fn handle_remove(&mut self, u: usize, v: usize) -> GraphalgResult<()> {
183 let n = self.graph.num_nodes();
184 if u >= n || v >= n {
185 return Ok(());
186 }
187 let cu = self.comp_of[u];
188 let cv = self.comp_of[v];
189 if cu != cv {
190 return Ok(());
192 }
193 let component = self.members[cu].clone();
195 let sub = self.tarjan_on_subset(&component)?;
196 if sub.len() == 1 {
197 return Ok(());
199 }
200 self.replace_component(cu, sub);
201 Ok(())
202 }
203
204 fn condensation_cycle_components(&self, start_comp: usize, target_comp: usize) -> Vec<usize> {
209 let nc = self.members.len();
210 let forward = self.condensation_reach(start_comp, false);
212 if !forward[target_comp] {
213 return Vec::new();
214 }
215 let backward = self.condensation_reach(target_comp, true);
217 let mut out = Vec::new();
218 for c in 0..nc {
219 if forward[c] && backward[c] {
220 out.push(c);
221 }
222 }
223 out
224 }
225
226 fn condensation_reach(&self, src: usize, reverse: bool) -> Vec<bool> {
230 let nc = self.members.len();
231 let mut seen = vec![false; nc];
232 if src >= nc {
233 return seen;
234 }
235 seen[src] = true;
236 let mut queue = VecDeque::new();
237 queue.push_back(src);
238 while let Some(c) = queue.pop_front() {
239 for &x in &self.members[c] {
241 let neigh = if reverse {
242 self.graph.in_neighbors(x)
243 } else {
244 self.graph.out_neighbors(x)
245 };
246 if let Ok(list) = neigh {
247 for &y in list {
248 let cy = self.comp_of[y];
249 if cy != c && !seen[cy] {
250 seen[cy] = true;
251 queue.push_back(cy);
252 }
253 }
254 }
255 }
256 }
257 seen
258 }
259
260 fn merge_components(&mut self, merge: &[usize]) {
263 let mut keep = vec![true; self.members.len()];
264 let mut fused: Vec<usize> = Vec::new();
265 for &c in merge {
266 keep[c] = false;
267 fused.extend(std::mem::take(&mut self.members[c]));
268 }
269 let mut new_members: Vec<Vec<usize>> = Vec::with_capacity(self.members.len());
271 for c in 0..self.members.len() {
272 if keep[c] {
273 new_members.push(std::mem::take(&mut self.members[c]));
274 }
275 }
276 new_members.push(fused);
277 self.reinstall(new_members);
278 }
279
280 fn replace_component(&mut self, cid: usize, sub: Vec<Vec<usize>>) {
283 let mut new_members: Vec<Vec<usize>> = Vec::with_capacity(self.members.len() + sub.len());
284 for c in 0..self.members.len() {
285 if c == cid {
286 continue;
287 }
288 new_members.push(std::mem::take(&mut self.members[c]));
289 }
290 for s in sub {
291 new_members.push(s);
292 }
293 self.reinstall(new_members);
294 }
295
296 fn reinstall(&mut self, members: Vec<Vec<usize>>) {
298 let n = self.graph.num_nodes();
299 let mut comp_of = vec![usize::MAX; n];
300 for (id, comp) in members.iter().enumerate() {
301 for &v in comp {
302 comp_of[v] = id;
303 }
304 }
305 self.comp_of = comp_of;
306 self.members = members;
307 }
308
309 fn tarjan_on_subset(&self, subset: &[usize]) -> GraphalgResult<Vec<Vec<usize>>> {
313 let k = subset.len();
315 let mut local = vec![usize::MAX; self.graph.num_nodes()];
316 for (i, &v) in subset.iter().enumerate() {
317 local[v] = i;
318 }
319 let mut sub = crate::repr::adjacency_list::AdjacencyList::new(k);
321 for &v in subset {
322 let lv = local[v];
323 for &w in self.graph.out_neighbors(v)? {
324 let lw = local[w];
325 if lw != usize::MAX {
326 sub.add_edge(lv, lw)?;
328 }
329 }
330 }
331 let local_sccs = scc_tarjan(&sub)?;
332 let translated = local_sccs
334 .into_iter()
335 .map(|comp| comp.into_iter().map(|li| subset[li]).collect::<Vec<_>>())
336 .collect();
337 Ok(translated)
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344 use crate::handle::LcgRng;
345
346 fn canon(mut parts: Vec<Vec<usize>>) -> Vec<Vec<usize>> {
349 for p in &mut parts {
350 p.sort_unstable();
351 }
352 parts.sort_by_key(|p| p.first().copied().unwrap_or(usize::MAX));
353 parts
354 }
355
356 fn ground_truth(g: &DynamicGraph) -> Vec<Vec<usize>> {
358 canon(scc_tarjan(&g.to_adjacency_list()).expect("tarjan"))
359 }
360
361 #[test]
362 fn insertion_merges_into_cycle() {
363 let mut g = DynamicGraph::new(3);
365 g.add_edge(0, 1).expect("e");
366 g.add_edge(1, 2).expect("e");
367 let mut scc = IncrementalScc::new(g).expect("new");
368 assert_eq!(scc.num_components(), 3);
369 scc.apply_insert(2, 0).expect("insert");
370 assert_eq!(scc.num_components(), 1);
371 assert!(scc.same_component(0, 2).expect("same"));
372 assert_eq!(scc.components(), ground_truth(scc.graph()));
373 }
374
375 #[test]
376 fn insertion_without_cycle_keeps_partition() {
377 let mut g = DynamicGraph::new(4);
378 g.add_edge(0, 1).expect("e");
379 g.add_edge(2, 3).expect("e");
380 let mut scc = IncrementalScc::new(g).expect("new");
381 let before = scc.num_components();
382 scc.apply_insert(1, 2).expect("insert"); assert_eq!(scc.num_components(), before);
384 assert_eq!(scc.components(), ground_truth(scc.graph()));
385 }
386
387 #[test]
388 fn deletion_splits_cycle() {
389 let mut g = DynamicGraph::new(3);
391 g.add_edge(0, 1).expect("e");
392 g.add_edge(1, 2).expect("e");
393 g.add_edge(2, 0).expect("e");
394 let mut scc = IncrementalScc::new(g).expect("new");
395 assert_eq!(scc.num_components(), 1);
396 scc.apply_remove(2, 0).expect("remove");
397 assert_eq!(scc.num_components(), 3);
398 assert_eq!(scc.components(), ground_truth(scc.graph()));
399 }
400
401 #[test]
402 fn deletion_of_cross_edge_noop() {
403 let mut g = DynamicGraph::new(4);
404 g.add_edge(0, 1).expect("e");
405 g.add_edge(1, 0).expect("e"); g.add_edge(1, 2).expect("e"); g.add_edge(2, 3).expect("e");
408 let mut scc = IncrementalScc::new(g).expect("new");
409 let before = scc.components();
410 scc.apply_remove(1, 2).expect("remove");
411 assert_eq!(scc.components(), before);
412 assert_eq!(scc.components(), ground_truth(scc.graph()));
413 }
414
415 #[test]
416 fn deletion_keeps_scc_when_alternate_path() {
417 let mut g = DynamicGraph::new(3);
421 g.add_edge(0, 1).expect("e");
422 g.add_edge(1, 2).expect("e");
423 g.add_edge(1, 2).expect("e"); g.add_edge(2, 0).expect("e");
425 let mut scc = IncrementalScc::new(g).expect("new");
426 assert_eq!(scc.num_components(), 1);
427 scc.apply_remove(1, 2).expect("remove"); assert_eq!(scc.num_components(), 1, "parallel edge keeps SCC");
429 assert_eq!(scc.components(), ground_truth(scc.graph()));
430 }
431
432 #[test]
433 fn merge_of_two_existing_sccs() {
434 let mut g = DynamicGraph::new(4);
436 g.add_edge(0, 1).expect("e");
437 g.add_edge(1, 0).expect("e");
438 g.add_edge(2, 3).expect("e");
439 g.add_edge(3, 2).expect("e");
440 let mut scc = IncrementalScc::new(g).expect("new");
441 assert_eq!(scc.num_components(), 2);
442 scc.apply_insert(1, 2).expect("insert"); assert_eq!(scc.num_components(), 2);
444 scc.apply_insert(3, 0).expect("insert"); assert_eq!(scc.num_components(), 1);
446 assert_eq!(scc.components(), ground_truth(scc.graph()));
447 }
448
449 #[test]
450 fn random_update_stream_matches_tarjan() {
451 let mut rng = LcgRng::new(0x5EED_1234);
452 for trial in 0..40 {
453 let n = 4 + rng.next_usize(7); let mut scc = IncrementalScc::new(DynamicGraph::new(n)).expect("new");
455 let mut present: Vec<(usize, usize)> = Vec::new();
457 for step in 0..60 {
458 let do_insert = present.is_empty() || rng.next_bool();
459 if do_insert {
460 let u = rng.next_usize(n);
461 let mut v = rng.next_usize(n);
462 if v == u {
463 v = (v + 1) % n;
464 }
465 scc.apply_insert(u, v).expect("insert");
466 if !present.contains(&(u, v)) {
467 present.push((u, v));
468 }
469 } else {
470 let idx = rng.next_usize(present.len());
471 let (u, v) = present[idx];
472 scc.apply_remove(u, v).expect("remove");
473 present.swap_remove(idx);
475 }
476 assert_eq!(
477 scc.components(),
478 ground_truth(scc.graph()),
479 "mismatch trial {trial} step {step} (n={n})"
480 );
481 for v in 0..n {
483 let cid = scc.component_of(v).expect("comp");
484 assert!(scc.members[cid].contains(&v));
485 }
486 }
487 }
488 }
489
490 #[test]
491 fn batch_update_matches_tarjan() {
492 let mut g = DynamicGraph::new(5);
493 g.add_edge(0, 1).expect("e");
494 g.add_edge(1, 2).expect("e");
495 let mut scc = IncrementalScc::new(g).expect("new");
496 let updates = [
497 (2, 0, true), (3, 4, true), (4, 3, true), (2, 3, true), (0, 1, false), ];
503 scc.apply_batch(&updates).expect("batch");
504 assert_eq!(scc.components(), ground_truth(scc.graph()));
505 }
506
507 #[test]
508 fn self_loop_is_its_own_scc_member() {
509 let mut g = DynamicGraph::new(2);
510 g.add_edge(0, 0).expect("e"); g.add_edge(0, 1).expect("e");
512 let scc = IncrementalScc::new(g).expect("new");
513 assert_eq!(scc.num_components(), 2);
514 assert_eq!(scc.components(), ground_truth(scc.graph()));
515 }
516
517 #[test]
518 fn component_of_out_of_range_errors() {
519 let scc = IncrementalScc::new(DynamicGraph::new(2)).expect("new");
520 assert!(scc.component_of(5).is_err());
521 }
522}