weavatrix_graph/algo/components/
dag.rs1use 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}