petgraph/algo/
simple_paths.rs

1use alloc::vec;
2use core::{
3    hash::{BuildHasher, Hash},
4    iter::{from_fn, FromIterator},
5};
6
7use hashbrown::HashSet;
8use indexmap::IndexSet;
9
10use crate::{
11    visit::{IntoNeighborsDirected, NodeCount},
12    Direction::Outgoing,
13};
14
15/// Calculate all simple paths with specified constraints from node `from` to node `to`.
16///
17/// A simple path is a path without repeating nodes.
18/// The number of simple paths between a given pair of vertices can grow exponentially,
19/// reaching `O(|V|!)` on complete graphs with `|V|` vertices.
20///
21/// So if you have a large enough graph, be prepared to wait on the results for years.
22/// Or consider extracting only part of the simple paths using the adapter [`Iterator::take`].
23/// Also note, that this algorithm does not check that a path exists between `from` and `to`. This may lead to very long running times and it may be worth it to check if a path exists before running this algorithm on large graphs.
24///
25/// This algorithm is adapted from [NetworkX](https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.simple_paths.all_simple_paths.html).
26/// # Arguments
27/// * `graph`: an input graph.
28/// * `from`: an initial node of desired paths.
29/// * `to`: a target node of desired paths.
30/// * `min_intermediate_nodes`: the minimum number of nodes in the desired paths.
31/// * `max_intermediate_nodes`: the maximum number of nodes in the desired paths (optional).
32/// # Returns
33/// Returns an iterator that produces all simple paths from `from` node to `to`, which contains at least `min_intermediate_nodes`
34/// and at most `max_intermediate_nodes` intermediate nodes, if given, or limited by the graph's order otherwise.
35///
36/// # Complexity
37/// * Time complexity: for computing the first **k** paths, the running time will be **O(k|V| + k|E|)**.
38/// * Auxillary space: **O(|V|)**.
39///
40/// where **|V|** is the number of nodes and **|E|** is the number of edges.
41///
42/// # Example
43/// ```
44/// use std::collections::hash_map::RandomState;
45/// use petgraph::{algo, prelude::*};
46///
47/// let mut graph = DiGraph::<&str, i32>::new();
48///
49/// let a = graph.add_node("a");
50/// let b = graph.add_node("b");
51/// let c = graph.add_node("c");
52/// let d = graph.add_node("d");
53///
54/// graph.extend_with_edges(&[(a, b, 1), (b, c, 1), (c, d, 1), (a, b, 1), (b, d, 1)]);
55///
56/// let paths = algo::all_simple_paths::<Vec<_>, _, RandomState>(&graph, a, d, 0, None)
57///   .collect::<Vec<_>>();
58///
59/// assert_eq!(paths.len(), 4);
60///
61///
62/// // Take only 2 paths.
63/// let paths = algo::all_simple_paths::<Vec<_>, _, RandomState>(&graph, a, d, 0, None)
64///   .take(2)
65///   .collect::<Vec<_>>();
66///
67/// assert_eq!(paths.len(), 2);
68///
69/// ```
70pub fn all_simple_paths<TargetColl, G, S>(
71    graph: G,
72    from: G::NodeId,
73    to: G::NodeId,
74    min_intermediate_nodes: usize,
75    max_intermediate_nodes: Option<usize>,
76) -> impl Iterator<Item = TargetColl>
77where
78    G: NodeCount,
79    G: IntoNeighborsDirected,
80    G::NodeId: Eq + Hash,
81    TargetColl: FromIterator<G::NodeId>,
82    S: BuildHasher + Default,
83{
84    // how many nodes are allowed in simple path up to target node
85    // it is min/max allowed path length minus one, because it is more appropriate when implementing lookahead
86    // than constantly add 1 to length of current path
87    let max_length = if let Some(l) = max_intermediate_nodes {
88        l + 1
89    } else {
90        graph.node_count() - 1
91    };
92
93    let min_length = min_intermediate_nodes + 1;
94
95    // list of visited nodes
96    let mut visited: IndexSet<G::NodeId, S> = IndexSet::from_iter(Some(from));
97    // list of childs of currently exploring path nodes,
98    // last elem is list of childs of last visited node
99    let mut stack = vec![graph.neighbors_directed(from, Outgoing)];
100
101    from_fn(move || {
102        while let Some(children) = stack.last_mut() {
103            if let Some(child) = children.next() {
104                if visited.contains(&child) {
105                    continue;
106                }
107                if visited.len() < max_length {
108                    if child == to {
109                        if visited.len() >= min_length {
110                            let path = visited
111                                .iter()
112                                .cloned()
113                                .chain(Some(to))
114                                .collect::<TargetColl>();
115                            return Some(path);
116                        }
117                    } else {
118                        visited.insert(child);
119                        stack.push(graph.neighbors_directed(child, Outgoing));
120                    }
121                } else {
122                    if (child == to || children.any(|v| v == to && !visited.contains(&v)))
123                        && visited.len() >= min_length
124                    {
125                        let path = visited
126                            .iter()
127                            .cloned()
128                            .chain(Some(to))
129                            .collect::<TargetColl>();
130                        return Some(path);
131                    }
132                    stack.pop();
133                    visited.pop();
134                }
135            } else {
136                stack.pop();
137                visited.pop();
138            }
139        }
140        None
141    })
142}
143
144/// Calculate all simple paths from a source node to any of several target nodes.
145///
146/// This function is a variant of [`all_simple_paths`] that accepts a `HashSet` of
147/// target nodes instead of a single one. A path is yielded as soon as it reaches any
148/// node in the `to` set.
149///
150/// # Performance Considerations
151///
152/// The efficiency of this function hinges on the graph's structure. It provides significant
153/// performance gains on graphs where paths share long initial segments (e.g., trees and DAGs),
154/// as the benefit of a single traversal outweighs the `HashSet` lookup overhead.
155///
156/// Conversely, in dense graphs where paths diverge quickly or for targets very close
157/// to the source, the lookup overhead could make repeated calls to [`all_simple_paths`]
158/// a faster alternative.
159///
160/// **Note**: If security is not a concern, a faster hasher (e.g., `FxBuildHasher`)
161/// can be specified to minimize the `HashSet` lookup overhead.
162///
163/// # Arguments
164/// * `graph`: an input graph.
165/// * `from`: an initial node of desired paths.
166/// * `to`: a `HashSet` of target nodes. A path is yielded as soon as it reaches any node in this set.
167/// * `min_intermediate_nodes`: the minimum number of nodes in the desired paths.
168/// * `max_intermediate_nodes`: the maximum number of nodes in the desired paths (optional).
169/// # Returns
170/// Returns an iterator that produces all simple paths from `from` node to any node in the `to` set, which contains at least `min_intermediate_nodes`
171/// and at most `max_intermediate_nodes` intermediate nodes, if given, or limited by the graph's order otherwise.
172///
173/// # Complexity
174/// * Time complexity: for computing the first **k** paths, the running time will be **O(k|V| + k|E|)**.
175/// * Auxillary space: **O(|V|)**.
176///
177/// where **|V|** is the number of nodes and **|E|** is the number of edges.
178///
179/// # Example
180/// ```
181/// use petgraph::{algo, prelude::*};
182/// use hashbrown::HashSet;
183/// use std::collections::hash_map::RandomState;
184///
185/// let mut graph = DiGraph::<&str, i32>::new();
186///
187/// let a = graph.add_node("a");
188/// let b = graph.add_node("b");
189/// let c = graph.add_node("c");
190/// let d = graph.add_node("d");
191/// graph.extend_with_edges(&[(a, b, 1), (b, c, 1), (b, d, 1)]);
192///
193/// // Find paths from "a" to either "c" or "d".
194/// let targets = HashSet::from_iter([c, d]);
195/// let mut paths = algo::all_simple_paths_multi::<Vec<_>, _, RandomState>(&graph, a, &targets, 0, None)
196///     .collect::<Vec<_>>();
197///
198/// paths.sort_by_key(|p| p.clone());
199/// let expected_paths = vec![
200///     vec![a, b, c],
201///     vec![a, b, d],
202/// ];
203///
204/// assert_eq!(paths, expected_paths);
205///
206/// ```
207pub fn all_simple_paths_multi<'a, TargetColl, G, S>(
208    graph: G,
209    from: G::NodeId,
210    to: &'a HashSet<G::NodeId, S>,
211    min_intermediate_nodes: usize,
212    max_intermediate_nodes: Option<usize>,
213) -> impl Iterator<Item = TargetColl> + 'a
214where
215    G: NodeCount + IntoNeighborsDirected + 'a,
216    <G as IntoNeighborsDirected>::NeighborsDirected: 'a,
217    G::NodeId: Eq + Hash,
218    TargetColl: FromIterator<G::NodeId>,
219    S: BuildHasher + Default,
220{
221    let max_nodes = if let Some(l) = max_intermediate_nodes {
222        l + 2
223    } else {
224        graph.node_count()
225    };
226
227    let min_nodes = min_intermediate_nodes + 2;
228
229    // list of visited nodes
230    let mut visited: IndexSet<G::NodeId, S> = IndexSet::from_iter(Some(from));
231    // list of childs of currently exploring path nodes,
232    // last elem is list of childs of last visited node
233    let mut stack = vec![graph.neighbors_directed(from, Outgoing)];
234
235    from_fn(move || {
236        while let Some(children) = stack.last_mut() {
237            if let Some(child) = children.next() {
238                if visited.contains(&child) {
239                    continue;
240                }
241
242                let current_nodes = visited.len();
243                let mut valid_path: Option<TargetColl> = None;
244
245                // Check if we've reached a target node
246                if to.contains(&child) && (current_nodes + 1) >= min_nodes {
247                    valid_path = Some(
248                        visited
249                            .iter()
250                            .cloned()
251                            .chain(Some(child))
252                            .collect::<TargetColl>(),
253                    );
254                }
255
256                // Expand the search only if within max length and unexplored target nodes remain
257                if (current_nodes < max_nodes)
258                    && to.iter().any(|n| *n != child && !visited.contains(n))
259                {
260                    visited.insert(child);
261                    stack.push(graph.neighbors_directed(child, Outgoing));
262                }
263
264                // yield the valid path if found
265                if valid_path.is_some() {
266                    return valid_path;
267                }
268            } else {
269                // All neighbors of the current node have been explored
270                stack.pop();
271                visited.pop();
272            }
273        }
274        None
275    })
276}
277
278#[cfg(test)]
279mod test {
280    use alloc::{vec, vec::Vec};
281    use core::iter::FromIterator;
282    use std::{collections::hash_map::RandomState, println};
283
284    use hashbrown::HashSet;
285    use itertools::assert_equal;
286
287    use super::{all_simple_paths, all_simple_paths_multi};
288    use crate::{
289        dot::Dot,
290        prelude::{DiGraph, UnGraph},
291    };
292
293    #[test]
294    fn test_all_simple_paths() {
295        let graph = DiGraph::<i32, i32, _>::from_edges([
296            (0, 1),
297            (0, 2),
298            (0, 3),
299            (1, 2),
300            (1, 3),
301            (2, 3),
302            (2, 4),
303            (3, 2),
304            (3, 4),
305            (4, 2),
306            (4, 5),
307            (5, 2),
308            (5, 3),
309        ]);
310
311        let expexted_simple_paths_0_to_5 = vec![
312            vec![0usize, 1, 2, 3, 4, 5],
313            vec![0, 1, 2, 4, 5],
314            vec![0, 1, 3, 2, 4, 5],
315            vec![0, 1, 3, 4, 5],
316            vec![0, 2, 3, 4, 5],
317            vec![0, 2, 4, 5],
318            vec![0, 3, 2, 4, 5],
319            vec![0, 3, 4, 5],
320        ];
321
322        println!("{}", Dot::new(&graph));
323        let actual_simple_paths_0_to_5: HashSet<Vec<_>> =
324            all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 5u32.into(), 0, None)
325                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
326                .collect();
327        assert_eq!(actual_simple_paths_0_to_5.len(), 8);
328        assert_eq!(
329            HashSet::from_iter(expexted_simple_paths_0_to_5),
330            actual_simple_paths_0_to_5
331        );
332    }
333
334    #[test]
335    fn test_one_simple_path() {
336        let graph = DiGraph::<i32, i32, _>::from_edges([(0, 1), (2, 1)]);
337
338        let expexted_simple_paths_0_to_1 = &[vec![0usize, 1]];
339        println!("{}", Dot::new(&graph));
340        let actual_simple_paths_0_to_1: Vec<Vec<_>> =
341            all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 1u32.into(), 0, None)
342                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
343                .collect();
344
345        assert_eq!(actual_simple_paths_0_to_1.len(), 1);
346        assert_equal(expexted_simple_paths_0_to_1, &actual_simple_paths_0_to_1);
347    }
348
349    #[test]
350    fn test_no_simple_paths() {
351        let graph = DiGraph::<i32, i32, _>::from_edges([(0, 1), (2, 1)]);
352
353        println!("{}", Dot::new(&graph));
354        let actual_simple_paths_0_to_2: Vec<Vec<_>> =
355            all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 2u32.into(), 0, None)
356                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
357                .collect();
358
359        assert_eq!(actual_simple_paths_0_to_2.len(), 0);
360    }
361
362    #[test]
363    fn test_path_graph() {
364        let graph = UnGraph::<i32, i32>::from_edges([(0, 1), (1, 2), (2, 3)]);
365        let paths: Vec<Vec<_>> =
366            all_simple_paths::<_, _, RandomState>(&graph, 0u32.into(), 3u32.into(), 0, None)
367                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
368                .collect();
369        assert_eq!(paths, vec![vec![0, 1, 2, 3]]);
370    }
371
372    #[test]
373    fn test_multi_target_paths() {
374        let graph = UnGraph::<i32, i32>::from_edges([(0, 1), (1, 2), (2, 3), (2, 4)]);
375        let targets = HashSet::from_iter([3.into(), 4.into()]);
376        let paths: HashSet<Vec<_>> =
377            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, None)
378                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
379                .collect();
380        let expected: HashSet<Vec<_>> = HashSet::from_iter([vec![0, 1, 2, 3], vec![0, 1, 2, 4]]);
381        assert_eq!(paths, expected);
382    }
383
384    #[test]
385    fn test_digraph_multi_target_paths() {
386        let graph = DiGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 3), (2, 4)]);
387        let targets = HashSet::from_iter([3.into(), 4.into()]);
388        let paths: HashSet<Vec<_>> =
389            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, None)
390                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
391                .collect();
392        let expected: HashSet<Vec<_>> = HashSet::from_iter([vec![0, 1, 2, 3], vec![0, 1, 2, 4]]);
393        assert_eq!(paths, expected);
394    }
395
396    #[test]
397    fn test_multi_target_paths_cutoff() {
398        let graph = UnGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 3), (2, 4)]);
399        let targets = HashSet::from_iter([3.into(), 4.into()]);
400        let paths: HashSet<Vec<_>> =
401            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, Some(2))
402                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
403                .collect();
404        let expected: HashSet<Vec<_>> = HashSet::from_iter([vec![0, 1, 2, 3], vec![0, 1, 2, 4]]);
405        assert_eq!(paths, expected);
406    }
407
408    #[test]
409    fn test_digraph_multi_target_paths_cutoff() {
410        let graph = DiGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 3), (2, 4)]);
411        let targets = HashSet::from_iter([3.into(), 4.into()]);
412        let paths: HashSet<Vec<_>> =
413            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, Some(2))
414                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
415                .collect();
416        let expected: HashSet<Vec<_>> = HashSet::from_iter([vec![0, 1, 2, 3], vec![0, 1, 2, 4]]);
417        assert_eq!(paths, expected);
418    }
419
420    #[test]
421    fn test_multi_target_paths_inline() {
422        let graph = UnGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 3)]);
423        let targets = HashSet::from_iter([2.into(), 3.into()]);
424        let paths: HashSet<Vec<_>> =
425            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, None)
426                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
427                .collect();
428        let expected: HashSet<Vec<_>> = HashSet::from_iter([vec![0, 1, 2], vec![0, 1, 2, 3]]);
429        assert_eq!(paths, expected);
430    }
431
432    #[test]
433    fn test_all_simple_paths_ignores_cycle() {
434        let graph = DiGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3)]);
435        let paths: Vec<Vec<_>> =
436            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 3.into(), 0, None)
437                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
438                .collect();
439        assert_eq!(paths, vec![vec![0, 1, 3]]);
440    }
441
442    #[test]
443    fn test_multi_target_paths_in_cycle() {
444        let graph = DiGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 0), (1, 3)]);
445        let targets = HashSet::from_iter([2.into(), 3.into()]);
446        let paths: HashSet<Vec<_>> =
447            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, None)
448                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
449                .collect();
450        let expected = HashSet::from_iter([vec![0, 1, 2], vec![0, 1, 3]]);
451        assert_eq!(paths, expected);
452    }
453
454    #[test]
455    fn test_simple_path_source_target() {
456        let graph = UnGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 3)]);
457        let paths: Vec<Vec<_>> =
458            all_simple_paths::<_, _, RandomState>(&graph, 1.into(), 1.into(), 0, None)
459                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
460                .collect();
461        assert!(paths.is_empty());
462    }
463
464    #[test]
465    fn test_simple_paths_cutoff() {
466        let graph =
467            UnGraph::<i32, ()>::from_edges([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
468        let paths: HashSet<Vec<_>> =
469            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 1.into(), 0, Some(0))
470                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
471                .collect();
472        assert_eq!(paths, HashSet::from_iter([vec![0, 1]]));
473
474        let paths: HashSet<Vec<_>> =
475            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 1.into(), 0, Some(1))
476                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
477                .collect();
478        let expected = HashSet::from_iter([vec![0, 1], vec![0, 2, 1], vec![0, 3, 1]]);
479        assert_eq!(paths, expected);
480    }
481
482    #[test]
483    fn test_multi_target_source_is_target() {
484        let graph = UnGraph::<i32, ()>::from_edges([(0, 1), (1, 2)]);
485        let targets = HashSet::from_iter([0.into(), 1.into(), 2.into()]);
486        let paths: HashSet<Vec<_>> =
487            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 0, None)
488                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
489                .collect();
490        let expected = HashSet::from_iter([vec![0, 1], vec![0, 1, 2]]);
491        assert_eq!(paths, expected);
492    }
493
494    #[test]
495    fn test_all_simple_paths_empty() {
496        let graph = UnGraph::<i32, ()>::from_edges([(0, 1), (1, 2), (2, 3)]);
497        let paths: Vec<Vec<_>> =
498            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 3.into(), 0, Some(1))
499                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
500                .collect();
501        assert!(paths.is_empty());
502    }
503
504    #[test]
505    fn test_all_simple_paths_on_non_trivial_graph() {
506        let graph = DiGraph::<i32, ()>::from_edges([
507            (0, 1),
508            (1, 2),
509            (2, 3),
510            (3, 4),
511            (0, 5),
512            (1, 5),
513            (1, 3),
514            (5, 4),
515            (4, 2),
516            (4, 3),
517        ]);
518        let targets = HashSet::from_iter([2.into(), 3.into()]);
519        let paths: HashSet<Vec<_>> =
520            all_simple_paths_multi::<_, _, RandomState>(&graph, 1.into(), &targets, 0, None)
521                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
522                .collect();
523        let expected = HashSet::from_iter([
524            vec![1, 2],
525            vec![1, 5, 4, 2],
526            vec![1, 3, 4, 2],
527            vec![1, 3],
528            vec![1, 2, 3],
529            vec![1, 5, 4, 3],
530            vec![1, 5, 4, 2, 3],
531        ]);
532        assert_eq!(paths, expected);
533    }
534
535    #[test]
536    fn test_all_simple_paths_directed() {
537        let graph = DiGraph::<i32, ()>::from_edges([(1, 2), (2, 3), (3, 2), (2, 1)]);
538        let paths: HashSet<Vec<_>> =
539            all_simple_paths::<_, _, RandomState>(&graph, 1.into(), 3.into(), 0, None)
540                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
541                .collect();
542        let expected = HashSet::from_iter([vec![1, 2, 3]]);
543        assert_eq!(paths, expected);
544    }
545
546    #[test]
547    fn test_all_simple_paths_corner_cases() {
548        let mut graph = DiGraph::<i32, ()>::new();
549        graph.add_node(0);
550        graph.add_node(1);
551
552        let paths: Vec<Vec<_>> =
553            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 0.into(), 0, None)
554                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
555                .collect();
556        assert!(paths.is_empty());
557
558        let paths: Vec<Vec<_>> =
559            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 1.into(), 0, None)
560                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
561                .collect();
562        assert!(paths.is_empty());
563    }
564
565    #[test]
566    fn test_simple_paths_min_intermediate_nodes() {
567        let graph =
568            UnGraph::<i32, ()>::from_edges([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
569        let paths: HashSet<Vec<_>> =
570            all_simple_paths::<_, _, RandomState>(&graph, 0.into(), 1.into(), 2, None)
571                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
572                .collect();
573        let expected = HashSet::from_iter([vec![0, 2, 3, 1], vec![0, 3, 2, 1]]);
574        assert_eq!(paths, expected);
575    }
576
577    #[test]
578    fn test_multi_target_paths_min_intermediate_nodes() {
579        let graph =
580            UnGraph::<i32, ()>::from_edges([(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
581        let targets = HashSet::from_iter([1.into(), 3.into()]);
582        let paths: HashSet<Vec<_>> =
583            all_simple_paths_multi::<_, _, RandomState>(&graph, 0.into(), &targets, 2, None)
584                .map(|v: Vec<_>| v.into_iter().map(|i| i.index()).collect())
585                .collect();
586        let expected = HashSet::from_iter([
587            vec![0, 2, 3, 1],
588            vec![0, 3, 2, 1],
589            vec![0, 1, 2, 3],
590            vec![0, 2, 1, 3],
591        ]);
592        assert_eq!(paths, expected);
593    }
594
595    #[test]
596    fn test_simple_paths_from_node_to_itself_in_complete_graph() {
597        // Create a directed graph
598        let mut graph = DiGraph::new();
599
600        let a = graph.add_node("A");
601        let b = graph.add_node("B");
602        let c = graph.add_node("C");
603
604        // Add edges for complete graph (every node connected to every other node)
605        graph.add_edge(a, b, ());
606        graph.add_edge(a, c, ());
607        graph.add_edge(b, a, ());
608        graph.add_edge(b, c, ());
609        graph.add_edge(c, a, ());
610        graph.add_edge(c, b, ());
611
612        // Find all simple paths from A to A
613        let paths: Vec<Vec<_>> =
614            all_simple_paths::<Vec<_>, _, RandomState>(&graph, a, a, 0, None).collect();
615
616        // The only simple path from a node to itself is a path of length 1 (just the node itself).
617        assert!(paths.is_empty());
618    }
619}