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/// # Example returning `Control`.
69///
70/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
71/// the goal vertex.
72///
73/// ```
74/// use rustworkx_core::petgraph::prelude::*;
75/// use rustworkx_core::petgraph::graph::node_index as n;
76/// use rustworkx_core::petgraph::visit::Control;
77///
78/// use rustworkx_core::traversal::{DfsEvent, depth_first_search};
79///
80/// let gr: Graph<(), ()> = Graph::from_edges(&[
81/// (0, 1), (0, 2), (0, 3),
82/// (1, 3),
83/// (2, 3), (2, 4),
84/// (4, 0), (4, 5),
85/// ]);
86///
87/// // record each predecessor, mapping node → node
88/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
89/// let start = n(0);
90/// let goal = n(5);
91/// depth_first_search(&gr, Some(start), |event| {
92/// if let DfsEvent::TreeEdge(u, v, _) = event {
93/// predecessor[v.index()] = u;
94/// if v == goal {
95/// return Control::Break(v);
96/// }
97/// }
98/// Control::Continue
99/// });
100///
101/// let mut next = goal;
102/// let mut path = vec![next];
103/// while next != start {
104/// let pred = predecessor[next.index()];
105/// path.push(pred);
106/// next = pred;
107/// }
108/// path.reverse();
109/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
110/// ```
111///
112/// # Example returning a `Result`.
113/// ```
114/// use rustworkx_core::petgraph::graph::node_index as n;
115/// use rustworkx_core::petgraph::prelude::*;
116/// use rustworkx_core::petgraph::visit::Time;
117///
118/// use rustworkx_core::traversal::{DfsEvent, depth_first_search};
119///
120/// let gr: Graph<(), ()> = Graph::from_edges(&[(0, 1), (1, 2), (1, 1), (2, 1)]);
121/// let start = n(0);
122/// let mut back_edges = 0;
123/// let mut discover_time = 0;
124///
125/// #[derive(Debug)]
126/// struct BackEdgeFound {
127/// source: NodeIndex,
128/// target: NodeIndex,
129/// }
130///
131/// // Stop the search, the first time a BackEdge is encountered.
132/// let result = depth_first_search(&gr, Some(start), |event| {
133/// match event {
134/// // In the cases where Ok(()) is returned,
135/// // Result falls back to the implementation of Control on the value ().
136/// // In the case of (), this is to always return Control::Continue.
137/// // continuing the search.
138/// DfsEvent::Discover(_, Time(t)) => {
139/// discover_time = t;
140/// Ok(())
141/// }
142/// DfsEvent::BackEdge(u, v, _) => {
143/// back_edges += 1;
144/// // the implementation of ControlFlow for Result,
145/// // treats this Err value as Continue::Break
146/// Err(BackEdgeFound {source: u, target: v})
147/// }
148/// _ => Ok(()),
149/// }
150/// });
151///
152/// // Even though the graph has more than one cycle,
153/// // The number of back_edges visited by the search should always be 1.
154/// assert_eq!(back_edges, 1);
155/// println!("discover time:{:?}", discover_time);
156/// println!("number of backedges encountered: {}", back_edges);
157/// println!("back edge: ({:?})", result.unwrap_err());
158/// ```
159pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
160where
161 G: IntoEdges + Visitable,
162 I: IntoIterator<Item = G::NodeId>,
163 F: FnMut(DfsEvent<G::NodeId, &G::EdgeWeight>) -> C,
164 C: ControlFlow,
165{
166 let time = &mut Time(0);
167 let discovered = &mut graph.visit_map();
168 let finished = &mut graph.visit_map();
169
170 for start in starts {
171 try_control!(
172 dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
173 unreachable!()
174 );
175 }
176 C::continuing()
177}
178
179fn dfs_visitor<G, F, C>(
180 graph: G,
181 u: G::NodeId,
182 visitor: &mut F,
183 discovered: &mut G::Map,
184 finished: &mut G::Map,
185 time: &mut Time,
186) -> C
187where
188 G: IntoEdges + Visitable,
189 F: FnMut(DfsEvent<G::NodeId, &G::EdgeWeight>) -> C,
190 C: ControlFlow,
191{
192 if !discovered.visit(u) {
193 return C::continuing();
194 }
195
196 try_control!(visitor(DfsEvent::Discover(u, time_post_inc(time))), {}, {
197 let mut stack: Vec<(G::NodeId, <G as IntoEdges>::Edges)> = Vec::new();
198 stack.push((u, graph.edges(u)));
199
200 while let Some(elem) = stack.last_mut() {
201 let u = elem.0;
202 let adjacent_edges = &mut elem.1;
203 let mut next = None;
204
205 for edge in adjacent_edges {
206 let v = edge.target();
207 if !discovered.is_visited(&v) {
208 try_control!(visitor(DfsEvent::TreeEdge(u, v, edge.weight())), continue);
209 discovered.visit(v);
210 try_control!(
211 visitor(DfsEvent::Discover(v, time_post_inc(time))),
212 continue
213 );
214 next = Some(v);
215 break;
216 } else if !finished.is_visited(&v) {
217 try_control!(visitor(DfsEvent::BackEdge(u, v, edge.weight())), continue);
218 } else {
219 try_control!(
220 visitor(DfsEvent::CrossForwardEdge(u, v, edge.weight())),
221 continue
222 );
223 }
224 }
225
226 match next {
227 Some(v) => stack.push((v, graph.edges(v))),
228 None => {
229 let first_finish = finished.visit(u);
230 debug_assert!(first_finish);
231 try_control!(
232 visitor(DfsEvent::Finish(u, time_post_inc(time))),
233 panic!("Pruning on the `DfsEvent::Finish` is not supported!")
234 );
235 stack.pop();
236 }
237 };
238 }
239 });
240
241 C::continuing()
242}
243
244fn time_post_inc(x: &mut Time) -> Time {
245 let v = *x;
246 x.0 += 1;
247 v
248}