webgraph/visits/mod.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
8//! Visits on graphs.
9//!
10//! Implementation of [sequential](Sequential) and [parallel][Parallel] visits
11//! depend on a type parameter `A` implementing the trait [`Event`]; they
12//! provide visit methods accepting a callback function with argument `A` and
13//! returning a `ControlFlow<E, ()>`, where `E` is a type parameter of the visit
14//! method: for example, `E` might be [`StoppedWhenDone`] when completing early,
15//! [`Interrupted`] when interrupted or [`Infallible`](std::convert::Infallible)
16//! if the visit cannot be interrupted.
17//!
18//! If a callback returns a [`Break`](ControlFlow::Break), the visit will be
19//! interrupted, and the [`Break`](ControlFlow::Break) value will be the return
20//! value of the visit method; for uninterruptible visits we suggest to use the
21//! [`no-break`](https://crates.io/crates/no-break) crate and its
22//! [`continue_value_no_break`](no_break::NoBreak::continue_value_no_break)
23//! method on the result to let type inference run smoothly.
24//!
25//! Note that an interruption does not necessarily denote an error condition
26//! (see, e.g., [`StoppedWhenDone`]).
27//!
28//! [Sequential visits](Sequential) are visits that are executed in a single
29//! thread, whereas [parallel visits](Parallel) use multiple threads. The
30//! signature of callbacks reflects this difference ([`FnMut`] for the
31//! sequential case vs. [`Fn`] + [`Sync`] for the parallel case).
32//!
33//! In case of interruption sequential visits usually return immediately to the
34//! caller, whereas in general parallel visits might need to complete part of
35//! their subtasks before returning to the caller.
36//!
37//! Additionally, implementations might accept a filter function accepting a
38//! [`Event::FilterArgs`] that will be called when a new node is discovered. If
39//! the filter returns false, the node will be ignored, that is, not even marked
40//! as known. Note that in case of parallel visits the filter might be called
41//! multiple times on the same node (and with a different predecessor, if
42//! available) due to race conditions.
43//!
44//! All visits have also methods accepting an `init` item similarly to the
45//! [Rayon](rayon) [`map_with`](rayon::iter::ParallelIterator::map_with) method.
46//! For parallel visits, the item will be cloned.
47//!
48//! There is a blanket implementation of the [`Parallel`] trait for all types
49//! implementing the [`Sequential`] trait. This approach makes it possible to
50//! have structures that can use both sequential and parallel visits.
51//!
52//! Visit must provide a `reset` method that makes it possible to reuse the
53//! visit.
54//!
55//! # Examples
56//!
57//! There are examples of visits in
58//! [`SeqIter`](crate::visits::depth_first::SeqIter),
59//! [`ParFair`](crate::visits::breadth_first::ParFair) and
60//! [`ParLowMem`](crate::visits::breadth_first::ParLowMem).
61
62pub mod breadth_first;
63pub mod depth_first;
64
65use std::ops::ControlFlow;
66use thiserror::Error;
67
68#[derive(Error, Debug)]
69/// The visit was interrupted.
70#[error("The visit was interrupted")]
71pub struct Interrupted;
72
73#[derive(Error, Debug)]
74/// The result of the visit was computed without completing the visit; for
75/// example, during an acyclicity test a single arc pointing at the visit path
76/// is sufficient to compute the result.
77#[error("Stopped when done")]
78pub struct StoppedWhenDone;
79
80/// Types usable as arguments for the callbacks in visits.
81///
82/// Arguments are usually enums in which variants represent visit events
83/// (previsits, postvisits, etc.). Each variant then contains additional data
84/// related to the specific event.
85///
86/// The associated type [`Event::FilterArgs`] is the type of the arguments
87/// passed to the filter associated with the visit. It can be set to `()` if
88/// filtering is not supported
89pub trait Event {
90 /// The type passed as input to the filter.
91 type FilterArgs;
92}
93
94/// A convenience type alias for the filter arguments of an event.
95///
96/// It is useful to write match patterns using destructuring syntax.
97pub type FilterArgs<A> = <A as Event>::FilterArgs;
98
99/// A sequential visit.
100///
101/// Implementation of this trait must provide the
102/// [`visit_filtered_with`](Sequential::visit_filtered_with) method, which
103/// should perform a visit of a graph starting from a given set of nodes. Note
104/// that different visits types might interpret the set of nodes differently:
105/// for example, a [breadth-first visit](breadth_first) will interpret the set
106/// of nodes as the initial queue, whereas a [depth-first visit](depth_first)
107/// will interpret the set of nodes as a list of nodes from which to start
108/// visits.
109pub trait Sequential<A: Event> {
110 /// Visits the graph from the specified nodes with an initialization value
111 /// and a filter function.
112 ///
113 /// See the [module documentation](crate::visits) for more information on
114 /// the return value.
115 ///
116 /// # Arguments
117 ///
118 /// * `roots`: The nodes to start the visit from.
119 ///
120 /// * `init`: a value the will be passed to the callback function.
121 ///
122 /// * `callback`: The callback function.
123 ///
124 /// * `filter`: The filter function.
125 fn visit_filtered_with<
126 R: IntoIterator<Item = usize>,
127 T,
128 E,
129 C: FnMut(&mut T, A) -> ControlFlow<E, ()>,
130 F: FnMut(&mut T, A::FilterArgs) -> bool,
131 >(
132 &mut self,
133 roots: R,
134 init: T,
135 callback: C,
136 filter: F,
137 ) -> ControlFlow<E, ()>;
138
139 /// Visits the graph from the specified nodes with a filter function.
140 ///
141 /// See the [module documentation](crate::visits) for more information on
142 /// the return value.
143 ///
144 /// # Arguments
145 ///
146 /// * `roots`: The nodes to start the visit from.
147 ///
148 /// * `callback`: The callback function.
149 ///
150 /// * `filter`: The filter function.
151 fn visit_filtered<
152 R: IntoIterator<Item = usize>,
153 E,
154 C: FnMut(A) -> ControlFlow<E, ()>,
155 F: FnMut(A::FilterArgs) -> bool,
156 >(
157 &mut self,
158 roots: R,
159 mut callback: C,
160 mut filter: F,
161 ) -> ControlFlow<E, ()> {
162 self.visit_filtered_with(roots, (), |(), a| callback(a), |(), a| filter(a))
163 }
164
165 /// Visits the graph from the specified nodes with an initialization value.
166 ///
167 /// See the [module documentation](crate::visits) for more information on
168 /// the return value.
169 ///
170 /// # Arguments
171 ///
172 /// * `roots`: The nodes to start the visit from.
173 ///
174 /// * `init`: a value the will be passed to the callback function.
175 ///
176 /// * `callback`: The callback function.
177 fn visit_with<
178 R: IntoIterator<Item = usize>,
179 T,
180 E,
181 C: FnMut(&mut T, A) -> ControlFlow<E, ()>,
182 >(
183 &mut self,
184 roots: R,
185 init: T,
186 callback: C,
187 ) -> ControlFlow<E, ()> {
188 self.visit_filtered_with(roots, init, callback, |_, _| true)
189 }
190
191 /// Visits the graph from the specified nodes.
192 ///
193 /// See the [module documentation](crate::visits) for more information on
194 /// the return value.
195 ///
196 /// # Arguments
197 ///
198 /// * `roots`: The nodes to start the visit from.
199 ///
200 /// * `callback`: The callback function.
201 fn visit<R: IntoIterator<Item = usize>, E, C: FnMut(A) -> ControlFlow<E, ()>>(
202 &mut self,
203 roots: R,
204 callback: C,
205 ) -> ControlFlow<E, ()> {
206 self.visit_filtered(roots, callback, |_| true)
207 }
208
209 /// Resets the visit status, making it possible to reuse it.
210 fn reset(&mut self);
211}
212
213/// A parallel visit.
214///
215/// Implementation of this trait must provide the
216/// [`par_visit_filtered_with`](Parallel::par_visit_filtered_with) method, which
217/// should perform a parallel visit of a graph starting from a given set of
218/// nodes. Note that different visits types might interpret the set of nodes
219/// differently: for example, a [breadth-first visit](breadth_first) will
220/// interpret the set of nodes as the initial queue, whereas a [depth-first
221/// visit](depth_first) will interpret the set of nodes as a list of nodes from
222/// which to start visits.
223pub trait Parallel<A: Event> {
224 /// Visits the graph from the specified nodes with an initialization value
225 /// and a filter function.
226 ///
227 /// See the [module documentation](crate::visits) for more information on
228 /// the return value.
229 ///
230 /// # Arguments
231 ///
232 /// * `roots`: The nodes to start the visit from.
233 ///
234 /// * `init`: a value the will be cloned and passed to the callback
235 /// function.
236 ///
237 /// * `callback`: The callback function.
238 ///
239 /// * `filter`: The filter function.
240 fn par_visit_filtered_with<
241 R: IntoIterator<Item = usize>,
242 T: Clone + Send + Sync,
243 E: Send,
244 C: Fn(&mut T, A) -> ControlFlow<E, ()> + Sync,
245 F: Fn(&mut T, A::FilterArgs) -> bool + Sync,
246 >(
247 &mut self,
248 roots: R,
249 init: T,
250 callback: C,
251 filter: F,
252 ) -> ControlFlow<E, ()>;
253
254 /// Visits the graph from the specified nodes with a filter function.
255 ///
256 /// See the [module documentation](crate::visits) for more information on
257 /// the return value.
258 ///
259 /// # Arguments
260 ///
261 /// * `roots`: The nodes to start the visit from.
262 ///
263 /// * `callback`: The callback function.
264 ///
265 /// * `filter`: The filter function.
266 fn par_visit_filtered<
267 R: IntoIterator<Item = usize>,
268 E: Send,
269 C: Fn(A) -> ControlFlow<E, ()> + Sync,
270 F: Fn(A::FilterArgs) -> bool + Sync,
271 >(
272 &mut self,
273 roots: R,
274 callback: C,
275 filter: F,
276 ) -> ControlFlow<E, ()> {
277 self.par_visit_filtered_with(roots, (), |(), a| callback(a), |(), a| filter(a))
278 }
279
280 /// Visits the graph from the specified nodes with an initialization value.
281 ///
282 /// See the [module documentation](crate::visits) for more information on
283 /// the return value.
284 ///
285 /// # Arguments
286 ///
287 /// * `roots`: The nodes to start the visit from.
288 ///
289 /// * `init`: a value the will be cloned and passed to the callback
290 /// function.
291 ///
292 /// * `callback`: The callback function.
293 fn par_visit_with<
294 R: IntoIterator<Item = usize>,
295 T: Clone + Send + Sync,
296 E: Send,
297 C: Fn(&mut T, A) -> ControlFlow<E, ()> + Sync,
298 >(
299 &mut self,
300 roots: R,
301 init: T,
302 callback: C,
303 ) -> ControlFlow<E, ()> {
304 self.par_visit_filtered_with(roots, init, callback, |_, _| true)
305 }
306
307 /// Visits the graph from the specified nodes.
308 ///
309 /// See the [module documentation](crate::visits) for more information on
310 /// the return value.
311 ///
312 /// # Arguments
313 ///
314 /// * `roots`: The nodes to start the visit from.
315 ///
316 /// * `callback`: The callback function.
317 fn par_visit<R: IntoIterator<Item = usize>, E: Send, C: Fn(A) -> ControlFlow<E, ()> + Sync>(
318 &mut self,
319 roots: R,
320 callback: C,
321 ) -> ControlFlow<E, ()> {
322 self.par_visit_filtered(roots, callback, |_| true)
323 }
324
325 /// Resets the visit status, making it possible to reuse it.
326 fn reset(&mut self);
327}