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}