webgraph_algo/visits/depth_first/
seq.rs

1/*
2 * SPDX-FileCopyrightText: 2024 Matteo Dell'Acqua
3 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
4 *
5 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
6 */
7
8use crate::visits::{
9    depth_first::{EventNoPred, EventPred, FilterArgsNoPred, FilterArgsPred},
10    Sequential,
11};
12use sealed::sealed;
13use std::ops::ControlFlow::{self, Continue};
14use sux::bits::BitVec;
15use webgraph::traits::{RandomAccessGraph, RandomAccessLabeling};
16
17/// A depth-first visit which does not keep track of predecessors, or nodes on the stack.
18pub type SeqNoPred<'a, G> = SeqIter<'a, TwoStates, G, (), false>;
19
20/// A depth-first visit which keeps track of predecessors, but not nodes on the stack.
21pub type SeqPred<'a, G> = SeqIter<'a, TwoStates, G, usize, true>;
22
23/// A depth-first visit which keeps track of predecessors and nodes on the stack.
24pub type SeqPath<'a, G> = SeqIter<'a, ThreeStates, G, usize, true>;
25
26/// Sequential depth-first visits.
27///
28/// This is an iterative implementation that does not need a large stack size.
29///
30/// There are three version of the visit, which are type aliases to the same
31/// common implementation: [`SeqNoPred`], [`SeqPred`] and [`SeqPath`] (the
32/// generic implementation should not be instantiated by the user).
33///
34/// * [`SeqNoPred`] does not keep track of predecessors, nor of nodes on the
35///   stack; it can be used, for example, to compute reachability information.
36/// * [`SeqPred`] keeps track of predecessors, but not of nodes on the stack; it
37///   can be used, for example, to compute a [topological
38///   sort](crate::top_sort).
39/// * [`SeqPath`] keeps track of predecessors and nodes on the stack; it can be
40///   used, for example, to establish [acyclicity](crate::is_acyclic)).
41///
42/// Each type of visit uses incrementally more space:
43/// * [`SeqNoPred`] uses one bit per node to remember known nodes and a stack of
44///   iterators, one for each node on the visit path.
45/// * [`SeqPred`] uses one bit per node to remember known nodes and a stack of
46///   pairs made of an iterator and a predecessor, one for each node on the
47///   visit path.
48/// * [`SeqPath`] uses two bits per node to remember known nodes and whether the
49///   node is on the visit path, and a stack of pairs made of an iterator and a
50///   predecessor, one for each node on the visit path.
51///
52/// The visits differ also in the type of events they generate:
53/// * [`SeqNoPred`] generates events of type [`EventNoPred`].
54/// * [`SeqPred`] generates events of type [`EventPred`], with the proviso that
55///   the Boolean associated with events of type
56///   [`Revisit`](`EventPred::Revisit`) is always false.
57/// * [`SeqPath`] generates events of type [`EventPred`].
58///
59/// With respect to [`EventNoPred`], [`EventPred`] provides the predecessor of the
60/// current node and a [postvisit event](EventPred::Postvisit).
61///
62/// If the visit was interrupted, the nodes still on the visit path can be
63/// retrieved using the [`stack`](SeqPred::stack) method (only for [`SeqPred`] and
64/// [`SeqPath`]).
65///
66/// # Examples
67///
68/// Let's test acyclicity:
69///
70/// ```
71/// use webgraph_algo::visits::*;
72/// use webgraph_algo::visits::depth_first::*;
73/// use webgraph::graphs::vec_graph::VecGraph;
74/// use webgraph::traits::SequentialLabeling;
75/// use webgraph::labels::proj::Left;
76/// use std::ops::ControlFlow::*;
77///
78/// let graph = VecGraph::from_arcs([(0, 1), (1, 2), (2, 0), (1, 3)]);
79/// let mut visit = depth_first::SeqPath::new(&graph);
80///
81/// assert!(visit.visit(
82///     0..graph.num_nodes(),
83///     |event| {
84///         match event {
85///             // Stop the visit as soon as a back edge is found
86///             EventPred::Revisit { on_stack: true, .. } => Break(StoppedWhenDone),
87///             _ => Continue(()),
88///         }
89///     },
90/// ).is_break()); // As the graph is not acyclic
91/// ```
92///
93/// Or, assuming the input is acyclic, let us compute the reverse of a
94/// topological sort:
95///
96/// ```
97/// use webgraph_algo::visits::*;
98/// use webgraph_algo::visits::depth_first::*;
99/// use webgraph::graphs::vec_graph::VecGraph;
100/// use webgraph::labels::proj::Left;
101/// use webgraph::traits::labels::SequentialLabeling;
102/// use std::ops::ControlFlow::Continue;
103/// use no_break::NoBreak;
104///
105/// let graph = VecGraph::from_arcs([(0, 1), (1, 2), (1, 3), (0, 3)]);
106/// let mut visit = depth_first::SeqPred::new(&graph);
107/// let mut top_sort = Vec::with_capacity(graph.num_nodes());
108///
109/// visit.visit(
110///     0..graph.num_nodes(),
111///     |event| {
112///         if let EventPred::Postvisit { node, .. } = event {
113///             top_sort.push(node);
114///         }
115///         Continue(())
116///     }
117/// ).continue_value_no_break();
118/// ```
119pub struct SeqIter<'a, S, G: RandomAccessGraph, P, const PRED: bool> {
120    graph: &'a G,
121    /// Entries on this stack represent the iterator on the successors of a node
122    /// and the parent of the node. This approach makes it possible to avoid
123    /// storing both the current and the parent node in the stack.
124    stack: Vec<(
125        <<G as RandomAccessLabeling>::Labels<'a> as IntoIterator>::IntoIter,
126        P,
127    )>,
128    state: S,
129    // General depth-first visit implementation. The user shouldn't see this.
130    // Allowed combinations for `PRED`, `S` and `P` are:
131    // * `false`, `TwoStates` and `()` (no predecessors, no stack tracking)
132    // * `true`, `TwoStates` and `usize` (predecessors, no stack tracking)
133    // * `true`, `ThreeStates` and `usize` (predecessors, stack tracking)
134}
135
136/// The iterator returned by [`stack`](SeqPred::stack).
137pub struct StackIterator<'a, 'b, S, G: RandomAccessGraph> {
138    visit: &'b mut SeqIter<'a, S, G, usize, true>,
139}
140
141impl<S, G: RandomAccessGraph> Iterator for StackIterator<'_, '_, S, G> {
142    type Item = usize;
143
144    fn next(&mut self) -> Option<usize> {
145        // Since we put predecessors on the stack, the
146        // first two stack entries are equal to the root,
147        // so we avoid to return the first one
148        if self.visit.stack.len() <= 1 {
149            return None;
150        }
151        self.visit.stack.pop().map(|(_, parent)| parent)
152    }
153}
154
155impl<'a, S: NodeStates, G: RandomAccessGraph, P, const PRED: bool> SeqIter<'a, S, G, P, PRED> {
156    /// Creates a new sequential visit.
157    ///
158    /// # Arguments
159    /// * `graph`: an immutable reference to the graph to visit.
160    pub fn new(graph: &'a G) -> SeqIter<'a, S, G, P, PRED> {
161        let num_nodes = graph.num_nodes();
162        Self {
163            graph,
164            stack: Vec::with_capacity(16),
165            state: S::new(num_nodes),
166        }
167    }
168}
169
170impl<'a, S, G: RandomAccessGraph> SeqIter<'a, S, G, usize, true> {
171    /// Returns an iterator over the nodes still on the visit path,
172    /// except for the last one.
173    ///
174    /// Node will be returned in reverse order of visit.
175    ///
176    /// This method is useful only in the case of interrupted visits,
177    /// as in a completed visit the stack will be empty. The last node
178    /// on the visit path at the moment of the interruption must be
179    /// treated separately.
180    pub fn stack(&mut self) -> StackIterator<'a, '_, S, G> {
181        StackIterator { visit: self }
182    }
183}
184
185#[doc(hidden)]
186#[sealed]
187pub trait NodeStates {
188    fn new(n: usize) -> Self;
189    fn set_on_stack(&mut self, node: usize);
190    fn set_off_stack(&mut self, node: usize);
191    fn on_stack(&self, node: usize) -> bool;
192    fn set_known(&mut self, node: usize);
193    fn known(&self, node: usize) -> bool;
194    fn reset(&mut self);
195}
196
197#[doc(hidden)]
198/// A two-state selector type for [sequential depth-first visits](Seq).
199///
200/// This implementation does not keep track of nodes on the stack, so events of
201/// type [`Revisit`](`EventPred::Revisit`) will always have the associated
202/// Boolean equal to false.
203pub struct TwoStates(BitVec);
204
205#[sealed]
206impl NodeStates for TwoStates {
207    fn new(n: usize) -> TwoStates {
208        TwoStates(BitVec::new(n))
209    }
210    #[inline(always)]
211    fn set_on_stack(&mut self, _node: usize) {}
212    #[inline(always)]
213    fn set_off_stack(&mut self, _node: usize) {}
214    #[inline(always)]
215    fn on_stack(&self, _node: usize) -> bool {
216        false
217    }
218    #[inline(always)]
219    fn set_known(&mut self, node: usize) {
220        self.0.set(node, true);
221    }
222    #[inline(always)]
223    fn known(&self, node: usize) -> bool {
224        self.0.get(node)
225    }
226    #[inline(always)]
227    fn reset(&mut self) {
228        self.0.reset();
229    }
230}
231
232#[doc(hidden)]
233/// A three-state selector type for [sequential depth-first visits](Seq).
234///
235/// This implementation does keep track of nodes on the stack, so events of type
236/// [`Revisit`](`EventPred::Revisit`) will provide information about whether the
237/// node associated with event is currently on the visit path.
238pub struct ThreeStates(BitVec);
239
240#[sealed]
241impl NodeStates for ThreeStates {
242    fn new(n: usize) -> ThreeStates {
243        ThreeStates(BitVec::new(2 * n))
244    }
245    #[inline(always)]
246    fn set_on_stack(&mut self, node: usize) {
247        self.0.set(node * 2 + 1, true);
248    }
249    #[inline(always)]
250    fn set_off_stack(&mut self, node: usize) {
251        self.0.set(node * 2 + 1, false);
252    }
253    #[inline(always)]
254    fn on_stack(&self, node: usize) -> bool {
255        self.0.get(node * 2 + 1)
256    }
257    #[inline(always)]
258    fn set_known(&mut self, node: usize) {
259        self.0.set(node * 2, true);
260    }
261    #[inline(always)]
262    fn known(&self, node: usize) -> bool {
263        self.0.get(node * 2)
264    }
265    #[inline(always)]
266    fn reset(&mut self) {
267        self.0.reset();
268    }
269}
270
271impl<S: NodeStates, G: RandomAccessGraph> Sequential<EventPred> for SeqIter<'_, S, G, usize, true> {
272    fn visit_filtered_with<
273        R: IntoIterator<Item = usize>,
274        T,
275        E,
276        C: FnMut(&mut T, EventPred) -> ControlFlow<E, ()>,
277        F: FnMut(&mut T, FilterArgsPred) -> bool,
278    >(
279        &mut self,
280        roots: R,
281        mut init: T,
282        mut callback: C,
283        mut filter: F,
284    ) -> ControlFlow<E, ()> {
285        let state = &mut self.state;
286
287        for root in roots {
288            if state.known(root)
289                || !filter(
290                    &mut init,
291                    FilterArgsPred {
292                        node: root,
293                        pred: root,
294                        root,
295                        depth: 0,
296                    },
297                )
298            {
299                // We ignore the node: it might be visited later
300                continue;
301            }
302
303            callback(&mut init, EventPred::Init { root })?;
304
305            state.set_known(root);
306            callback(
307                &mut init,
308                EventPred::Previsit {
309                    node: root,
310                    pred: root,
311                    root,
312                    depth: 0,
313                },
314            )?;
315
316            self.stack
317                .push((self.graph.successors(root).into_iter(), root));
318
319            state.set_on_stack(root);
320
321            // This variable keeps track of the current node being visited; the
322            // parent node is derived at each iteration of the 'recurse loop.
323            let mut curr = root;
324
325            'recurse: loop {
326                let depth = self.stack.len();
327                let Some((iter, parent)) = self.stack.last_mut() else {
328                    callback(&mut init, EventPred::Done { root })?;
329                    break;
330                };
331
332                for succ in iter {
333                    // Check if node should be visited
334                    if state.known(succ) {
335                        // Node has already been discovered
336                        callback(
337                            &mut init,
338                            EventPred::Revisit {
339                                node: succ,
340                                pred: curr,
341                                root,
342                                depth,
343                                on_stack: state.on_stack(succ),
344                            },
345                        )?;
346                    } else {
347                        // First time seeing node
348                        if filter(
349                            &mut init,
350                            FilterArgsPred {
351                                node: succ,
352                                pred: curr,
353                                root,
354                                depth,
355                            },
356                        ) {
357                            state.set_known(succ);
358                            callback(
359                                &mut init,
360                                EventPred::Previsit {
361                                    node: succ,
362                                    pred: curr,
363                                    root,
364                                    depth,
365                                },
366                            )?;
367                            // current_node is the parent of succ
368                            self.stack
369                                .push((self.graph.successors(succ).into_iter(), curr));
370
371                            state.set_on_stack(succ);
372
373                            // At the next iteration, succ will be the current node
374                            curr = succ;
375
376                            continue 'recurse;
377                        } // Else we ignore the node: it might be visited later
378                    }
379                }
380
381                callback(
382                    &mut init,
383                    EventPred::Postvisit {
384                        node: curr,
385                        pred: *parent,
386                        root,
387                        depth: depth - 1,
388                    },
389                )?;
390
391                state.set_off_stack(curr);
392
393                // We're going up one stack level, so the next current_node
394                // is the current parent.
395                curr = *parent;
396                self.stack.pop();
397            }
398        }
399
400        Continue(())
401    }
402
403    fn reset(&mut self) {
404        self.stack.clear();
405        self.state.reset();
406    }
407}
408
409impl<G: RandomAccessGraph> Sequential<EventNoPred> for SeqIter<'_, TwoStates, G, (), false> {
410    fn visit_filtered_with<
411        R: IntoIterator<Item = usize>,
412        T,
413        E,
414        C: FnMut(&mut T, EventNoPred) -> ControlFlow<E, ()>,
415        F: FnMut(&mut T, FilterArgsNoPred) -> bool,
416    >(
417        &mut self,
418        roots: R,
419        mut init: T,
420        mut callback: C,
421        mut filter: F,
422    ) -> ControlFlow<E, ()> {
423        let state = &mut self.state;
424
425        for root in roots {
426            if state.known(root)
427                || !filter(
428                    &mut init,
429                    FilterArgsNoPred {
430                        node: root,
431                        root,
432                        depth: 0,
433                    },
434                )
435            {
436                // We ignore the node: it might be visited later
437                continue;
438            }
439
440            callback(&mut init, EventNoPred::Init { root })?;
441
442            state.set_known(root);
443
444            callback(
445                &mut init,
446                EventNoPred::Previsit {
447                    node: root,
448                    root,
449                    depth: 0,
450                },
451            )?;
452
453            self.stack
454                .push((self.graph.successors(root).into_iter(), ()));
455
456            'recurse: loop {
457                let depth = self.stack.len();
458                let Some((iter, _)) = self.stack.last_mut() else {
459                    callback(&mut init, EventNoPred::Done { root })?;
460                    break;
461                };
462
463                for succ in iter {
464                    // Check if node should be visited
465                    if state.known(succ) {
466                        // Node has already been discovered
467                        callback(
468                            &mut init,
469                            EventNoPred::Revisit {
470                                node: succ,
471                                root,
472                                depth,
473                            },
474                        )?;
475                    } else {
476                        // First time seeing node
477                        if filter(
478                            &mut init,
479                            FilterArgsNoPred {
480                                node: succ,
481                                root,
482                                depth,
483                            },
484                        ) {
485                            state.set_known(succ);
486                            callback(
487                                &mut init,
488                                EventNoPred::Previsit {
489                                    node: succ,
490                                    root,
491                                    depth,
492                                },
493                            )?;
494                            // current_node is the parent of succ
495                            self.stack
496                                .push((self.graph.successors(succ).into_iter(), ()));
497
498                            continue 'recurse;
499                        } // Else we ignore the node: it might be visited later
500                    }
501                }
502
503                // We're going up one stack level, so the next current_node
504                // is the current parent.
505                self.stack.pop();
506            }
507        }
508
509        Continue(())
510    }
511
512    fn reset(&mut self) {
513        self.stack.clear();
514        self.state.reset();
515    }
516}