igraph/components.rs
1//! Connectivity: connected components, cut vertices and bridges, vertex
2//! separators, cohesive blocks, reachability and neighborhoods.
3//!
4//! This module binds five igraph headers:
5//!
6//! - `igraph_components.h`: (weakly or strongly) connected components,
7//! decomposition into component graphs, articulation points, bridges,
8//! biconnected components and percolation curves;
9//! - `igraph_separators.h`: vertex separators (sets of vertices whose removal
10//! disconnects the graph);
11//! - `igraph_cohesive_blocks.h`: the Moody–White hierarchy of cohesive blocks;
12//! - `igraph_reachability.h`: which vertices can reach which others, and the
13//! transitive closure;
14//! - `igraph_neighborhood.h`: the vertices within a given distance of some
15//! vertices, as sets, counts or induced subgraphs.
16//!
17//! All the graph algorithms are methods of [`Graph`]; the only free function
18//! is [`edgelist_percolation`]. The randomized percolation curves draw from
19//! the calling thread's default random number generator, so they are
20//! reproducible after [`rng::seed`](crate::rng::seed).
21//!
22//! # Example
23//!
24//! ```
25//! use igraph::prelude::*;
26//!
27//! // Two triangles joined by the edge 2-3, plus an isolated vertex 6.
28//! let g = Graph::from_edges(
29//! &[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)],
30//! 7,
31//! false,
32//! )
33//! .unwrap();
34//!
35//! let cc = g.connected_components(Connectedness::Weak).unwrap();
36//! assert_eq!(cc.count, 2);
37//! assert_eq!(cc.sizes, vec![6, 1]);
38//! assert!(!g.is_connected(Connectedness::Weak).unwrap());
39//!
40//! // The edge 2-3 (id 3) is the only bridge; 2 and 3 are the cut vertices.
41//! assert_eq!(g.bridges().unwrap(), vec![3]);
42//! let mut cut = g.articulation_points().unwrap();
43//! cut.sort();
44//! assert_eq!(cut, vec![2, 3]);
45//!
46//! // Removing vertex 2 separates vertex 0 from vertex 5.
47//! assert!(g.is_separator(2).unwrap());
48//!
49//! // Vertices at distance at most 1 from vertex 3.
50//! assert_eq!(g.neighborhood(3, Some(1), NeighborMode::All, 0).unwrap(), vec![vec![3, 2, 4, 5]]);
51//!
52//! // Zachary's karate club: one component, a single cut vertex (the
53//! // instructor, 0) and a nested hierarchy of cohesive blocks.
54//! let karate = Graph::famous("Zachary").unwrap();
55//! assert!(karate.is_connected(Connectedness::Weak).unwrap());
56//! assert_eq!(karate.articulation_points().unwrap(), vec![0]);
57//! let blocks = karate.cohesive_blocks().unwrap();
58//! assert_eq!(blocks.cohesion, vec![1, 2, 2, 4, 3, 3, 4, 3]);
59//! ```
60//!
61//! # Provided functionality
62//!
63//! | Rust | C function | Purpose |
64//! |------|------------|---------|
65//! | [`Graph::connected_components`] | `igraph_connected_components` | membership, sizes and number of components |
66//! | [`Graph::is_connected`] | `igraph_is_connected` | weak / strong connectedness test |
67//! | [`Graph::decompose`] | `igraph_decompose` | one [`Graph`] per component |
68//! | [`Graph::articulation_points`] | `igraph_articulation_points` | cut vertices |
69//! | [`Graph::bridges`] | `igraph_bridges` | cut edges |
70//! | [`Graph::biconnected_components`] | `igraph_biconnected_components` | [`BiconnectedComponents`] |
71//! | [`Graph::is_biconnected`] | `igraph_is_biconnected` | 2-vertex-connectedness test |
72//! | [`Graph::bond_percolation`] | `igraph_bond_percolation` | giant component while adding edges |
73//! | [`Graph::site_percolation`] | `igraph_site_percolation` | giant component while adding vertices |
74//! | [`edgelist_percolation`] | `igraph_edgelist_percolation` | bond percolation of a bare edge list |
75//! | [`Graph::is_separator`] | `igraph_is_separator` | does removing a vertex set disconnect the graph? |
76//! | [`Graph::is_minimal_separator`] | `igraph_is_minimal_separator` | ... and no proper subset does? |
77//! | [`Graph::all_minimal_st_separators`] | `igraph_all_minimal_st_separators` | all minimal (s,t) separators |
78//! | [`Graph::minimum_size_separators`] | `igraph_minimum_size_separators` | all minimum-size vertex separators |
79//! | [`Graph::cohesive_blocks`] | `igraph_cohesive_blocks` | [`CohesiveBlocks`] hierarchy |
80//! | [`Graph::reachability`] | `igraph_reachability` | [`Reachability`] bitsets per strong component |
81//! | [`Graph::count_reachable`] | `igraph_count_reachable` | number of reachable vertices |
82//! | [`Graph::transitive_closure`] | `igraph_transitive_closure` | the transitive closure graph |
83//! | [`Graph::neighborhood_size`] | `igraph_neighborhood_size` | sizes of the k-neighborhoods |
84//! | [`Graph::neighborhood`] | `igraph_neighborhood` | vertices of the k-neighborhoods |
85//! | [`Graph::neighborhood_graphs`] | `igraph_neighborhood_graphs` | induced subgraphs of the k-neighborhoods |
86//!
87//! # See also
88//!
89//! - Single-source reachability: [`Graph::subcomponent`]; traversals:
90//! [`Graph::bfs`], [`Graph::dfs`]; distances:
91//! [`Graph::distances`].
92//! - Quantitative connectivity (minimum cuts, Menger paths):
93//! [`Graph::vertex_connectivity`], [`Graph::edge_connectivity`],
94//! [`Graph::st_vertex_connectivity`], [`Graph::all_st_mincuts`] and
95//! [`Graph::dominator_tree`] in [`flow`](crate::flow).
96//! - Forests, DAGs and orderings: [`Graph::is_tree`], [`Graph::is_forest`],
97//! [`Graph::is_dag`], [`Graph::topological_sorting`].
98//! - Subgraphs of components or neighborhoods: [`Graph::induced_subgraph`];
99//! graphs whose edges are k-neighborhoods: [`Graph::connect_neighborhood`],
100//! [`Graph::graph_power`].
101//! - Another nested "cohesion" decomposition: [`Graph::coreness`]
102//! (k-cores).
103
104use crate::{
105 bitset::BitsetList,
106 constants::{Connectedness, NeighborMode},
107 error::{Error, ErrorKind, Result},
108 ffi::*,
109 graph::{EdgeId, Graph, VertexId},
110 igraph_call,
111 list::{GraphList, VectorIntList},
112 selector::VertexSelector,
113 vector::VectorInt,
114};
115
116fn to_usizes(v: VectorInt) -> Vec<usize> {
117 v.iter().map(|&x| x as usize).collect()
118}
119
120/// Converts a count to `igraph_int_t`, saturating instead of wrapping to a
121/// negative value (which igraph would interpret differently).
122fn int_sat(x: usize) -> igraph_int_t {
123 igraph_int_t::try_from(x).unwrap_or(igraph_int_t::MAX)
124}
125
126/// Converts an optional non-negative order / limit into igraph's convention
127/// where a negative value means "unlimited".
128fn order_raw(order: Option<usize>) -> igraph_int_t {
129 order.map_or(-1, int_sat)
130}
131
132// ---------------------------------------------------------------------------
133// Result structs
134// ---------------------------------------------------------------------------
135
136/// The connected components of a graph, see [`Graph::connected_components`].
137#[derive(Debug, Clone, PartialEq, Eq)]
138pub struct ConnectedComponents {
139 /// `membership[v]` is the id (in `0..count`) of the component of vertex `v`.
140 pub membership: Vec<VertexId>,
141 /// `sizes[c]` is the number of vertices in component `c`.
142 pub sizes: Vec<usize>,
143 /// The number of components.
144 pub count: usize,
145}
146
147impl ConnectedComponents {
148 /// The vertices of component `c`, in increasing id order (empty if `c`
149 /// is out of range).
150 pub fn members(&self, c: usize) -> Vec<VertexId> {
151 self.membership
152 .iter()
153 .enumerate()
154 .filter(|&(_, &m)| m as usize == c)
155 .map(|(v, _)| v as VertexId)
156 .collect()
157 }
158
159 /// All the components as vertex lists, indexed by component id.
160 pub fn groups(&self) -> Vec<Vec<VertexId>> {
161 let mut groups: Vec<Vec<VertexId>> =
162 self.sizes.iter().map(|&s| Vec::with_capacity(s)).collect();
163 for (v, &m) in self.membership.iter().enumerate() {
164 groups[m as usize].push(v as VertexId);
165 }
166 groups
167 }
168
169 /// Id of a largest ("giant") component, the first one on ties; `None`
170 /// for the null graph.
171 pub fn largest(&self) -> Option<usize> {
172 let max = *self.sizes.iter().max()?;
173 self.sizes.iter().position(|&s| s == max)
174 }
175
176 /// Whether vertices `u` and `v` lie in the same component (`false` for
177 /// out-of-range ids).
178 pub fn same_component(&self, u: VertexId, v: VertexId) -> bool {
179 let get = |x: VertexId| usize::try_from(x).ok().and_then(|i| self.membership.get(i));
180 matches!((get(u), get(v)), (Some(a), Some(b)) if a == b)
181 }
182}
183
184/// The biconnected components of a graph, see
185/// [`Graph::biconnected_components`].
186///
187/// All the lists are indexed by component id, in `0..count`.
188#[derive(Debug, Clone, PartialEq, Eq)]
189pub struct BiconnectedComponents {
190 /// The number of biconnected components.
191 pub count: usize,
192 /// For each component, the edges of one of its spanning trees.
193 pub tree_edges: Vec<Vec<EdgeId>>,
194 /// For each component, all of its edges. Every non-loop edge of the graph
195 /// belongs to exactly one component.
196 pub component_edges: Vec<Vec<EdgeId>>,
197 /// For each component, its vertices. A vertex may belong to several
198 /// components (exactly when it is an articulation point) and isolated
199 /// vertices belong to none.
200 pub components: Vec<Vec<VertexId>>,
201 /// The articulation points (cut vertices) of the graph.
202 pub articulation_points: Vec<VertexId>,
203}
204
205/// A bond (edge) percolation curve, see [`Graph::bond_percolation`] and
206/// [`edgelist_percolation`].
207#[derive(Debug, Clone, PartialEq, Eq)]
208pub struct BondPercolation {
209 /// `giant_size[i]` is the size of the largest component after the first
210 /// `i + 1` edges have been added.
211 pub giant_size: Vec<usize>,
212 /// `vertex_count[i]` is the number of vertices having at least one
213 /// incident edge after the first `i + 1` edges have been added.
214 pub vertex_count: Vec<usize>,
215}
216
217/// A site (vertex) percolation curve, see [`Graph::site_percolation`].
218#[derive(Debug, Clone, PartialEq, Eq)]
219pub struct SitePercolation {
220 /// `giant_size[i]` is the size of the largest component after the first
221 /// `i + 1` vertices have been added.
222 pub giant_size: Vec<usize>,
223 /// `edge_count[i]` is the number of edges among the first `i + 1` added
224 /// vertices.
225 pub edge_count: Vec<usize>,
226}
227
228/// The cohesive block hierarchy of a graph, see [`Graph::cohesive_blocks`].
229///
230/// Block `0` is always the whole graph; the lists are indexed by block id.
231#[derive(Debug, Clone, PartialEq)]
232pub struct CohesiveBlocks {
233 /// The vertex ids of each block.
234 pub blocks: Vec<Vec<VertexId>>,
235 /// The cohesion (vertex connectivity) of each block.
236 pub cohesion: Vec<usize>,
237 /// The parent block of each block in the hierarchy (`None` for the root).
238 pub parent: Vec<Option<usize>>,
239 /// The hierarchy as a (directed) tree graph: vertex `i` is block `i`, and
240 /// there is an edge from each block to each of its children.
241 pub block_tree: Graph,
242}
243
244impl CohesiveBlocks {
245 /// Number of blocks.
246 pub fn len(&self) -> usize {
247 self.blocks.len()
248 }
249
250 /// Whether there are no blocks. Results of [`Graph::cohesive_blocks`]
251 /// always contain at least the root block (even for the null graph,
252 /// whose root block is empty), so this is `false` for them.
253 pub fn is_empty(&self) -> bool {
254 self.blocks.is_empty()
255 }
256
257 /// Ids of the child blocks of block `b`.
258 pub fn children(&self, b: usize) -> Vec<usize> {
259 (0..self.parent.len())
260 .filter(|&i| self.parent[i] == Some(b))
261 .collect()
262 }
263
264 /// The largest cohesion of a block containing vertex `v` (its
265 /// "embeddedness"), or `None` if no block contains it.
266 pub fn max_cohesion_of(&self, v: VertexId) -> Option<usize> {
267 self.blocks
268 .iter()
269 .zip(&self.cohesion)
270 .filter(|(b, _)| b.contains(&v))
271 .map(|(_, &c)| c)
272 .max()
273 }
274}
275
276/// Reachability information, see [`Graph::reachability`].
277///
278/// Vertices in the same strongly connected component reach exactly the same
279/// vertices, so the reachable sets are stored once per component.
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct Reachability {
282 /// `membership[v]` is the id of the (strongly, for directed graphs with
283 /// `Out`/`In` modes) connected component of `v`.
284 pub membership: Vec<VertexId>,
285 /// `sizes[c]` is the number of vertices of component `c`.
286 pub sizes: Vec<usize>,
287 /// The number of components.
288 pub count: usize,
289 /// `reach[c][v]` is `true` when vertex `v` is reachable from the vertices
290 /// of component `c` (every vertex reaches itself).
291 pub reach: Vec<Vec<bool>>,
292}
293
294impl Reachability {
295 /// Whether `to` is reachable from `from` (`false` for out-of-range ids).
296 pub fn is_reachable(&self, from: VertexId, to: VertexId) -> bool {
297 usize::try_from(from)
298 .ok()
299 .and_then(|f| self.membership.get(f))
300 .and_then(|&c| self.reach.get(c as usize))
301 .and_then(|row| usize::try_from(to).ok().and_then(|t| row.get(t).copied()))
302 .unwrap_or(false)
303 }
304
305 /// The vertices reachable from `from`, in increasing id order (empty for
306 /// an out-of-range id).
307 pub fn reachable_from(&self, from: VertexId) -> Vec<VertexId> {
308 let Some(&c) = usize::try_from(from)
309 .ok()
310 .and_then(|f| self.membership.get(f))
311 else {
312 return Vec::new();
313 };
314 self.reach[c as usize]
315 .iter()
316 .enumerate()
317 .filter(|&(_, &r)| r)
318 .map(|(v, _)| v as VertexId)
319 .collect()
320 }
321}
322
323/// Runs `f` with a raw `igraph_vs_t` built from `sel`, keeping any backing
324/// storage alive (and at a fixed address) for the whole call.
325///
326/// Vertex lists are passed as a zero-copy view of the Rust slice, whose
327/// `igraph_vector_int_t` header lives in this stack frame and never moves
328/// while `f` runs (`igraph_vss_vector` stores a pointer to that header).
329/// The other selectors go through [`VertexSelector::to_raw`].
330fn with_vs(sel: VertexSelector<'_>, f: impl FnOnce(igraph_vs_t) -> Result<()>) -> Result<()> {
331 if let VertexSelector::List(list) = &sel {
332 let view = VectorInt::view(list);
333 // SAFETY: `view` outlives the call of `f` and is not moved meanwhile.
334 let vs = unsafe { igraph_vss_vector(view.as_ptr()) };
335 return f(vs);
336 }
337 let raw = sel.to_raw()?;
338 f(raw.get())
339}
340
341// ---------------------------------------------------------------------------
342// Free functions
343// ---------------------------------------------------------------------------
344
345/// Bond percolation curve of a bare list of vertex pairs.
346///
347/// Edges `(u, v)` are added one after the other, and after each addition the
348/// size of the largest connected component and the number of non-isolated
349/// vertices are recorded. It differs from [`Graph::bond_percolation`] in that
350/// no graph is needed: vertices are identified by the ids appearing in
351/// `edges`, which must be non-negative (the largest id determines how much
352/// memory is allocated). Self-loops are allowed.
353///
354/// This function is marked *experimental* in igraph.
355///
356/// Time complexity: O(|E| α(|E|)), α being the inverse Ackermann function.
357///
358/// Binds [`igraph_edgelist_percolation`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_edgelist_percolation).
359///
360/// # Errors
361/// [`ErrorKind::InvalidVertexId`] for negative vertex ids and for the id
362/// `i64::MAX` (rejected on the Rust side: igraph 1.0.1 computes the vertex
363/// count as the largest id plus one, which would overflow and abort the
364/// process). [`ErrorKind::OutOfMemory`] when the largest id is too large
365/// to allocate per-vertex storage for.
366///
367/// # Examples
368/// ```
369/// use igraph::components::edgelist_percolation;
370///
371/// // A triangle with an extra self-loop on vertex 0.
372/// let p = edgelist_percolation(&[(0, 0), (0, 1), (1, 2), (2, 0)]).unwrap();
373/// assert_eq!(p.giant_size, vec![1, 2, 3, 3]);
374/// assert_eq!(p.vertex_count, vec![1, 2, 3, 3]);
375/// ```
376pub fn edgelist_percolation(edges: &[(VertexId, VertexId)]) -> Result<BondPercolation> {
377 if edges
378 .iter()
379 .any(|&(a, b)| a == igraph_int_t::MAX || b == igraph_int_t::MAX)
380 {
381 return Err(Error::new(
382 ErrorKind::InvalidVertexId,
383 format!("vertex id {} is too large", igraph_int_t::MAX),
384 ));
385 }
386 let flat: VectorInt = edges.iter().flat_map(|&(a, b)| [a, b]).collect();
387 let mut giant = VectorInt::new();
388 let mut count = VectorInt::new();
389 igraph_call!(igraph_edgelist_percolation(&flat, &mut giant, &mut count))?;
390 Ok(BondPercolation {
391 giant_size: to_usizes(giant),
392 vertex_count: to_usizes(count),
393 })
394}
395
396// ---------------------------------------------------------------------------
397// Graph methods
398// ---------------------------------------------------------------------------
399
400impl igraph_t {
401 // ----- components -------------------------------------------------------
402
403 /// The (weakly or strongly) connected components of the graph.
404 ///
405 /// With [`Connectedness::Weak`] edge directions are ignored; with
406 /// [`Connectedness::Strong`] two vertices are in the same component when
407 /// each is reachable from the other by a directed path. The mode is
408 /// ignored for undirected graphs.
409 ///
410 /// Strongly connected components are numbered in *topological order*:
411 /// vertex `v` is reachable from `u` only if
412 /// `membership[u] <= membership[v]`. Weak components are numbered in
413 /// order of their smallest vertex id.
414 ///
415 /// Time complexity: O(|V| + |E|).
416 ///
417 /// See also [`is_connected`](Self::is_connected) for a (cached) yes/no
418 /// answer, [`decompose`](Self::decompose) to get the components as
419 /// graphs, and [`Graph::subcomponent`] for the component of a single
420 /// vertex.
421 ///
422 /// Binds [`igraph_connected_components`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_connected_components).
423 ///
424 /// # Examples
425 /// ```
426 /// use igraph::prelude::*;
427 ///
428 /// // 0 -> 1 -> 2 -> 0 is a directed cycle, 3 only has an in-edge.
429 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, true).unwrap();
430 /// let weak = g.connected_components(Connectedness::Weak).unwrap();
431 /// assert_eq!(weak.count, 1);
432 /// let strong = g.connected_components(Connectedness::Strong).unwrap();
433 /// assert_eq!(strong.count, 2);
434 /// assert!(strong.same_component(0, 2));
435 /// assert!(!strong.same_component(0, 3));
436 /// ```
437 pub fn connected_components(&self, mode: Connectedness) -> Result<ConnectedComponents> {
438 let mut membership = VectorInt::new();
439 let mut csize = VectorInt::new();
440 let mut no: igraph_int_t = 0;
441 igraph_call!(igraph_connected_components(
442 self,
443 &mut membership,
444 &mut csize,
445 &mut no,
446 mode.into()
447 ))?;
448 Ok(ConnectedComponents {
449 membership: membership.into(),
450 sizes: to_usizes(csize),
451 count: no as usize,
452 })
453 }
454
455 /// Whether the graph is (weakly or strongly) connected.
456 ///
457 /// A graph is connected when every vertex is reachable from every other;
458 /// for directed graphs, [`Connectedness::Strong`] follows edge directions
459 /// while [`Connectedness::Weak`] ignores them. The mode is ignored for
460 /// undirected graphs. By definition the null graph (no vertices) is
461 /// **not** connected, while the singleton graph is.
462 ///
463 /// The result is cached inside the graph, so repeated calls without
464 /// modifications are O(1); otherwise the time complexity is O(|V| + |E|).
465 ///
466 /// See also [`is_biconnected`](Self::is_biconnected) for
467 /// 2-vertex-connectedness, and [`Graph::vertex_connectivity`] /
468 /// [`Graph::edge_connectivity`] for *how* connected a graph is.
469 ///
470 /// Binds [`igraph_is_connected`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_connected).
471 ///
472 /// # Examples
473 /// ```
474 /// use igraph::prelude::*;
475 /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
476 /// assert!(g.is_connected(Connectedness::Weak).unwrap());
477 /// assert!(!g.is_connected(Connectedness::Strong).unwrap());
478 /// assert!(!Graph::new(0, false).is_connected(Connectedness::Weak).unwrap());
479 /// ```
480 pub fn is_connected(&self, mode: Connectedness) -> Result<bool> {
481 let mut res = false;
482 igraph_call!(igraph_is_connected(self, &mut res, mode.into()))?;
483 Ok(res)
484 }
485
486 /// Splits the graph into one separate [`Graph`] per connected component.
487 ///
488 /// `max_components` limits the number of returned graphs (`None` for no
489 /// limit): the first ones found are kept (for weak components, in order
490 /// of their smallest vertex id). Components
491 /// with fewer than `min_vertices` vertices are skipped (e.g. `2` drops the
492 /// isolated vertices). Vertex ids are renumbered in each component graph,
493 /// preserving their relative order; directedness is preserved.
494 ///
495 /// Time complexity: O(|V| + |E|).
496 ///
497 /// Each graph is the subgraph induced by one component: use
498 /// [`connected_components`](Self::connected_components) with
499 /// [`Graph::induced_subgraph`] to extract only some of them, or to keep
500 /// the mapping to the original vertex ids.
501 ///
502 /// Binds [`igraph_decompose`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_decompose).
503 ///
504 /// # Examples
505 /// ```
506 /// use igraph::prelude::*;
507 /// // A triangle, a single edge and an isolated vertex.
508 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4)], 6, false).unwrap();
509 /// let parts = g.decompose(Connectedness::Weak, None, 2).unwrap();
510 /// let sizes: Vec<_> = parts.iter().map(|p| (p.vcount(), p.ecount())).collect();
511 /// assert_eq!(sizes, vec![(3, 3), (2, 1)]);
512 /// ```
513 pub fn decompose(
514 &self,
515 mode: Connectedness,
516 max_components: Option<usize>,
517 min_vertices: usize,
518 ) -> Result<Vec<Graph>> {
519 let mut list = GraphList::new();
520 igraph_call!(igraph_decompose(
521 self,
522 &mut list,
523 mode.into(),
524 order_raw(max_components),
525 int_sat(min_vertices)
526 ))?;
527 Ok(list.into_vec())
528 }
529
530 /// The articulation points (cut vertices) of the graph.
531 ///
532 /// A vertex is an articulation point if its removal increases the number
533 /// of (weakly) connected components. Edge directions are ignored. The
534 /// vertices are returned in no particular order.
535 ///
536 /// A graph without articulation points is not necessarily biconnected:
537 /// the null graph, the singleton graph and edgeless graphs have none
538 /// either; use [`is_biconnected`](Self::is_biconnected) for that.
539 /// Conversely `K2`, which igraph considers biconnected, has no
540 /// articulation points. (The C documentation of 1.0.0 and 1.0.1 lists
541 /// `K2` among the counterexamples, but [`is_biconnected`](Self::is_biconnected)
542 /// returns `true` for it.)
543 ///
544 /// Time complexity: O(|V| + |E|).
545 ///
546 /// See also [`biconnected_components`](Self::biconnected_components),
547 /// which also returns the articulation points, and
548 /// [`bridges`](Self::bridges) for the edge analogue.
549 ///
550 /// Binds [`igraph_articulation_points`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_articulation_points).
551 ///
552 /// # Examples
553 /// ```
554 /// use igraph::prelude::*;
555 /// // A path 0-1-2-3: the inner vertices are cut vertices.
556 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
557 /// let mut ap = g.articulation_points().unwrap();
558 /// ap.sort();
559 /// assert_eq!(ap, vec![1, 2]);
560 /// ```
561 pub fn articulation_points(&self) -> Result<Vec<VertexId>> {
562 let mut res = VectorInt::new();
563 igraph_call!(igraph_articulation_points(self, &mut res))?;
564 Ok(res.into())
565 }
566
567 /// The bridges (cut edges) of the graph, as edge ids.
568 ///
569 /// An edge is a bridge if its removal increases the number of (weakly)
570 /// connected components. Edge directions are ignored. The edges are
571 /// returned in no particular order. A multi-edge is never a bridge (its
572 /// parallel copies keep the endpoints connected), and neither is a
573 /// self-loop.
574 ///
575 /// Time complexity: O(|V| + |E|).
576 ///
577 /// A connected graph has a bridge exactly when its
578 /// [`edge_connectivity`](Graph::edge_connectivity) is 1 (and it has more
579 /// than one vertex).
580 ///
581 /// Binds [`igraph_bridges`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_bridges).
582 ///
583 /// # Examples
584 /// ```
585 /// use igraph::prelude::*;
586 /// // A square 0-1-2-3 with a pendant edge 3-4 (edge id 4).
587 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (3, 4)], 5, false).unwrap();
588 /// assert_eq!(g.bridges().unwrap(), vec![4]);
589 /// ```
590 pub fn bridges(&self) -> Result<Vec<EdgeId>> {
591 let mut res = VectorInt::new();
592 igraph_call!(igraph_bridges(self, &mut res))?;
593 Ok(res.into())
594 }
595
596 /// The biconnected components of the graph (edge directions are ignored).
597 ///
598 /// A graph is biconnected if removing any single vertex leaves it
599 /// connected; a biconnected component is a maximal biconnected subgraph.
600 /// The components partition the (non-loop) *edges* of the graph, while a
601 /// vertex can belong to several of them (the articulation points) or to
602 /// none (isolated vertices). igraph considers the two-vertex complete
603 /// graph `K2` biconnected, but not single vertices, so a single
604 /// biconnected component does not imply that the graph is biconnected
605 /// (there may be isolated vertices): use
606 /// [`is_biconnected`](Self::is_biconnected) for that. Self-loops belong to
607 /// no component.
608 ///
609 /// All outputs are computed: the number of components, a spanning tree of
610 /// each, their edges and vertices, and the articulation points. Time
611 /// complexity: O(|V| + |E|) for the trees alone, but igraph documents
612 /// computing the vertex sets as quadratic and the edge sets as cubic in
613 /// |V| in the worst case.
614 ///
615 /// Binds [`igraph_biconnected_components`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_biconnected_components).
616 ///
617 /// # Examples
618 /// ```
619 /// use igraph::prelude::*;
620 /// // Two triangles sharing vertex 2 ("bowtie").
621 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
622 /// .unwrap();
623 /// let bc = g.biconnected_components().unwrap();
624 /// assert_eq!(bc.count, 2);
625 /// assert_eq!(bc.articulation_points, vec![2]);
626 /// assert!(bc.components.iter().all(|c| c.len() == 3 && c.contains(&2)));
627 /// ```
628 pub fn biconnected_components(&self) -> Result<BiconnectedComponents> {
629 let mut no: igraph_int_t = 0;
630 let mut tree = VectorIntList::new();
631 let mut edges = VectorIntList::new();
632 let mut comps = VectorIntList::new();
633 let mut ap = VectorInt::new();
634 igraph_call!(igraph_biconnected_components(
635 self, &mut no, &mut tree, &mut edges, &mut comps, &mut ap
636 ))?;
637 Ok(BiconnectedComponents {
638 count: no as usize,
639 tree_edges: tree.to_vecs(),
640 component_edges: edges.to_vecs(),
641 components: comps.to_vecs(),
642 articulation_points: ap.into(),
643 })
644 }
645
646 /// Whether the graph is biconnected (edge directions are ignored).
647 ///
648 /// A graph is biconnected if removing any single vertex (and its
649 /// incident edges) does not disconnect it. igraph does not consider the
650 /// null and singleton graphs biconnected, but does consider `K2`
651 /// biconnected. For graphs with at least three vertices this is the same
652 /// as having [`vertex_connectivity`](Graph::vertex_connectivity) at
653 /// least 2, but much cheaper to compute.
654 ///
655 /// Time complexity: O(|V| + |E|).
656 ///
657 /// Binds [`igraph_is_biconnected`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_biconnected).
658 ///
659 /// # Examples
660 /// ```
661 /// use igraph::prelude::*;
662 /// let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
663 /// assert!(square.is_biconnected().unwrap());
664 /// let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
665 /// assert!(!path.is_biconnected().unwrap());
666 /// ```
667 pub fn is_biconnected(&self) -> Result<bool> {
668 let mut res = false;
669 igraph_call!(igraph_is_biconnected(self, &mut res))?;
670 Ok(res)
671 }
672
673 /// Bond (edge) percolation curve: the size of the giant component as the
674 /// edges of the graph are added one by one to its vertices.
675 ///
676 /// `edge_order` gives the order in which the edge ids are added and must
677 /// not contain duplicates; it may list only a subset of the edges. With
678 /// `None` a uniformly random order is used, drawn from the calling
679 /// thread's default random number generator (reproducible after
680 /// [`rng::seed`](crate::rng::seed)). Reversing both the order and the
681 /// resulting `giant_size` gives the curve for edge *removal*. Edge
682 /// directions are ignored; the vertices are only counted once they get
683 /// an incident edge, so isolated vertices never contribute.
684 ///
685 /// This function is marked *experimental* in igraph.
686 ///
687 /// Time complexity: O(|V| + |E| α(|E|)), α being the inverse Ackermann
688 /// function.
689 ///
690 /// See also [`site_percolation`](Self::site_percolation) for the
691 /// vertex version, [`edgelist_percolation`] to percolate arbitrary vertex
692 /// pairs, and [`connected_components`](Self::connected_components) for
693 /// the component sizes of a fixed graph.
694 ///
695 /// Binds [`igraph_bond_percolation`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_bond_percolation).
696 ///
697 /// # Errors
698 /// [`ErrorKind::InvalidEdgeId`] for an edge id out of range and
699 /// [`ErrorKind::InvalidValue`] for duplicates in `edge_order`. Both are
700 /// checked on the Rust side: igraph 1.0.0 and 1.0.1 size their
701 /// duplicate-detection bitset by the *length* of `edge_order` rather than
702 /// by the number of edges, so an unchecked partial order or out-of-range
703 /// id would make them access memory out of bounds.
704 ///
705 /// # Examples
706 /// ```
707 /// use igraph::prelude::*;
708 /// // The 4-cycle 0-1-2-3-0, adding edges 0-1, 2-3, 1-2, 3-0.
709 /// let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
710 /// let p = c4.bond_percolation(Some(&[0, 2, 1, 3])).unwrap();
711 /// assert_eq!(p.giant_size, vec![2, 2, 4, 4]);
712 /// assert_eq!(p.vertex_count, vec![2, 4, 4, 4]);
713 ///
714 /// // A random order is reproducible after seeding; the final point is
715 /// // always the whole (connected) graph.
716 /// let grid = Graph::square_lattice(&[5, 5], 1, false, false, None).unwrap();
717 /// rng::seed(7).unwrap();
718 /// let a = grid.bond_percolation(None).unwrap();
719 /// rng::seed(7).unwrap();
720 /// let b = grid.bond_percolation(None).unwrap();
721 /// assert_eq!(a, b);
722 /// assert_eq!(a.giant_size.last(), Some(&25));
723 /// ```
724 pub fn bond_percolation(&self, edge_order: Option<&[EdgeId]>) -> Result<BondPercolation> {
725 if let Some(order) = edge_order {
726 let m = self.ecount();
727 let mut seen = vec![false; m];
728 for &e in order {
729 let i = usize::try_from(e).ok().filter(|&i| i < m).ok_or_else(|| {
730 Error::new(
731 ErrorKind::InvalidEdgeId,
732 format!("invalid edge id {e} in edge order (graph has {m} edges)"),
733 )
734 })?;
735 if std::mem::replace(&mut seen[i], true) {
736 return Err(Error::invalid(format!(
737 "duplicate edge {e} in edge order vector"
738 )));
739 }
740 }
741 }
742 let order = edge_order.map(VectorInt::view);
743 let op = order.as_ref().map_or(std::ptr::null(), |v| v.as_ptr());
744 let mut giant = VectorInt::new();
745 let mut count = VectorInt::new();
746 igraph_call!(igraph_bond_percolation(self, &mut giant, &mut count, op))?;
747 Ok(BondPercolation {
748 giant_size: to_usizes(giant),
749 vertex_count: to_usizes(count),
750 })
751 }
752
753 /// Site (vertex) percolation curve: the size of the giant component as
754 /// the vertices of the graph are added one by one, together with the
755 /// edges among the vertices added so far.
756 ///
757 /// `vertex_order` gives the order in which vertices are added and must
758 /// not contain duplicates; it may list only a subset of the vertices.
759 /// With `None` a uniformly random order is used, drawn from the calling
760 /// thread's default random number generator exactly as igraph would
761 /// (reproducible after [`rng::seed`](crate::rng::seed)). Reversing both
762 /// the order and the resulting `giant_size` gives the curve for vertex
763 /// *removal* (an attack/failure scenario). Edge directions are ignored;
764 /// multi-edges count with their multiplicity in `edge_count`.
765 ///
766 /// This function is marked *experimental* in igraph.
767 ///
768 /// Time complexity: O(|V| + |E| α(|E|)).
769 ///
770 /// See also [`bond_percolation`](Self::bond_percolation) for the edge
771 /// version and [`Graph::coreness`] or
772 /// [`Graph::vertex_connectivity`] for other robustness measures.
773 ///
774 /// Binds [`igraph_site_percolation`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_site_percolation).
775 ///
776 /// # Self-loops
777 /// igraph 1.0.0 and 1.0.1 count every self-loop *twice* in `edge_count`
778 /// (it lists loops twice among a vertex's neighbors). This wrapper
779 /// corrects the count, so a self-loop counts as one edge. To be able to
780 /// do so with `vertex_order = None`, the random order is generated on
781 /// the Rust side with the same random draws igraph makes internally.
782 ///
783 /// # Errors
784 /// [`ErrorKind::InvalidVertexId`] for invalid vertex ids and
785 /// [`ErrorKind::InvalidValue`] for duplicates in `vertex_order`.
786 ///
787 /// # Examples
788 /// ```
789 /// use igraph::prelude::*;
790 /// let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
791 /// let p = c4.site_percolation(Some(&[0, 2, 1, 3])).unwrap();
792 /// assert_eq!(p.giant_size, vec![1, 1, 3, 4]);
793 /// assert_eq!(p.edge_count, vec![0, 0, 2, 4]);
794 ///
795 /// // Removing the hub of a star shatters it at once.
796 /// let star = Graph::star(6, StarMode::Undirected, 0).unwrap();
797 /// let attack = [0, 1, 2, 3, 4, 5]; // hub first
798 /// let build: Vec<i64> = attack.iter().rev().copied().collect();
799 /// let mut after = star.site_percolation(Some(&build)).unwrap().giant_size;
800 /// after.reverse(); // after[k]: largest component with k vertices removed
801 /// assert_eq!(after, vec![6, 1, 1, 1, 1, 1]);
802 /// ```
803 pub fn site_percolation(&self, vertex_order: Option<&[VertexId]>) -> Result<SitePercolation> {
804 let shuffled: VectorInt;
805 let order: &[VertexId] = match vertex_order {
806 Some(order) => order,
807 None => {
808 // Same as igraph: `igraph_vector_int_init_range` + `_shuffle`.
809 let mut all: VectorInt = (0..self.vcount() as VertexId).collect();
810 all.shuffle();
811 shuffled = all;
812 &shuffled
813 }
814 };
815 let view = VectorInt::view(order);
816 let mut giant = VectorInt::new();
817 let mut count = VectorInt::new();
818 igraph_call!(igraph_site_percolation(
819 self,
820 &mut giant,
821 &mut count,
822 view.as_ptr()
823 ))?;
824 let mut edge_count = to_usizes(count);
825 if self.has_loop()? {
826 // igraph succeeded, so every id in `order` is valid.
827 let mut loops = vec![0usize; self.vcount()];
828 for (from, to) in self.edge_list() {
829 if from == to {
830 loops[from as usize] += 1;
831 }
832 }
833 let mut seen = 0;
834 for (count, &v) in edge_count.iter_mut().zip(order) {
835 seen += loops[v as usize];
836 *count -= seen;
837 }
838 }
839 Ok(SitePercolation {
840 giant_size: to_usizes(giant),
841 edge_count,
842 })
843 }
844
845 // ----- separators -------------------------------------------------------
846
847 /// Whether removing the `candidate` vertices disconnects the graph.
848 ///
849 /// A vertex set `S` is a separator if there are vertices `u` and `v`
850 /// outside `S`, connected in the graph, such that every path between
851 /// them passes through `S`.
852 /// Edge directions are ignored and duplicate ids in `candidate` are
853 /// ignored. Removing *all* vertices, or all but one, is never a
854 /// separation, and the empty set is never a separator either, not even
855 /// in a disconnected graph: what matters is whether *removing* the set
856 /// separates some vertices that were connected before.
857 ///
858 /// Time complexity: O(|V| + |E|).
859 ///
860 /// See also [`minimum_size_separators`](Self::minimum_size_separators)
861 /// to *find* the smallest separators, and
862 /// [`Graph::st_vertex_connectivity`] for the size of the smallest set
863 /// separating two given vertices.
864 ///
865 /// Binds [`igraph_is_separator`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_is_separator).
866 ///
867 /// # Examples
868 /// ```
869 /// use igraph::prelude::*;
870 /// // A star with center 0.
871 /// let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false).unwrap();
872 /// assert!(star.is_separator(0).unwrap());
873 /// assert!(!star.is_separator(1).unwrap());
874 /// assert!(!star.is_separator(1..4).unwrap());
875 /// ```
876 pub fn is_separator<'a>(&self, candidate: impl Into<VertexSelector<'a>>) -> Result<bool> {
877 let mut res = false;
878 with_vs(candidate.into(), |vs| {
879 igraph_call!(igraph_is_separator(self, vs, &mut res))
880 })?;
881 Ok(res)
882 }
883
884 /// Whether `candidate` is a *minimal* separator: a separator (see
885 /// [`is_separator`](Self::is_separator)) none of whose proper subsets is
886 /// a separator. Edge directions are ignored.
887 ///
888 /// Time complexity: O(|V| + |E|).
889 ///
890 /// Binds [`igraph_is_minimal_separator`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_is_minimal_separator).
891 ///
892 /// # Examples
893 /// ```
894 /// use igraph::prelude::*;
895 /// // The path 0-1-2-3.
896 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
897 /// assert!(g.is_minimal_separator(1).unwrap());
898 /// assert!(g.is_separator(&[1, 2]).unwrap());
899 /// assert!(!g.is_minimal_separator(&[1, 2]).unwrap());
900 /// ```
901 pub fn is_minimal_separator<'a>(
902 &self,
903 candidate: impl Into<VertexSelector<'a>>,
904 ) -> Result<bool> {
905 let mut res = false;
906 with_vs(candidate.into(), |vs| {
907 igraph_call!(igraph_is_minimal_separator(self, vs, &mut res))
908 })?;
909 Ok(res)
910 }
911
912 /// All the vertex sets that are minimal (s, t) separators for some pair
913 /// of vertices s, t.
914 ///
915 /// Some returned sets are not minimal with respect to disconnecting the
916 /// graph: in the graph `0-1-2-3-4-1` the sets `{1}`, `{2, 4}` and
917 /// `{1, 3}` are returned, and `{1, 3}` is minimal for separating 2 from 4
918 /// although `{1}` alone disconnects the graph. Edge directions are
919 /// ignored. Unlike [`minimum_size_separators`](Self::minimum_size_separators),
920 /// a disconnected graph is handled component by component (its
921 /// separators are those of its components). Uses the algorithm of Berry,
922 /// Bordat and Cogis (1999).
923 ///
924 /// Time complexity: O(n |V|³), n being the number of separators.
925 ///
926 /// Binds [`igraph_all_minimal_st_separators`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_all_minimal_st_separators).
927 ///
928 /// # Examples
929 /// ```
930 /// use igraph::prelude::*;
931 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 1)], 5, false).unwrap();
932 /// let mut seps: Vec<Vec<i64>> = g
933 /// .all_minimal_st_separators()
934 /// .unwrap()
935 /// .into_iter()
936 /// .map(|mut s| { s.sort(); s })
937 /// .collect();
938 /// seps.sort();
939 /// assert_eq!(seps, vec![vec![1], vec![1, 3], vec![2, 4]]);
940 /// ```
941 pub fn all_minimal_st_separators(&self) -> Result<Vec<Vec<VertexId>>> {
942 let mut res = VectorIntList::new();
943 igraph_call!(igraph_all_minimal_st_separators(self, &mut res))?;
944 Ok(res.to_vecs())
945 }
946
947 /// All the vertex separators of minimum size.
948 ///
949 /// A vertex set is a separator if its removal disconnects the graph. The
950 /// graph must be undirected. A graph that is already disconnected has no
951 /// separators (an empty list is returned), and neither do complete
952 /// graphs. Each separator has exactly
953 /// [`vertex_connectivity`](Graph::vertex_connectivity) vertices. The
954 /// separators are returned in arbitrary order. Uses the algorithm of
955 /// Kanevsky (1993).
956 ///
957 /// See also [`Graph::all_st_mincuts`] for the edge analogue between two
958 /// given vertices.
959 ///
960 /// Binds [`igraph_minimum_size_separators`](https://igraph.org/c/html/latest/igraph-Separators.html#igraph_minimum_size_separators).
961 ///
962 /// # Errors
963 /// [`ErrorKind::InvalidValue`] for
964 /// directed graphs.
965 ///
966 /// # Examples
967 /// ```
968 /// use igraph::prelude::*;
969 /// // The 5-cycle: removing any two non-adjacent vertices disconnects it.
970 /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
971 /// let seps = c5.minimum_size_separators().unwrap();
972 /// assert_eq!(seps.len(), 5);
973 /// assert!(seps.iter().all(|s| s.len() == 2));
974 /// ```
975 pub fn minimum_size_separators(&self) -> Result<Vec<Vec<VertexId>>> {
976 let mut res = VectorIntList::new();
977 igraph_call!(igraph_minimum_size_separators(self, &mut res))?;
978 Ok(res.to_vecs())
979 }
980
981 // ----- cohesive blocks --------------------------------------------------
982
983 /// The hierarchical cohesive block structure of the graph (Moody and
984 /// White, 2003).
985 ///
986 /// A vertex set is *k-cohesive* when the subgraph it induces has vertex
987 /// connectivity at least k; it is *maximally* k-cohesive when no superset
988 /// is. Cohesive blocking starts from the whole graph and recursively
989 /// identifies the maximally l-cohesive subsets, l > k, of each k-cohesive
990 /// block, yielding a tree of nested blocks rooted at the whole graph
991 /// (block `0`, with `parent` `None`).
992 ///
993 /// The cohesion of each block is the
994 /// [`vertex_connectivity`](Graph::vertex_connectivity) of the subgraph it
995 /// induces. The root block has cohesion 0 when the graph is
996 /// disconnected; the null graph yields a single, empty root block.
997 ///
998 /// The graph must be undirected and simple (see [`Graph::simplify`]).
999 ///
1000 /// See also [`Graph::coreness`] for the (cheaper, degree-based) k-core
1001 /// hierarchy, and [`Graph::cohesion`] for the cohesion of the whole
1002 /// graph.
1003 ///
1004 /// Binds [`igraph_cohesive_blocks`](https://igraph.org/c/html/latest/igraph-Flows.html#igraph_cohesive_blocks).
1005 ///
1006 /// # Errors
1007 /// [`ErrorKind::InvalidValue`] for
1008 /// directed or non-simple graphs.
1009 ///
1010 /// # Examples
1011 /// ```
1012 /// use igraph::prelude::*;
1013 /// // Two 4-cliques (0..4 and 3..7) sharing vertex 3.
1014 /// let mut edges = vec![];
1015 /// for block in [[0, 1, 2, 3], [3, 4, 5, 6]] {
1016 /// for i in 0..4 {
1017 /// for j in i + 1..4 {
1018 /// edges.push((block[i], block[j]));
1019 /// }
1020 /// }
1021 /// }
1022 /// let g = Graph::from_edges(&edges, 7, false).unwrap();
1023 /// let cb = g.cohesive_blocks().unwrap();
1024 /// assert_eq!(cb.len(), 3);
1025 /// assert_eq!(cb.cohesion, vec![1, 3, 3]);
1026 /// assert_eq!(cb.parent, vec![None, Some(0), Some(0)]);
1027 /// assert_eq!(cb.children(0), vec![1, 2]);
1028 /// ```
1029 pub fn cohesive_blocks(&self) -> Result<CohesiveBlocks> {
1030 let mut blocks = VectorIntList::new();
1031 let mut cohesion = VectorInt::new();
1032 let mut parent = VectorInt::new();
1033 let block_tree = Graph::init_with(|tree| unsafe {
1034 igraph_cohesive_blocks(self, &mut blocks, &mut cohesion, &mut parent, tree)
1035 })?;
1036 Ok(CohesiveBlocks {
1037 blocks: blocks.to_vecs(),
1038 cohesion: to_usizes(cohesion),
1039 parent: parent.iter().map(|&p| usize::try_from(p).ok()).collect(),
1040 block_tree,
1041 })
1042 }
1043
1044 // ----- reachability -----------------------------------------------------
1045
1046 /// Which vertices are reachable from each vertex.
1047 ///
1048 /// The result groups vertices by the component they belong to (strongly
1049 /// connected components for directed graphs with
1050 /// [`NeighborMode::Out`]/[`NeighborMode::In`], plain components
1051 /// otherwise), since vertices of one component reach the same set, and
1052 /// stores for each component the set of reachable vertices as booleans.
1053 /// With `Out` edges are followed along their direction, with `In`
1054 /// against it, with `All` directions are ignored; `mode` is ignored for
1055 /// undirected graphs. A vertex always reaches itself.
1056 ///
1057 /// Time complexity: O(|C||V|/w + |V| + |E|), |C| being the number of
1058 /// components and w the machine word size.
1059 ///
1060 /// See also [`count_reachable`](Self::count_reachable) for the sizes
1061 /// only, [`transitive_closure`](Self::transitive_closure) for the same
1062 /// information as a graph, and [`Graph::subcomponent`] for the vertices
1063 /// reachable from a single vertex.
1064 ///
1065 /// Binds [`igraph_reachability`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_reachability).
1066 ///
1067 /// # Examples
1068 /// ```
1069 /// use igraph::prelude::*;
1070 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (3, 2)], 4, true).unwrap();
1071 /// let r = g.reachability(NeighborMode::Out).unwrap();
1072 /// assert!(r.is_reachable(0, 2));
1073 /// assert!(!r.is_reachable(2, 0));
1074 /// assert_eq!(r.reachable_from(3), vec![2, 3]);
1075 /// ```
1076 pub fn reachability(&self, mode: NeighborMode) -> Result<Reachability> {
1077 let mut membership = VectorInt::new();
1078 let mut csize = VectorInt::new();
1079 let mut no: igraph_int_t = 0;
1080 let mut reach = BitsetList::new();
1081 igraph_call!(igraph_reachability(
1082 self,
1083 &mut membership,
1084 &mut csize,
1085 &mut no,
1086 &mut reach,
1087 mode.into()
1088 ))?;
1089 Ok(Reachability {
1090 membership: membership.into(),
1091 sizes: to_usizes(csize),
1092 count: no as usize,
1093 reach: reach.iter().map(|bits| bits.to_vec()).collect(),
1094 })
1095 }
1096
1097 /// The number of vertices reachable from each vertex, itself included.
1098 ///
1099 /// `mode` has the same meaning as in [`reachability`](Self::reachability):
1100 /// with [`NeighborMode::In`] it counts the vertices that can reach each
1101 /// vertex.
1102 ///
1103 /// Time complexity: O(|C||V|/w + |V| + |E|).
1104 ///
1105 /// Equivalently, it is [`neighborhood_size`](Self::neighborhood_size)
1106 /// with unlimited order and `mindist = 0`, but faster: the work is shared
1107 /// by all the vertices of a strongly connected component instead of
1108 /// running one breadth-first search per vertex.
1109 ///
1110 /// Binds [`igraph_count_reachable`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_count_reachable).
1111 ///
1112 /// # Examples
1113 /// ```
1114 /// use igraph::prelude::*;
1115 /// // The directed path 0 -> 1 -> 2.
1116 /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
1117 /// assert_eq!(g.count_reachable(NeighborMode::Out).unwrap(), vec![3, 2, 1]);
1118 /// assert_eq!(g.count_reachable(NeighborMode::In).unwrap(), vec![1, 2, 3]);
1119 /// ```
1120 pub fn count_reachable(&self, mode: NeighborMode) -> Result<Vec<usize>> {
1121 let mut res = VectorInt::new();
1122 igraph_call!(igraph_count_reachable(self, &mut res, mode.into()))?;
1123 Ok(to_usizes(res))
1124 }
1125
1126 /// The transitive closure of the graph.
1127 ///
1128 /// The result has the same vertices and directedness, and an edge
1129 /// `i -> j` (`i != j`) exactly when `j` is reachable from `i` in the
1130 /// original graph; it is simple (no loops nor multi-edges). For undirected
1131 /// graphs every component becomes a clique.
1132 ///
1133 /// Time complexity: O(|V|² + |E|).
1134 ///
1135 /// For a bounded number of steps use [`Graph::graph_power`] (a new graph)
1136 /// or [`Graph::connect_neighborhood`] (in place).
1137 ///
1138 /// Binds [`igraph_transitive_closure`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_transitive_closure).
1139 ///
1140 /// # Examples
1141 /// ```
1142 /// use igraph::prelude::*;
1143 /// let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
1144 /// let tc = g.transitive_closure().unwrap();
1145 /// assert_eq!(tc.ecount(), 3);
1146 /// assert!(tc.get_eid(0, 2, true).unwrap().is_some());
1147 /// ```
1148 pub fn transitive_closure(&self) -> Result<Graph> {
1149 Graph::init_with(|closure| unsafe { igraph_transitive_closure(self, closure) })
1150 }
1151
1152 // ----- neighborhoods ----------------------------------------------------
1153
1154 /// The sizes of the neighborhoods of the selected vertices.
1155 ///
1156 /// The neighborhood of order `k` of a vertex contains the vertices at
1157 /// distance at most `k` from it: order 0 is the vertex itself, order 1
1158 /// adds its neighbors, and so on; `order = None` means no limit (the
1159 /// whole reachable set). Vertices closer than `mindist` are not counted:
1160 /// `mindist = 1` excludes the vertex itself, `2` its neighbors too, etc.
1161 /// With [`NeighborMode::Out`] paths follow edge directions, with
1162 /// [`NeighborMode::In`] they go against them, [`NeighborMode::All`]
1163 /// ignores them.
1164 ///
1165 /// Time complexity: O(n d o), n being the number of selected vertices,
1166 /// d the average degree and o the order.
1167 ///
1168 /// See also [`Graph::distances`] for the distances themselves and
1169 /// [`Graph::bfs`] for a full breadth-first traversal.
1170 ///
1171 /// Binds [`igraph_neighborhood_size`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_neighborhood_size).
1172 ///
1173 /// # Errors
1174 /// Invalid vertex ids, or `mindist > order`.
1175 ///
1176 /// # Examples
1177 /// ```
1178 /// use igraph::prelude::*;
1179 /// // The path 0-1-2-3-4.
1180 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
1181 /// assert_eq!(g.neighborhood_size(.., Some(1), NeighborMode::All, 0).unwrap(), vec![2, 3, 3, 3, 2]);
1182 /// // Vertices at distance exactly 2.
1183 /// assert_eq!(g.neighborhood_size(.., Some(2), NeighborMode::All, 2).unwrap(), vec![1, 1, 2, 1, 1]);
1184 /// ```
1185 pub fn neighborhood_size<'a>(
1186 &self,
1187 vids: impl Into<VertexSelector<'a>>,
1188 order: Option<usize>,
1189 mode: NeighborMode,
1190 mindist: usize,
1191 ) -> Result<Vec<usize>> {
1192 let mut res = VectorInt::new();
1193 with_vs(vids.into(), |vs| {
1194 igraph_call!(igraph_neighborhood_size(
1195 self,
1196 &mut res,
1197 vs,
1198 order_raw(order),
1199 mode.into(),
1200 int_sat(mindist)
1201 ))
1202 })?;
1203 Ok(to_usizes(res))
1204 }
1205
1206 /// The neighborhoods of the selected vertices, as vertex lists.
1207 ///
1208 /// See [`neighborhood_size`](Self::neighborhood_size) for the meaning of
1209 /// `order` (`None` = unlimited), `mode` and `mindist`. Each list is in
1210 /// breadth-first order: the vertex itself first (unless excluded by
1211 /// `mindist`), then vertices at distance 1, 2, ...
1212 ///
1213 /// Time complexity: O(n d o).
1214 ///
1215 /// See also [`Graph::connect_neighborhood`], which adds an edge from each
1216 /// vertex to every member of its neighborhood.
1217 ///
1218 /// Binds [`igraph_neighborhood`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_neighborhood).
1219 ///
1220 /// # Errors
1221 /// Invalid vertex ids, or `mindist > order`.
1222 ///
1223 /// # Examples
1224 /// ```
1225 /// use igraph::prelude::*;
1226 /// // The directed path 0 -> 1 -> 2 -> 3.
1227 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
1228 /// assert_eq!(g.neighborhood(1, None, NeighborMode::Out, 0).unwrap(), vec![vec![1, 2, 3]]);
1229 /// assert_eq!(g.neighborhood(&[1, 3], Some(1), NeighborMode::In, 1).unwrap(), vec![vec![0], vec![2]]);
1230 /// ```
1231 pub fn neighborhood<'a>(
1232 &self,
1233 vids: impl Into<VertexSelector<'a>>,
1234 order: Option<usize>,
1235 mode: NeighborMode,
1236 mindist: usize,
1237 ) -> Result<Vec<Vec<VertexId>>> {
1238 let mut res = VectorIntList::new();
1239 with_vs(vids.into(), |vs| {
1240 igraph_call!(igraph_neighborhood(
1241 self,
1242 &mut res,
1243 vs,
1244 order_raw(order),
1245 mode.into(),
1246 int_sat(mindist)
1247 ))
1248 })?;
1249 Ok(res.to_vecs())
1250 }
1251
1252 /// The subgraphs induced by the neighborhoods of the selected vertices.
1253 ///
1254 /// See [`neighborhood_size`](Self::neighborhood_size) for the meaning of
1255 /// `order` (`None` = unlimited), `mode` and `mindist`. In each graph the
1256 /// vertices are renumbered consecutively *preserving their relative
1257 /// order* in the original graph (as in an induced subgraph), so the
1258 /// new id of an original vertex `v` is the number of neighborhood
1259 /// members with a smaller id than `v`. Each graph is thus the same as
1260 /// [`Graph::induced_subgraph`] of the corresponding
1261 /// [`neighborhood`](Self::neighborhood) (an "ego network").
1262 ///
1263 /// Time complexity: O(n (|V| + |E|)).
1264 ///
1265 /// Binds [`igraph_neighborhood_graphs`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_neighborhood_graphs).
1266 ///
1267 /// # Errors
1268 /// Invalid vertex ids, or `mindist > order`.
1269 ///
1270 /// # Examples
1271 /// ```
1272 /// use igraph::prelude::*;
1273 /// // A star with center 0 and leaves 1..=4: its 1-neighborhoods.
1274 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], 5, false).unwrap();
1275 /// let egos = g.neighborhood_graphs(&[0, 1], Some(1), NeighborMode::All, 0).unwrap();
1276 /// assert_eq!((egos[0].vcount(), egos[0].ecount()), (5, 4));
1277 /// assert_eq!((egos[1].vcount(), egos[1].ecount()), (2, 1));
1278 /// ```
1279 pub fn neighborhood_graphs<'a>(
1280 &self,
1281 vids: impl Into<VertexSelector<'a>>,
1282 order: Option<usize>,
1283 mode: NeighborMode,
1284 mindist: usize,
1285 ) -> Result<Vec<Graph>> {
1286 let mut res = GraphList::new();
1287 with_vs(vids.into(), |vs| {
1288 igraph_call!(igraph_neighborhood_graphs(
1289 self,
1290 &mut res,
1291 vs,
1292 order_raw(order),
1293 mode.into(),
1294 int_sat(mindist)
1295 ))
1296 })?;
1297 Ok(res.into_vec())
1298 }
1299}