weavatrix_graph/algo/
components.rs1use super::traversal::{Direction, for_each_neighbor};
2use crate::IndexGraphView;
3use std::collections::VecDeque;
4
5#[must_use]
6pub fn strongly_connected_components<G>(graph: &G) -> Vec<Vec<G::Node>>
7where
8 G: IndexGraphView,
9{
10 let mut seen = vec![false; graph.node_bound()];
11 let mut finish = Vec::with_capacity(graph.node_count());
12 for start in graph.node_indices() {
13 finish_from(graph, start, &mut seen, &mut finish);
14 }
15
16 seen.fill(false);
17 let mut components = Vec::new();
18 for start in finish.into_iter().rev() {
19 if seen[G::node_slot(start)] {
20 continue;
21 }
22 let mut component = Vec::new();
23 let mut stack = vec![start];
24 seen[G::node_slot(start)] = true;
25 while let Some(node) = stack.pop() {
26 component.push(node);
27 for_each_neighbor(
28 graph,
29 node,
30 Direction::Incoming,
31 &mut |_| true,
32 |neighbor| {
33 let slot = G::node_slot(neighbor);
34 if !seen[slot] {
35 seen[slot] = true;
36 stack.push(neighbor);
37 }
38 },
39 );
40 }
41 components.push(component);
42 }
43 components
44}
45
46#[must_use]
47pub fn topological_sort<G>(graph: &G) -> Option<Vec<G::Node>>
48where
49 G: IndexGraphView,
50{
51 let mut indegree = vec![0_usize; graph.node_bound()];
52 for edge in graph.edge_indices() {
53 if let Some(endpoints) = graph.edge_endpoints(edge) {
54 indegree[G::node_slot(endpoints.target())] += 1;
55 }
56 }
57 let mut ready = graph
58 .node_indices()
59 .filter(|node| indegree[G::node_slot(*node)] == 0)
60 .collect::<VecDeque<_>>();
61 let mut order = Vec::with_capacity(graph.node_count());
62 while let Some(node) = ready.pop_front() {
63 order.push(node);
64 for edge in graph.outgoing_edges(node) {
65 let Some(endpoints) = graph.edge_endpoints(edge) else {
66 continue;
67 };
68 let slot = G::node_slot(endpoints.target());
69 indegree[slot] -= 1;
70 if indegree[slot] == 0 {
71 ready.push_back(endpoints.target());
72 }
73 }
74 }
75 (order.len() == graph.node_count()).then_some(order)
76}
77
78#[must_use]
79pub fn has_cycle<G>(graph: &G) -> bool
80where
81 G: IndexGraphView,
82{
83 topological_sort(graph).is_none()
84}
85
86#[must_use]
87pub fn find_cycle<G>(graph: &G) -> Option<Vec<G::Node>>
88where
89 G: IndexGraphView,
90{
91 let mut adjacency = vec![Vec::new(); graph.node_bound()];
92 for edge in graph.edge_indices() {
93 if let Some(endpoints) = graph.edge_endpoints(edge) {
94 adjacency[G::node_slot(endpoints.source())].push(endpoints.target());
95 }
96 }
97 let mut color = vec![0_u8; graph.node_bound()];
98 let mut parent = vec![None; graph.node_bound()];
99 for start in graph.node_indices() {
100 if color[G::node_slot(start)] != 0 {
101 continue;
102 }
103 color[G::node_slot(start)] = 1;
104 let mut stack = vec![(start, 0_usize)];
105 while let Some((node, next)) = stack.last_mut() {
106 let slot = G::node_slot(*node);
107 let Some(&target) = adjacency[slot].get(*next) else {
108 color[slot] = 2;
109 stack.pop();
110 continue;
111 };
112 *next += 1;
113 let target_slot = G::node_slot(target);
114 if color[target_slot] == 0 {
115 parent[target_slot] = Some(*node);
116 color[target_slot] = 1;
117 stack.push((target, 0));
118 } else if color[target_slot] == 1 {
119 return reconstruct_cycle::<G>(*node, target, &parent);
120 }
121 }
122 }
123 None
124}
125
126fn reconstruct_cycle<G>(
127 node: G::Node,
128 target: G::Node,
129 parent: &[Option<G::Node>],
130) -> Option<Vec<G::Node>>
131where
132 G: IndexGraphView,
133{
134 let mut cycle = vec![node];
135 let mut cursor = node;
136 while cursor != target {
137 cursor = parent[G::node_slot(cursor)]?;
138 cycle.push(cursor);
139 }
140 cycle.reverse();
141 cycle.push(target);
142 Some(cycle)
143}
144
145fn finish_from<G>(graph: &G, start: G::Node, seen: &mut [bool], finish: &mut Vec<G::Node>)
146where
147 G: IndexGraphView,
148{
149 if seen[G::node_slot(start)] {
150 return;
151 }
152 let mut stack = vec![(start, false)];
153 while let Some((node, exiting)) = stack.pop() {
154 let slot = G::node_slot(node);
155 if exiting {
156 finish.push(node);
157 continue;
158 }
159 if seen[slot] {
160 continue;
161 }
162 seen[slot] = true;
163 stack.push((node, true));
164 for edge in graph.outgoing_edges(node).rev() {
165 let Some(target) = graph.edge_endpoints(edge).map(crate::EdgeEndpoints::target) else {
166 continue;
167 };
168 if !seen[G::node_slot(target)] {
169 stack.push((target, false));
170 }
171 }
172 }
173}