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}