toolbox_rs/
bfs.rs

1use crate::graph::{EdgeID, Graph, INVALID_NODE_ID, NodeID};
2use bitvec::vec::BitVec;
3use log::{debug, info};
4use std::{collections::VecDeque, time::Instant};
5
6pub struct BFS {
7    sources: Vec<NodeID>,
8    target_set: BitVec,
9    parents: Vec<NodeID>,
10    target: NodeID,
11    queue: VecDeque<usize>,
12    empty_target_set: bool,
13}
14
15impl BFS {
16    // TODO: Also pass Graph instance
17    pub fn new(source_list: &[NodeID], target_list: &[NodeID], number_of_nodes: usize) -> Self {
18        let mut temp = Self {
19            sources: source_list.to_vec(),
20            target_set: BitVec::with_capacity(number_of_nodes),
21            parents: Vec::new(),
22            target: INVALID_NODE_ID,
23            queue: VecDeque::new(),
24            empty_target_set: target_list.is_empty(),
25        };
26
27        // initialize bit vector storing which nodes are targets
28        temp.target_set.resize(number_of_nodes, false);
29        for i in target_list {
30            temp.target_set.set(*i, true);
31        }
32
33        temp.populate_sources(number_of_nodes);
34        temp
35    }
36
37    fn populate_sources(&mut self, number_of_nodes: usize) {
38        self.parents.resize(number_of_nodes, INVALID_NODE_ID);
39        for s in &self.sources {
40            self.parents[*s] = *s;
41        }
42    }
43
44    pub fn run<T, G: Graph<T>>(&mut self, graph: &G) -> bool {
45        self.run_with_filter(graph, |_graph, _edge| false)
46    }
47
48    /// explore the graph in a BFS
49    /// returns true if a path between s and t was found or no target was given
50    pub fn run_with_filter<T, F, G: Graph<T>>(&mut self, graph: &G, filter: F) -> bool
51    where
52        F: Fn(&G, EdgeID) -> bool,
53    {
54        let start = Instant::now();
55        // reset queue w/o allocating
56        self.queue.clear();
57        self.queue.extend(self.sources.iter().copied());
58
59        // reset parents vector
60        self.parents.fill(INVALID_NODE_ID);
61        for s in &self.sources {
62            self.parents[*s] = *s;
63        }
64
65        while let Some(node) = self.queue.pop_front() {
66            let node_is_source = self.parents[node] == node;
67            // sources have themselves as parents
68            for edge in graph.edge_range(node) {
69                if filter(graph, edge) {
70                    continue;
71                }
72                let target = graph.target(edge);
73                if self.parents[target] != INVALID_NODE_ID
74                    || (node_is_source && self.parents[target] == target)
75                {
76                    // we already have seen this node and can ignore it, or
77                    // edge is fully contained within source set and we can ignore it, too
78                    continue;
79                }
80                self.parents[target] = node;
81                unsafe {
82                    // unsafe is used for performance, here, as the graph is consistent by construction
83                    if *self.target_set.get_unchecked(target) {
84                        self.target = target;
85                        debug!("setting target {}", self.target);
86                        // check if we have found our target if it exists
87                        let duration = start.elapsed();
88                        info!("D/BFS took: {duration:?} (done)");
89                        return true;
90                    }
91                }
92                self.queue.push_back(target);
93            }
94        }
95
96        let duration = start.elapsed();
97        info!("BFS took: {duration:?} (done)");
98
99        // return true only if target set was empty
100        self.empty_target_set
101    }
102
103    // path unpacking, by searching for the first target that was found
104    // and by then unwinding the path of nodes to it.
105    pub fn fetch_node_path(&self) -> Vec<NodeID> {
106        self.fetch_node_path_from_node(self.target)
107    }
108
109    // path unpacking, by searching for the first target that was found
110    // and by then unwinding the path of nodes to it.
111    // TODO: needs test to check what happens when t is unknown, or unvisited. Can this be removed?
112    pub fn fetch_node_path_from_node(&self, t: NodeID) -> Vec<NodeID> {
113        let mut id = t;
114        let mut path = Vec::new();
115        while id != self.parents[id] {
116            path.push(id);
117            id = self.parents[id];
118        }
119        path.push(id);
120        path.reverse();
121        path
122    }
123
124    // TODO: the reverse might be unnecessary to some applications
125    pub fn fetch_edge_path<T>(&self, graph: &impl Graph<T>) -> Vec<EdgeID> {
126        // path unpacking
127        let mut id = self.target;
128        let mut path = Vec::new();
129        while id != self.parents[id] {
130            let edge_id = graph.find_edge(self.parents[id], id).unwrap();
131            path.push(edge_id);
132            id = self.parents[id];
133        }
134
135        path.reverse();
136        path
137    }
138
139    //TODO: Add test covering this iterator
140    pub fn path_iter(&self) -> PathIter {
141        PathIter::new(self)
142    }
143}
144
145pub struct PathIter<'a> {
146    bfs: &'a BFS,
147    id: usize,
148}
149
150impl PathIter<'_> {
151    pub fn new(bfs: &BFS) -> PathIter {
152        debug!("init: {}", bfs.target);
153        PathIter {
154            bfs,
155            id: bfs.target,
156        }
157    }
158}
159
160impl Iterator for PathIter<'_> {
161    type Item = NodeID;
162    fn next(&mut self) -> Option<NodeID> {
163        if self.id == INVALID_NODE_ID {
164            // INVALID_NODE_ID is the indicator that unpacking is done or not possible
165            return None;
166        }
167
168        // path unpacking step
169        let result = self.id;
170        self.id = self.bfs.parents[self.id];
171        if result == self.bfs.parents[result] {
172            self.id = INVALID_NODE_ID;
173        }
174        Some(result)
175    }
176}
177
178#[cfg(test)]
179mod tests {
180    use crate::edge::InputEdge;
181    use crate::graph::Graph;
182    use crate::{bfs::BFS, static_graph::StaticGraph};
183
184    #[test]
185    fn s_t_query_fetch_node_string() {
186        type Graph = StaticGraph<i32>;
187        let edges = vec![
188            InputEdge::new(0, 1, 3),
189            InputEdge::new(1, 2, 3),
190            InputEdge::new(4, 2, 1),
191            InputEdge::new(2, 3, 6),
192            InputEdge::new(0, 4, 2),
193            InputEdge::new(4, 5, 2),
194            InputEdge::new(5, 3, 7),
195            InputEdge::new(1, 5, 2),
196        ];
197        let graph = Graph::new(edges);
198        let mut bfs = BFS::new(&[0], &[5], graph.number_of_nodes());
199        assert!(bfs.run(&graph));
200
201        let path = bfs.fetch_node_path();
202        assert_eq!(path, vec![0, 1, 5]);
203
204        let path: Vec<usize> = bfs.path_iter().collect();
205        assert_eq!(path, vec![5, 1, 0]);
206    }
207
208    #[test]
209    fn s_t_query_edge_list() {
210        type Graph = StaticGraph<i32>;
211        let edges = vec![
212            InputEdge::new(0, 1, 3),
213            InputEdge::new(1, 2, 3),
214            InputEdge::new(4, 2, 1),
215            InputEdge::new(2, 3, 6),
216            InputEdge::new(0, 4, 2),
217            InputEdge::new(4, 5, 2),
218            InputEdge::new(5, 3, 7),
219            InputEdge::new(1, 5, 2),
220        ];
221        let graph = Graph::new(edges);
222        let mut bfs = BFS::new(&[0], &[5], graph.number_of_nodes());
223        assert!(bfs.run(&graph));
224        let path = bfs.fetch_edge_path(&graph);
225        assert_eq!(path, vec![0, 3]);
226    }
227
228    #[test]
229    fn s_all_query() {
230        type Graph = StaticGraph<i32>;
231        let edges = vec![
232            InputEdge::new(0, 1, 3),
233            InputEdge::new(1, 2, 3),
234            InputEdge::new(4, 2, 1),
235            InputEdge::new(2, 3, 6),
236            InputEdge::new(0, 4, 2),
237            InputEdge::new(4, 5, 2),
238            InputEdge::new(5, 3, 7),
239            InputEdge::new(1, 5, 2),
240        ];
241        let graph = Graph::new(edges);
242        let mut bfs = BFS::new(&[0], &[], graph.number_of_nodes());
243        assert!(bfs.run(&graph));
244
245        let path = bfs.fetch_node_path_from_node(3);
246        assert_eq!(path, vec![0, 1, 2, 3]);
247
248        let path: Vec<usize> = bfs.path_iter().collect();
249        assert!(path.is_empty());
250    }
251
252    #[test]
253    fn multi_s_all_query() {
254        type Graph = StaticGraph<i32>;
255        let edges = vec![
256            InputEdge::new(0, 1, 3),
257            InputEdge::new(1, 2, 3),
258            InputEdge::new(4, 2, 1),
259            InputEdge::new(2, 3, 6),
260            InputEdge::new(0, 4, 2),
261            InputEdge::new(4, 5, 2),
262            InputEdge::new(5, 3, 7),
263            InputEdge::new(1, 5, 2),
264        ];
265        let graph = Graph::new(edges);
266        let mut bfs = BFS::new(&[0, 1], &[], graph.number_of_nodes());
267        assert!(bfs.run(&graph));
268
269        // path unpacking
270        let path = bfs.fetch_node_path_from_node(3);
271        assert_eq!(path, vec![1, 2, 3]);
272
273        let path: Vec<usize> = bfs.path_iter().collect();
274        assert!(path.is_empty());
275    }
276}