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        if let Control::Break(_) = *self {
100            true
101        } else {
102            false
103        }
104    }
105    fn should_prune(&self) -> bool {
106        match *self {
107            Control::Prune => true,
108            Control::Continue | Control::Break(_) => false,
109        }
110    }
111}
112
113impl<C: ControlFlow, E> ControlFlow for Result<C, E> {
114    fn continuing() -> Self {
115        Ok(C::continuing())
116    }
117    fn should_break(&self) -> bool {
118        if let Ok(ref c) = *self {
119            c.should_break()
120        } else {
121            true
122        }
123    }
124    fn should_prune(&self) -> bool {
125        if let Ok(ref c) = *self {
126            c.should_prune()
127        } else {
128            false
129        }
130    }
131}
132
133/// The default is `Continue`.
134impl<B> Default for Control<B> {
135    fn default() -> Self {
136        Control::Continue
137    }
138}
139
140/// A recursive depth first search.
141///
142/// Starting points are the nodes in the iterator `starts` (specify just one
143/// start vertex *x* by using `Some(x)`).
144///
145/// The traversal emits discovery and finish events for each reachable vertex,
146/// and edge classification of each reachable edge. `visitor` is called for each
147/// event, see [`DfsEvent`][de] for possible values.
148///
149/// The return value should implement the trait `ControlFlow`, and can be used to change
150/// the control flow of the search.
151///
152/// `Control` Implements `ControlFlow` such that `Control::Continue` resumes the search.
153/// `Control::Break` will stop the visit early, returning the contained value.
154/// `Control::Prune` will stop traversing any additional edges from the current
155/// node and proceed immediately to the `Finish` event.
156///
157/// There are implementations of `ControlFlow` for `()`, and `Result<C, E>` where
158/// `C: ControlFlow`. The implementation for `()` will continue until finished.
159/// For `Result`, upon encountering an `E` it will break, otherwise acting the same as `C`.
160///
161/// ***Panics** if you attempt to prune a node from its `Finish` event.
162///
163/// [de]: enum.DfsEvent.html
164///
165/// # Example returning `Control`.
166///
167/// Find a path from vertex 0 to 5, and exit the visit as soon as we reach
168/// the goal vertex.
169///
170/// ```
171/// use petgraph::prelude::*;
172/// use petgraph::graph::node_index as n;
173/// use petgraph::visit::depth_first_search;
174/// use petgraph::visit::{DfsEvent, Control};
175///
176/// let gr: Graph<(), ()> = Graph::from_edges(&[
177///     (0, 1), (0, 2), (0, 3),
178///     (1, 3),
179///     (2, 3), (2, 4),
180///     (4, 0), (4, 5),
181/// ]);
182///
183/// // record each predecessor, mapping node → node
184/// let mut predecessor = vec![NodeIndex::end(); gr.node_count()];
185/// let start = n(0);
186/// let goal = n(5);
187/// depth_first_search(&gr, Some(start), |event| {
188///     if let DfsEvent::TreeEdge(u, v) = event {
189///         predecessor[v.index()] = u;
190///         if v == goal {
191///             return Control::Break(v);
192///         }
193///     }
194///     Control::Continue
195/// });
196///
197/// let mut next = goal;
198/// let mut path = vec![next];
199/// while next != start {
200///     let pred = predecessor[next.index()];
201///     path.push(pred);
202///     next = pred;
203/// }
204/// path.reverse();
205/// assert_eq!(&path, &[n(0), n(2), n(4), n(5)]);
206/// ```
207///
208/// # Example returning a `Result`.
209/// ```
210/// use petgraph::graph::node_index as n;
211/// use petgraph::prelude::*;
212/// use petgraph::visit::depth_first_search;
213/// use petgraph::visit::{DfsEvent, Time};
214///
215/// let gr: Graph<(), ()> = Graph::from_edges(&[(0, 1), (1, 2), (1, 1), (2, 1)]);
216/// let start = n(0);
217/// let mut back_edges = 0;
218/// let mut discover_time = 0;
219/// // Stop the search, the first time a BackEdge is encountered.
220/// let result = depth_first_search(&gr, Some(start), |event| {
221///     match event {
222///         // In the cases where Ok(()) is returned,
223///         // Result falls back to the implementation of Control on the value ().
224///         // In the case of (), this is to always return Control::Continue.
225///         // continuing the search.
226///         DfsEvent::Discover(_, Time(t)) => {
227///             discover_time = t;
228///             Ok(())
229///         }
230///         DfsEvent::BackEdge(_, _) => {
231///             back_edges += 1;
232///             // the implementation of ControlFlow for Result,
233///             // treats this Err value as Continue::Break
234///             Err(event)
235///         }
236///         _ => Ok(()),
237///     }
238/// });
239///
240/// // Even though the graph has more than one cycle,
241/// // The number of back_edges visited by the search should always be 1.
242/// assert_eq!(back_edges, 1);
243/// println!("discover time:{:?}", discover_time);
244/// println!("number of backedges encountered: {}", back_edges);
245/// println!("back edge: {:?}", result);
246/// ```
247pub fn depth_first_search<G, I, F, C>(graph: G, starts: I, mut visitor: F) -> C
248where
249    G: IntoNeighbors + Visitable,
250    I: IntoIterator<Item = G::NodeId>,
251    F: FnMut(DfsEvent<G::NodeId>) -> C,
252    C: ControlFlow,
253{
254    let time = &mut Time(0);
255    let discovered = &mut graph.visit_map();
256    let finished = &mut graph.visit_map();
257
258    for start in starts {
259        try_control!(
260            dfs_visitor(graph, start, &mut visitor, discovered, finished, time),
261            unreachable!()
262        );
263    }
264    C::continuing()
265}
266
267pub(crate) fn dfs_visitor<G, F, C>(
268    graph: G,
269    u: G::NodeId,
270    visitor: &mut F,
271    discovered: &mut impl VisitMap<G::NodeId>,
272    finished: &mut impl VisitMap<G::NodeId>,
273    time: &mut Time,
274) -> C
275where
276    G: IntoNeighbors + Visitable,
277    F: FnMut(DfsEvent<G::NodeId>) -> C,
278    C: ControlFlow,
279{
280    if !discovered.visit(u) {
281        return C::continuing();
282    }
283
284    try_control!(
285        visitor(DfsEvent::Discover(u, time_post_inc(time))),
286        {},
287        for v in graph.neighbors(u) {
288            if !discovered.is_visited(&v) {
289                try_control!(visitor(DfsEvent::TreeEdge(u, v)), continue);
290                try_control!(
291                    dfs_visitor(graph, v, visitor, discovered, finished, time),
292                    unreachable!()
293                );
294            } else if !finished.is_visited(&v) {
295                try_control!(visitor(DfsEvent::BackEdge(u, v)), continue);
296            } else {
297                try_control!(visitor(DfsEvent::CrossForwardEdge(u, v)), continue);
298            }
299        }
300    );
301    let first_finish = finished.visit(u);
302    debug_assert!(first_finish);
303    try_control!(
304        visitor(DfsEvent::Finish(u, time_post_inc(time))),
305        panic!("Pruning on the `DfsEvent::Finish` is not supported!")
306    );
307    C::continuing()
308}
309
310fn time_post_inc(x: &mut Time) -> Time {
311    let v = *x;
312    x.0 += 1;
313    v
314}