petgraph/visit/dfsvisit.rs
1use crate::visit::IntoNeighbors;
2use crate::visit::{VisitMap, Visitable};
3
4/// Strictly monotonically increasing event time for a depth first search.
5#[derive(Copy, Clone, Debug, PartialEq, PartialOrd, Eq, Ord, Default, Hash)]
6pub struct Time(pub usize);
7
8/// A depth first search (DFS) visitor event.
9#[derive(Copy, Clone, Debug)]
10pub enum DfsEvent<N> {
11 Discover(N, Time),
12 /// An edge of the tree formed by the traversal.
13 TreeEdge(N, N),
14 /// An edge to an already visited node.
15 BackEdge(N, N),
16 /// A cross or forward edge.
17 ///
18 /// For an edge *(u, v)*, if the discover time of *v* is greater than *u*,
19 /// then it is a forward edge, else a cross edge.
20 CrossForwardEdge(N, N),
21 /// All edges from a node have been reported.
22 Finish(N, Time),
23}
24
25/// Return if the expression is a break value, execute the provided statement
26/// if it is a prune value.
27macro_rules! try_control {
28 ($e:expr, $p:stmt) => {
29 try_control!($e, $p, ());
30 };
31 ($e:expr, $p:stmt, $q:stmt) => {
32 match $e {
33 x => {
34 if x.should_break() {
35 return x;
36 } else if x.should_prune() {
37 $p
38 } else {
39 $q
40 }
41 }
42 }
43 };
44}
45
46/// Control flow for `depth_first_search` callbacks.
47#[derive(Copy, Clone, Debug)]
48pub enum Control<B> {
49 /// Continue the DFS traversal as normal.
50 Continue,
51 /// Prune the current node from the DFS traversal. No more edges from this
52 /// node will be reported to the callback. A `DfsEvent::Finish` for this
53 /// node will still be reported. This can be returned in response to any
54 /// `DfsEvent`, except `Finish`, which will panic.
55 Prune,
56 /// Stop the DFS traversal and return the provided value.
57 Break(B),
58}
59
60impl<B> Control<B> {
61 pub fn breaking() -> Control<()> {
62 Control::Break(())
63 }
64 /// Get the value in `Control::Break(_)`, if present.
65 pub fn break_value(self) -> Option<B> {
66 match self {
67 Control::Continue | Control::Prune => None,
68 Control::Break(b) => Some(b),
69 }
70 }
71}
72
73/// Control flow for callbacks.
74///
75/// The empty return value `()` is equivalent to continue.
76pub trait ControlFlow {
77 fn continuing() -> Self;
78 fn should_break(&self) -> bool;
79 fn should_prune(&self) -> bool;
80}
81
82impl ControlFlow for () {
83 fn continuing() {}
84 #[inline]
85 fn should_break(&self) -> bool {
86 false
87 }
88 #[inline]
89 fn should_prune(&self) -> bool {
90 false
91 }
92}
93
94impl<B> ControlFlow for Control<B> {
95 fn continuing() -> Self {
96 Control::Continue
97 }
98 fn should_break(&self) -> bool {
99 matches!(*self, Control::Break(_))
100 }
101 fn should_prune(&self) -> bool {
102 matches!(*self, Control::Prune)
103 }
104}
105
106impl<C: ControlFlow, E> ControlFlow for Result<C, E> {
107 fn continuing() -> Self {
108 Ok(C::continuing())
109 }
110 fn should_break(&self) -> bool {
111 if let Ok(ref c) = *self {
112 c.should_break()
113 } else {
114 true
115 }
116 }
117 fn should_prune(&self) -> bool {
118 if let Ok(ref c) = *self {
119 c.should_prune()
120 } else {
121 false
122 }
123 }
124}
125
126/// The default is `Continue`.
127impl<B> Default for Control<B> {
128 fn default() -> Self {
129 Control::Continue
130 }
131}
132
133/// A recursive depth first search.
134///
135/// Starting points are the nodes in the iterator `starts` (specify just one
136/// start vertex *x* by using `Some(x)`).
137///
138/// The traversal emits discovery and finish events for each reachable vertex,
139/// and edge classification of each reachable edge. `visitor` is called for each
140/// event, see [`DfsEvent`][de] for possible values.
141///
142/// The return value should implement the trait `ControlFlow`, and can be used to change
143/// the control flow of the search.
144///
145/// `Control` Implements `ControlFlow` such that `Control::Continue` resumes the search.
146/// `Control::Break` will stop the visit early, returning the contained value.
147/// `Control::Prune` will stop traversing any additional edges from the current
148/// node and proceed immediately to the `Finish` event.
149///
150/// There are implementations of `ControlFlow` for `()`, and `Result<C, E>` where
151/// `C: ControlFlow`. The implementation for `()` will continue until finished.
152/// For `Result`, upon encountering an `E` it will break, otherwise acting the same as `C`.
153///
154/// **Panics** if you attempt to prune a node from its `Finish` event.
155///
156/// [de]: enum.DfsEvent.html
157///
158/// # Example returning `Control`.
159///
160/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
161/// the goal vertex.
162///
163/// ```
164/// use petgraph::prelude::*;
165/// use petgraph::graph::node_index as n;
166/// use petgraph::visit::depth_first_search;
167/// use petgraph::visit::{DfsEvent, Control};
168///
169/// let gr: Graph<(), ()> = Graph::from_edges(&[
170/// (0, 1), (0, 2), (0, 3),
171/// (1, 3),
172/// (2, 3), (2, 4),
173/// (4, 0), (4, 5),
174/// ]);
175///
176/// // record each predecessor, mapping node → node
177/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
178/// let start = n(0);
179/// let goal = n(5);
180/// depth_first_search(&gr, Some(start), |event| {
181/// if let DfsEvent::TreeEdge(u, v) = event {
182/// predecessor[v.index()] = u;
183/// if v == goal {
184/// return Control::Break(v);
185/// }
186/// }
187/// Control::Continue
188/// });
189///
190/// let mut next = goal;
191/// let mut path = vec![next];
192/// while next != start {
193/// let pred = predecessor[next.index()];
194/// path.push(pred);
195/// next = pred;
196/// }
197/// path.reverse();
198/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
199/// ```
200///
201/// # Example returning a `Result`.
202/// ```
203/// use petgraph::graph::node_index as n;
204/// use petgraph::prelude::*;
205/// use petgraph::visit::depth_first_search;
206/// use petgraph::visit::{DfsEvent, Time};
207///
208/// let gr: Graph<(), ()> = Graph::from_edges(&[(0, 1), (1, 2), (1, 1), (2, 1)]);
209/// let start = n(0);
210/// let mut back_edges = 0;
211/// let mut discover_time = 0;
212/// // Stop the search, the first time a BackEdge is encountered.
213/// let result = depth_first_search(&gr, Some(start), |event| {
214/// match event {
215/// // In the cases where Ok(()) is returned,
216/// // Result falls back to the implementation of Control on the value ().
217/// // In the case of (), this is to always return Control::Continue.
218/// // continuing the search.
219/// DfsEvent::Discover(_, Time(t)) => {
220/// discover_time = t;
221/// Ok(())
222/// }
223/// DfsEvent::BackEdge(_, _) => {
224/// back_edges += 1;
225/// // the implementation of ControlFlow for Result,
226/// // treats this Err value as Continue::Break
227/// Err(event)
228/// }
229/// _ => Ok(()),
230/// }
231/// });
232///
233/// // Even though the graph has more than one cycle,
234/// // The number of back_edges visited by the search should always be 1.
235/// assert_eq!(back_edges, 1);
236/// println!("discover time:{:?}", discover_time);
237/// println!("number of backedges encountered: {}", back_edges);
238/// println!("back edge: {:?}", result);
239/// ```
240#[track_caller]
241pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
242where
243 G: IntoNeighbors + Visitable,
244 I: IntoIterator<Item = G::NodeId>,
245 F: FnMut(DfsEvent<G::NodeId>) -> C,
246 C: ControlFlow,
247{
248 let time = &mut Time(0);
249 let discovered = &mut graph.visit_map();
250 let finished = &mut graph.visit_map();
251
252 for start in starts {
253 try_control!(
254 dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
255 unreachable!()
256 );
257 }
258 C::continuing()
259}
260
261pub(crate) fn dfs_visitor<G, F, C>(
262 graph: G,
263 u: G::NodeId,
264 visitor: &mut F,
265 discovered: &mut impl VisitMap<G::NodeId>,
266 finished: &mut impl VisitMap<G::NodeId>,
267 time: &mut Time,
268) -> C
269where
270 G: IntoNeighbors + Visitable,
271 F: FnMut(DfsEvent<G::NodeId>) -> C,
272 C: ControlFlow,
273{
274 if !discovered.visit(u) {
275 return C::continuing();
276 }
277
278 try_control!(
279 visitor(DfsEvent::Discover(u, time_post_inc(time))),
280 {},
281 for v in graph.neighbors(u) {
282 if !discovered.is_visited(&v) {
283 try_control!(visitor(DfsEvent::TreeEdge(u, v)), continue);
284 try_control!(
285 dfs_visitor(graph, v, visitor, discovered, finished, time),
286 unreachable!()
287 );
288 } else if !finished.is_visited(&v) {
289 try_control!(visitor(DfsEvent::BackEdge(u, v)), continue);
290 } else {
291 try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)), continue);
292 }
293 }
294 );
295 let first_finish = finished.visit(u);
296 debug_assert!(first_finish);
297 try_control!(
298 visitor(DfsEvent::Finish(u, time_post_inc(time))),
299 panic!("Pruning on the `DfsEvent::Finish` is not supported!")
300 );
301 C::continuing()
302}
303
304fn time_post_inc(x: &mut Time) -> Time {
305 let v = *x;
306 x.0 += 1;
307 v
308}