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}