Skip to main content

rustworkx_core/traversal/
bfs_visit.rs

1// Licensed under the Apache License, Version 2.0 (the "License"); you may
2// not use this file except in compliance with the License. You may obtain
3// a copy of the License at
4//
5//     http://www.apache.org/licenses/LICENSE-2.0
6//
7// Unless required by applicable law or agreed to in writing, software
8// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10// License for the specific language governing permissions and limitations
11// under the License.
12
13use super::try_control;
14use petgraph::visit::{ControlFlow, EdgeRef, IntoEdges, VisitMap, Visitable};
15use std::collections::VecDeque;
16
17/// A breadth first search (BFS) visitor event.
18#[derive(Copy, Clone, Debug)]
19pub enum BfsEvent<N, E> {
20    Discover(N),
21    /// An edge of the tree formed by the traversal.
22    TreeEdge(N, N, E),
23    /// An edge that does not belong to the tree.
24    NonTreeEdge(N, N, E),
25    /// For an edge *(u, v)*, if node *v* is currently in the queue
26    /// at the time of examination, then it is a gray-target edge.
27    GrayTargetEdge(N, N, E),
28    /// For an edge *(u, v)*, if node *v* has been removed from the queue
29    /// at the time of examination, then it is a black-target edge.
30    BlackTargetEdge(N, N, E),
31    /// All edges from a node have been reported.
32    Finish(N),
33}
34
35/// An iterative breadth first search.
36///
37/// Starting points are the nodes in the iterator `starts` (specify just one
38/// start vertex *x* by using `Some(x)`).
39///
40/// The traversal emits discovery and finish events for each reachable vertex,
41/// and edge classification of each reachable edge. `visitor` is called for each
42/// event, see [`BfsEvent`] for possible values.
43///
44/// The return value should implement the trait [`ControlFlow`], and can be used to change
45/// the control flow of the search.
46///
47/// [`Control`](petgraph::visit::Control) Implements [`ControlFlow`] such that `Control::Continue` resumes the search.
48/// `Control::Break` will stop the visit early, returning the contained value.
49/// `Control::Prune` will stop traversing any additional edges from the current
50/// node and proceed immediately to the `Finish` event.
51///
52/// There are implementations of [`ControlFlow`] for `()`, and [`Result<C, E>`] where
53/// `C: ControlFlow`. The implementation for `()` will continue until finished.
54/// For [`Result`], upon encountering an `E` it will break, otherwise acting the same as `C`.
55///
56/// ***Panics** if you attempt to prune a node from its `Finish` event.
57///
58/// Pseudo-code for the BFS algorithm is listed below, with the annotated event points,
59/// for which the given visitor object will be called with the appropriate method.
60///
61/// ```norust
62/// // G - graph, s - single source node
63/// BFS(G, s)
64///   let color be a mapping             // color[u] - vertex u color WHITE/GRAY/BLACK
65///   for each u in G                    // u - vertex in G
66///     color[u] := WHITE                // color all vertices as undiscovered
67///   end for
68///   let Q be a queue
69///   ENQUEUE(Q, s)
70///   color[s] := GRAY                   // event: Discover(s)
71///   while (Q is not empty)
72///     u := DEQUEUE(Q)
73///     for each v, w in OutEdges(G, u)  // v - target vertex, w - edge weight
74///       if (WHITE = color[v])          // event: TreeEdge(u, v, w)
75///         color[v] := GRAY             // event: Discover(v)
76///         ENQUEUE(Q, v)
77///       else                           // event: NonTreeEdge(u, v, w)
78///         if (GRAY = color[v])         // event: GrayTargetEdge(u, v, w)
79///           ...                       
80///         elif (BLACK = color[v])      // event: BlackTargetEdge(u, v, w)
81///           ...                       
82///     end for
83///     color[u] := BLACK                // event: Finish(u)
84///   end while
85/// ```
86///
87/// # Example returning [`Control`](petgraph::visit::Control).
88///
89/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
90/// the goal vertex.
91///
92/// ```
93/// use rustworkx_core::petgraph::prelude::*;
94/// use rustworkx_core::petgraph::graph::node_index as n;
95/// use rustworkx_core::petgraph::visit::Control;
96///
97/// use rustworkx_core::traversal::{BfsEvent, breadth_first_search};
98///
99/// let gr: Graph<(), ()> = Graph::from_edges(&[
100///     (0, 1), (0, 2), (0, 3),
101///     (1, 3),
102///     (2, 3), (2, 4),
103///     (4, 0), (4, 5),
104/// ]);
105///
106/// // record each predecessor, mapping node → node
107/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
108/// let start = n(0);
109/// let goal = n(5);
110/// breadth_first_search(&gr, Some(start), |event| {
111///     if let BfsEvent::TreeEdge(u, v, _) = event {
112///         predecessor[v.index()] = u;
113///         if v == goal {
114///             return Control::Break(v);
115///         }
116///     }
117///     Control::Continue
118/// });
119///
120/// let mut next = goal;
121/// let mut path = vec![next];
122/// while next != start {
123///     let pred = predecessor[next.index()];
124///     path.push(pred);
125///     next = pred;
126/// }
127/// path.reverse();
128/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
129/// ```
130///
131/// # Example returning a `Result`.
132/// ```
133/// use rustworkx_core::petgraph::graph::node_index as n;
134/// use rustworkx_core::petgraph::prelude::*;
135///
136/// use rustworkx_core::traversal::{BfsEvent, breadth_first_search};
137///
138/// let gr: Graph<(), ()> = Graph::from_edges(&[(0, 1), (1, 2), (1, 1), (2, 1)]);
139/// let start = n(0);
140/// let mut non_tree_edges = 0;
141///
142/// #[derive(Debug)]
143/// struct NonTreeEdgeFound {
144///     source: NodeIndex,
145///     target: NodeIndex,
146/// }
147///
148/// // Stop the search, the first time a BackEdge is encountered.
149/// let result = breadth_first_search(&gr, Some(start), |event| {
150///     match event {
151///         BfsEvent::NonTreeEdge(u, v, _) => {
152///             non_tree_edges += 1;
153///             // the implementation of ControlFlow for Result,
154///             // treats this Err value as Continue::Break
155///             Err(NonTreeEdgeFound {source: u, target: v})
156///         }
157///         // In the cases where Ok(()) is returned,
158///         // Result falls back to the implementation of Control on the value ().
159///         // In the case of (), this is to always return Control::Continue.
160///         // continuing the search.
161///         _ => Ok(()),
162///     }
163/// });
164///
165/// assert_eq!(non_tree_edges, 1);
166/// println!("number of non-tree edges encountered: {}", non_tree_edges);
167/// println!("non-tree edge: ({:?})", result.unwrap_err());
168/// ```
169pub fn breadth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
170where
171    G: IntoEdges + Visitable,
172    I: IntoIterator<Item = G::NodeId>,
173    F: FnMut(BfsEvent<G::NodeId, &G::EdgeWeight>) -> C,
174    C: ControlFlow,
175{
176    let discovered = &mut graph.visit_map();
177    let finished = &mut graph.visit_map();
178
179    for start in starts {
180        // `bfs_visitor` returns a "signal" to either continue or exit early
181        // but it never "prunes", so we use `unreachable`.
182        try_control!(
183            bfs_visitor(graph, start, &mut visitor, discovered, finished),
184            unreachable!()
185        );
186    }
187    C::continuing()
188}
189
190fn bfs_visitor<G, F, C>(
191    graph: G,
192    u: G::NodeId,
193    visitor: &mut F,
194    discovered: &mut G::Map,
195    finished: &mut G::Map,
196) -> C
197where
198    G: IntoEdges + Visitable,
199    F: FnMut(BfsEvent<G::NodeId, &G::EdgeWeight>) -> C,
200    C: ControlFlow,
201{
202    if !discovered.visit(u) {
203        return C::continuing();
204    }
205
206    try_control!(visitor(BfsEvent::Discover(u)), {}, {
207        let mut stack: VecDeque<G::NodeId> = VecDeque::new();
208        stack.push_front(u);
209
210        while let Some(u) = stack.pop_front() {
211            for edge in graph.edges(u) {
212                let v = edge.target();
213                if !discovered.is_visited(&v) {
214                    try_control!(visitor(BfsEvent::TreeEdge(u, v, edge.weight())), continue);
215                    discovered.visit(v);
216                    try_control!(visitor(BfsEvent::Discover(v)), continue);
217                    stack.push_back(v);
218                } else {
219                    // non - tree edge.
220                    try_control!(
221                        visitor(BfsEvent::NonTreeEdge(u, v, edge.weight())),
222                        continue
223                    );
224
225                    if !finished.is_visited(&v) {
226                        try_control!(
227                            visitor(BfsEvent::GrayTargetEdge(u, v, edge.weight())),
228                            continue
229                        );
230                    } else {
231                        try_control!(
232                            visitor(BfsEvent::BlackTargetEdge(u, v, edge.weight())),
233                            continue
234                        );
235                    }
236                }
237            }
238
239            let first_finish = finished.visit(u);
240            debug_assert!(first_finish);
241            try_control!(
242                visitor(BfsEvent::Finish(u)),
243                panic!("Pruning on the `BfsEvent::Finish` is not supported!")
244            );
245        }
246    });
247
248    C::continuing()
249}
250
251pub fn bfs_layers<G, I>(graph: G, sources: I) -> Vec<Vec<G::NodeId>>
252where
253    G: IntoEdges + Visitable,
254    I: IntoIterator<Item = G::NodeId>,
255    G::NodeId: Copy + std::hash::Hash + Eq,
256{
257    let mut visited = hashbrown::HashSet::new();
258    let mut current_layer: Vec<G::NodeId> = sources.into_iter().collect();
259
260    for &node in &current_layer {
261        visited.insert(node);
262    }
263
264    let mut layers: Vec<Vec<G::NodeId>> = Vec::new();
265
266    while !current_layer.is_empty() {
267        layers.push(current_layer.clone());
268
269        let mut next_layer = Vec::new();
270        for &node in &current_layer {
271            for edge in graph.edges(node) {
272                let child = edge.target();
273                if visited.insert(child) {
274                    next_layer.push(child);
275                }
276            }
277        }
278
279        current_layer = next_layer;
280    }
281
282    layers
283}