igraph/paths.rs
1//! Shortest paths, distances, eccentricity, efficiency, random walks and
2//! related path algorithms (`igraph_paths.h`).
3//!
4//! Everything here is a method of [`Graph`] (or a free function when no graph
5//! is involved). Distances are returned as a [`Matrix`] (rows are sources,
6//! columns are targets, unreachable pairs hold `f64::INFINITY`), paths as
7//! `Vec<VertexId>` / `Vec<EdgeId>`, and multi-output functions return small
8//! named structs such as [`ShortestPaths`] or [`Diameter`].
9//!
10//! Edge weights are always passed as `Option<&[f64]>` indexed by edge id:
11//! `None` means an unweighted graph (every edge has length one). Most
12//! functions ignore edges with a positive infinite weight.
13//!
14//! # Example
15//!
16//! ```
17//! use igraph::prelude::*;
18//!
19//! // A weighted directed "diamond" with a shortcut: 0 → 1 → 3 and 0 → 2 → 3.
20//! let g = Graph::from_edges(&[(0, 1), (1, 3), (0, 2), (2, 3), (0, 3)], 4, true).unwrap();
21//! let w = [1.0, 1.0, 2.0, 2.0, 5.0];
22//!
23//! // Unweighted: the direct edge wins.
24//! assert_eq!(g.get_shortest_path(0, 3, None, NeighborMode::Out).unwrap().vertices, vec![0, 3]);
25//! // Weighted: go through vertex 1 (length 2 instead of 5).
26//! let p = g.get_shortest_path(0, 3, Some(&w), NeighborMode::Out).unwrap();
27//! assert_eq!(p.vertices, vec![0, 1, 3]);
28//! assert_eq!(p.edges, vec![0, 1]);
29//!
30//! let d = g.distances(0, .., Some(&w), NeighborMode::Out).unwrap();
31//! assert_eq!(d.row(0), vec![0.0, 1.0, 2.0, 2.0]);
32//!
33//! // The three shortest routes from 0 to 3, by increasing length.
34//! let k = g.get_k_shortest_paths(0, 3, 3, Some(&w), NeighborMode::Out).unwrap();
35//! let routes: Vec<_> = k.into_iter().map(|p| p.vertices).collect();
36//! assert_eq!(routes, vec![vec![0, 1, 3], vec![0, 2, 3], vec![0, 3]]);
37//!
38//! // Directed diameter (the longest shortest path) of the unweighted graph.
39//! assert_eq!(g.diameter().unwrap(), 1.0);
40//! ```
41//!
42//! # Provided functionality
43//!
44//! | Topic | Methods |
45//! |-------|---------|
46//! | Distance matrices | [`distances`](Graph::distances), [`distances_cutoff`](Graph::distances_cutoff), [`distances_dijkstra`](Graph::distances_dijkstra), [`distances_dijkstra_cutoff`](Graph::distances_dijkstra_cutoff), [`distances_bellman_ford`](Graph::distances_bellman_ford), [`distances_johnson`](Graph::distances_johnson), [`distances_floyd_warshall`](Graph::distances_floyd_warshall) |
47//! | One shortest path per target | [`get_shortest_paths`](Graph::get_shortest_paths), [`get_shortest_paths_dijkstra`](Graph::get_shortest_paths_dijkstra), [`get_shortest_paths_bellman_ford`](Graph::get_shortest_paths_bellman_ford) |
48//! | A single shortest path | [`get_shortest_path`](Graph::get_shortest_path), [`get_shortest_path_dijkstra`](Graph::get_shortest_path_dijkstra), [`get_shortest_path_bellman_ford`](Graph::get_shortest_path_bellman_ford), [`get_shortest_path_astar`](Graph::get_shortest_path_astar) |
49//! | All shortest paths | [`get_all_shortest_paths`](Graph::get_all_shortest_paths), [`get_all_shortest_paths_dijkstra`](Graph::get_all_shortest_paths_dijkstra) |
50//! | Other path enumerations | [`get_k_shortest_paths`](Graph::get_k_shortest_paths), [`get_all_simple_paths`](Graph::get_all_simple_paths) |
51//! | Widest (bottleneck) paths | [`get_widest_paths`](Graph::get_widest_paths), [`get_widest_path`](Graph::get_widest_path), [`widest_path_widths_dijkstra`](Graph::widest_path_widths_dijkstra), [`widest_path_widths_floyd_warshall`](Graph::widest_path_widths_floyd_warshall) |
52//! | Global path statistics | [`diameter`](Graph::diameter), [`diameter_with_path`](Graph::diameter_with_path), [`pseudo_diameter`](Graph::pseudo_diameter), [`radius`](Graph::radius), [`average_path_length`](Graph::average_path_length), [`average_path_length_details`](Graph::average_path_length_details), [`path_length_hist`](Graph::path_length_hist) |
53//! | Vertex path statistics | [`eccentricity`](Graph::eccentricity), [`graph_center`](Graph::graph_center) |
54//! | Efficiency | [`global_efficiency`](Graph::global_efficiency), [`local_efficiency`](Graph::local_efficiency), [`average_local_efficiency`](Graph::average_local_efficiency) |
55//! | Miscellaneous | [`random_walk`](Graph::random_walk), [`spanner`](Graph::spanner), [`voronoi`](Graph::voronoi), [`vertex_path_from_edge_path`](Graph::vertex_path_from_edge_path), [`expand_path_to_pairs`] |
56//!
57//! Global statistics on a classic network, built with
58//! [`Graph::famous`] from the [`constructors`](crate::constructors) module:
59//!
60//! ```
61//! use igraph::prelude::*;
62//!
63//! let karate = Graph::famous("Zachary").unwrap();
64//! assert_eq!(karate.diameter().unwrap(), 5.0);
65//! assert_eq!(karate.radius(None, NeighborMode::All).unwrap(), 3.0);
66//! let apl = karate.average_path_length(None, false, true).unwrap();
67//! assert!((apl - 2.408199643).abs() < 1e-9);
68//! // Histogram of the 561 vertex pairs by distance (the 78 edges first).
69//! let h = karate.path_length_hist(false).unwrap();
70//! assert_eq!(h.counts[0], 78.0);
71//! assert_eq!(h.counts.iter().sum::<f64>(), 561.0);
72//! ```
73//!
74//! # See also
75//!
76//! - [`centrality`](crate::centrality): distance based centralities such as
77//! [`closeness`](Graph::closeness),
78//! [`harmonic_centrality`](Graph::harmonic_centrality) and
79//! [`betweenness`](Graph::betweenness) (which counts shortest paths).
80//! - [`visitor`](crate::visitor): breadth-first and depth-first traversals
81//! ([`bfs`](Graph::bfs), [`dfs`](Graph::dfs)), the building blocks of
82//! unweighted shortest paths.
83//! - [`components`](crate::components): reachability questions
84//! ([`is_connected`](Graph::is_connected),
85//! [`reachability`](Graph::reachability)) and bounded-distance
86//! neighborhoods ([`neighborhood`](Graph::neighborhood)).
87//! - [`structural`](crate::structural): [`girth`](Graph::girth) (the
88//! shortest cycle) and [`minimum_spanning_tree`](Graph::minimum_spanning_tree).
89//! - [`cycles`](crate::cycles): closed walks and special walks such as
90//! [`find_cycle`](Graph::find_cycle) and
91//! [`eulerian_path`](Graph::eulerian_path) (whose edge sequences
92//! [`vertex_path_from_edge_path`](Graph::vertex_path_from_edge_path)
93//! converts to vertices).
94//! - [`flow`](crate::flow): [`maxflow`](Graph::maxflow), the "capacity"
95//! counterpart of the widest paths offered here.
96//! - [`community`](crate::community):
97//! [`community_voronoi`](Graph::community_voronoi), a community detection
98//! method built on [`voronoi`](Graph::voronoi).
99
100use crate::{
101 constants::{NeighborMode, RandomWalkStuck, VoronoiTiebreaker},
102 error::{Error, ErrorKind, Result, catch_panic},
103 ffi::*,
104 ffi_enum,
105 graph::{EdgeId, VertexId},
106 igraph_call,
107 list::VectorIntList,
108 matrix::Matrix,
109 selector::{EdgeSelector, VertexSelector},
110 vector::{Vector, VectorInt, View},
111};
112use std::{ffi::c_void, ptr};
113
114#[cfg(doc)]
115use crate::graph::Graph;
116
117ffi_enum! {
118 /// Variant of the Floyd–Warshall algorithm used by
119 /// [`Graph::distances_floyd_warshall`] (`igraph_floyd_warshall_algorithm_t`).
120 ///
121 /// The default is [`Automatic`](Self::Automatic).
122 #[derive(Default)]
123 pub enum FloydWarshallAlgorithm: igraph_floyd_warshall_algorithm_t {
124 /// Let igraph choose the best performing variant (currently always `Tree`).
125 #[default]
126 Automatic = igraph_floyd_warshall_algorithm_t_IGRAPH_FLOYD_WARSHALL_AUTOMATIC,
127 /// The textbook O(|V|³) Floyd–Warshall algorithm.
128 Original = igraph_floyd_warshall_algorithm_t_IGRAPH_FLOYD_WARSHALL_ORIGINAL,
129 /// The "Tree" speed-up of Brodnik, Grgurovič and Požar, faster in most cases.
130 Tree = igraph_floyd_warshall_algorithm_t_IGRAPH_FLOYD_WARSHALL_TREE,
131 }
132}
133
134/// A single path, described both by the vertices it visits and by the edges it
135/// traverses.
136///
137/// For a path of `k` edges, `vertices` has `k + 1` entries (source and target
138/// included) and `edges` has `k` entries. An empty `vertices` list means that
139/// there is no path.
140#[derive(Debug, Clone, PartialEq, Eq, Default)]
141pub struct GraphPath {
142 /// The vertex ids along the path, including both endpoints.
143 pub vertices: Vec<VertexId>,
144 /// The edge ids along the path.
145 pub edges: Vec<EdgeId>,
146}
147
148impl GraphPath {
149 /// Number of edges of the path (its unweighted length).
150 pub fn len(&self) -> usize {
151 self.edges.len()
152 }
153
154 /// Whether the path has no edges (no path at all, or a path from a vertex
155 /// to itself).
156 pub fn is_empty(&self) -> bool {
157 self.edges.is_empty()
158 }
159
160 /// Whether a path was found, i.e. `vertices` is non-empty.
161 pub fn exists(&self) -> bool {
162 !self.vertices.is_empty()
163 }
164
165 /// Sum of the weights of the traversed edges (`weights` is indexed by edge id).
166 ///
167 /// # Panics
168 /// If some edge id of the path is out of the bounds of `weights`.
169 pub fn weight(&self, weights: &[f64]) -> f64 {
170 self.edges.iter().map(|&e| weights[e as usize]).sum()
171 }
172}
173
174/// Single-source shortest (or widest) paths towards a set of targets, as
175/// returned by [`Graph::get_shortest_paths`] and friends.
176#[derive(Debug, Clone, PartialEq, Eq, Default)]
177pub struct ShortestPaths {
178 /// `vertices[i]` lists the vertices on the path to the `i`-th target
179 /// (empty if the target is unreachable).
180 pub vertices: Vec<Vec<VertexId>>,
181 /// `edges[i]` lists the edges on the path to the `i`-th target.
182 pub edges: Vec<Vec<EdgeId>>,
183 /// The shortest path tree, indexed by vertex id: the vertex from which each
184 /// vertex was reached. The source has `-1`, and vertices not reached
185 /// during the search have `-2` (the search stops as soon as all the
186 /// targets are reached).
187 pub parents: Vec<VertexId>,
188 /// The shortest path tree, indexed by vertex id: the edge through which
189 /// each vertex was reached; `-1` for the source and unreached vertices.
190 pub inbound_edges: Vec<EdgeId>,
191}
192
193impl ShortestPaths {
194 /// The `i`-th path (towards the `i`-th target) as a [`GraphPath`].
195 pub fn path(&self, i: usize) -> Option<GraphPath> {
196 Some(GraphPath {
197 vertices: self.vertices.get(i)?.clone(),
198 edges: self.edges.get(i)?.clone(),
199 })
200 }
201}
202
203/// All the shortest paths from one source, as returned by
204/// [`Graph::get_all_shortest_paths`].
205#[derive(Debug, Clone, PartialEq, Eq, Default)]
206pub struct AllShortestPaths {
207 /// Vertex lists of all the shortest paths, grouped by target in increasing
208 /// target id order. Unreachable targets contribute no path.
209 pub vertices: Vec<Vec<VertexId>>,
210 /// Edge lists of the same paths, in the same order.
211 pub edges: Vec<Vec<EdgeId>>,
212 /// Number of shortest paths from the source to each vertex (indexed by
213 /// vertex id). Only accurate for the requested targets.
214 pub nrgeo: Vec<i64>,
215}
216
217/// The diameter of a graph together with one longest geodesic, see
218/// [`Graph::diameter_with_path`].
219#[derive(Debug, Clone, PartialEq, Default)]
220pub struct Diameter {
221 /// The diameter: the (weighted) length of the longest shortest path.
222 /// `NaN` for the null graph; `INFINITY` for disconnected graphs when
223 /// `unconn` is `false`.
224 pub length: f64,
225 /// Source of a longest geodesic (`None` if there is no such path).
226 pub from: Option<VertexId>,
227 /// Target of a longest geodesic (`None` if there is no such path).
228 pub to: Option<VertexId>,
229 /// The longest geodesic, by vertices and edges.
230 pub path: GraphPath,
231}
232
233/// A pseudo-diameter and its endpoints, see [`Graph::pseudo_diameter`].
234#[derive(Debug, Clone, Copy, PartialEq, Default)]
235pub struct PseudoDiameter {
236 /// The eccentricity of the pseudo-peripheral vertex found: a lower bound
237 /// of the diameter.
238 pub length: f64,
239 /// Source of the corresponding path (`None` if there is no such path).
240 pub from: Option<VertexId>,
241 /// Target of the corresponding path (`None` if there is no such path).
242 pub to: Option<VertexId>,
243}
244
245/// Average shortest path length and number of disconnected pairs, see
246/// [`Graph::average_path_length_details`].
247#[derive(Debug, Clone, Copy, PartialEq, Default)]
248pub struct AveragePathLength {
249 /// The average length of the shortest paths.
250 pub average: f64,
251 /// The number of ordered vertex pairs `(u, v)` such that `v` is not
252 /// reachable from `u`.
253 pub unconnected_pairs: f64,
254}
255
256/// Histogram of shortest path lengths, see [`Graph::path_length_hist`].
257#[derive(Debug, Clone, PartialEq, Default)]
258pub struct PathLengthHistogram {
259 /// `counts[i]` is the number of vertex pairs at distance `i + 1`.
260 pub counts: Vec<f64>,
261 /// The number of vertex pairs whose second vertex is unreachable from the first.
262 pub unconnected: f64,
263}
264
265/// A Voronoi partitioning, see [`Graph::voronoi`].
266///
267/// Not to be confused with [`community::Voronoi`](crate::community::Voronoi),
268/// the result of the Voronoi *community detection*
269/// [`Graph::community_voronoi`].
270#[derive(Debug, Clone, PartialEq, Default)]
271pub struct VoronoiPartition {
272 /// For each vertex, the *index* (into the `generators` slice) of the
273 /// generator it belongs to, or `-1` when no generator reaches it.
274 pub membership: Vec<i64>,
275 /// For each vertex, the distance to/from its generator (`INFINITY` when unreachable).
276 pub distances: Vec<f64>,
277}
278
279/// Length limits for [`Graph::get_all_simple_paths`].
280///
281/// `None` means "no limit"; the default has no limits at all.
282///
283/// ```
284/// use igraph::paths::SimplePathsOptions;
285/// let opts = SimplePathsOptions::default().with_max_len(3).with_max_results(10);
286/// assert_eq!(opts.min_len, None);
287/// assert_eq!(opts.max_len, Some(3));
288/// ```
289#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
290pub struct SimplePathsOptions {
291 /// Minimum length (number of edges) of the returned paths.
292 pub min_len: Option<usize>,
293 /// Maximum length (number of edges) of the returned paths.
294 pub max_len: Option<usize>,
295 /// Stop after this many paths have been found.
296 pub max_results: Option<usize>,
297}
298
299impl SimplePathsOptions {
300 /// Sets the minimum path length.
301 pub fn with_min_len(mut self, min_len: usize) -> Self {
302 self.min_len = Some(min_len);
303 self
304 }
305
306 /// Sets the maximum path length.
307 pub fn with_max_len(mut self, max_len: usize) -> Self {
308 self.max_len = Some(max_len);
309 self
310 }
311
312 /// Sets the maximum number of returned paths.
313 pub fn with_max_results(mut self, max_results: usize) -> Self {
314 self.max_results = Some(max_results);
315 self
316 }
317}
318
319// ---------------------------------------------------------------------------
320// Private helpers.
321
322fn weights_view(weights: Option<&[f64]>) -> Option<View<'_, Vector>> {
323 weights.map(Vector::view)
324}
325
326fn weights_ptr(view: &Option<View<'_, Vector>>) -> *const igraph_vector_t {
327 view.as_ref().map_or(ptr::null(), |v| v.as_ptr())
328}
329
330fn opt_id(id: igraph_int_t) -> Option<VertexId> {
331 (id >= 0).then_some(id)
332}
333
334/// Converts a count to `igraph_int_t`, saturating at `igraph_int_t::MAX`
335/// (a plain `as` cast would turn huge counts into negative values, which
336/// igraph interprets as "unlimited" or rejects).
337fn saturating_int(value: usize) -> igraph_int_t {
338 igraph_int_t::try_from(value).unwrap_or(igraph_int_t::MAX)
339}
340
341fn limit(value: Option<usize>) -> igraph_int_t {
342 value.map_or(IGRAPH_UNLIMITED as igraph_int_t, saturating_int)
343}
344
345/// Maps an optional vertex to igraph's "negative means automatic"
346/// convention, rejecting explicit ids outside `0..vcount` (igraph would
347/// silently treat negative ones as `None`, and some functions report
348/// `IGRAPH_EINVAL` rather than `IGRAPH_EINVVID` for too large ones).
349fn opt_vertex_arg(vertex: Option<VertexId>, vcount: usize) -> Result<igraph_int_t> {
350 match vertex {
351 Some(v) if !(0..vcount as VertexId).contains(&v) => Err(Error::new(
352 ErrorKind::InvalidVertexId,
353 format!("invalid vertex id {v}"),
354 )),
355 Some(v) => Ok(v),
356 None => Ok(-1),
357 }
358}
359
360/// igraph treats negative cutoffs as "no cutoff"; a NaN cutoff has no
361/// meaning and is rejected.
362fn cutoff_value(cutoff: Option<f64>) -> Result<igraph_real_t> {
363 match cutoff {
364 Some(c) if c.is_nan() => Err(Error::invalid("the cutoff must not be NaN")),
365 Some(c) => Ok(c),
366 None => Ok(-1.0),
367 }
368}
369
370impl igraph_t {
371 /// Checks that the weight vector, if given, has one entry per edge.
372 fn paths_check_weights(&self, weights: Option<&[f64]>) -> Result<()> {
373 match weights {
374 Some(w) if w.len() != self.ecount() => Err(Error::invalid(format!(
375 "the weight vector has length {} but the graph has {} edges",
376 w.len(),
377 self.ecount()
378 ))),
379 _ => Ok(()),
380 }
381 }
382
383 /// Shared plumbing of the `igraph_distances*`-like functions.
384 fn distance_matrix<'a>(
385 &self,
386 from: impl Into<VertexSelector<'a>>,
387 to: impl Into<VertexSelector<'a>>,
388 weights: Option<&[f64]>,
389 call: impl FnOnce(
390 *mut igraph_matrix_t,
391 igraph_vs_t,
392 igraph_vs_t,
393 *const igraph_vector_t,
394 ) -> igraph_error_t,
395 ) -> Result<Matrix> {
396 self.paths_check_weights(weights)?;
397 let from = from.into().to_raw()?;
398 let to = to.into().to_raw()?;
399 let w = weights_view(weights);
400 let mut res = Matrix::new();
401 igraph_call!(call(&mut res, from.get(), to.get(), weights_ptr(&w)))?;
402 Ok(res)
403 }
404
405 /// Shared plumbing of the `igraph_get_shortest_paths*`-like functions.
406 fn shortest_paths_with<'a>(
407 &self,
408 to: impl Into<VertexSelector<'a>>,
409 weights: Option<&[f64]>,
410 call: impl FnOnce(
411 *mut igraph_vector_int_list_t,
412 *mut igraph_vector_int_list_t,
413 igraph_vs_t,
414 *const igraph_vector_t,
415 *mut igraph_vector_int_t,
416 *mut igraph_vector_int_t,
417 ) -> igraph_error_t,
418 ) -> Result<ShortestPaths> {
419 self.paths_check_weights(weights)?;
420 let to = to.into().to_raw()?;
421 let w = weights_view(weights);
422 let mut vertices = VectorIntList::new();
423 let mut edges = VectorIntList::new();
424 let mut parents = VectorInt::new();
425 let mut inbound = VectorInt::new();
426 igraph_call!(call(
427 &mut vertices,
428 &mut edges,
429 to.get(),
430 weights_ptr(&w),
431 &mut parents,
432 &mut inbound
433 ))?;
434 Ok(ShortestPaths {
435 vertices: vertices.to_vecs(),
436 edges: edges.to_vecs(),
437 parents: parents.into(),
438 inbound_edges: inbound.into(),
439 })
440 }
441
442 /// Shared plumbing of the `igraph_get_shortest_path*`-like functions.
443 fn single_path_with(
444 &self,
445 weights: Option<&[f64]>,
446 call: impl FnOnce(
447 *mut igraph_vector_int_t,
448 *mut igraph_vector_int_t,
449 *const igraph_vector_t,
450 ) -> igraph_error_t,
451 ) -> Result<GraphPath> {
452 self.paths_check_weights(weights)?;
453 let w = weights_view(weights);
454 let mut vertices = VectorInt::new();
455 let mut edges = VectorInt::new();
456 igraph_call!(call(&mut vertices, &mut edges, weights_ptr(&w)))?;
457 Ok(GraphPath {
458 vertices: vertices.into(),
459 edges: edges.into(),
460 })
461 }
462
463 /// Shared plumbing of the `igraph_get_all_shortest_paths*` functions.
464 fn all_shortest_paths_with<'a>(
465 &self,
466 to: impl Into<VertexSelector<'a>>,
467 weights: Option<&[f64]>,
468 call: impl FnOnce(
469 *mut igraph_vector_int_list_t,
470 *mut igraph_vector_int_list_t,
471 *mut igraph_vector_int_t,
472 igraph_vs_t,
473 *const igraph_vector_t,
474 ) -> igraph_error_t,
475 ) -> Result<AllShortestPaths> {
476 self.paths_check_weights(weights)?;
477 let to = to.into().to_raw()?;
478 let w = weights_view(weights);
479 let mut vertices = VectorIntList::new();
480 let mut edges = VectorIntList::new();
481 let mut nrgeo = VectorInt::new();
482 igraph_call!(call(
483 &mut vertices,
484 &mut edges,
485 &mut nrgeo,
486 to.get(),
487 weights_ptr(&w)
488 ))?;
489 Ok(AllShortestPaths {
490 vertices: vertices.to_vecs(),
491 edges: edges.to_vecs(),
492 nrgeo: nrgeo.into(),
493 })
494 }
495}
496
497// ---------------------------------------------------------------------------
498// Diameter, radius, eccentricity and averages.
499
500impl igraph_t {
501 /// The diameter of the graph: the length of its longest shortest path.
502 ///
503 /// This is the backwards-compatible shorthand of
504 /// [`diameter_with_path`](Self::diameter_with_path) for the most common
505 /// case: unweighted, following edge directions in directed graphs
506 /// (`directed = self.is_directed()`), and, for disconnected graphs,
507 /// returning the longest geodesic *within* a component (`unconn = true`).
508 /// The diameter of the null graph is `NaN`.
509 ///
510 /// See also [`girth`](Self::girth), the length of the *shortest* cycle,
511 /// and [`radius`](Self::radius), the smallest eccentricity.
512 ///
513 /// Binds [`igraph_diameter`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_diameter).
514 /// Time complexity: O(|V| |E|).
515 ///
516 /// # Examples
517 ///
518 /// ```
519 /// use igraph::prelude::*;
520 /// // A path on 5 vertices has diameter 4 ...
521 /// let path = Graph::path_graph(5, false, false).unwrap();
522 /// assert_eq!(path.diameter().unwrap(), 4.0);
523 /// // ... and closing it into a cycle halves it.
524 /// let mut cycle = path.clone();
525 /// cycle.add_edge(4, 0).unwrap();
526 /// assert_eq!(cycle.diameter().unwrap(), 2.0);
527 /// assert!(Graph::new(0, false).diameter().unwrap().is_nan());
528 /// ```
529 pub fn diameter(&self) -> Result<f64> {
530 let mut res = 0.0;
531 igraph_call!(igraph_diameter(
532 self,
533 ptr::null(),
534 &mut res,
535 ptr::null_mut(),
536 ptr::null_mut(),
537 ptr::null_mut(),
538 ptr::null_mut(),
539 self.is_directed(),
540 true
541 ))?;
542 Ok(res)
543 }
544
545 /// The (weighted) diameter of the graph together with its endpoints and
546 /// one longest geodesic.
547 ///
548 /// The diameter is the maximum eccentricity of the vertices, i.e. the
549 /// length of the longest shortest path.
550 ///
551 /// - `weights`: optional edge lengths (Dijkstra's algorithm is used when
552 /// given); edges with positive infinite weight are ignored.
553 /// - `directed`: whether to follow edge directions (ignored for
554 /// undirected graphs).
555 /// - `unconn`: for disconnected graphs, `true` returns the longest
556 /// geodesic within a component, `false` returns `INFINITY`.
557 ///
558 /// The null graph has diameter `NaN`; `from`/`to` are `None` and the path
559 /// is empty when there is no diameter path.
560 ///
561 /// Binds [`igraph_diameter`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_diameter).
562 /// Time complexity: O(|V| |E|) unweighted, O(|V| |E| log |E|) weighted.
563 ///
564 /// # Examples
565 ///
566 /// ```
567 /// use igraph::prelude::*;
568 /// // The directed path 0 → 1 → ... → 9 (a non-circular directed ring, as
569 /// // in igraph's own example).
570 /// let ring = Graph::ring(10, true, false, false).unwrap();
571 /// let d = ring.diameter_with_path(None, true, true).unwrap();
572 /// assert_eq!(d.length, 9.0);
573 /// assert_eq!((d.from, d.to), (Some(0), Some(9)));
574 /// assert_eq!(d.path.vertices, (0..10).collect::<Vec<_>>());
575 /// assert_eq!(d.path.edges, (0..9).collect::<Vec<_>>());
576 /// ```
577 pub fn diameter_with_path(
578 &self,
579 weights: Option<&[f64]>,
580 directed: bool,
581 unconn: bool,
582 ) -> Result<Diameter> {
583 self.paths_check_weights(weights)?;
584 let w = weights_view(weights);
585 let mut length = 0.0;
586 let (mut from, mut to) = (-1, -1);
587 let mut vpath = VectorInt::new();
588 let mut epath = VectorInt::new();
589 igraph_call!(igraph_diameter(
590 self,
591 weights_ptr(&w),
592 &mut length,
593 &mut from,
594 &mut to,
595 &mut vpath,
596 &mut epath,
597 directed,
598 unconn
599 ))?;
600 Ok(Diameter {
601 length,
602 from: opt_id(from),
603 to: opt_id(to),
604 path: GraphPath {
605 vertices: vpath.into(),
606 edges: epath.into(),
607 },
608 })
609 }
610
611 /// An approximation (and lower bound) of the diameter, computed from a
612 /// pseudo-peripheral vertex.
613 ///
614 /// A pseudo-peripheral vertex `v` is such that for every vertex `u` as far
615 /// away from `v` as possible, `v` is also as far away from `u` as
616 /// possible. The search starts at `start` (a random vertex when `None`);
617 /// in disconnected graphs the result refers to the component of the start
618 /// vertex. `directed` and `unconn` have the same meaning as in
619 /// [`diameter_with_path`](Self::diameter_with_path): with `unconn =
620 /// false` a disconnected graph gives `INFINITY` and no endpoints. Returns
621 /// `NaN` for the null graph. With `start = None` the start vertex is drawn
622 /// from the calling thread's default random number generator (seed it
623 /// with [`rng::seed`](crate::rng::seed) for reproducible results).
624 ///
625 /// # Errors
626 /// [`ErrorKind::InvalidVertexId`] if
627 /// `start` is not a vertex of the graph (negative ids included).
628 ///
629 /// Binds [`igraph_pseudo_diameter`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_pseudo_diameter).
630 /// Time complexity: O(|V| |E| log |E|).
631 ///
632 /// # Examples
633 ///
634 /// ```
635 /// use igraph::prelude::*;
636 /// let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
637 /// let pd = path.pseudo_diameter(None, Some(1), false, true).unwrap();
638 /// // On trees the pseudo-diameter is exact.
639 /// assert_eq!(pd.length, 3.0);
640 /// ```
641 pub fn pseudo_diameter(
642 &self,
643 weights: Option<&[f64]>,
644 start: Option<VertexId>,
645 directed: bool,
646 unconn: bool,
647 ) -> Result<PseudoDiameter> {
648 self.paths_check_weights(weights)?;
649 let start = opt_vertex_arg(start, self.vcount())?;
650 let w = weights_view(weights);
651 let mut length = 0.0;
652 let (mut from, mut to) = (-1, -1);
653 self.with_fresh_multi_cache(|| {
654 igraph_call!(igraph_pseudo_diameter(
655 self,
656 weights_ptr(&w),
657 &mut length,
658 start,
659 &mut from,
660 &mut to,
661 directed,
662 unconn
663 ))
664 })?;
665 Ok(PseudoDiameter {
666 length,
667 from: opt_id(from),
668 to: opt_id(to),
669 })
670 }
671
672 /// Eccentricity of the selected vertices: the largest distance from (or
673 /// to, depending on `mode`) each vertex to any vertex reachable from it.
674 ///
675 /// Vertex pairs in different components are ignored, so isolated vertices
676 /// have eccentricity zero. `weights` must be non-negative and not NaN;
677 /// edges with infinite weight are ignored. The maximum eccentricity is
678 /// the [diameter](Self::diameter_with_path), the minimum the
679 /// [radius](Self::radius). See also [`closeness`](Self::closeness), which
680 /// averages the distances instead of taking their maximum.
681 ///
682 /// Binds [`igraph_eccentricity`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_eccentricity).
683 /// Time complexity: O(|V| |E| log |V| + |V|).
684 ///
685 /// # Examples
686 ///
687 /// ```
688 /// use igraph::prelude::*;
689 /// // A star: the center has eccentricity 1, the leaves 2.
690 /// let star = Graph::star(4, StarMode::Undirected, 0).unwrap();
691 /// assert_eq!(star.eccentricity(.., None, NeighborMode::All).unwrap(), vec![1.0, 2.0, 2.0, 2.0]);
692 /// // Only some vertices, in the requested order.
693 /// assert_eq!(star.eccentricity(vec![3, 0], None, NeighborMode::All).unwrap(), vec![2.0, 1.0]);
694 /// ```
695 pub fn eccentricity<'a>(
696 &self,
697 vids: impl Into<VertexSelector<'a>>,
698 weights: Option<&[f64]>,
699 mode: NeighborMode,
700 ) -> Result<Vec<f64>> {
701 self.paths_check_weights(weights)?;
702 let vs = vids.into().to_raw()?;
703 let w = weights_view(weights);
704 let mut res = Vector::new();
705 self.with_fresh_multi_cache(|| {
706 igraph_call!(igraph_eccentricity(
707 self,
708 weights_ptr(&w),
709 &mut res,
710 vs.get(),
711 mode.into()
712 ))
713 })?;
714 Ok(res.into())
715 }
716
717 /// The radius of the graph: the smallest eccentricity of its vertices
718 /// (`NaN` for the null graph).
719 ///
720 /// Binds [`igraph_radius`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_radius).
721 /// Time complexity: O(|V| |E| log |V| + |V|).
722 ///
723 /// # Examples
724 ///
725 /// ```
726 /// use igraph::prelude::*;
727 /// let path = Graph::path_graph(5, false, false).unwrap();
728 /// assert_eq!(path.radius(None, NeighborMode::All).unwrap(), 2.0);
729 /// // Weighted: stretching the first edge moves the center towards it.
730 /// let w = [10.0, 1.0, 1.0, 1.0];
731 /// assert_eq!(path.radius(Some(&w), NeighborMode::All).unwrap(), 10.0);
732 /// ```
733 pub fn radius(&self, weights: Option<&[f64]>, mode: NeighborMode) -> Result<f64> {
734 self.paths_check_weights(weights)?;
735 let w = weights_view(weights);
736 let mut res = 0.0;
737 self.with_fresh_multi_cache(|| {
738 igraph_call!(igraph_radius(self, weights_ptr(&w), &mut res, mode.into()))
739 })?;
740 Ok(res)
741 }
742
743 /// The center of the graph: the vertices of minimum eccentricity.
744 ///
745 /// In disconnected graphs the minimum is taken across all components.
746 /// This function is marked *experimental* in igraph.
747 ///
748 /// Binds [`igraph_graph_center`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_graph_center).
749 /// Time complexity: O(|V| |E| log |V| + |V|).
750 ///
751 /// # Examples
752 ///
753 /// ```
754 /// use igraph::prelude::*;
755 /// // The center of a path with an even number of vertices is its middle edge.
756 /// let path = Graph::path_graph(4, false, false).unwrap();
757 /// assert_eq!(path.graph_center(None, NeighborMode::All).unwrap(), vec![1, 2]);
758 /// ```
759 pub fn graph_center(
760 &self,
761 weights: Option<&[f64]>,
762 mode: NeighborMode,
763 ) -> Result<Vec<VertexId>> {
764 self.paths_check_weights(weights)?;
765 let w = weights_view(weights);
766 let mut res = VectorInt::new();
767 self.with_fresh_multi_cache(|| {
768 igraph_call!(igraph_graph_center(
769 self,
770 weights_ptr(&w),
771 &mut res,
772 mode.into()
773 ))
774 })?;
775 Ok(res.into())
776 }
777
778 /// The average shortest path length over all ordered pairs of distinct
779 /// vertices.
780 ///
781 /// - `weights`: optional non-negative edge lengths.
782 /// - `directed`: whether to follow edge directions (ignored for
783 /// undirected graphs).
784 /// - `unconn`: if `true`, only pairs connected by a path are averaged;
785 /// if `false`, disconnected graphs give `INFINITY`.
786 ///
787 /// Returns `NaN` when no pair can be included (e.g. fewer than two
788 /// vertices). See [`average_path_length_details`](Self::average_path_length_details)
789 /// to also obtain the number of disconnected pairs, and
790 /// [`closeness`](Self::closeness) for the per-vertex (inverse) averages.
791 ///
792 /// Binds [`igraph_average_path_length`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_average_path_length).
793 /// Time complexity: O(|V| |E| log |E| + |V|).
794 ///
795 /// # Examples
796 ///
797 /// ```
798 /// use igraph::prelude::*;
799 /// // In a triangle every pair is adjacent.
800 /// let k3 = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
801 /// assert_eq!(k3.average_path_length(None, false, true).unwrap(), 1.0);
802 /// // Path 0-1-2: distances 1, 1, 2 → average 4/3.
803 /// let p3 = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
804 /// assert!((p3.average_path_length(None, false, true).unwrap() - 4.0 / 3.0).abs() < 1e-12);
805 /// ```
806 pub fn average_path_length(
807 &self,
808 weights: Option<&[f64]>,
809 directed: bool,
810 unconn: bool,
811 ) -> Result<f64> {
812 Ok(self
813 .average_path_length_details(weights, directed, unconn)?
814 .average)
815 }
816
817 /// Like [`average_path_length`](Self::average_path_length), also
818 /// returning the number of ordered vertex pairs `(u, v)` with `v`
819 /// unreachable from `u`.
820 ///
821 /// Binds [`igraph_average_path_length`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_average_path_length).
822 ///
823 /// # Examples
824 ///
825 /// ```
826 /// use igraph::prelude::*;
827 /// // Two disjoint edges: 4 ordered connected pairs, 8 unconnected ones.
828 /// let g = Graph::from_edges(&[(0, 1), (2, 3)], 4, false).unwrap();
829 /// let apl = g.average_path_length_details(None, false, true).unwrap();
830 /// assert_eq!(apl.average, 1.0);
831 /// assert_eq!(apl.unconnected_pairs, 8.0);
832 /// ```
833 pub fn average_path_length_details(
834 &self,
835 weights: Option<&[f64]>,
836 directed: bool,
837 unconn: bool,
838 ) -> Result<AveragePathLength> {
839 self.paths_check_weights(weights)?;
840 let w = weights_view(weights);
841 let (mut average, mut unconnected_pairs) = (0.0, 0.0);
842 igraph_call!(igraph_average_path_length(
843 self,
844 weights_ptr(&w),
845 &mut average,
846 &mut unconnected_pairs,
847 directed,
848 unconn
849 ))?;
850 Ok(AveragePathLength {
851 average,
852 unconnected_pairs,
853 })
854 }
855
856 /// Histogram of the (unweighted) shortest path lengths between all vertex
857 /// pairs.
858 ///
859 /// `counts[0]` is the number of pairs at distance 1, `counts[1]` at
860 /// distance 2, and so on. In undirected graphs (or with `directed =
861 /// false`) each unordered pair is counted once; in directed graphs with
862 /// `directed = true` both directions are counted.
863 ///
864 /// Binds [`igraph_path_length_hist`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_path_length_hist).
865 /// Time complexity: O(|V| |E|).
866 ///
867 /// # Examples
868 ///
869 /// ```
870 /// use igraph::prelude::*;
871 /// // A path on 4 vertices: 3 pairs at distance 1, 2 at distance 2, 1 at distance 3.
872 /// let p4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
873 /// let h = p4.path_length_hist(false).unwrap();
874 /// assert_eq!(h.counts, vec![3.0, 2.0, 1.0]);
875 /// assert_eq!(h.unconnected, 0.0);
876 /// ```
877 pub fn path_length_hist(&self, directed: bool) -> Result<PathLengthHistogram> {
878 let mut counts = Vector::new();
879 let mut unconnected = 0.0;
880 igraph_call!(igraph_path_length_hist(
881 self,
882 &mut counts,
883 &mut unconnected,
884 directed
885 ))?;
886 Ok(PathLengthHistogram {
887 counts: counts.into(),
888 unconnected,
889 })
890 }
891
892 /// The global efficiency of the network: the average of the inverse
893 /// distances between all ordered pairs of distinct vertices,
894 /// `E = 1/(N(N-1)) Σ_{i≠j} 1/d_ij` (Latora & Marchiori, 2001).
895 ///
896 /// Unreachable pairs contribute zero, so, unlike the
897 /// [average path length](Self::average_path_length), it is well defined
898 /// for disconnected graphs; graphs with fewer than two vertices give
899 /// `NaN`. It equals the mean of the normalized
900 /// [`harmonic_centrality`](Self::harmonic_centrality) of the vertices.
901 ///
902 /// Binds [`igraph_global_efficiency`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_global_efficiency).
903 /// Time complexity: O(|V| |E|) unweighted, O(|V| |E| log |E| + |V|) weighted.
904 ///
905 /// # Examples
906 ///
907 /// ```
908 /// use igraph::prelude::*;
909 /// let k4 = Graph::full(4, false, false).unwrap();
910 /// assert_eq!(k4.global_efficiency(None, false).unwrap(), 1.0);
911 /// // Path 0-1-2: inverse distances 1, 1, 1/2 (each counted twice) → 5/6.
912 /// let p3 = Graph::path_graph(3, false, false).unwrap();
913 /// assert!((p3.global_efficiency(None, false).unwrap() - 5.0 / 6.0).abs() < 1e-12);
914 /// ```
915 pub fn global_efficiency(&self, weights: Option<&[f64]>, directed: bool) -> Result<f64> {
916 self.paths_check_weights(weights)?;
917 let w = weights_view(weights);
918 let mut res = 0.0;
919 igraph_call!(igraph_global_efficiency(
920 self,
921 weights_ptr(&w),
922 &mut res,
923 directed
924 ))?;
925 Ok(res)
926 }
927
928 /// The local efficiency around each selected vertex.
929 ///
930 /// The vertex is removed and the average inverse distance between its
931 /// neighbors (through the rest of the network) is computed. Unreachable
932 /// pairs contribute zero; vertices with fewer than two neighbors have
933 /// local efficiency zero. `mode` selects which neighbors form the local
934 /// neighborhood in directed graphs (`NeighborMode::All` is a sensible
935 /// default), `directed` whether distances follow edge directions.
936 /// It is a distance based analogue of the local clustering coefficient
937 /// ([`transitivity_local_undirected`](Self::transitivity_local_undirected)).
938 ///
939 /// Binds [`igraph_local_efficiency`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_local_efficiency).
940 /// Time complexity: O(|E|² log |E|) weighted, O(|E|²) unweighted.
941 ///
942 /// # Examples
943 ///
944 /// ```
945 /// use igraph::prelude::*;
946 /// // In a 4-cycle the two neighbors of a vertex are at distance 2 once it is removed.
947 /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
948 /// assert_eq!(c4.local_efficiency(.., None, false, NeighborMode::All).unwrap(), vec![0.5; 4]);
949 /// ```
950 pub fn local_efficiency<'a>(
951 &self,
952 vids: impl Into<VertexSelector<'a>>,
953 weights: Option<&[f64]>,
954 directed: bool,
955 mode: NeighborMode,
956 ) -> Result<Vec<f64>> {
957 self.paths_check_weights(weights)?;
958 let vs = vids.into().to_raw()?;
959 let w = weights_view(weights);
960 let mut res = Vector::new();
961 igraph_call!(igraph_local_efficiency(
962 self,
963 weights_ptr(&w),
964 &mut res,
965 vs.get(),
966 directed,
967 mode.into()
968 ))?;
969 Ok(res.into())
970 }
971
972 /// The average of the [local efficiencies](Self::local_efficiency) of all
973 /// vertices (zero for the null graph).
974 ///
975 /// Binds [`igraph_average_local_efficiency`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_average_local_efficiency).
976 /// Time complexity: O(|E|² log |E|) weighted, O(|E|²) unweighted.
977 ///
978 /// # Examples
979 ///
980 /// ```
981 /// use igraph::prelude::*;
982 /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
983 /// assert_eq!(c4.average_local_efficiency(None, false, NeighborMode::All).unwrap(), 0.5);
984 /// ```
985 pub fn average_local_efficiency(
986 &self,
987 weights: Option<&[f64]>,
988 directed: bool,
989 mode: NeighborMode,
990 ) -> Result<f64> {
991 self.paths_check_weights(weights)?;
992 let w = weights_view(weights);
993 let mut res = 0.0;
994 igraph_call!(igraph_average_local_efficiency(
995 self,
996 weights_ptr(&w),
997 &mut res,
998 directed,
999 mode.into()
1000 ))?;
1001 Ok(res)
1002 }
1003}
1004
1005// ---------------------------------------------------------------------------
1006// Distance matrices.
1007
1008impl igraph_t {
1009 /// Shortest path lengths between the `from` and `to` vertices.
1010 ///
1011 /// Row `i` of the result holds the distances from the `i`-th source to
1012 /// every target (`INFINITY` if unreachable). `to` must not contain
1013 /// duplicates. `mode` chooses outgoing (`Out`), incoming (`In`) or
1014 /// undirected (`All`) paths in directed graphs.
1015 ///
1016 /// With `weights = None` a BFS is used; with weights, igraph picks the
1017 /// most suitable algorithm: Floyd–Warshall for dense all-pairs problems,
1018 /// Dijkstra for non-negative weights, and Bellman–Ford or Johnson when
1019 /// negative weights are present.
1020 ///
1021 /// See also [`bfs`](Self::bfs) (which also reports BFS distances),
1022 /// [`neighborhood`](Self::neighborhood) (the vertices within a given
1023 /// distance) and [`closeness`](Self::closeness) /
1024 /// [`harmonic_centrality`](Self::harmonic_centrality), which summarize
1025 /// the rows of this matrix.
1026 ///
1027 /// Binds [`igraph_distances`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances).
1028 /// Time complexity (unweighted): O(n(|V| + |E|)) for n sources.
1029 ///
1030 /// # Errors
1031 /// [`ErrorKind::InvalidVertexId`] for
1032 /// invalid vertices, [`ErrorKind::NegativeCycle`]
1033 /// if negative weights form a negative cycle.
1034 ///
1035 /// # Examples
1036 ///
1037 /// ```
1038 /// use igraph::prelude::*;
1039 /// let p = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
1040 /// let d = p.distances(.., .., None, NeighborMode::All).unwrap();
1041 /// assert_eq!(d.to_rows(), vec![vec![0.0, 1.0, 2.0], vec![1.0, 0.0, 1.0], vec![2.0, 1.0, 0.0]]);
1042 /// // Only from vertex 0 to vertices 1 and 2:
1043 /// let d = p.distances(0, vec![1, 2], None, NeighborMode::All).unwrap();
1044 /// assert_eq!(d.to_rows(), vec![vec![1.0, 2.0]]);
1045 /// ```
1046 pub fn distances<'a>(
1047 &self,
1048 from: impl Into<VertexSelector<'a>>,
1049 to: impl Into<VertexSelector<'a>>,
1050 weights: Option<&[f64]>,
1051 mode: NeighborMode,
1052 ) -> Result<Matrix> {
1053 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1054 igraph_distances(self, w, res, from, to, mode.into())
1055 })
1056 }
1057
1058 /// Like [`distances`](Self::distances), but paths longer than `cutoff`
1059 /// are ignored (their length is reported as `INFINITY`).
1060 ///
1061 /// `cutoff = None` (or a negative value) means no cutoff. The search from
1062 /// each source stops at the cutoff, which may save much time. With
1063 /// `weights` this is the same as
1064 /// [`distances_dijkstra_cutoff`](Self::distances_dijkstra_cutoff).
1065 ///
1066 /// # Errors
1067 /// [`ErrorKind::InvalidValue`] for a NaN
1068 /// cutoff or invalid weights,
1069 /// [`ErrorKind::InvalidVertexId`] for
1070 /// invalid vertices.
1071 ///
1072 /// Binds [`igraph_distances_cutoff`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_cutoff).
1073 /// Time complexity: O(s |E| + |V|) for s sources (unweighted).
1074 ///
1075 /// # Examples
1076 ///
1077 /// ```
1078 /// use igraph::prelude::*;
1079 /// let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1080 /// let d = p.distances_cutoff(0, .., None, NeighborMode::All, Some(2.0)).unwrap();
1081 /// assert_eq!(d.row(0), vec![0.0, 1.0, 2.0, f64::INFINITY]);
1082 /// ```
1083 pub fn distances_cutoff<'a>(
1084 &self,
1085 from: impl Into<VertexSelector<'a>>,
1086 to: impl Into<VertexSelector<'a>>,
1087 weights: Option<&[f64]>,
1088 mode: NeighborMode,
1089 cutoff: Option<f64>,
1090 ) -> Result<Matrix> {
1091 let cutoff = cutoff_value(cutoff)?;
1092 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1093 igraph_distances_cutoff(self, w, res, from, to, mode.into(), cutoff)
1094 })
1095 }
1096
1097 /// Weighted shortest path lengths with Dijkstra's algorithm (binary heap),
1098 /// run independently from each source.
1099 ///
1100 /// Weights must be non-negative and not NaN; `None` falls back to the
1101 /// unweighted [`distances`](Self::distances).
1102 ///
1103 /// Binds [`igraph_distances_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_dijkstra).
1104 /// Time complexity: O(s |E| log |V| + |V|) for s sources.
1105 ///
1106 /// # Errors
1107 /// [`ErrorKind::InvalidValue`] for
1108 /// negative or NaN weights, or a weight vector of the wrong length.
1109 ///
1110 /// # Examples
1111 ///
1112 /// ```
1113 /// use igraph::prelude::*;
1114 /// let tri = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, false).unwrap();
1115 /// let d = tri.distances_dijkstra(0, 2, Some(&[1.0, 1.0, 5.0]), NeighborMode::All).unwrap();
1116 /// assert_eq!(d[(0, 0)], 2.0);
1117 /// ```
1118 pub fn distances_dijkstra<'a>(
1119 &self,
1120 from: impl Into<VertexSelector<'a>>,
1121 to: impl Into<VertexSelector<'a>>,
1122 weights: Option<&[f64]>,
1123 mode: NeighborMode,
1124 ) -> Result<Matrix> {
1125 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1126 igraph_distances_dijkstra(self, res, from, to, w, mode.into())
1127 })
1128 }
1129
1130 /// Like [`distances_dijkstra`](Self::distances_dijkstra), ignoring paths
1131 /// longer than `cutoff` (`None` or negative: no cutoff).
1132 ///
1133 /// Binds [`igraph_distances_dijkstra_cutoff`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_dijkstra_cutoff).
1134 /// Time complexity: at most O(s |E| log |V| + |V|); the cutoff limits the
1135 /// explored region.
1136 ///
1137 /// # Examples
1138 ///
1139 /// ```
1140 /// use igraph::prelude::*;
1141 /// // Path 0 -2- 1 -2- 2 -2- 3: with cutoff 4 vertex 3 (at distance 6) is out of reach.
1142 /// let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1143 /// let d = p
1144 /// .distances_dijkstra_cutoff(0, .., Some(&[2.0; 3]), NeighborMode::All, Some(4.0))
1145 /// .unwrap();
1146 /// assert_eq!(d.row(0), vec![0.0, 2.0, 4.0, f64::INFINITY]);
1147 /// ```
1148 pub fn distances_dijkstra_cutoff<'a>(
1149 &self,
1150 from: impl Into<VertexSelector<'a>>,
1151 to: impl Into<VertexSelector<'a>>,
1152 weights: Option<&[f64]>,
1153 mode: NeighborMode,
1154 cutoff: Option<f64>,
1155 ) -> Result<Matrix> {
1156 let cutoff = cutoff_value(cutoff)?;
1157 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1158 igraph_distances_dijkstra_cutoff(self, res, from, to, w, mode.into(), cutoff)
1159 })
1160 }
1161
1162 /// Weighted shortest path lengths with the Bellman–Ford algorithm, which
1163 /// allows negative weights (but no negative cycles).
1164 ///
1165 /// If there are no negative weights,
1166 /// [`distances_dijkstra`](Self::distances_dijkstra) is faster.
1167 ///
1168 /// Binds [`igraph_distances_bellman_ford`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_bellman_ford).
1169 /// Time complexity: O(s |E| |V|) for s sources.
1170 ///
1171 /// # Errors
1172 /// [`ErrorKind::NegativeCycle`] when a
1173 /// negative cycle is reachable.
1174 ///
1175 /// # Examples
1176 ///
1177 /// ```
1178 /// use igraph::prelude::*;
1179 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1180 /// let d = g.distances_bellman_ford(0, .., Some(&[2.0, -3.0, 1.0]), NeighborMode::Out).unwrap();
1181 /// assert_eq!(d.row(0), vec![0.0, 2.0, -1.0]);
1182 /// ```
1183 pub fn distances_bellman_ford<'a>(
1184 &self,
1185 from: impl Into<VertexSelector<'a>>,
1186 to: impl Into<VertexSelector<'a>>,
1187 weights: Option<&[f64]>,
1188 mode: NeighborMode,
1189 ) -> Result<Matrix> {
1190 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1191 igraph_distances_bellman_ford(self, res, from, to, w, mode.into())
1192 })
1193 }
1194
1195 /// Weighted shortest path lengths with Johnson's algorithm: negative
1196 /// weights are allowed (directed graphs only, no negative cycles).
1197 ///
1198 /// A single Bellman–Ford run reweights the edges to non-negative values,
1199 /// then Dijkstra is run from each source; this beats Bellman–Ford when
1200 /// there are many sources. Without negative weights Dijkstra is used
1201 /// directly; with `None` the unweighted algorithm is used. Undirected
1202 /// graphs with any negative weight are rejected, even when no negative
1203 /// edge is reachable from the sources, and so is `NeighborMode::All`
1204 /// combined with negative weights: igraph treats an undirected negative
1205 /// edge as a negative cycle.
1206 ///
1207 /// Binds [`igraph_distances_johnson`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_johnson).
1208 /// Time complexity: O(s |V| log |V| + |V| |E|).
1209 ///
1210 /// # Errors
1211 /// [`ErrorKind::NegativeCycle`] when a negative cycle is reachable, and
1212 /// also for negative weights in an undirected graph or together with
1213 /// `NeighborMode::All`; [`ErrorKind::InvalidValue`] for NaN weights or a
1214 /// weight vector of the wrong length.
1215 ///
1216 /// # Examples
1217 ///
1218 /// ```
1219 /// use igraph::prelude::*;
1220 /// // 0 → 1 → 2 with a negative "discount" edge 1 → 2.
1221 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1222 /// let w = [2.0, -3.0, 1.0];
1223 /// let d = g.distances_johnson(.., .., Some(&w), NeighborMode::Out).unwrap();
1224 /// assert_eq!(d.row(0), vec![0.0, 2.0, -1.0]);
1225 /// // Following the edges backwards gives the transposed matrix.
1226 /// let d_in = g.distances_johnson(.., .., Some(&w), NeighborMode::In).unwrap();
1227 /// assert_eq!(d_in, d.transposed());
1228 /// ```
1229 pub fn distances_johnson<'a>(
1230 &self,
1231 from: impl Into<VertexSelector<'a>>,
1232 to: impl Into<VertexSelector<'a>>,
1233 weights: Option<&[f64]>,
1234 mode: NeighborMode,
1235 ) -> Result<Matrix> {
1236 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1237 igraph_distances_johnson(self, res, from, to, w, mode.into())
1238 })
1239 }
1240
1241 /// All-pairs weighted shortest path lengths with the Floyd–Warshall
1242 /// algorithm (or one of its faster variants, see
1243 /// [`FloydWarshallAlgorithm`]).
1244 ///
1245 /// Negative weights are allowed but negative cycles are not. The full
1246 /// all-pairs matrix is always computed internally, `from` and `to` only
1247 /// subset it. Useful for very dense graphs.
1248 ///
1249 /// Binds [`igraph_distances_floyd_warshall`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_distances_floyd_warshall).
1250 /// Time complexity: O(|V|³ + |E|) for the original variant, expected
1251 /// O(|V|² log² |V|) for the tree variant.
1252 ///
1253 /// # Examples
1254 ///
1255 /// ```
1256 /// use igraph::{paths::FloydWarshallAlgorithm, prelude::*};
1257 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
1258 /// let d = g
1259 /// .distances_floyd_warshall(.., .., None, NeighborMode::Out, FloydWarshallAlgorithm::Original)
1260 /// .unwrap();
1261 /// assert_eq!(d.row(0), vec![0.0, 1.0, 2.0]);
1262 /// ```
1263 pub fn distances_floyd_warshall<'a>(
1264 &self,
1265 from: impl Into<VertexSelector<'a>>,
1266 to: impl Into<VertexSelector<'a>>,
1267 weights: Option<&[f64]>,
1268 mode: NeighborMode,
1269 method: FloydWarshallAlgorithm,
1270 ) -> Result<Matrix> {
1271 self.distance_matrix(from, to, weights, |res, from, to, w| unsafe {
1272 igraph_distances_floyd_warshall(self, res, from, to, w, mode.into(), method.into())
1273 })
1274 }
1275}
1276
1277// ---------------------------------------------------------------------------
1278// Shortest paths themselves.
1279
1280impl igraph_t {
1281 /// One shortest path from `from` to each of the `to` vertices.
1282 ///
1283 /// When several geodesics exist only one is returned (see
1284 /// [`get_all_shortest_paths`](Self::get_all_shortest_paths)). `to` may
1285 /// contain duplicates. With `weights` the weighted algorithms are used
1286 /// (Dijkstra, or Bellman–Ford for negative weights). The result also
1287 /// contains the shortest path tree (`parents` / `inbound_edges`).
1288 ///
1289 /// Binds [`igraph_get_shortest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_paths).
1290 /// Time complexity: O(|V| + |E|) unweighted.
1291 ///
1292 /// # Examples
1293 ///
1294 /// ```
1295 /// use igraph::prelude::*;
1296 /// let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1297 /// let sp = p.get_shortest_paths(0, vec![3, 1], None, NeighborMode::All).unwrap();
1298 /// assert_eq!(sp.vertices, vec![vec![0, 1, 2, 3], vec![0, 1]]);
1299 /// assert_eq!(sp.edges, vec![vec![0, 1, 2], vec![0]]);
1300 /// assert_eq!(sp.parents, vec![-1, 0, 1, 2]);
1301 /// ```
1302 pub fn get_shortest_paths<'a>(
1303 &self,
1304 from: VertexId,
1305 to: impl Into<VertexSelector<'a>>,
1306 weights: Option<&[f64]>,
1307 mode: NeighborMode,
1308 ) -> Result<ShortestPaths> {
1309 self.shortest_paths_with(to, weights, |v, e, to, w, p, i| unsafe {
1310 igraph_get_shortest_paths(self, w, v, e, from, to, mode.into(), p, i)
1311 })
1312 }
1313
1314 /// Weighted shortest paths from one vertex with Dijkstra's algorithm
1315 /// (non-negative weights); `None` falls back to BFS.
1316 ///
1317 /// The result has the same shape as for
1318 /// [`get_shortest_paths`](Self::get_shortest_paths); the search stops as
1319 /// soon as all the targets are reached, so `parents` may contain `-2`
1320 /// for vertices that were never reached.
1321 ///
1322 /// Binds [`igraph_get_shortest_paths_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_paths_dijkstra).
1323 /// Time complexity: O(|E| log |V| + |V|).
1324 ///
1325 /// # Errors
1326 /// [`ErrorKind::InvalidValue`] for negative or NaN weights,
1327 /// [`ErrorKind::InvalidVertexId`] for invalid vertices.
1328 ///
1329 /// # Examples
1330 ///
1331 /// ```
1332 /// use igraph::prelude::*;
1333 /// // A square 0-1-2-3-0 whose edge (3, 0) is slow.
1334 /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
1335 /// let w = [1.0, 1.0, 1.0, 5.0];
1336 /// let sp = c4.get_shortest_paths_dijkstra(0, .., Some(&w), NeighborMode::All).unwrap();
1337 /// assert_eq!(sp.vertices[3], vec![0, 1, 2, 3]);
1338 /// assert_eq!(sp.parents, vec![-1, 0, 1, 2]);
1339 /// assert_eq!(sp.inbound_edges, vec![-1, 0, 1, 2]);
1340 /// ```
1341 pub fn get_shortest_paths_dijkstra<'a>(
1342 &self,
1343 from: VertexId,
1344 to: impl Into<VertexSelector<'a>>,
1345 weights: Option<&[f64]>,
1346 mode: NeighborMode,
1347 ) -> Result<ShortestPaths> {
1348 self.shortest_paths_with(to, weights, |v, e, to, w, p, i| unsafe {
1349 igraph_get_shortest_paths_dijkstra(self, v, e, from, to, w, mode.into(), p, i)
1350 })
1351 }
1352
1353 /// Weighted shortest paths from one vertex with the Bellman–Ford
1354 /// algorithm, allowing negative weights (but no negative cycles).
1355 ///
1356 /// Binds [`igraph_get_shortest_paths_bellman_ford`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_paths_bellman_ford).
1357 /// Time complexity: O(|E| |V|).
1358 ///
1359 /// # Errors
1360 /// [`ErrorKind::NegativeCycle`] when a
1361 /// negative cycle is found.
1362 ///
1363 /// # Examples
1364 ///
1365 /// ```
1366 /// use igraph::prelude::*;
1367 /// // The negative edge 1 → 2 makes the detour through 1 the shortest route to 2.
1368 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1369 /// let sp = g
1370 /// .get_shortest_paths_bellman_ford(0, .., Some(&[2.0, -3.0, 1.0]), NeighborMode::Out)
1371 /// .unwrap();
1372 /// assert_eq!(sp.vertices, vec![vec![0], vec![0, 1], vec![0, 1, 2]]);
1373 /// ```
1374 pub fn get_shortest_paths_bellman_ford<'a>(
1375 &self,
1376 from: VertexId,
1377 to: impl Into<VertexSelector<'a>>,
1378 weights: Option<&[f64]>,
1379 mode: NeighborMode,
1380 ) -> Result<ShortestPaths> {
1381 self.shortest_paths_with(to, weights, |v, e, to, w, p, i| unsafe {
1382 igraph_get_shortest_paths_bellman_ford(self, v, e, from, to, w, mode.into(), p, i)
1383 })
1384 }
1385
1386 /// A single shortest path between two vertices (an arbitrary one if there
1387 /// are several). An empty [`GraphPath`] means `to` is unreachable (the
1388 /// BFS and Dijkstra searches then also emit an igraph warning).
1389 ///
1390 /// Binds [`igraph_get_shortest_path`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path).
1391 /// Time complexity: O(|V| + |E|) unweighted.
1392 ///
1393 /// # Examples
1394 ///
1395 /// ```
1396 /// use igraph::prelude::*;
1397 /// let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
1398 /// let p = c.get_shortest_path(0, 3, None, NeighborMode::All).unwrap();
1399 /// assert_eq!(p.vertices, vec![0, 4, 3]);
1400 /// assert_eq!(p.len(), 2);
1401 /// ```
1402 pub fn get_shortest_path(
1403 &self,
1404 from: VertexId,
1405 to: VertexId,
1406 weights: Option<&[f64]>,
1407 mode: NeighborMode,
1408 ) -> Result<GraphPath> {
1409 self.single_path_with(weights, |v, e, w| unsafe {
1410 igraph_get_shortest_path(self, w, v, e, from, to, mode.into())
1411 })
1412 }
1413
1414 /// A single weighted shortest path with Dijkstra's algorithm
1415 /// (non-negative weights; `None` falls back to BFS). An empty
1416 /// [`GraphPath`] means that `to` is unreachable.
1417 ///
1418 /// Binds [`igraph_get_shortest_path_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path_dijkstra).
1419 /// Time complexity: O(|E| log |V| + |V|).
1420 ///
1421 /// # Examples
1422 ///
1423 /// ```
1424 /// use igraph::prelude::*;
1425 /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
1426 /// let w = [1.0, 1.0, 1.0, 5.0];
1427 /// let p = c4.get_shortest_path_dijkstra(0, 3, Some(&w), NeighborMode::All).unwrap();
1428 /// assert_eq!(p.vertices, vec![0, 1, 2, 3]);
1429 /// assert_eq!(p.weight(&w), 3.0);
1430 /// ```
1431 pub fn get_shortest_path_dijkstra(
1432 &self,
1433 from: VertexId,
1434 to: VertexId,
1435 weights: Option<&[f64]>,
1436 mode: NeighborMode,
1437 ) -> Result<GraphPath> {
1438 self.single_path_with(weights, |v, e, w| unsafe {
1439 igraph_get_shortest_path_dijkstra(self, v, e, from, to, w, mode.into())
1440 })
1441 }
1442
1443 /// A single weighted shortest path with the Bellman–Ford algorithm
1444 /// (negative weights allowed, negative cycles are not).
1445 ///
1446 /// Binds [`igraph_get_shortest_path_bellman_ford`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path_bellman_ford).
1447 /// Time complexity: O(|E| |V|).
1448 ///
1449 /// # Errors
1450 /// [`ErrorKind::NegativeCycle`] when a negative cycle is found.
1451 ///
1452 /// # Examples
1453 ///
1454 /// ```
1455 /// use igraph::prelude::*;
1456 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1457 /// let w = [2.0, -3.0, 1.0];
1458 /// let p = g.get_shortest_path_bellman_ford(0, 2, Some(&w), NeighborMode::Out).unwrap();
1459 /// assert_eq!(p.edges, vec![0, 1]);
1460 /// assert_eq!(p.weight(&w), -1.0);
1461 /// // Closing a negative cycle makes shortest paths meaningless.
1462 /// let mut cyclic = g.clone();
1463 /// cyclic.add_edge(2, 0).unwrap();
1464 /// let err = cyclic
1465 /// .get_shortest_path_bellman_ford(0, 2, Some(&[2.0, -3.0, 1.0, 0.5]), NeighborMode::Out)
1466 /// .unwrap_err();
1467 /// assert_eq!(err.kind(), ErrorKind::NegativeCycle);
1468 /// ```
1469 pub fn get_shortest_path_bellman_ford(
1470 &self,
1471 from: VertexId,
1472 to: VertexId,
1473 weights: Option<&[f64]>,
1474 mode: NeighborMode,
1475 ) -> Result<GraphPath> {
1476 self.single_path_with(weights, |v, e, w| unsafe {
1477 igraph_get_shortest_path_bellman_ford(self, v, e, from, to, w, mode.into())
1478 })
1479 }
1480
1481 /// A single shortest path with the A* algorithm, guided by a heuristic.
1482 ///
1483 /// `heuristic(v, to)` must estimate the distance from the candidate
1484 /// vertex `v` to the target `to`; smaller values make `v` a better
1485 /// candidate. The result is a true shortest path if the heuristic is
1486 /// *admissible*, i.e. never overestimates the distance. A heuristic
1487 /// returning always `0.0` turns A* into Dijkstra's algorithm. Weights must
1488 /// be non-negative. The heuristic should return finite, non-negative
1489 /// values (NaN estimates break the priority queue ordering and give
1490 /// meaningless paths). A panic in the heuristic aborts the search and is
1491 /// propagated to the caller. An empty [`GraphPath`] means that `to` is
1492 /// unreachable.
1493 ///
1494 /// # Errors
1495 /// [`ErrorKind::InvalidVertexId`] for
1496 /// invalid `from`/`to`,
1497 /// [`ErrorKind::InvalidValue`] for
1498 /// negative or NaN weights.
1499 ///
1500 /// Binds [`igraph_get_shortest_path_astar`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_shortest_path_astar).
1501 /// Time complexity: worst case O(|E| log |V| + |V|); better heuristics
1502 /// mean faster searches.
1503 ///
1504 /// # Examples
1505 ///
1506 /// ```
1507 /// use igraph::prelude::*;
1508 /// // A 10 x 10 grid; vertex id = x + 10 y. Manhattan distance is admissible.
1509 /// let n = 10;
1510 /// let grid = Graph::square_lattice(&[10, 10], 1, false, false, None).unwrap();
1511 /// let manhattan = |a: i64, b: i64| ((a % n - b % n).abs() + (a / n - b / n).abs()) as f64;
1512 /// let p = grid.get_shortest_path_astar(0, 99, None, NeighborMode::All, manhattan).unwrap();
1513 /// assert_eq!(p.len(), 18);
1514 /// ```
1515 pub fn get_shortest_path_astar<F>(
1516 &self,
1517 from: VertexId,
1518 to: VertexId,
1519 weights: Option<&[f64]>,
1520 mode: NeighborMode,
1521 mut heuristic: F,
1522 ) -> Result<GraphPath>
1523 where
1524 F: FnMut(VertexId, VertexId) -> f64,
1525 {
1526 unsafe extern "C" fn trampoline<F: FnMut(VertexId, VertexId) -> f64>(
1527 result: *mut igraph_real_t,
1528 from: igraph_int_t,
1529 to: igraph_int_t,
1530 extra: *mut c_void,
1531 ) -> igraph_error_t {
1532 // The heuristic runs in a fresh level of igraph's "finally"
1533 // stack: otherwise a failing igraph call made by the closure
1534 // would free the temporaries of the running A* search (a
1535 // use-after-free in C).
1536 // SAFETY: bookkeeping on igraph's thread-local finally stack; the
1537 // matching EXIT runs below, as `catch_panic` never unwinds.
1538 unsafe { IGRAPH_FINALLY_ENTER() };
1539 let code = catch_panic(|| {
1540 let f = unsafe { &mut *(extra as *mut F) };
1541 let value = f(from, to);
1542 unsafe { *result = value };
1543 igraph_error_type_t_IGRAPH_SUCCESS
1544 });
1545 // SAFETY: closes the level opened above; any failed nested call
1546 // has already freed its own objects of that level.
1547 unsafe { IGRAPH_FINALLY_EXIT() };
1548 code
1549 }
1550 let extra = &mut heuristic as *mut F as *mut c_void;
1551 self.single_path_with(weights, |v, e, w| unsafe {
1552 igraph_get_shortest_path_astar(
1553 self,
1554 v,
1555 e,
1556 from,
1557 to,
1558 w,
1559 mode.into(),
1560 Some(trampoline::<F>),
1561 extra,
1562 )
1563 })
1564 }
1565
1566 /// *All* the shortest paths (geodesics) from `from` to the `to` vertices.
1567 ///
1568 /// Paths are grouped by target in increasing vertex id order; unreachable
1569 /// targets contribute nothing. Multi-edges are considered separately, so
1570 /// multigraphs may yield very many paths. `nrgeo[v]` counts the
1571 /// geodesics from `from` to `v`. With `weights`, Dijkstra's algorithm is
1572 /// used (see [`get_all_shortest_paths_dijkstra`](Self::get_all_shortest_paths_dijkstra)).
1573 /// Counting geodesics through each vertex is what
1574 /// [`betweenness`](Self::betweenness) does, for all sources at once.
1575 ///
1576 /// Binds [`igraph_get_all_shortest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_all_shortest_paths).
1577 /// Time complexity: O(|V| + |E|) for most graphs, O(|V|²) worst case.
1578 ///
1579 /// # Examples
1580 ///
1581 /// ```
1582 /// use igraph::prelude::*;
1583 /// // A 4-cycle has two geodesics between opposite corners.
1584 /// let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
1585 /// let all = c4.get_all_shortest_paths(0, 2, None, NeighborMode::All).unwrap();
1586 /// let mut paths = all.vertices.clone();
1587 /// paths.sort();
1588 /// assert_eq!(paths, vec![vec![0, 1, 2], vec![0, 3, 2]]);
1589 /// assert_eq!(all.nrgeo[2], 2);
1590 /// ```
1591 pub fn get_all_shortest_paths<'a>(
1592 &self,
1593 from: VertexId,
1594 to: impl Into<VertexSelector<'a>>,
1595 weights: Option<&[f64]>,
1596 mode: NeighborMode,
1597 ) -> Result<AllShortestPaths> {
1598 self.all_shortest_paths_with(to, weights, |v, e, n, to, w| unsafe {
1599 igraph_get_all_shortest_paths(self, w, v, e, n, from, to, mode.into())
1600 })
1601 }
1602
1603 /// All the weighted shortest paths from one vertex, with Dijkstra's
1604 /// algorithm (non-negative weights; `None` falls back to the unweighted
1605 /// [`get_all_shortest_paths`](Self::get_all_shortest_paths)).
1606 ///
1607 /// Binds [`igraph_get_all_shortest_paths_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_all_shortest_paths_dijkstra).
1608 /// Time complexity: O(|E| log |V| + |V|).
1609 ///
1610 /// # Examples
1611 ///
1612 /// ```
1613 /// use igraph::prelude::*;
1614 /// let c4 = Graph::cycle_graph(4, false, false).unwrap();
1615 /// // Equal weights: two geodesics between opposite corners ...
1616 /// let all = c4.get_all_shortest_paths_dijkstra(0, 2, Some(&[1.0; 4]), NeighborMode::All).unwrap();
1617 /// assert_eq!(all.nrgeo[2], 2);
1618 /// // ... a slow edge (3, 0) leaves only one.
1619 /// let w = [1.0, 1.0, 1.0, 5.0];
1620 /// let all = c4.get_all_shortest_paths_dijkstra(0, 2, Some(&w), NeighborMode::All).unwrap();
1621 /// assert_eq!(all.vertices, vec![vec![0, 1, 2]]);
1622 /// assert_eq!(all.edges, vec![vec![0, 1]]);
1623 /// ```
1624 pub fn get_all_shortest_paths_dijkstra<'a>(
1625 &self,
1626 from: VertexId,
1627 to: impl Into<VertexSelector<'a>>,
1628 weights: Option<&[f64]>,
1629 mode: NeighborMode,
1630 ) -> Result<AllShortestPaths> {
1631 self.all_shortest_paths_with(to, weights, |v, e, n, to, w| unsafe {
1632 igraph_get_all_shortest_paths_dijkstra(self, v, e, n, from, to, w, mode.into())
1633 })
1634 }
1635
1636 /// The `k` shortest paths between two vertices, in order of increasing
1637 /// length (Yen's algorithm). Fewer than `k` paths are returned when fewer
1638 /// exist. Infinite weights are treated as missing edges.
1639 ///
1640 /// Binds [`igraph_get_k_shortest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_k_shortest_paths).
1641 /// Time complexity: O(k |V| (|V| log |V| + |E|)).
1642 ///
1643 /// # Examples
1644 ///
1645 /// ```
1646 /// use igraph::prelude::*;
1647 /// // In a 5-cycle there are exactly two paths between any two vertices.
1648 /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
1649 /// let paths = c5.get_k_shortest_paths(0, 2, 10, None, NeighborMode::All).unwrap();
1650 /// assert_eq!(paths.len(), 2);
1651 /// assert_eq!(paths[0].vertices, vec![0, 1, 2]);
1652 /// assert_eq!(paths[1].vertices, vec![0, 4, 3, 2]);
1653 /// ```
1654 pub fn get_k_shortest_paths(
1655 &self,
1656 from: VertexId,
1657 to: VertexId,
1658 k: usize,
1659 weights: Option<&[f64]>,
1660 mode: NeighborMode,
1661 ) -> Result<Vec<GraphPath>> {
1662 self.paths_check_weights(weights)?;
1663 let w = weights_view(weights);
1664 let mut vpaths = VectorIntList::new();
1665 let mut epaths = VectorIntList::new();
1666 igraph_call!(igraph_get_k_shortest_paths(
1667 self,
1668 weights_ptr(&w),
1669 &mut vpaths,
1670 &mut epaths,
1671 saturating_int(k),
1672 from,
1673 to,
1674 mode.into()
1675 ))?;
1676 Ok(vpaths
1677 .to_vecs()
1678 .into_iter()
1679 .zip(epaths.to_vecs())
1680 .map(|(vertices, edges)| GraphPath { vertices, edges })
1681 .collect())
1682 }
1683
1684 /// All the *simple* paths (no repeated vertex) starting at `from` and
1685 /// ending at one of the `to` vertices, as vertex lists.
1686 ///
1687 /// Multi-edges are ignored. There may be exponentially many simple paths:
1688 /// use `options` to bound their length or number. Paths are listed in
1689 /// the order they are found.
1690 ///
1691 /// Binds [`igraph_get_all_simple_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_all_simple_paths).
1692 /// Time complexity: O(n!) in the worst case.
1693 ///
1694 /// # Examples
1695 ///
1696 /// ```
1697 /// use igraph::{paths::SimplePathsOptions, prelude::*};
1698 /// let k4 = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false).unwrap();
1699 /// // Paths from 0 to 3: the edge, two of length 2, two of length 3.
1700 /// let all = k4.get_all_simple_paths(0, 3, NeighborMode::All, SimplePathsOptions::default()).unwrap();
1701 /// assert_eq!(all.len(), 5);
1702 /// let short = SimplePathsOptions::default().with_max_len(2);
1703 /// assert_eq!(k4.get_all_simple_paths(0, 3, NeighborMode::All, short).unwrap().len(), 3);
1704 /// ```
1705 pub fn get_all_simple_paths<'a>(
1706 &self,
1707 from: VertexId,
1708 to: impl Into<VertexSelector<'a>>,
1709 mode: NeighborMode,
1710 options: SimplePathsOptions,
1711 ) -> Result<Vec<Vec<VertexId>>> {
1712 let to = to.into().to_raw()?;
1713 let mut res = VectorIntList::new();
1714 self.with_fresh_multi_cache(|| {
1715 igraph_call!(igraph_get_all_simple_paths(
1716 self,
1717 &mut res,
1718 from,
1719 to.get(),
1720 mode.into(),
1721 limit(options.min_len),
1722 limit(options.max_len),
1723 limit(options.max_results)
1724 ))
1725 })?;
1726 Ok(res.to_vecs())
1727 }
1728}
1729
1730// ---------------------------------------------------------------------------
1731// Widest paths.
1732
1733impl igraph_t {
1734 /// Widest (maximum bottleneck) paths from one vertex to the `to` vertices.
1735 ///
1736 /// The width of a path is the smallest weight among its edges, and a
1737 /// widest path maximizes it. `weights` are *widths* and are mandatory;
1738 /// they may be negative but not NaN, edges of width `-INFINITY` are
1739 /// ignored. The result has the same shape as for
1740 /// [`get_shortest_paths`](Self::get_shortest_paths). A widest path
1741 /// tells how much can be pushed along a *single* route; see
1742 /// [`maxflow`](Self::maxflow) for the total capacity over all routes.
1743 ///
1744 /// Binds [`igraph_get_widest_paths`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_widest_paths).
1745 /// Time complexity: O(|E| log |E| + |V|).
1746 ///
1747 /// # Examples
1748 ///
1749 /// ```
1750 /// use igraph::prelude::*;
1751 /// // Two routes from 0 to 3: a thin direct pipe and a wide detour.
1752 /// let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1753 /// let widths = [1.0, 10.0, 8.0, 9.0];
1754 /// let wp = g.get_widest_paths(0, .., &widths, NeighborMode::All).unwrap();
1755 /// assert_eq!(wp.vertices, vec![vec![0], vec![0, 1], vec![0, 1, 2], vec![0, 1, 2, 3]]);
1756 /// assert_eq!(wp.parents, vec![-1, 0, 1, 2]);
1757 /// ```
1758 pub fn get_widest_paths<'a>(
1759 &self,
1760 from: VertexId,
1761 to: impl Into<VertexSelector<'a>>,
1762 weights: &[f64],
1763 mode: NeighborMode,
1764 ) -> Result<ShortestPaths> {
1765 self.shortest_paths_with(to, Some(weights), |v, e, to, w, p, i| unsafe {
1766 igraph_get_widest_paths(self, v, e, from, to, w, mode.into(), p, i)
1767 })
1768 }
1769
1770 /// A single widest (maximum bottleneck) path between two vertices, see
1771 /// [`get_widest_paths`](Self::get_widest_paths).
1772 ///
1773 /// Binds [`igraph_get_widest_path`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_widest_path).
1774 /// Time complexity: O(|E| log |E| + |V|).
1775 ///
1776 /// # Examples
1777 ///
1778 /// ```
1779 /// use igraph::prelude::*;
1780 /// // Two routes from 0 to 3: a thin direct pipe and a wide detour.
1781 /// let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1782 /// let widths = [1.0, 10.0, 8.0, 9.0];
1783 /// let p = g.get_widest_path(0, 3, &widths, NeighborMode::All).unwrap();
1784 /// assert_eq!(p.vertices, vec![0, 1, 2, 3]);
1785 /// ```
1786 pub fn get_widest_path(
1787 &self,
1788 from: VertexId,
1789 to: VertexId,
1790 weights: &[f64],
1791 mode: NeighborMode,
1792 ) -> Result<GraphPath> {
1793 self.single_path_with(Some(weights), |v, e, w| unsafe {
1794 igraph_get_widest_path(self, v, e, from, to, w, mode.into())
1795 })
1796 }
1797
1798 /// Widths of the widest paths between vertices, with a modified
1799 /// Floyd–Warshall algorithm (good for dense graphs).
1800 ///
1801 /// Unreachable pairs have width `-INFINITY`, a vertex has width
1802 /// `INFINITY` to itself. The full matrix is always computed internally.
1803 /// The result equals that of
1804 /// [`widest_path_widths_dijkstra`](Self::widest_path_widths_dijkstra).
1805 ///
1806 /// Binds [`igraph_widest_path_widths_floyd_warshall`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_widest_path_widths_floyd_warshall).
1807 /// Time complexity: O(|V|³).
1808 ///
1809 /// # Examples
1810 ///
1811 /// ```
1812 /// use igraph::prelude::*;
1813 /// // Directed: 0 → 1 → 2 is wide, the direct edge 0 → 2 is thin, 2 reaches nothing.
1814 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
1815 /// let w = g
1816 /// .widest_path_widths_floyd_warshall(.., .., &[5.0, 4.0, 1.0], NeighborMode::Out)
1817 /// .unwrap();
1818 /// let inf = f64::INFINITY;
1819 /// assert_eq!(w.to_rows(), vec![vec![inf, 5.0, 4.0], vec![-inf, inf, 4.0], vec![-inf, -inf, inf]]);
1820 /// ```
1821 pub fn widest_path_widths_floyd_warshall<'a>(
1822 &self,
1823 from: impl Into<VertexSelector<'a>>,
1824 to: impl Into<VertexSelector<'a>>,
1825 weights: &[f64],
1826 mode: NeighborMode,
1827 ) -> Result<Matrix> {
1828 self.distance_matrix(from, to, Some(weights), |res, from, to, w| unsafe {
1829 igraph_widest_path_widths_floyd_warshall(self, res, from, to, w, mode.into())
1830 })
1831 }
1832
1833 /// Widths of the widest paths between vertices, with a modified Dijkstra
1834 /// algorithm run from each source (good for sparse graphs). `to` must not
1835 /// contain duplicates.
1836 ///
1837 /// Unreachable pairs have width `-INFINITY`, a vertex has width
1838 /// `INFINITY` to itself.
1839 ///
1840 /// Binds [`igraph_widest_path_widths_dijkstra`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_widest_path_widths_dijkstra).
1841 /// Time complexity: O(s (|E| log |E| + |V|)) for s sources.
1842 ///
1843 /// # Examples
1844 ///
1845 /// ```
1846 /// use igraph::prelude::*;
1847 /// let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
1848 /// let w = g.widest_path_widths_dijkstra(0, .., &[1.0, 10.0, 8.0, 9.0], NeighborMode::All).unwrap();
1849 /// assert_eq!(w.row(0), vec![f64::INFINITY, 10.0, 8.0, 8.0]);
1850 /// ```
1851 pub fn widest_path_widths_dijkstra<'a>(
1852 &self,
1853 from: impl Into<VertexSelector<'a>>,
1854 to: impl Into<VertexSelector<'a>>,
1855 weights: &[f64],
1856 mode: NeighborMode,
1857 ) -> Result<Matrix> {
1858 self.distance_matrix(from, to, Some(weights), |res, from, to, w| unsafe {
1859 igraph_widest_path_widths_dijkstra(self, res, from, to, w, mode.into())
1860 })
1861 }
1862}
1863
1864// ---------------------------------------------------------------------------
1865// Random walks, spanners, Voronoi partitions and path conversions.
1866
1867impl igraph_t {
1868 /// A random walk of (at most) `steps` steps starting at `start`.
1869 ///
1870 /// The walk follows edges according to `mode` (in directed graphs);
1871 /// multi-edges and loops are respected. With `weights` the next edge is
1872 /// chosen with probability proportional to its weight (non-negative, at
1873 /// least one positive weight among the out-edges of each vertex). When
1874 /// the walk reaches a vertex without outgoing edges, `stuck` decides
1875 /// whether to return the shorter walk ([`RandomWalkStuck::Return`]) or to
1876 /// fail ([`RandomWalkStuck::Error`]). The returned path has `steps + 1`
1877 /// vertices and `steps` edges unless it got stuck. Draws from the calling
1878 /// thread's default random number generator: seed it with
1879 /// [`rng::seed`](crate::rng::seed) for reproducible walks, or run the
1880 /// walk with a private generator through
1881 /// [`Rng::scoped`](crate::rng::Rng::scoped).
1882 ///
1883 /// Binds [`igraph_random_walk`](https://igraph.org/c/html/latest/igraph-Visitors.html#igraph_random_walk).
1884 /// Time complexity: O(l + d) unweighted, O(l log k + d) weighted, where l
1885 /// is the walk length, d the total degree of the visited vertices and k
1886 /// the average degree.
1887 ///
1888 /// # Errors
1889 /// - [`ErrorKind::RandomWalkStuck`] if
1890 /// the walk gets stuck and `stuck` is [`RandomWalkStuck::Error`] (the
1891 /// partial walk is discarded; use [`RandomWalkStuck::Return`] to keep it).
1892 /// - [`ErrorKind::InvalidValue`] for an
1893 /// invalid `start` vertex (igraph 1.0.0 and 1.0.1 report
1894 /// `IGRAPH_EINVAL` here, not `IGRAPH_EINVVID`), invalid weights
1895 /// (negative, NaN or wrong length), or `steps >= i64::MAX`. The Rust side
1896 /// also rejects infinite weights, and weights whose total over the edges
1897 /// a walk can leave some vertex through exceeds `f64::MAX / 2` (loops
1898 /// count twice in `All` mode): with such weights igraph 1.0.1 never
1899 /// terminates. The check covers every vertex, not only the visited ones.
1900 ///
1901 /// # Examples
1902 ///
1903 /// ```
1904 /// use igraph::prelude::*;
1905 /// let c = Graph::cycle_graph(4, false, false).unwrap();
1906 /// // Seeding the (per-thread) default generator makes the walk reproducible.
1907 /// rng::seed(42).unwrap();
1908 /// let walk = c.random_walk(0, 20, None, NeighborMode::All, RandomWalkStuck::Error).unwrap();
1909 /// rng::seed(42).unwrap();
1910 /// let again = c.random_walk(0, 20, None, NeighborMode::All, RandomWalkStuck::Error).unwrap();
1911 /// assert_eq!(walk, again);
1912 /// assert_eq!(walk.vertices.len(), 21);
1913 /// assert_eq!(walk.edges.len(), 20);
1914 /// // Consecutive vertices are adjacent.
1915 /// for pair in walk.vertices.windows(2) {
1916 /// assert!(c.get_eid(pair[0], pair[1], false).unwrap().is_some());
1917 /// }
1918 /// // A private generator leaves the default one untouched.
1919 /// let mut private = Rng::new(RngType::Pcg64, 7).unwrap();
1920 /// let w = private
1921 /// .scoped(|| c.random_walk(0, 5, None, NeighborMode::All, RandomWalkStuck::Error))
1922 /// .unwrap();
1923 /// assert_eq!(w.len(), 5);
1924 /// ```
1925 pub fn random_walk(
1926 &self,
1927 start: VertexId,
1928 steps: usize,
1929 weights: Option<&[f64]>,
1930 mode: NeighborMode,
1931 stuck: RandomWalkStuck,
1932 ) -> Result<GraphPath> {
1933 // igraph allocates `steps + 1` vertices: `steps` must stay below
1934 // `igraph_int_t::MAX` to avoid a signed overflow in C.
1935 let steps = match igraph_int_t::try_from(steps) {
1936 Ok(s) if s < igraph_int_t::MAX => s,
1937 _ => {
1938 return Err(Error::invalid(format!(
1939 "too many random walk steps: {steps}"
1940 )));
1941 }
1942 };
1943 // igraph 1.0.1 rejects only negative and NaN weights. An infinite
1944 // weight, or finite weights whose sum at a vertex overflows, make the
1945 // cumulative distribution of that vertex infinite, and sampling from
1946 // [0, inf) never terminates in C.
1947 if let Some(ws) = weights {
1948 self.random_walk_check_weights(ws, mode)?;
1949 }
1950 self.single_path_with(weights, |v, e, w| unsafe {
1951 igraph_random_walk(self, w, v, e, start, mode.into(), steps, stuck.into())
1952 })
1953 }
1954
1955 /// Rust-side weight check of [`random_walk`](Self::random_walk): every
1956 /// weight must be finite, and the total weight of the edges a walk can
1957 /// leave each vertex through (loops count twice in `All` mode, as in
1958 /// igraph's incidence lists) must be at most `f64::MAX / 2`. The factor
1959 /// 2 leaves room for the rounding of igraph's own summation order.
1960 /// Weights of the wrong length are left to igraph, which reports them.
1961 fn random_walk_check_weights(&self, ws: &[f64], mode: NeighborMode) -> Result<()> {
1962 if ws.iter().any(|x| !x.is_finite()) {
1963 return Err(Error::invalid("random walk weights must be finite"));
1964 }
1965 if ws.len() != self.ecount() {
1966 return Ok(());
1967 }
1968 let (out, inn) = match mode {
1969 _ if !self.is_directed() => (true, true),
1970 NeighborMode::Out => (true, false),
1971 NeighborMode::In => (false, true),
1972 _ => (true, true),
1973 };
1974 let mut total = vec![0.0_f64; self.vcount()];
1975 for (&(from, to), &w) in self.edges(EdgeSelector::All)?.iter().zip(ws) {
1976 if out {
1977 total[from as usize] += w;
1978 }
1979 if inn {
1980 total[to as usize] += w;
1981 }
1982 }
1983 if total.iter().any(|&t| t > f64::MAX / 2.0) {
1984 return Err(Error::invalid(
1985 "the total random walk weight at some vertex overflows",
1986 ));
1987 }
1988 Ok(())
1989 }
1990
1991 /// The edge ids of a *spanner* of the graph with stretch factor `stretch`.
1992 ///
1993 /// A t-spanner is a subgraph `H` with the same vertices, in which the
1994 /// distance between any two vertices is at most `t` times their distance
1995 /// in the original graph. The randomized Baswana–Sen algorithm is used
1996 /// (drawing from the thread's default random number generator); edge
1997 /// directions are ignored. Use
1998 /// [`subgraph_from_edges`](Self::subgraph_from_edges) (with
1999 /// `delete_vertices = false`) to extract the spanner as a graph. See also
2000 /// [`minimum_spanning_tree`](Self::minimum_spanning_tree), the sparsest
2001 /// connected subgraph, which offers no stretch guarantee.
2002 ///
2003 /// Binds [`igraph_spanner`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_spanner).
2004 /// Expected time complexity: O(k |E|), with k = (t + 1) / 2.
2005 ///
2006 /// # Errors
2007 /// [`ErrorKind::InvalidValue`] if `stretch` is below one or not finite
2008 /// (checked on the Rust side for NaN and infinity: igraph 1.0.1 accepts a
2009 /// NaN stretch and never terminates for an infinite one), or if the
2010 /// weights are negative, NaN or of the wrong length.
2011 ///
2012 /// # Examples
2013 ///
2014 /// ```
2015 /// use igraph::prelude::*;
2016 /// let k8 = Graph::full(8, false, false).unwrap();
2017 /// rng::seed(1).unwrap();
2018 /// let kept = k8.spanner(3.0, None).unwrap();
2019 /// assert!(kept.len() <= k8.ecount());
2020 /// // Distances in the spanner are at most 3 times the original ones (all 1 here).
2021 /// let h = k8.subgraph_from_edges(kept, false).unwrap();
2022 /// assert_eq!(h.vcount(), 8);
2023 /// let d = h.distances(.., .., None, NeighborMode::All).unwrap();
2024 /// assert!(d.as_slice().iter().all(|&x| x <= 3.0));
2025 /// ```
2026 pub fn spanner(&self, stretch: f64, weights: Option<&[f64]>) -> Result<Vec<EdgeId>> {
2027 // igraph runs `(stretch + 1) / 2 - 1` clustering rounds: an infinite
2028 // stretch never terminates and a NaN one is silently accepted.
2029 if !stretch.is_finite() {
2030 return Err(Error::invalid(format!(
2031 "the stretch factor must be finite, got {stretch}"
2032 )));
2033 }
2034 self.paths_check_weights(weights)?;
2035 let w = weights_view(weights);
2036 let mut res = VectorInt::new();
2037 igraph_call!(igraph_spanner(self, &mut res, stretch, weights_ptr(&w)))?;
2038 Ok(res.into())
2039 }
2040
2041 /// Voronoi partitioning: assigns each vertex to its closest generator.
2042 ///
2043 /// Distances are computed from the generators (`mode = Out`), towards
2044 /// them (`In`) or ignoring directions (`All`); BFS is used for unweighted
2045 /// graphs, Dijkstra for (non-negative) weights. `tiebreaker` decides
2046 /// which generator wins at equal distance; note that
2047 /// [`VoronoiTiebreaker::Random`] may produce non-contiguous cells
2048 /// (and draws from the thread's default random number generator). See
2049 /// also [`community_voronoi`](Self::community_voronoi), which picks the
2050 /// generators automatically to detect communities.
2051 ///
2052 /// Binds [`igraph_voronoi`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_voronoi).
2053 /// Time complexity: O(log |S| |E| log |V| + |V|) weighted, O(log |S| |E| +
2054 /// |V|) unweighted, for |S| generators.
2055 ///
2056 /// # Examples
2057 ///
2058 /// ```
2059 /// use igraph::prelude::*;
2060 /// // Path 0-1-2-3-4-5 with generators at both ends.
2061 /// let p = Graph::path_graph(6, false, false).unwrap();
2062 /// let v = p.voronoi(&[0, 5], None, NeighborMode::All, VoronoiTiebreaker::First).unwrap();
2063 /// assert_eq!(v.membership, vec![0, 0, 0, 1, 1, 1]);
2064 /// assert_eq!(v.distances, vec![0.0, 1.0, 2.0, 2.0, 1.0, 0.0]);
2065 /// ```
2066 pub fn voronoi(
2067 &self,
2068 generators: &[VertexId],
2069 weights: Option<&[f64]>,
2070 mode: NeighborMode,
2071 tiebreaker: VoronoiTiebreaker,
2072 ) -> Result<VoronoiPartition> {
2073 self.paths_check_weights(weights)?;
2074 let gens = VectorInt::view(generators);
2075 let w = weights_view(weights);
2076 let mut membership = VectorInt::new();
2077 let mut distances = Vector::new();
2078 igraph_call!(igraph_voronoi(
2079 self,
2080 &mut membership,
2081 &mut distances,
2082 gens.as_ptr(),
2083 weights_ptr(&w),
2084 mode.into(),
2085 tiebreaker.into()
2086 ))?;
2087 Ok(VoronoiPartition {
2088 membership: membership.into(),
2089 distances: distances.into(),
2090 })
2091 }
2092
2093 /// Converts a walk given by its edge ids into the sequence of vertices it
2094 /// visits (one more than the number of edges).
2095 ///
2096 /// `start` is the first vertex; with `None` it is inferred from the walk
2097 /// (which then must have at least one edge). Vertices may repeat, so any
2098 /// walk, cycle or path is accepted, e.g. the edge sequences returned by
2099 /// [`find_cycle`](Self::find_cycle) or [`eulerian_path`](Self::eulerian_path).
2100 /// Edge ids are validated on the Rust side (igraph 1.0.0 and 1.0.1 do not
2101 /// check them).
2102 ///
2103 /// Binds [`igraph_vertex_path_from_edge_path`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_vertex_path_from_edge_path).
2104 /// Time complexity: O(n) for a walk of length n.
2105 ///
2106 /// # Errors
2107 /// [`ErrorKind::InvalidValue`] if the
2108 /// edges do not form a continuous walk from `start`, or if `start` is
2109 /// `None` and the walk is empty;
2110 /// [`ErrorKind::InvalidEdgeId`] for an
2111 /// edge id out of range;
2112 /// [`ErrorKind::InvalidVertexId`] for
2113 /// an invalid `start` vertex.
2114 ///
2115 /// # Examples
2116 ///
2117 /// ```
2118 /// use igraph::prelude::*;
2119 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
2120 /// let v = g.vertex_path_from_edge_path(Some(0), &[0, 1, 2], NeighborMode::Out).unwrap();
2121 /// assert_eq!(v, vec![0, 1, 2, 3]);
2122 /// // Walking backwards along the directed edges.
2123 /// let v = g.vertex_path_from_edge_path(Some(3), &[2, 1], NeighborMode::In).unwrap();
2124 /// assert_eq!(v, vec![3, 2, 1]);
2125 /// ```
2126 pub fn vertex_path_from_edge_path(
2127 &self,
2128 start: Option<VertexId>,
2129 edge_path: &[EdgeId],
2130 mode: NeighborMode,
2131 ) -> Result<Vec<VertexId>> {
2132 // igraph 1.0.0 and 1.0.1 do not validate the edge ids here and would
2133 // read out of bounds: check them on the Rust side.
2134 let ecount = self.ecount() as EdgeId;
2135 if let Some(&e) = edge_path.iter().find(|&&e| !(0..ecount).contains(&e)) {
2136 return Err(Error::new(
2137 ErrorKind::InvalidEdgeId,
2138 format!("invalid edge id {e} in edge path"),
2139 ));
2140 }
2141 let start = opt_vertex_arg(start, self.vcount())?;
2142 let edges = VectorInt::view(edge_path);
2143 let mut res = VectorInt::new();
2144 igraph_call!(igraph_vertex_path_from_edge_path(
2145 self,
2146 start,
2147 edges.as_ptr(),
2148 &mut res,
2149 mode.into()
2150 ))?;
2151 Ok(res.into())
2152 }
2153}
2154
2155/// Turns a path given as a vertex sequence into the list of its consecutive
2156/// vertex pairs, e.g. `[a, b, c]` into `[(a, b), (b, c)]`.
2157///
2158/// The result can be passed to [`Graph::get_eids`] to find the edges along
2159/// the path. Paths with fewer than two vertices give an empty list.
2160///
2161/// Binds [`igraph_expand_path_to_pairs`](https://igraph.org/c/html/latest/igraph-Basic.html#igraph_expand_path_to_pairs).
2162///
2163/// # Examples
2164///
2165/// ```
2166/// use igraph::{paths::expand_path_to_pairs, prelude::*};
2167/// assert_eq!(expand_path_to_pairs(&[0, 4, 2]).unwrap(), vec![(0, 4), (4, 2)]);
2168/// assert!(expand_path_to_pairs(&[7]).unwrap().is_empty());
2169///
2170/// let g = Graph::from_edges(&[(0, 4), (4, 2)], 5, false).unwrap();
2171/// let pairs = expand_path_to_pairs(&[0, 4, 2]).unwrap();
2172/// // Graph::get_eids (core) turns the pairs into edge ids.
2173/// assert_eq!(g.get_eids(&pairs, false).unwrap(), vec![0, 1]);
2174/// ```
2175pub fn expand_path_to_pairs(path: &[VertexId]) -> Result<Vec<(VertexId, VertexId)>> {
2176 let mut v = VectorInt::from_slice(path);
2177 igraph_call!(igraph_expand_path_to_pairs(&mut v))?;
2178 Ok(v.as_chunks::<2>().0.iter().map(|&[a, b]| (a, b)).collect())
2179}