toposort_scc/lib.rs
1// Copyright 2020 Ferdinand Bachmann
2//
3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5// http://opensource.org/licenses/MIT>, at your option. This file may not be
6// copied, modified, or distributed except according to those terms.
7
8//! An implementation of
9//! [Kahn's algorithm](https://en.wikipedia.org/wiki/Topological_sorting)
10//! for topological sorting and
11//! [Kosaraju's algorithm](https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm)
12//! for strongly connected components.
13//!
14//! This crate provides:
15//!
16//! - an adjacency-list based graph data structure (`IndexGraph`)
17//! - an implementation of a topological sorting algorithm that runs in
18//! `O(V + E)` time and `O(V)` additional space (Kahn's algorithm)
19//! - an implementation of an algorithm that finds the strongly connected
20//! components of a graph in `O(V + E)` time and `O(V)` additional space
21//! (Kosaraju's algorithm)
22//! - both algorithms are available either as single methods (`.toposort()` and
23//! `.scc()`) or as a combined method (`.toposort_or_scc()`) on `IndexGraph`
24//!
25//! The `id-arena` feature adds an additional wrapper type (`ArenaGraph`) that
26//! allows topological sorting and finding of strongly connected components on
27//! arbitrary graph structures built with the `id-arena` crate by creating a
28//! proxy graph that is sorted and returning a list of indices into the original
29//! graph.
30//!
31//! # Example
32//!
33//! This example creates an `IndexGraph` of the example graph from the
34//! Wikipedia page for
35//! [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting).
36//!
37//! A copy of the graph with cycles in it is created to demonstrate finding
38//! of strongly connected components.
39//!
40//! ```rust
41//! use toposort_scc::IndexGraph;
42//!
43//! let g = IndexGraph::from_adjacency_list(&vec![
44//! vec![3],
45//! vec![3, 4],
46//! vec![4, 7],
47//! vec![5, 6, 7],
48//! vec![6],
49//! vec![],
50//! vec![],
51//! vec![]
52//! ]);
53//!
54//! let mut g2 = g.clone();
55//! g2.add_edge(0, 0); // trivial cycle [0]
56//! g2.add_edge(6, 2); // cycle [2, 4, 6]
57//!
58//! assert_eq!(g.toposort_or_scc(), Ok(vec![0, 1, 2, 3, 4, 5, 7, 6]));
59//! assert_eq!(g2.toposort_or_scc(), Err(vec![vec![0], vec![4, 2, 6]]));
60//! ```
61
62use std::collections::VecDeque as Queue;
63use std::vec::IntoIter as VecIntoIter;
64use std::slice::Iter as SliceIter;
65use std::ops::Index;
66use std::mem;
67
68#[cfg(feature = "id-arena")]
69mod arena_graph;
70
71#[cfg(feature = "id-arena")]
72pub use arena_graph::*;
73
74/// An adjacency-list-based graph data structure
75///
76/// Stores graph vertices as lists of incoming and outgoing edges by their
77/// index in the graph. No additional data is stored per vertex.
78#[derive(Debug, Clone)]
79pub struct IndexGraph {
80 vertices: Vec<Vertex>,
81}
82
83/// A vertex in an `IndexGraph`
84///
85/// Every vertex stores the vertices it is connected to via edges in both
86/// directions.
87#[derive(Debug, Clone, Default)]
88pub struct Vertex {
89 in_degree: usize,
90 out_degree: usize,
91 pub in_edges: Vec<usize>,
92 pub out_edges: Vec<usize>,
93}
94
95/// A builder object that allows to easily add edges to a graph
96///
97/// It stores a vertex index, so that edges can be added specifying only the
98/// target edge or source edge.
99///
100/// See `IndexGraph::from_graph()` for usage examples
101#[derive(Debug)]
102pub struct IndexGraphBuilder<'g> {
103 graph: &'g mut IndexGraph,
104 index: usize
105}
106
107impl IndexGraphBuilder<'_> {
108 /// Returns a reference to the stored graph
109 pub fn as_graph(&self) -> &IndexGraph {
110 self.graph
111 }
112
113 /// Returns a mutable reference to the stored graph
114 pub fn as_mut_graph(&mut self) -> &mut IndexGraph {
115 self.graph
116 }
117
118 /// Returns the stored index
119 pub fn index(&self) -> usize {
120 self.index
121 }
122
123 /// Add an edge from the stored index to the passed index
124 ///
125 /// This method does not check for duplicate edges.
126 pub fn add_out_edge(&mut self, index: usize) {
127 self.graph.add_edge(self.index, index)
128 }
129
130 /// Add an edge from the passed index to the stored index
131 ///
132 /// This method does not check for duplicate edges.
133 pub fn add_in_edge(&mut self, index: usize) {
134 self.graph.add_edge(index, self.index)
135 }
136}
137
138impl IndexGraph {
139 /// Create a new graph with `len` vertices and no edges
140 ///
141 /// Edges can then be added with the `.add_edge()` method.
142 ///
143 /// # Example
144 ///
145 /// This example creates a graph with three vertices connected together in a
146 /// cycle.
147 ///
148 /// ```rust
149 /// use toposort_scc::IndexGraph;
150 ///
151 /// let mut g = IndexGraph::with_vertices(3);
152 /// g.add_edge(0, 1);
153 /// g.add_edge(1, 2);
154 /// g.add_edge(2, 0);
155 /// ```
156 pub fn with_vertices(len: usize) -> Self {
157 let mut vertices = Vec::with_capacity(len);
158 vertices.resize_with(len, Default::default);
159
160 IndexGraph { vertices }
161 }
162
163 /// Create a new graph from a list of adjacent vertices
164 ///
165 /// The graph will contain outgoing edges from each vertex to the vertices
166 /// in its adjacency list.
167 ///
168 /// # Example
169 ///
170 /// This example creates an `IndexGraph` of the example graph from the
171 /// Wikipedia page for
172 /// [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting).
173 ///
174 /// ```rust
175 /// use toposort_scc::IndexGraph;
176 ///
177 /// let g = IndexGraph::from_adjacency_list(&vec![
178 /// vec![3],
179 /// vec![3, 4],
180 /// vec![4, 7],
181 /// vec![5, 6, 7],
182 /// vec![6],
183 /// vec![],
184 /// vec![],
185 /// vec![]
186 /// ]);
187 /// ```
188 pub fn from_adjacency_list<S>(g: &[S]) -> Self
189 where S: AsRef<[usize]>
190 {
191 IndexGraph::from_graph(g, |mut builder, edges| for &edge in edges.as_ref() {
192 builder.add_out_edge(edge)
193 })
194 }
195
196 /// Create a new graph from an existing graph-like data structure
197 ///
198 /// The given closure will be called once for every element of `g`, with an
199 /// `IndexGraphBuilder` instance so that edges can be easily added.
200 ///
201 /// This method is useful for creating `IndexGraphs` from existing
202 /// structures.
203 ///
204 /// # Example
205 ///
206 /// This example creates a graph of dependencies in a hypothetical compiler
207 /// or build tool, with edges from a dependency to the targets that use
208 /// them.
209 ///
210 /// ```rust
211 /// use toposort_scc::IndexGraph;
212 ///
213 /// // a target during compilation, having a name and dependencies
214 /// struct Target { name: &'static str, deps: Vec<usize> }
215 /// impl Target {
216 /// fn new(name: &'static str, deps: Vec<usize>) -> Self {
217 /// Target { name, deps }
218 /// }
219 /// }
220 ///
221 /// let targets = vec![
222 /// Target::new("program", vec![1, 2, 4]),
223 /// Target::new("main.c", vec![3]),
224 /// Target::new("util.c", vec![3]),
225 /// Target::new("util.h", vec![]),
226 /// Target::new("libfoo.so", vec![])
227 /// ];
228 ///
229 /// let g = IndexGraph::from_graph(&targets, |mut builder, target| {
230 /// for &dep in &target.deps {
231 /// builder.add_in_edge(dep);
232 /// }
233 /// });
234 /// ```
235 ///
236 /// To get a graph with edges in the other direction, use `.add_out_edge()`
237 /// or the `.transpose()` method of the graph.
238 ///
239 /// More complicated graph structures or structures that don't store edges
240 /// as indices will need to first create a `Map` to look up indices in.
241 /// Alternatively, the `ArenaGraph` type from the `id-arena` feature can be
242 /// used.
243 pub fn from_graph<T, F>(g: &[T], mut f: F) -> Self
244 where F: FnMut(IndexGraphBuilder<'_>, &T)
245 {
246 let mut graph = Self::with_vertices(g.len());
247
248 for (idx, element) in g.iter().enumerate() {
249 f(IndexGraphBuilder { graph: &mut graph, index: idx }, element)
250 }
251
252 graph
253 }
254
255 /// Returns an iterator over the contained vertices
256 pub fn iter(&self) -> SliceIter<'_, Vertex> {
257 self.vertices.iter()
258 }
259
260 /// Add a new edge to the graph
261 ///
262 /// This method does not check for duplicate edges.
263 pub fn add_edge(&mut self, from: usize, to: usize) {
264 self.vertices[from].out_degree += 1;
265 self.vertices[to].in_degree += 1;
266 self.vertices[from].out_edges.push(to);
267 self.vertices[to].in_edges.push(from);
268 }
269
270 /// Transpose the graph
271 ///
272 /// Inverts the direction of all edges in the graph
273 pub fn transpose(&mut self) {
274 for vertex in &mut self.vertices {
275 mem::swap(&mut vertex.in_degree, &mut vertex.out_degree);
276 mem::swap(&mut vertex.in_edges, &mut vertex.out_edges);
277 }
278 }
279
280 /// Internal method that attempts to perform topological sort
281 ///
282 /// If the graph contains no cycles, finds the topological ordering of this
283 /// graph using Kahn's algorithm and returns it as `Ok(sorted)`.
284 ///
285 /// If the graph contains cycles, returns the graph as Err(self).
286 ///
287 /// This method is not public because it breaks the invariants of
288 /// `Vertex.in_degree` and `Vertex.out_degree`
289 ///
290 /// This method sets `Vertex.out_degree` to zero for every vertex so that
291 /// the precondition of `IndexGraph::scc_internal()` is fulfilled
292 fn try_toposort_internal(mut self) -> Result<Vec<usize>, IndexGraph> {
293 let mut queue = Queue::new();
294 let mut sorted = Vec::new();
295
296 // Kahn's algorithm for toposort
297
298 // enqueue vertices with in-degree zero
299 for (idx, vertex) in self.vertices.iter_mut().enumerate() {
300 // out_degree is unused in this algorithm
301 // set out_degree to zero to be used as a 'visited' flag by
302 // Kosaraju's algorithm later
303 vertex.out_degree = 0;
304
305 if vertex.in_degree == 0 {
306 queue.push_back(idx);
307 }
308 }
309
310 // add vertices from queue to sorted list
311 // decrement in-degree of neighboring edges
312 // add to queue if in-degree zero
313 while let Some(idx) = queue.pop_front() {
314 sorted.push(idx);
315
316 for edge_idx in 0..self.vertices[idx].out_edges.len() {
317 let next_idx = self.vertices[idx].out_edges[edge_idx];
318
319 self.vertices[next_idx].in_degree -= 1;
320 if self.vertices[next_idx].in_degree == 0 {
321 queue.push_back(next_idx);
322 }
323 }
324 }
325
326 // if every vertex appears in sorted list, sort is successful
327 if sorted.len() == self.vertices.len() {
328 Ok(sorted)
329 } else {
330 Err(self)
331 }
332 }
333
334 /// Try to perform topological sort on the graph
335 ///
336 /// If the graph contains no cycles, finds the topological ordering of this
337 /// graph using Kahn's algorithm and returns it as `Ok(sorted)`.
338 ///
339 /// If the graph contains cycles, returns the graph as `Err(self)`.
340 ///
341 /// For examples, see `IndexGraph::toposort()`
342 pub fn try_toposort(self) -> Result<Vec<usize>, IndexGraph> {
343 self.try_toposort_internal()
344 .map_err(|mut graph| {
345 for vertex in graph.vertices.iter_mut() {
346 vertex.in_degree = vertex.in_edges.len();
347 vertex.out_degree = vertex.out_edges.len();
348 }
349
350 graph
351 })
352 }
353
354 /// Perform topological sort on the graph
355 ///
356 /// If the graph contains no cycles, finds the topological ordering of this
357 /// graph using Kahn's algorithm and returns it as `Some(sorted)`.
358 ///
359 /// If the graph contains cycles, returns `None`.
360 ///
361 /// # Example
362 ///
363 /// This example creates an `IndexGraph` of the example graph from the
364 /// Wikipedia page for
365 /// [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting)
366 /// and performs a topological sort.
367 ///
368 /// A copy of the graph with cycles in it is created to demonstrate failure.
369 ///
370 /// ```rust
371 /// use toposort_scc::IndexGraph;
372 ///
373 /// let g = IndexGraph::from_adjacency_list(&vec![
374 /// vec![3],
375 /// vec![3, 4],
376 /// vec![4, 7],
377 /// vec![5, 6, 7],
378 /// vec![6],
379 /// vec![],
380 /// vec![],
381 /// vec![]
382 /// ]);
383 ///
384 /// let mut g2 = g.clone();
385 /// g2.add_edge(0, 0); // trivial cycle [0]
386 /// g2.add_edge(6, 2); // cycle [2, 4, 6]
387 ///
388 /// assert_eq!(g.toposort(), Some(vec![0, 1, 2, 3, 4, 5, 7, 6]));
389 /// assert_eq!(g2.toposort(), None);
390 /// ```
391 pub fn toposort(self) -> Option<Vec<usize>> {
392 self.try_toposort_internal().ok()
393 }
394
395 /// Internal method that finds strongly connected components
396 ///
397 /// Finds the strongly connected components of this graph using Kosaraju's
398 /// algorithm and returns them.
399 ///
400 /// This method is not public because it assumes `Vertex.out_degree` is
401 /// zero for every vertex.
402 fn scc_internal(mut self) -> Vec<Vec<usize>> {
403 // assumes out_degree is zero everywhere, to be used as a 'visited' flag
404
405 // empty graphs are always cycle-free
406 if self.vertices.is_empty() {
407 return Vec::new()
408 }
409
410 // Kosaraju's algorithm for strongly connected components
411
412 // start depth-first search with first vertex
413 let mut queue = Queue::new();
414 let mut dfs_stack = vec![(0, 0)];
415 self.vertices[0].out_degree = 1;
416
417 // add vertices to queue in post-order
418 while let Some((idx, edge_idx)) = dfs_stack.pop() {
419 if edge_idx < self.vertices[idx].out_edges.len() {
420 dfs_stack.push((idx, edge_idx + 1));
421
422 let next_idx = self.vertices[idx].out_edges[edge_idx];
423 if self.vertices[next_idx].out_degree == 0 {
424 self.vertices[next_idx].out_degree = 1;
425 dfs_stack.push((next_idx, 0));
426 }
427 } else {
428 queue.push_back(idx);
429 }
430 }
431
432 // collect cycles by depth-first search in opposite edge direction
433 // from each vertex in queue
434 let mut cycles = Vec::new();
435 while let Some(root_idx) = queue.pop_back() {
436 if self.vertices[root_idx].out_degree == 2 {
437 continue
438 }
439
440 let mut cur_cycle = Vec::new();
441
442 dfs_stack.push((root_idx, 0));
443
444 while let Some((idx, edge_idx)) = dfs_stack.pop() {
445 if edge_idx < self.vertices[idx].in_edges.len() {
446 dfs_stack.push((idx, edge_idx + 1));
447
448 let next_idx = self.vertices[idx].in_edges[edge_idx];
449 if self.vertices[next_idx].out_degree == 1 {
450 self.vertices[next_idx].out_degree = 2;
451 dfs_stack.push((self.vertices[idx].in_edges[edge_idx], 0));
452 cur_cycle.push(next_idx);
453 }
454 }
455 }
456
457 if self.vertices[root_idx].out_degree == 2 {
458 cycles.push(cur_cycle);
459 } else {
460 self.vertices[root_idx].out_degree = 2;
461 }
462 }
463
464 // return collected cycles
465 cycles
466 }
467
468 /// Find strongly connected components
469 ///
470 /// Finds the strongly connected components of this graph using Kosaraju's
471 /// algorithm and returns them.
472 ///
473 /// # Example
474 ///
475 /// This examples creates an `IndexGraph` of the example graph from the
476 /// Wikipedia page for
477 /// [Strongly connected compoents](https://en.wikipedia.org/wiki/Strongly_connected_component)
478 /// and finds the strongly connected components.
479 ///
480 /// ```rust
481 /// use toposort_scc::IndexGraph;
482 ///
483 /// let g = IndexGraph::from_adjacency_list(&vec![
484 /// vec![1],
485 /// vec![2, 4, 5],
486 /// vec![3, 6],
487 /// vec![2, 7],
488 /// vec![0, 5],
489 /// vec![6],
490 /// vec![5],
491 /// vec![3, 6]
492 /// ]);
493 ///
494 /// assert_eq!(g.scc(), vec![vec![4, 1, 0], vec![3, 2, 7], vec![5, 6]]);
495 /// ```
496 pub fn scc(mut self) -> Vec<Vec<usize>> {
497 for vertex in self.vertices.iter_mut() {
498 vertex.out_degree = 0;
499 }
500
501 self.scc_internal()
502 }
503
504 /// Perform topological sort or find strongly connected components
505 ///
506 /// If the graph contains no cycles, finds the topological ordering of this
507 /// graph using Kahn's algorithm and returns it as `Ok(sorted)`.
508 ///
509 /// If the graph contains cycles, finds the strongly connected components of
510 /// this graph using Kosaraju's algorithm and returns them as `Err(cycles)`.
511 ///
512 /// # Example
513 ///
514 /// This example creates an `IndexGraph` of the example graph from the
515 /// Wikipedia page for
516 /// [Topological sorting](https://en.wikipedia.org/wiki/Topological_sorting)
517 /// and performs a topological sort.
518 ///
519 /// A copy of the graph with cycles in it is created to demonstrate finding
520 /// of strongly connected components.
521 ///
522 /// ```rust
523 /// use toposort_scc::IndexGraph;
524 ///
525 /// let g = IndexGraph::from_adjacency_list(&vec![
526 /// vec![3],
527 /// vec![3, 4],
528 /// vec![4, 7],
529 /// vec![5, 6, 7],
530 /// vec![6],
531 /// vec![],
532 /// vec![],
533 /// vec![]
534 /// ]);
535 ///
536 /// let mut g2 = g.clone();
537 /// g2.add_edge(0, 0); // trivial cycle [0]
538 /// g2.add_edge(6, 2); // cycle [2, 4, 6]
539 ///
540 /// assert_eq!(g.toposort_or_scc(), Ok(vec![0, 1, 2, 3, 4, 5, 7, 6]));
541 /// assert_eq!(g2.toposort_or_scc(), Err(vec![vec![0], vec![4, 2, 6]]));
542 /// ```
543 pub fn toposort_or_scc(self) -> Result<Vec<usize>, Vec<Vec<usize>>> {
544 match self.try_toposort_internal() {
545 Ok(sorted) => Ok(sorted),
546 Err(graph) => Err(graph.scc_internal())
547 }
548 }
549}
550
551impl Index<usize> for IndexGraph {
552 type Output = Vertex;
553
554 fn index(&self, index: usize) -> &Vertex {
555 &self.vertices[index]
556 }
557}
558
559impl<'g> IntoIterator for &'g IndexGraph {
560 type Item = &'g Vertex;
561 type IntoIter = SliceIter<'g, Vertex>;
562
563 fn into_iter(self) -> Self::IntoIter {
564 self.vertices.iter()
565 }
566}
567
568impl IntoIterator for IndexGraph {
569 type Item = Vertex;
570 type IntoIter = VecIntoIter<Vertex>;
571
572 fn into_iter(self) -> Self::IntoIter {
573 self.vertices.into_iter()
574 }
575}