causal_hub/graphs/algorithms/traversal/
topological_sort.rs

1use std::collections::{HashMap, VecDeque};
2
3use crate::{graphs::DirectedGraph, Ch, V};
4
5/// Topological sort search structure.
6pub struct TopologicalSort<'a, G>
7where
8    G: DirectedGraph,
9{
10    /// Given graph reference.
11    g: &'a G,
12    // To-be-visited queue.
13    queue: VecDeque<usize>,
14    // Visit map with vertices in-degrees.
15    visit: HashMap<usize, usize>,
16}
17
18impl<'a, G> TopologicalSort<'a, G>
19where
20    G: DirectedGraph,
21{
22    /// Build a TopologicalSort iterator.
23    ///
24    /// Build a TopologicalSort[^1] iterator for a given directed graph.
25    ///
26    /// If the graph is cyclic, this iterator returns an error while unrolling.
27    ///
28    /// [^1]: [Kahn, A. B. (1962). Topological sorting of large networks. Communications of the ACM, 5(11), 558-562.](https://scholar.google.com/scholar?q=Topological+sorting+of+large+networks)
29    ///
30    pub fn new(g: &'a G) -> Self {
31        // Initialize default search object.
32        let mut search = Self {
33            // Set target graph.
34            g,
35            // Initialize the to-be-visited queue with the source vertex.
36            queue: Default::default(),
37            // Initialize the visit map.
38            visit: Default::default(),
39        };
40        // For each vertex in the graph.
41        for x in V!(search.g) {
42            // Compute its in-degree.
43            match search.g.in_degree(x) {
44                // If the in-degree is zero, then add it to the queue.
45                0 => search.queue.push_back(x),
46                // Otherwise, add it to the map.
47                y => {
48                    search.visit.insert(x, y);
49                }
50            }
51        }
52
53        search
54    }
55}
56
57impl<'a, G> Iterator for TopologicalSort<'a, G>
58where
59    G: DirectedGraph,
60{
61    type Item = usize;
62
63    fn next(&mut self) -> Option<Self::Item> {
64        // While there are still vertices with zero in-degree.
65        if let Some(x) = self.queue.pop_front() {
66            // For each child of the selected vertex.
67            for y in Ch!(self.g, x) {
68                // If it was not visited before.
69                if let Some(z) = self.visit.get(&y) {
70                    // Update its in-degree.
71                    match z - 1 {
72                        // If the in-degree is zero ...
73                        0 => {
74                            // ... then add it to the queue ...
75                            self.queue.push_back(y);
76                            // ... and remove it from the visit map.
77                            self.visit.remove(&y);
78                        }
79                        // Otherwise, update its in-degree into the map.
80                        z => {
81                            self.visit.insert(y, z);
82                        }
83                    }
84                }
85            }
86            // Return current vertex.
87            return Some(x);
88        }
89
90        // If there are still vertices with non-zero in-degree ...
91        if !self.visit.is_empty() {
92            // ... no topological sort is defined, i.e. cyclic graph.
93            panic!("No topological sort is defined, i.e. cyclic graph");
94        }
95
96        None
97    }
98}
99
100impl<'a, G> From<&'a G> for TopologicalSort<'a, G>
101where
102    G: DirectedGraph,
103{
104    /// Builds a search object from a given graph.
105    ///
106    fn from(g: &'a G) -> Self {
107        Self::new(g)
108    }
109}