Skip to main content

weavatrix_graph/algo/components/
dag.rs

1use crate::IndexGraphView;
2use crate::Vec;
3use alloc::collections::VecDeque;
4
5#[must_use]
6pub fn topological_sort<G>(graph: &G) -> Option<Vec<G::Node>>
7where
8    G: IndexGraphView,
9{
10    sort_with_filter(graph, &|_| true)
11}
12
13#[must_use]
14pub fn topological_sort_filtered<G, F>(graph: &G, allows_edge: F) -> Option<Vec<G::Node>>
15where
16    G: IndexGraphView,
17    F: Fn(G::Edge) -> bool,
18{
19    sort_with_filter(graph, &allows_edge)
20}
21
22#[must_use]
23pub fn topological_generations<G>(graph: &G) -> Option<Vec<Vec<G::Node>>>
24where
25    G: IndexGraphView,
26{
27    generations_with_filter(graph, &|_| true)
28}
29
30#[must_use]
31pub fn topological_generations_filtered<G, F>(
32    graph: &G,
33    allows_edge: F,
34) -> Option<Vec<Vec<G::Node>>>
35where
36    G: IndexGraphView,
37    F: Fn(G::Edge) -> bool,
38{
39    generations_with_filter(graph, &allows_edge)
40}
41
42#[must_use]
43pub fn has_cycle<G>(graph: &G) -> bool
44where
45    G: IndexGraphView,
46{
47    topological_sort(graph).is_none()
48}
49
50#[must_use]
51pub fn has_cycle_filtered<G, F>(graph: &G, allows_edge: F) -> bool
52where
53    G: IndexGraphView,
54    F: Fn(G::Edge) -> bool,
55{
56    topological_sort_filtered(graph, allows_edge).is_none()
57}
58
59#[must_use]
60pub fn find_cycle<G>(graph: &G) -> Option<Vec<G::Node>>
61where
62    G: IndexGraphView,
63{
64    find_with_filter(graph, &|_| true)
65}
66
67#[must_use]
68pub fn find_cycle_filtered<G, F>(graph: &G, allows_edge: F) -> Option<Vec<G::Node>>
69where
70    G: IndexGraphView,
71    F: Fn(G::Edge) -> bool,
72{
73    find_with_filter(graph, &allows_edge)
74}
75
76fn sort_with_filter<G, F>(graph: &G, allows_edge: &F) -> Option<Vec<G::Node>>
77where
78    G: IndexGraphView,
79    F: Fn(G::Edge) -> bool,
80{
81    let mut indegree = vec![0_usize; graph.node_bound()];
82    for (edge, endpoints) in graph.edge_references() {
83        if !allows_edge(edge) {
84            continue;
85        }
86        if let Some(degree) = indegree.get_mut(G::node_slot(endpoints.target())) {
87            *degree += 1;
88        }
89    }
90    let mut ready = graph
91        .node_indices()
92        .filter(|node| indegree[G::node_slot(*node)] == 0)
93        .collect::<VecDeque<_>>();
94    let mut order = Vec::with_capacity(graph.node_count());
95    while let Some(node) = ready.pop_front() {
96        order.push(node);
97        for edge in graph.outgoing_edges(node) {
98            if !allows_edge(edge) {
99                continue;
100            }
101            let Some(endpoints) = graph.edge_endpoints(edge) else {
102                continue;
103            };
104            let Some(degree) = indegree.get_mut(G::node_slot(endpoints.target())) else {
105                continue;
106            };
107            *degree = degree.saturating_sub(1);
108            if *degree == 0 {
109                ready.push_back(endpoints.target());
110            }
111        }
112    }
113    (order.len() == graph.node_count()).then_some(order)
114}
115
116fn generations_with_filter<G, F>(graph: &G, allows_edge: &F) -> Option<Vec<Vec<G::Node>>>
117where
118    G: IndexGraphView,
119    F: Fn(G::Edge) -> bool,
120{
121    let mut indegree = vec![0_usize; graph.node_bound()];
122    for (edge, endpoints) in graph.edge_references() {
123        if allows_edge(edge) {
124            indegree[G::node_slot(endpoints.target())] += 1;
125        }
126    }
127    let mut ready = graph
128        .node_indices()
129        .filter(|node| indegree[G::node_slot(*node)] == 0)
130        .collect::<Vec<_>>();
131    let mut generations = Vec::new();
132    let mut visited = 0_usize;
133    while !ready.is_empty() {
134        visited += ready.len();
135        let generation = ready;
136        let mut next = Vec::new();
137        for &node in &generation {
138            for edge in graph.outgoing_edges(node) {
139                if !allows_edge(edge) {
140                    continue;
141                }
142                let Some(endpoints) = graph.edge_endpoints(edge) else {
143                    continue;
144                };
145                let degree = &mut indegree[G::node_slot(endpoints.target())];
146                *degree = degree.saturating_sub(1);
147                if *degree == 0 {
148                    next.push(endpoints.target());
149                }
150            }
151        }
152        generations.push(generation);
153        ready = next;
154    }
155    (visited == graph.node_count()).then_some(generations)
156}
157
158fn find_with_filter<G, F>(graph: &G, allows_edge: &F) -> Option<Vec<G::Node>>
159where
160    G: IndexGraphView,
161    F: Fn(G::Edge) -> bool,
162{
163    let mut adjacency = vec![Vec::new(); graph.node_bound()];
164    for (edge, endpoints) in graph.edge_references() {
165        if !allows_edge(edge) {
166            continue;
167        }
168        if let Some(neighbors) = adjacency.get_mut(G::node_slot(endpoints.source())) {
169            neighbors.push(endpoints.target());
170        }
171    }
172    let mut color = vec![0_u8; graph.node_bound()];
173    let mut parent = vec![None; graph.node_bound()];
174    for start in graph.node_indices() {
175        if color[G::node_slot(start)] != 0 {
176            continue;
177        }
178        color[G::node_slot(start)] = 1;
179        let mut stack = vec![(start, 0_usize)];
180        while let Some((node, next)) = stack.last_mut() {
181            let slot = G::node_slot(*node);
182            let Some(&target) = adjacency[slot].get(*next) else {
183                color[slot] = 2;
184                stack.pop();
185                continue;
186            };
187            *next += 1;
188            let target_slot = G::node_slot(target);
189            if color[target_slot] == 0 {
190                parent[target_slot] = Some(*node);
191                color[target_slot] = 1;
192                stack.push((target, 0));
193            } else if color[target_slot] == 1 {
194                return reconstruct_cycle::<G>(*node, target, &parent);
195            }
196        }
197    }
198    None
199}
200
201fn reconstruct_cycle<G>(
202    node: G::Node,
203    target: G::Node,
204    parent: &[Option<G::Node>],
205) -> Option<Vec<G::Node>>
206where
207    G: IndexGraphView,
208{
209    let mut cycle = vec![node];
210    let mut cursor = node;
211    while cursor != target {
212        cursor = parent[G::node_slot(cursor)]?;
213        cycle.push(cursor);
214    }
215    cycle.reverse();
216    cycle.push(target);
217    Some(cycle)
218}