rust_igraph/algorithms/connectivity/
strong.rs1use crate::algorithms::connectivity::components::{ConnectedComponents, connected_components};
31use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
32
33pub fn strongly_connected_components(graph: &Graph) -> IgraphResult<ConnectedComponents> {
61 let n = graph.vcount();
62 if n == 0 {
63 return Ok(ConnectedComponents {
64 membership: Vec::new(),
65 count: 0,
66 });
67 }
68
69 if !graph.is_directed() {
70 return connected_components(graph);
71 }
72
73 let n_us = n as usize;
74
75 let mut out_adj: Vec<Vec<VertexId>> = Vec::with_capacity(n_us);
78 for v in 0..n {
79 out_adj.push(graph.out_neighbors_vec(v)?);
80 }
81
82 let mut next_nei: Vec<usize> = vec![0; n_us];
90 let mut out: Vec<VertexId> = Vec::with_capacity(n_us);
91 let mut stack: Vec<VertexId> = Vec::new();
92
93 for start in 0..n {
94 if next_nei[start as usize] > out_adj[start as usize].len() {
95 continue;
96 }
97 stack.push(start);
98 while let Some(&act) = stack.last() {
99 let act_us = act as usize;
100 let nbrs_len = out_adj[act_us].len();
101 if next_nei[act_us] == 0 {
102 next_nei[act_us] = 1;
104 } else if next_nei[act_us] <= nbrs_len {
105 let nb = out_adj[act_us][next_nei[act_us] - 1];
106 if next_nei[nb as usize] == 0 {
107 stack.push(nb);
108 }
109 next_nei[act_us] += 1;
110 } else {
111 out.push(act);
113 stack.pop();
114 }
115 }
116 }
117
118 let mut in_adj: Vec<Vec<VertexId>> = Vec::with_capacity(n_us);
120 for v in 0..n {
121 in_adj.push(graph.in_neighbors_vec(v)?);
122 }
123
124 let mut visited: Vec<u8> = vec![0; n_us];
127 let mut membership = vec![u32::MAX; n_us];
128 let mut count: u32 = 0;
129 let mut walk: Vec<VertexId> = Vec::new();
130
131 while let Some(grandfather) = out.pop() {
132 if visited[grandfather as usize] != 0 {
133 continue;
134 }
135 visited[grandfather as usize] = 1;
136 let comp_id = count;
137 count = count
138 .checked_add(1)
139 .ok_or(IgraphError::Internal("too many components"))?;
140 membership[grandfather as usize] = comp_id;
141
142 walk.clear();
143 walk.push(grandfather);
144 while let Some(v) = walk.pop() {
145 for &nb in &in_adj[v as usize] {
146 if visited[nb as usize] == 0 {
147 visited[nb as usize] = 1;
148 membership[nb as usize] = comp_id;
149 walk.push(nb);
150 }
151 }
152 }
153 }
154
155 Ok(ConnectedComponents { membership, count })
156}
157
158#[cfg(test)]
159mod tests {
160 use super::*;
161
162 #[test]
163 fn empty_graph() {
164 let g = Graph::new(0, true).unwrap();
165 let scc = strongly_connected_components(&g).unwrap();
166 assert_eq!(scc.count, 0);
167 assert!(scc.membership.is_empty());
168 }
169
170 #[test]
171 fn single_vertex() {
172 let g = Graph::new(1, true).unwrap();
173 let scc = strongly_connected_components(&g).unwrap();
174 assert_eq!(scc.count, 1);
175 assert_eq!(scc.membership, vec![0]);
176 }
177
178 #[test]
179 fn n_isolated_vertices_yields_n_components() {
180 let g = Graph::new(5, true).unwrap();
181 let scc = strongly_connected_components(&g).unwrap();
182 assert_eq!(scc.count, 5);
183 assert_eq!(scc.membership, vec![4, 3, 2, 1, 0]);
187 }
188
189 #[test]
190 fn directed_path_each_vertex_alone() {
191 let mut g = Graph::new(4, true).unwrap();
193 g.add_edge(0, 1).unwrap();
194 g.add_edge(1, 2).unwrap();
195 g.add_edge(2, 3).unwrap();
196 let scc = strongly_connected_components(&g).unwrap();
197 assert_eq!(scc.count, 4);
198 let mut sorted = scc.membership.clone();
200 sorted.sort_unstable();
201 sorted.dedup();
202 assert_eq!(sorted, vec![0, 1, 2, 3]);
203 }
204
205 #[test]
206 fn directed_3_cycle_is_one_component() {
207 let mut g = Graph::new(3, true).unwrap();
209 g.add_edge(0, 1).unwrap();
210 g.add_edge(1, 2).unwrap();
211 g.add_edge(2, 0).unwrap();
212 let scc = strongly_connected_components(&g).unwrap();
213 assert_eq!(scc.count, 1);
214 assert_eq!(scc.membership, vec![0, 0, 0]);
215 }
216
217 #[test]
218 fn cycle_with_outgoing_chain() {
219 let mut g = Graph::new(5, true).unwrap();
222 g.add_edge(0, 1).unwrap();
223 g.add_edge(1, 2).unwrap();
224 g.add_edge(2, 0).unwrap();
225 g.add_edge(2, 3).unwrap();
226 g.add_edge(3, 4).unwrap();
227 let scc = strongly_connected_components(&g).unwrap();
228 assert_eq!(scc.count, 3);
229 assert_eq!(scc.membership[0], scc.membership[1]);
231 assert_eq!(scc.membership[1], scc.membership[2]);
232 assert_ne!(scc.membership[3], scc.membership[0]);
234 assert_ne!(scc.membership[4], scc.membership[0]);
235 assert_ne!(scc.membership[3], scc.membership[4]);
236 }
237
238 #[test]
239 fn two_disjoint_cycles() {
240 let mut g = Graph::new(6, true).unwrap();
242 g.add_edge(0, 1).unwrap();
243 g.add_edge(1, 2).unwrap();
244 g.add_edge(2, 0).unwrap();
245 g.add_edge(3, 4).unwrap();
246 g.add_edge(4, 5).unwrap();
247 g.add_edge(5, 3).unwrap();
248 let scc = strongly_connected_components(&g).unwrap();
249 assert_eq!(scc.count, 2);
250 assert_eq!(scc.membership[0], scc.membership[1]);
251 assert_eq!(scc.membership[1], scc.membership[2]);
252 assert_eq!(scc.membership[3], scc.membership[4]);
253 assert_eq!(scc.membership[4], scc.membership[5]);
254 assert_ne!(scc.membership[0], scc.membership[3]);
255 }
256
257 #[test]
258 fn self_loop_does_not_split_component() {
259 let mut g = Graph::new(2, true).unwrap();
261 g.add_edge(0, 0).unwrap();
262 g.add_edge(0, 1).unwrap();
263 g.add_edge(1, 0).unwrap();
264 let scc = strongly_connected_components(&g).unwrap();
265 assert_eq!(scc.count, 1);
266 assert_eq!(scc.membership, vec![0, 0]);
267 }
268
269 #[test]
270 fn parallel_edges_do_not_split_component() {
271 let mut g = Graph::new(2, true).unwrap();
273 g.add_edge(0, 1).unwrap();
274 g.add_edge(0, 1).unwrap();
275 g.add_edge(0, 1).unwrap();
276 g.add_edge(1, 0).unwrap();
277 let scc = strongly_connected_components(&g).unwrap();
278 assert_eq!(scc.count, 1);
279 assert_eq!(scc.membership, vec![0, 0]);
280 }
281
282 #[test]
283 fn undirected_graph_delegates_to_weak() {
284 let mut g = Graph::with_vertices(5);
286 g.add_edge(0, 1).unwrap();
287 g.add_edge(1, 2).unwrap();
288 g.add_edge(3, 4).unwrap();
289 let scc = strongly_connected_components(&g).unwrap();
290 let wcc = connected_components(&g).unwrap();
291 assert_eq!(scc.membership, wcc.membership);
292 assert_eq!(scc.count, wcc.count);
293 }
294
295 #[test]
296 fn membership_ids_are_dense() {
297 let mut g = Graph::new(5, true).unwrap();
299 g.add_edge(0, 1).unwrap();
300 g.add_edge(1, 2).unwrap();
301 g.add_edge(2, 0).unwrap();
302 g.add_edge(3, 4).unwrap();
303 let scc = strongly_connected_components(&g).unwrap();
304 assert_eq!(scc.count, 3);
305 let mut seen: Vec<u32> = scc.membership.clone();
306 seen.sort_unstable();
307 seen.dedup();
308 assert_eq!(seen, vec![0, 1, 2]);
309 }
310}