trueno_graph/algorithms/
traversal.rs1use crate::storage::CsrGraph;
6use crate::NodeId;
7use anyhow::Result;
8use std::collections::{HashSet, VecDeque};
9
10pub fn find_callers(graph: &CsrGraph, target: NodeId, max_depth: usize) -> Result<Vec<u32>> {
42 let mut visited = HashSet::new();
43 let mut frontier = VecDeque::new();
44 let mut depth = 0;
45
46 frontier.push_back(target.0);
48 visited.insert(target.0);
49
50 while !frontier.is_empty() && depth < max_depth {
51 let level_size = frontier.len();
52
53 for _ in 0..level_size {
54 if let Some(current) = frontier.pop_front() {
55 let callers = graph.incoming_neighbors(NodeId(current))?;
57
58 for &caller in callers {
59 if !visited.contains(&caller) {
60 visited.insert(caller);
61 frontier.push_back(caller);
62 }
63 }
64 }
65 }
66
67 depth += 1;
68 }
69
70 visited.remove(&target.0);
72
73 Ok(visited.into_iter().collect())
74}
75
76pub fn bfs(graph: &CsrGraph, source: NodeId) -> Result<Vec<u32>> {
106 let mut visited = HashSet::new();
107 let mut queue = VecDeque::new();
108
109 queue.push_back(source.0);
110 visited.insert(source.0);
111
112 while let Some(current) = queue.pop_front() {
113 let neighbors = graph.outgoing_neighbors(NodeId(current))?;
115
116 for &neighbor in neighbors {
117 if !visited.contains(&neighbor) {
118 visited.insert(neighbor);
119 queue.push_back(neighbor);
120 }
121 }
122 }
123
124 Ok(visited.into_iter().collect())
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
132 fn test_find_callers_direct() {
133 let edges = vec![(NodeId(0), NodeId(2), 1.0), (NodeId(1), NodeId(2), 1.0)];
135 let graph = CsrGraph::from_edge_list(&edges).unwrap();
136
137 let callers = find_callers(&graph, NodeId(2), 1).unwrap();
138 assert_eq!(callers.len(), 2);
139 assert!(callers.contains(&0));
140 assert!(callers.contains(&1));
141 }
142
143 #[test]
144 fn test_find_callers_transitive() {
145 let edges = vec![(NodeId(0), NodeId(1), 1.0), (NodeId(1), NodeId(2), 1.0)];
147 let graph = CsrGraph::from_edge_list(&edges).unwrap();
148
149 let callers = find_callers(&graph, NodeId(2), 10).unwrap();
150 assert_eq!(callers.len(), 2);
151 assert!(callers.contains(&0));
152 assert!(callers.contains(&1));
153 }
154
155 #[test]
156 fn test_find_callers_depth_limit() {
157 let edges = vec![
159 (NodeId(0), NodeId(1), 1.0),
160 (NodeId(1), NodeId(2), 1.0),
161 (NodeId(2), NodeId(3), 1.0),
162 ];
163 let graph = CsrGraph::from_edge_list(&edges).unwrap();
164
165 let callers = find_callers(&graph, NodeId(3), 1).unwrap();
167 assert_eq!(callers.len(), 1);
168 assert!(callers.contains(&2));
169
170 let callers = find_callers(&graph, NodeId(3), 2).unwrap();
172 assert_eq!(callers.len(), 2);
173 assert!(callers.contains(&1));
174 assert!(callers.contains(&2));
175
176 let callers = find_callers(&graph, NodeId(3), 10).unwrap();
178 assert_eq!(callers.len(), 3);
179 assert!(callers.contains(&0));
180 assert!(callers.contains(&1));
181 assert!(callers.contains(&2));
182 }
183
184 #[test]
185 fn test_bfs_simple() {
186 let edges = vec![(NodeId(0), NodeId(1), 1.0), (NodeId(1), NodeId(2), 1.0)];
187 let graph = CsrGraph::from_edge_list(&edges).unwrap();
188
189 let reachable = bfs(&graph, NodeId(0)).unwrap();
190 assert_eq!(reachable.len(), 3);
191 assert!(reachable.contains(&0));
192 assert!(reachable.contains(&1));
193 assert!(reachable.contains(&2));
194 }
195
196 #[test]
197 fn test_bfs_disconnected() {
198 let edges = vec![
199 (NodeId(0), NodeId(1), 1.0),
200 (NodeId(2), NodeId(3), 1.0), ];
202 let graph = CsrGraph::from_edge_list(&edges).unwrap();
203
204 let reachable = bfs(&graph, NodeId(0)).unwrap();
205 assert_eq!(reachable.len(), 2); assert!(reachable.contains(&0));
207 assert!(reachable.contains(&1));
208 assert!(!reachable.contains(&2)); }
210}