rustworkx_core/traversal/
dfs_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
13// This module is an iterative implementation of the upstream petgraph
14// ``depth_first_search`` function.
15// https://github.com/petgraph/petgraph/blob/0.6.0/src/visit/dfsvisit.rs
16
17use petgraph::visit::{ControlFlow, EdgeRef, IntoEdges, Time, VisitMap, Visitable};
18
19use super::try_control;
20
21/// A depth first search (DFS) visitor event.
22///
23/// It's similar to upstream petgraph
24/// [`DfsEvent`](https://docs.rs/petgraph/0.6.0/petgraph/visit/enum.DfsEvent.html)
25/// event.
26#[derive(Copy, Clone, Debug)]
27pub enum DfsEvent<N, E> {
28    Discover(N, Time),
29    /// An edge of the tree formed by the traversal.
30    TreeEdge(N, N, E),
31    /// An edge to an already visited node.
32    BackEdge(N, N, E),
33    /// A cross or forward edge.
34    ///
35    /// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
36    /// then it is a forward edge, else a cross edge.
37    CrossForwardEdge(N, N, E),
38    /// All edges from a node have been reported.
39    Finish(N, Time),
40}
41
42/// An iterative depth first search.
43///
44/// It is an iterative implementation of the upstream petgraph
45/// [`depth_first_search`](https://docs.rs/petgraph/0.6.0/petgraph/visit/fn.depth_first_search.html) function.
46///
47/// Starting points are the nodes in the iterator `starts` (specify just one
48/// start vertex *x* by using `Some(x)`).
49///
50/// The traversal emits discovery and finish events for each reachable vertex,
51/// and edge classification of each reachable edge. `visitor` is called for each
52/// event, see `petgraph::DfsEvent` for possible values.
53///
54/// The return value should implement the trait `ControlFlow`, and can be used to change
55/// the control flow of the search.
56///
57/// `Control` Implements `ControlFlow` such that `Control::Continue` resumes the search.
58/// `Control::Break` will stop the visit early, returning the contained value.
59/// `Control::Prune` will stop traversing any additional edges from the current
60/// node and proceed immediately to the `Finish` event.
61///
62/// There are implementations of `ControlFlow` for `()`, and `Result<C, E>` where
63/// `C: ControlFlow`. The implementation for `()` will continue until finished.
64/// For `Result`, upon encountering an `E` it will break, otherwise acting the same as `C`.
65///
66/// ***Panics** if you attempt to prune a node from its `Finish` event.
67///
68/// Pseudo-code for the DFS algorithm is listed below, with the annotated event points,
69/// for which the given visitor object will be called with the appropriate method.
70///
71/// ```norust
72/// // G - graph, s - single source node
73/// DFS(G, s)
74///   let color be a mapping                        // color[u] - vertex u color WHITE/GRAY/BLACK
75///   for each u in G                               // u - vertex in G
76///     color[u] := WHITE                           // color all as undiscovered
77///   end for
78///   time := 0
79///   let S be a stack
80///   PUSH(S, (s, iterator of OutEdges(G, s)))      // S - stack of vertices and edge iterators
81///   color[s] := GRAY                              // event: Discover(s, time)
82///   while (S is not empty)
83///     let (u, iterator) := LAST(S)
84///     flag := False                               // whether edge to undiscovered vertex found
85///     for each v, w in iterator                   // v - target vertex, w - edge weight
86///       if (WHITE = color[v])                     // event: TreeEdge(u, v, w)
87///         time := time + 1
88///         color[v] := GRAY                        // event: Discover(v, time)
89///         flag := True
90///         break
91///       elif (GRAY = color[v])                    // event: BackEdge(u, v, w)
92///         ...
93///       elif (BLACK = color[v])                   // event: CrossForwardEdge(u, v, w)
94///         ...
95///     end for
96///     if (flag is True)
97///       PUSH(S, (v, iterator of OutEdges(G, v)))
98///     elif (flag is False)
99///       time := time + 1
100///       color[u] := BLACK                         // event: Finish(u, time)
101///       POP(S)
102///   end while
103/// ```
104///
105/// # Example returning `Control`.
106///
107/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
108/// the goal vertex.
109///
110/// ```
111/// use rustworkx_core::petgraph::prelude::*;
112/// use rustworkx_core::petgraph::graph::node_index as n;
113/// use rustworkx_core::petgraph::visit::Control;
114///
115/// use rustworkx_core::traversal::{DfsEvent, depth_first_search};
116///
117/// let gr: Graph<(), ()> = Graph::from_edges(&[
118///     (0, 1), (0, 2), (0, 3),
119///     (1, 3),
120///     (2, 3), (2, 4),
121///     (4, 0), (4, 5),
122/// ]);
123///
124/// // record each predecessor, mapping node → node
125/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
126/// let start = n(0);
127/// let goal = n(5);
128/// depth_first_search(&gr, Some(start), |event| {
129///     if let DfsEvent::TreeEdge(u, v, _) = event {
130///         predecessor[v.index()] = u;
131///         if v == goal {
132///             return Control::Break(v);
133///         }
134///     }
135///     Control::Continue
136/// });
137///
138/// let mut next = goal;
139/// let mut path = vec![next];
140/// while next != start {
141///     let pred = predecessor[next.index()];
142///     path.push(pred);
143///     next = pred;
144/// }
145/// path.reverse();
146/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
147/// ```
148///
149/// # Example returning a `Result`.
150/// ```
151/// use rustworkx_core::petgraph::graph::node_index as n;
152/// use rustworkx_core::petgraph::prelude::*;
153/// use rustworkx_core::petgraph::visit::Time;
154///
155/// use rustworkx_core::traversal::{DfsEvent, depth_first_search};
156///
157/// let gr: Graph<(), ()> = Graph::from_edges(&[(0, 1), (1, 2), (1, 1), (2, 1)]);
158/// let start = n(0);
159/// let mut back_edges = 0;
160/// let mut discover_time = 0;
161///
162/// #[derive(Debug)]
163/// struct BackEdgeFound {
164///     source: NodeIndex,
165///     target: NodeIndex,
166/// }
167///
168/// // Stop the search, the first time a BackEdge is encountered.
169/// let result = depth_first_search(&gr, Some(start), |event| {
170///     match event {
171///         // In the cases where Ok(()) is returned,
172///         // Result falls back to the implementation of Control on the value ().
173///         // In the case of (), this is to always return Control::Continue.
174///         // continuing the search.
175///         DfsEvent::Discover(_, Time(t)) => {
176///             discover_time = t;
177///             Ok(())
178///         }
179///         DfsEvent::BackEdge(u, v, _) => {
180///             back_edges += 1;
181///             // the implementation of ControlFlow for Result,
182///             // treats this Err value as Continue::Break
183///             Err(BackEdgeFound {source: u, target: v})
184///         }
185///         _ => Ok(()),
186///     }
187/// });
188///
189/// // Even though the graph has more than one cycle,
190/// // The number of back_edges visited by the search should always be 1.
191/// assert_eq!(back_edges, 1);
192/// println!("discover time:{:?}", discover_time);
193/// println!("number of backedges encountered: {}", back_edges);
194/// println!("back edge: ({:?})", result.unwrap_err());
195/// ```
196pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
197where
198    G: IntoEdges + Visitable,
199    I: IntoIterator<Item = G::NodeId>,
200    F: FnMut(DfsEvent<G::NodeId, &G::EdgeWeight>) -> C,
201    C: ControlFlow,
202{
203    let time = &mut Time(0);
204    let discovered = &mut graph.visit_map();
205    let finished = &mut graph.visit_map();
206
207    for start in starts {
208        try_control!(
209            dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
210            unreachable!()
211        );
212    }
213    C::continuing()
214}
215
216fn dfs_visitor<G, F, C>(
217    graph: G,
218    u: G::NodeId,
219    visitor: &mut F,
220    discovered: &mut G::Map,
221    finished: &mut G::Map,
222    time: &mut Time,
223) -> C
224where
225    G: IntoEdges + Visitable,
226    F: FnMut(DfsEvent<G::NodeId, &G::EdgeWeight>) -> C,
227    C: ControlFlow,
228{
229    if !discovered.visit(u) {
230        return C::continuing();
231    }
232
233    try_control!(visitor(DfsEvent::Discover(u, time_post_inc(time))), {}, {
234        let mut stack: Vec<(G::NodeId, <G as IntoEdges>::Edges)> = Vec::new();
235        stack.push((u, graph.edges(u)));
236
237        while let Some(elem) = stack.last_mut() {
238            let u = elem.0;
239            let adjacent_edges = &mut elem.1;
240            let mut next = None;
241
242            for edge in adjacent_edges {
243                let v = edge.target();
244                if !discovered.is_visited(&v) {
245                    try_control!(visitor(DfsEvent::TreeEdge(u, v, edge.weight())), continue);
246                    discovered.visit(v);
247                    try_control!(
248                        visitor(DfsEvent::Discover(v, time_post_inc(time))),
249                        continue
250                    );
251                    next = Some(v);
252                    break;
253                } else if !finished.is_visited(&v) {
254                    try_control!(visitor(DfsEvent::BackEdge(u, v, edge.weight())), continue);
255                } else {
256                    try_control!(
257                        visitor(DfsEvent::CrossForwardEdge(u, v, edge.weight())),
258                        continue
259                    );
260                }
261            }
262
263            match next {
264                Some(v) => stack.push((v, graph.edges(v))),
265                None => {
266                    let first_finish = finished.visit(u);
267                    debug_assert!(first_finish);
268                    try_control!(
269                        visitor(DfsEvent::Finish(u, time_post_inc(time))),
270                        panic!("Pruning on the `DfsEvent::Finish` is not supported!")
271                    );
272                    stack.pop();
273                }
274            };
275        }
276    });
277
278    C::continuing()
279}
280
281fn time_post_inc(x: &mut Time) -> Time {
282    let v = *x;
283    x.0 += 1;
284    v
285}