webgraph_algo/sccs/
mod.rs

1/*
2 * SPDX-FileCopyrightText: 2024 Matteo Dell'Acqua
3 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6 */
7
8//! Algorithms used to compute and work with strongly connected components.
9//!
10//! There are two implementations for directed graph: [Tarjan's
11//! algorithm](tarjan) and [Kosaraju's algorithm](kosaraju). The former is to be
12//! preferred in almost all cases: Kosaraju's algorithm is slower and requires
13//! the transpose of the graph—it is mainly useful for testing and debugging.
14//!
15//! For symmetric (i.e., undirected) graphs there is a [sequential](symm_seq)
16//! and a [parallel](symm_par) implementation that computes connected
17//! components.
18//!
19//! # Examples
20//! ```
21//! use dsi_progress_logger::no_logging;
22//! use webgraph::{graphs::vec_graph::VecGraph};
23//! use webgraph_algo::sccs::*;
24//!
25//! let graph = VecGraph::from_arcs([(0, 1), (1, 2), (2, 0), (1, 3)]);
26//!
27//! // Let's build the graph SCCS with Tarjan's algorithm
28//! let mut scc = tarjan(graph, no_logging![]);
29//!
30//! // Let's sort the SCC by size
31//! let sizes = scc.sort_by_size();
32//!
33//! assert_eq!(sizes, vec![3, 1].into_boxed_slice());
34//! assert_eq!(scc.components(), &vec![0, 0, 0, 1]);
35//! ```
36
37mod tarjan;
38pub use tarjan::*;
39
40mod kosaraju;
41pub use kosaraju::*;
42mod symm_seq;
43pub use symm_seq::*;
44
45mod symm_par;
46pub use symm_par::*;
47
48use crate::llp;
49use rayon::{
50    iter::{IntoParallelRefMutIterator, ParallelIterator},
51    slice::ParallelSliceMut,
52};
53
54/// Strongly connected components.
55///
56/// An instance of this structure stores the [index of the
57/// component](Sccs::components) of each node. Components are numbered from 0 to
58/// [`num_components`](Sccs::num_components).
59///
60/// Moreover, this structure makes it possible to [sort the components by
61/// size](Sccs::sort_by_size), possibly using [parallel
62/// methods](Sccs::par_sort_by_size).
63pub struct Sccs {
64    num_components: usize,
65    components: Box<[usize]>,
66}
67
68impl Sccs {
69    pub fn new(num_components: usize, components: Box<[usize]>) -> Self {
70        Sccs {
71            num_components,
72            components,
73        }
74    }
75
76    /// Returns the number of strongly connected components.
77    pub fn num_components(&self) -> usize {
78        self.num_components
79    }
80
81    /// Returns a slice containing, for each node, the index of the component
82    /// it belongs to.
83    #[inline(always)]
84    pub fn components(&self) -> &[usize] {
85        &self.components
86    }
87
88    /// Returns the sizes of all components.
89    pub fn compute_sizes(&self) -> Box<[usize]> {
90        let mut sizes = vec![0; self.num_components()];
91        for &node_component in self.components() {
92            sizes[node_component] += 1;
93        }
94        sizes.into_boxed_slice()
95    }
96
97    /// Renumbers the components by decreasing size.
98    ///
99    /// After a call to this method, the sizes of strongly connected components
100    /// will decreasing in the component index. The method returns the sizes of
101    /// the components after the renumbering.
102    pub fn sort_by_size(&mut self) -> Box<[usize]> {
103        let mut sizes = self.compute_sizes();
104        assert!(sizes.len() == self.num_components());
105        let mut sort_perm = Vec::from_iter(0..sizes.len());
106        sort_perm.sort_unstable_by(|&x, &y| sizes[y].cmp(&sizes[x]));
107        let mut inv_perm = vec![0; sizes.len()];
108        sort_perm
109            .iter()
110            .enumerate()
111            .for_each(|(i, &x)| inv_perm[x] = i);
112
113        self.components
114            .iter_mut()
115            .for_each(|node_component| *node_component = inv_perm[*node_component]);
116        sizes.sort_by(|&x, &y| y.cmp(&x));
117        sizes
118    }
119
120    /// Renumbers the components by decreasing size using parallel methods.
121    ///
122    /// After a call to this method, the sizes of strongly connected components
123    /// will decreasing in the component index. The method returns the sizes of
124    /// the components after the renumbering.
125    pub fn par_sort_by_size(&mut self) -> Box<[usize]> {
126        let mut sizes = self.compute_sizes();
127        assert!(sizes.len() == self.num_components());
128        let mut sort_perm = Vec::from_iter(0..sizes.len());
129        sort_perm.par_sort_unstable_by(|&x, &y| sizes[y].cmp(&sizes[x]));
130        let mut inv_perm = vec![0; sizes.len()];
131        llp::invert_permutation(&sort_perm, &mut inv_perm);
132        self.components
133            .par_iter_mut()
134            .for_each(|node_component| *node_component = inv_perm[*node_component]);
135        sizes.sort_by(|&x, &y| y.cmp(&x));
136        sizes
137    }
138}