igraph/structural.rs
1//! Basic structural properties of graphs (`igraph_structural.h`).
2//!
3//! This module binds the functions declared in igraph's
4//! [`igraph_structural.h`](https://igraph.org/c/html/latest/igraph-Structural.html)
5//! header, as further methods of [`Graph`]. They answer the everyday
6//! questions one asks about a network:
7//!
8//! - *how big and how dense is it?* ([`density`](Graph::density),
9//! [`mean_degree`](Graph::mean_degree), [`maxdegree`](Graph::maxdegree),
10//! [`strength`](Graph::strength));
11//! - *is it a simple graph?* (loops, multi-edges and mutual edges);
12//! - *what kind of graph is it?* (tree, forest, acyclic, complete, chordal,
13//! perfect; cliques and independent sets);
14//! - *how are degrees correlated?* (average nearest neighbor degree,
15//! `k_nn(k)`, rich-club density sequence);
16//! - *which spanning trees does it have?* (minimum and uniformly random);
17//! - *what does its Laplacian look like?* (dense and sparse).
18//!
19//! # Example
20//!
21//! ```
22//! use igraph::prelude::*;
23//!
24//! // A 5-cycle with a chord, plus a pendant vertex.
25//! let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0), (0, 2), (4, 5)], 6, false)?;
26//! assert_eq!(g.mean_degree(true)?, 14.0 / 6.0);
27//! assert_eq!(g.maxdegree(.., NeighborMode::All, Loops::Twice)?, 3);
28//! assert!((g.density(None, false)? - 7.0 / 15.0).abs() < 1e-12);
29//! assert!(g.is_simple(true)?);
30//! assert_eq!(g.girth()?, Some(3));
31//! assert!(!g.is_tree(NeighborMode::All)?);
32//! assert!(g.is_clique(&[0, 1, 2], false)?);
33//! assert!(g.is_independent_vertex_set(&[1, 3, 5])?);
34//!
35//! // A spanning tree of a connected graph has n - 1 edges.
36//! let mst = g.minimum_spanning_tree(None, MstAlgorithm::Automatic)?;
37//! assert_eq!(mst.len(), 5);
38//!
39//! // Zachary's karate club: 34 members, 78 friendships, and the two leaders
40//! // (vertices 0 and 33) are the best connected members.
41//! let karate = Graph::famous("Zachary")?;
42//! assert_eq!(karate.maxdegree(.., NeighborMode::All, Loops::Twice)?, 17);
43//! let hubs = karate.sort_vertex_ids_by_degree(.., NeighborMode::All, Loops::Twice, Order::Descending, false)?;
44//! assert_eq!(&hubs[..2], &[33, 0]);
45//! # Ok::<(), igraph::Error>(())
46//! ```
47//!
48//! # Provided functionality
49//!
50//! | Topic | Methods of [`Graph`] |
51//! |-------|----------------------|
52//! | Adjacency | [`are_adjacent`](Graph::are_adjacent), [`subcomponent`](Graph::subcomponent) |
53//! | Density and degrees | [`density`](Graph::density), [`mean_degree`](Graph::mean_degree), [`maxdegree`](Graph::maxdegree), [`strength`](Graph::strength), [`sort_vertex_ids_by_degree`](Graph::sort_vertex_ids_by_degree), [`diversity`](Graph::diversity) |
54//! | Loops and multi-edges | [`has_loop`](Graph::has_loop), [`count_loops`](Graph::count_loops), [`is_loop`](Graph::is_loop), [`has_multiple`](Graph::has_multiple), [`is_multiple`](Graph::is_multiple), [`count_multiple`](Graph::count_multiple), [`count_multiple_1`](Graph::count_multiple_1), [`is_simple`](Graph::is_simple) |
55//! | Reciprocity | [`reciprocity`](Graph::reciprocity), [`is_mutual`](Graph::is_mutual), [`has_mutual`](Graph::has_mutual) |
56//! | Graph classes | [`is_tree`](Graph::is_tree), [`tree_root`](Graph::tree_root), [`is_forest`](Graph::is_forest), [`forest_roots`](Graph::forest_roots), [`is_acyclic`](Graph::is_acyclic), [`is_complete`](Graph::is_complete), [`is_perfect`](Graph::is_perfect), [`is_chordal`](Graph::is_chordal), [`is_chordal_with`](Graph::is_chordal_with), [`maximum_cardinality_search`](Graph::maximum_cardinality_search) |
57//! | Vertex sets | [`is_clique`](Graph::is_clique), [`is_independent_vertex_set`](Graph::is_independent_vertex_set) |
58//! | Cycles | [`girth`](Graph::girth), [`girth_with_cycle`](Graph::girth_with_cycle) |
59//! | Spanning trees | [`minimum_spanning_tree`](Graph::minimum_spanning_tree), [`random_spanning_tree`](Graph::random_spanning_tree), [`unfold_tree`](Graph::unfold_tree) |
60//! | Degree correlations | [`avg_nearest_neighbor_degree`](Graph::avg_nearest_neighbor_degree), [`degree_correlation_vector`](Graph::degree_correlation_vector), [`rich_club_sequence`](Graph::rich_club_sequence) |
61//! | Spectral | [`get_laplacian`](Graph::get_laplacian), [`get_laplacian_sparse`](Graph::get_laplacian_sparse), [`get_laplacian_sparsemat`](Graph::get_laplacian_sparsemat), [`LaplacianNormalization`] |
62//!
63//! All the functions of the header are covered.
64//!
65//! # See also
66//!
67//! - Degrees and neighbors: [`Graph::degree`], [`Graph::neighbors`] (core);
68//! removing loops and multi-edges: [`Graph::simplify`] ([`operators`](crate::operators)).
69//! - Connectivity: [`Graph::connected_components`], [`Graph::is_connected`],
70//! [`Graph::count_reachable`] ([`components`](crate::components)).
71//! - Cycles and DAGs: [`Graph::find_cycle`], [`Graph::minimum_cycle_basis`],
72//! [`Graph::is_dag`], [`Graph::topological_sorting`] ([`cycles`](crate::cycles)).
73//! - Cliques, independent sets and colorings: [`Graph::largest_cliques`],
74//! [`Graph::clique_number`], [`Graph::independence_number`],
75//! [`Graph::vertex_coloring_greedy`] ([`cliques`](crate::cliques)).
76//! - Degree correlations as a single number: [`Graph::assortativity_degree`],
77//! [`Graph::joint_degree_matrix`] ([`mixing`](crate::mixing)).
78//! - Matrices of a graph: [`Graph::get_adjacency`], [`Graph::get_adjacency_sparse`]
79//! ([`conversion`](crate::conversion)); spectra of the Laplacian with
80//! [`lapack_dsyevr`](crate::linalg::lapack_dsyevr) and
81//! [`Graph::laplacian_spectral_embedding`] ([`linalg`](crate::linalg)).
82//! - Trees: [`Graph::kary_tree`] ([`constructors`](crate::constructors)),
83//! [`Graph::tree_game`] ([`games`](crate::games)), [`Graph::to_prufer`]
84//! ([`conversion`](crate::conversion)), [`Graph::bfs`] ([`visitor`](crate::visitor)).
85//!
86//! # Notes on igraph 1.0.0 and 1.0.1
87//!
88//! A few wrappers work around behaviours of the C library that are present
89//! in both igraph 1.0.0 and 1.0.1 (the source files involved are unchanged
90//! in 1.0.1), so that the Rust API is consistent and memory safe:
91//!
92//! - [`count_multiple_1`](Graph::count_multiple_1) validates the edge id and
93//! reports an undirected self-loop once, like [`count_multiple`](Graph::count_multiple);
94//! - [`get_laplacian_sparse`](Graph::get_laplacian_sparse) (and
95//! [`get_laplacian_sparsemat`](Graph::get_laplacian_sparsemat)) ignore edge
96//! directions with [`NeighborMode::All`], like the dense
97//! [`get_laplacian`](Graph::get_laplacian);
98//! - [`is_chordal_with`](Graph::is_chordal_with) checks that the given
99//! vertex orders are permutations (igraph only checks their lengths);
100//! - [`diversity`](Graph::diversity) recomputes the value of degree-one
101//! vertices, which igraph derives from the weight of edge 0 instead of
102//! the vertex's own edge.
103//!
104//! One wrapper differs from the C calling convention on purpose:
105//! [`random_spanning_tree`](Graph::random_spanning_tree) spells igraph's
106//! documented "negative vertex id means all components" as `None`, and
107//! rejects negative ids instead of silently spanning all components.
108
109use crate::{
110 constants::*,
111 error::{Error, Result},
112 ffi::*,
113 graph::{EdgeId, Graph, VertexId},
114 igraph_call,
115 linalg::SparseMat,
116 matrix::Matrix,
117 selector::{EdgeSelector, VertexSelector},
118 vector::{Vector, VectorBool, VectorInt},
119};
120use std::{collections::BTreeMap, ptr};
121
122crate::ffi_enum! {
123 /// Normalization of the Laplacian matrix (`igraph_laplacian_normalization_t`),
124 /// used by [`Graph::get_laplacian`], [`Graph::get_laplacian_sparse`] and
125 /// [`Graph::get_laplacian_sparsemat`].
126 ///
127 /// `A` is the (possibly weighted) adjacency matrix and `D` the diagonal
128 /// matrix of degrees (or strengths, if weighted); out-, in- or total
129 /// degrees are used according to the `mode` argument.
130 pub enum LaplacianNormalization: igraph_laplacian_normalization_t {
131 /// Unnormalized Laplacian, `L = D - A`.
132 Unnormalized = igraph_laplacian_normalization_t_IGRAPH_LAPLACIAN_UNNORMALIZED,
133 /// Symmetrically normalized Laplacian, `L = I - D^(-1/2) A D^(-1/2)`.
134 Symmetric = igraph_laplacian_normalization_t_IGRAPH_LAPLACIAN_SYMMETRIC,
135 /// Left-stochastic normalized Laplacian, `L = I - D^-1 A` (rows sum to
136 /// zero when `D` holds the row sums of `A`, e.g. out-degrees).
137 Left = igraph_laplacian_normalization_t_IGRAPH_LAPLACIAN_LEFT,
138 /// Right-stochastic normalized Laplacian, `L = I - A D^-1` (columns sum
139 /// to zero when `D` holds the column sums of `A`, e.g. in-degrees).
140 Right = igraph_laplacian_normalization_t_IGRAPH_LAPLACIAN_RIGHT,
141 }
142}
143
144impl Default for LaplacianNormalization {
145 /// [`LaplacianNormalization::Unnormalized`].
146 fn default() -> Self {
147 Self::Unnormalized
148 }
149}
150
151/// Result of [`Graph::avg_nearest_neighbor_degree`].
152#[derive(Debug, Clone, PartialEq)]
153pub struct NeighborDegree {
154 /// `knn[i]` is the (possibly weighted) average degree of the neighbors of
155 /// the `i`-th selected vertex; NaN for isolated vertices (with weights:
156 /// for vertices of zero strength).
157 pub knn: Vec<f64>,
158 /// The `k_nn(k)` degree correlation function: `knnk[k - 1]` is the mean
159 /// of `knn` over the selected vertices of degree `k` (with weights, the
160 /// mean weighted by their strengths), NaN for degrees that do not occur.
161 /// Note the shift: the first element is for degree one. Its length is the
162 /// maximum degree (according to `mode`) of the whole graph.
163 pub knnk: Vec<f64>,
164}
165
166/// Result of [`Graph::maximum_cardinality_search`].
167#[derive(Debug, Clone, PartialEq)]
168pub struct CardinalitySearch {
169 /// `alpha[v]` is the rank of vertex `v`, in `0..n`; visiting vertices by
170 /// *decreasing* rank always picks the vertex with most visited neighbors.
171 pub alpha: Vec<i64>,
172 /// The inverse permutation of `alpha`: `alpham1[r]` is the vertex of rank
173 /// `r`, i.e. the vertices in *reverse* maximum cardinality search order.
174 pub alpham1: Vec<VertexId>,
175}
176
177/// Result of [`Graph::is_chordal_with`].
178#[derive(Debug, Clone, PartialEq)]
179pub struct Chordality {
180 /// Whether the graph is chordal.
181 pub is_chordal: bool,
182 /// The fill-in (chordal completion): edges whose addition makes the
183 /// graph chordal. Empty for chordal graphs; not necessarily minimal.
184 pub fill_in: Vec<(VertexId, VertexId)>,
185 /// The triangulated graph: a copy of the original graph (same
186 /// directedness) with the fill-in edges appended after the original ones.
187 pub triangulated: Graph,
188}
189
190/// Result of [`Graph::unfold_tree`].
191#[derive(Debug, Clone, PartialEq)]
192pub struct UnfoldedTree {
193 /// The unfolded tree (or forest); it has the directedness of the input.
194 pub tree: Graph,
195 /// `vertex_index[v]` is the vertex of the original graph that vertex `v`
196 /// of `tree` is a copy of. The first `vcount` entries are the identity.
197 pub vertex_index: Vec<VertexId>,
198}
199
200/// Returns a pointer to the optional weight view, or null.
201fn weights_ptr(w: &Option<crate::vector::View<'_, Vector>>) -> *const igraph_vector_t {
202 w.as_ref().map_or(ptr::null(), |v| v.as_ptr())
203}
204
205/// Checks that an optional weight vector has one entry per edge.
206fn check_weights(graph: &Graph, weights: Option<&[f64]>) -> Result<()> {
207 match weights {
208 Some(w) if w.len() != graph.ecount() => Err(Error::invalid(format!(
209 "weight vector length ({}) must match the number of edges ({})",
210 w.len(),
211 graph.ecount()
212 ))),
213 _ => Ok(()),
214 }
215}
216
217/// Whether `p` is a permutation of `0..n`.
218fn is_permutation(p: &[i64], n: usize) -> bool {
219 if p.len() != n {
220 return false;
221 }
222 let mut seen = vec![false; n];
223 p.iter()
224 .all(|&x| (0..n as i64).contains(&x) && !std::mem::replace(&mut seen[x as usize], true))
225}
226
227/// Runs `f` (a C call building an `IGRAPH_ALL`, `IGRAPH_NO_MULTIPLE`
228/// adjacency list) so that igraph's property cache cannot switch off the
229/// removal of duplicate neighbors.
230///
231/// igraph 1.0.1 `igraph_adjlist_init` / `igraph_lazy_adjlist_init` skip the
232/// deduplication when the cache says the graph has no multi-edges. On a
233/// directed graph with a mutual pair `u -> v`, `v -> u` that is wrong in
234/// `IGRAPH_ALL` mode (each of `u`, `v` is listed twice as a neighbor of the
235/// other), and `adjlist_init` then even caches `HAS_MULTI = true`. For
236/// directed graphs the cache is therefore cleared before and after `f`.
237/// Undirected graphs are not affected.
238/// See [`Graph::with_fresh_multi_cache`](crate::graph); this delegates to it
239/// (which also drops the cache when `f` panics).
240pub(crate) fn with_multi_cache_guard<T>(graph: &Graph, f: impl FnOnce() -> T) -> T {
241 graph.with_fresh_multi_cache(f)
242}
243
244impl igraph_t {
245 // ------------------------------------------------------------------
246 // Basic queries
247 // ------------------------------------------------------------------
248
249 /// Decides whether there is an edge from `v1` to `v2`.
250 ///
251 /// In directed graphs the direction matters (`v1 → v2`); in undirected
252 /// graphs the relation is symmetric.
253 ///
254 /// Binds [`igraph_are_adjacent`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_are_adjacent).
255 /// Time complexity: `O(min(log d1, log d2))` where `d1` is the
256 /// (out-)degree of `v1` and `d2` the (in-)degree of `v2`.
257 ///
258 /// # Errors
259 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) for
260 /// invalid vertex ids.
261 ///
262 /// # Examples
263 /// ```
264 /// use igraph::prelude::*;
265 /// let g = Graph::from_edges(&[(0, 1)], 3, true)?;
266 /// assert!(g.are_adjacent(0, 1)?);
267 /// assert!(!g.are_adjacent(1, 0)?);
268 /// assert_eq!(g.are_adjacent(0, 9).unwrap_err().kind(), ErrorKind::InvalidVertexId);
269 /// # Ok::<(), igraph::Error>(())
270 /// ```
271 ///
272 /// See also [`Graph::neighbors`] and [`Graph::get_adjacency`] for the
273 /// whole neighborhood or adjacency matrix.
274 pub fn are_adjacent(&self, v1: VertexId, v2: VertexId) -> Result<bool> {
275 let mut res = false;
276 igraph_call!(igraph_are_adjacent(self, v1, v2, &mut res))?;
277 Ok(res)
278 }
279
280 /// The multiplicity of the selected edges: for each edge, the number of
281 /// edges between its two endpoints (itself included).
282 ///
283 /// A simple graph gives all ones. In directed graphs, `(a, b)` and
284 /// `(b, a)` are different edges and don't count towards each other.
285 ///
286 /// Binds [`igraph_count_multiple`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_count_multiple).
287 /// Time complexity: `O(E d)`, `E` the number of edges to check and `d`
288 /// the average degree of their tails.
289 ///
290 /// # Examples
291 /// ```
292 /// use igraph::prelude::*;
293 /// let g = Graph::from_edges(&[(0, 1), (0, 1), (1, 2), (0, 1)], 3, false)?;
294 /// assert_eq!(g.count_multiple(..)?, vec![3, 3, 1, 3]);
295 /// # Ok::<(), igraph::Error>(())
296 /// ```
297 ///
298 /// See also [`is_multiple`](Self::is_multiple), and [`Graph::simplify`]
299 /// to merge parallel edges.
300 pub fn count_multiple<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<Vec<i64>> {
301 let es = edges.into().to_raw()?;
302 let mut res = VectorInt::new();
303 igraph_call!(igraph_count_multiple(self, &mut res, es.get()))?;
304 Ok(res.into())
305 }
306
307 /// The multiplicity of a single edge, see [`count_multiple`](Self::count_multiple).
308 ///
309 /// The result always agrees with the corresponding entry of
310 /// [`count_multiple`](Self::count_multiple), self-loops of undirected
311 /// graphs included: the C function of igraph 1.0.0 and 1.0.1 counts each
312 /// undirected self-loop twice (it scans the neighbor list of the tail,
313 /// where a loop appears twice), and this wrapper halves that count.
314 ///
315 /// Binds [`igraph_count_multiple_1`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_count_multiple_1).
316 /// Time complexity: `O(d)`, the out-degree of the tail of the edge.
317 ///
318 /// # Errors
319 /// [`ErrorKind::InvalidEdgeId`](crate::ErrorKind::InvalidEdgeId) for an invalid edge id.
320 ///
321 /// # Examples
322 /// ```
323 /// use igraph::prelude::*;
324 /// let g = Graph::from_edges(&[(0, 1), (1, 0), (2, 2), (2, 2)], 3, false)?;
325 /// assert_eq!(g.count_multiple_1(0)?, 2);
326 /// assert_eq!(g.count_multiple_1(2)?, 2); // two self-loops on vertex 2
327 /// assert_eq!(g.count_multiple_1(4).unwrap_err().kind(), ErrorKind::InvalidEdgeId);
328 /// # Ok::<(), igraph::Error>(())
329 /// ```
330 pub fn count_multiple_1(&self, edge: EdgeId) -> Result<i64> {
331 // igraph 1.0.0 and 1.0.1 do not validate the id (they would read
332 // out of bounds).
333 if !(0..self.ecount() as EdgeId).contains(&edge) {
334 return Err(Error::new(
335 crate::ErrorKind::InvalidEdgeId,
336 format!("invalid edge id {edge}"),
337 ));
338 }
339 let mut res = 0;
340 igraph_call!(igraph_count_multiple_1(self, &mut res, edge))?;
341 // igraph 1.0.0 and 1.0.1 count the neighbors of the tail, where an
342 // undirected self-loop appears twice: halve the count so that it
343 // agrees with `igraph_count_multiple` (which reports the number of
344 // loops).
345 let (from, to) = self.edge(edge)?;
346 if from == to && !self.is_directed() {
347 res /= 2;
348 }
349 Ok(res)
350 }
351
352 /// The density of the graph: the ratio of the number of edges to the
353 /// largest possible number of edges.
354 ///
355 /// The maximum number of edges is `n(n-1)/2` for undirected and `n(n-1)`
356 /// for directed graphs; when `loops` is `true`, self-loops are considered
357 /// possible and these become `n(n+1)/2` and `n²`. With `loops = false`
358 /// the result is only correct if the graph has no loops (this is not
359 /// checked).
360 ///
361 /// With `weights`, the total edge weight is used instead of the edge
362 /// count, and the result may exceed 1 (the same happens with
363 /// multigraphs). For the null graph the result is NaN.
364 ///
365 /// Binds [`igraph_density`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_density).
366 /// Time complexity: `O(1)` (`O(|E|)` when weighted).
367 ///
368 /// # Examples
369 /// ```
370 /// use igraph::prelude::*;
371 /// let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
372 /// assert_eq!(triangle.density(None, false)?, 1.0);
373 /// assert_eq!(triangle.density(None, true)?, 0.5); // 3 of 6 possible edges
374 /// assert_eq!(triangle.density(Some(&[0.5, 0.5, 0.5]), false)?, 0.5);
375 /// # Ok::<(), igraph::Error>(())
376 /// ```
377 ///
378 /// See also [`mean_degree`](Self::mean_degree) and
379 /// [`rich_club_sequence`](Self::rich_club_sequence), which computes the
380 /// density of a sequence of shrinking subgraphs.
381 pub fn density(&self, weights: Option<&[f64]>, loops: bool) -> Result<f64> {
382 check_weights(self, weights)?;
383 let w = weights.map(Vector::view);
384 let mut res = 0.0;
385 igraph_call!(igraph_density(self, weights_ptr(&w), &mut res, loops))?;
386 Ok(res)
387 }
388
389 /// The structural diversity index of the selected vertices.
390 ///
391 /// It is the Shannon entropy of the weights of the incident edges,
392 /// normalized by the logarithm of the degree:
393 /// `D(i) = H(i) / log k(i)` with `H(i) = -Σ_j p_ij log p_ij` and
394 /// `p_ij = w_ij / Σ_l w_il`, `k(i)` being the degree (zero-weight edges
395 /// included). Isolated vertices get NaN, vertices of degree one get 0
396 /// (NaN if the weight of their only edge is zero, as for any vertex whose
397 /// incident weights are all zero). Defined by Eagle, Macy and Claxton
398 /// (Science 328, 2010).
399 ///
400 /// igraph 1.0.0 and 1.0.1 compute the value of degree-one vertices from
401 /// the weight of edge 0 rather than of the vertex's own edge; this
402 /// wrapper recomputes those entries.
403 ///
404 /// The graph must be undirected and without multi-edges; `weights`
405 /// (required, one non-negative value per edge) are mandatory.
406 ///
407 /// Binds [`igraph_diversity`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_diversity).
408 /// Time complexity: `O(|V| + |E|)`.
409 ///
410 /// # Errors
411 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for directed
412 /// graphs, multigraphs, negative weights or a wrong number of weights.
413 ///
414 /// # Examples
415 /// ```
416 /// use igraph::prelude::*;
417 /// // A star with equal weights has maximal diversity at the center.
418 /// let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
419 /// let d = star.diversity(&[1.0, 1.0, 1.0], ..)?;
420 /// assert!((d[0] - 1.0).abs() < 1e-12);
421 /// assert_eq!(&d[1..], &[0.0, 0.0, 0.0]);
422 /// // A zero weight: the center's entropy is log 2 over log 3 (the edge
423 /// // still counts in its degree), and leaf 1 has no weight at all.
424 /// let d = star.diversity(&[0.0, 1.0, 1.0], ..)?;
425 /// assert!((d[0] - 2f64.ln() / 3f64.ln()).abs() < 1e-12);
426 /// assert!(d[1].is_nan());
427 /// assert_eq!(&d[2..], &[0.0, 0.0]);
428 /// # Ok::<(), igraph::Error>(())
429 /// ```
430 pub fn diversity<'a>(
431 &self,
432 weights: &[f64],
433 vertices: impl Into<VertexSelector<'a>>,
434 ) -> Result<Vec<f64>> {
435 check_weights(self, Some(weights))?;
436 let vs = vertices.into().to_raw()?;
437 let w = Vector::view(weights);
438 let mut res = Vector::new();
439 igraph_call!(igraph_diversity(self, w.as_ptr(), &mut res, vs.get()))?;
440 // igraph 1.0.0 and 1.0.1 decide the value of a degree-one vertex by
441 // looking at the weight of edge 0 instead of the vertex's own edge:
442 // recompute these entries (0 if the edge weight is positive, NaN if
443 // it is zero, as documented).
444 let mut ids = VectorInt::new();
445 igraph_call!(igraph_vs_as_vector(self, vs.get(), &mut ids))?;
446 let degrees = self.degree(&ids, NeighborMode::All, Loops::Twice)?;
447 let mut res: Vec<f64> = res.into();
448 for ((d, &v), _) in res
449 .iter_mut()
450 .zip(ids.iter())
451 .zip(°rees)
452 .filter(|(_, k)| **k == 1)
453 {
454 if let [e] = self.incident(v, NeighborMode::All, Loops::Twice)?[..] {
455 *d = if weights[e as usize] > 0.0 {
456 0.0
457 } else {
458 f64::NAN
459 };
460 }
461 }
462 Ok(res)
463 }
464
465 /// The girth of the graph: the length of its shortest cycle, or `None`
466 /// if the graph has no cycles.
467 ///
468 /// Edge directions are ignored; self-loops and multi-edges are ignored
469 /// as well, i.e. cycles of length 1 or 2 are not considered, so the
470 /// girth is always at least 3. `None` is returned exactly for graphs
471 /// without such cycles (forests, possibly with loops and multi-edges).
472 /// Algorithm by Itai and Rodeh (1977).
473 ///
474 /// Mutual edges `u -> v`, `v -> u` of a directed graph count as a single
475 /// undirected edge. igraph 1.0.1 can get this wrong when its property
476 /// cache already records "no multi-edges" (a girth of 2, or heap
477 /// corruption in the chordality code), so for directed graphs this
478 /// wrapper clears the cache around the C call.
479 ///
480 /// Binds [`igraph_girth`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_girth).
481 /// Time complexity: `O((|V| + |E|)²)` in general, `O(|V| + |E|)` for
482 /// acyclic graphs.
483 ///
484 /// # Examples
485 /// ```
486 /// use igraph::prelude::*;
487 /// // The Petersen graph has girth 5.
488 /// assert_eq!(Graph::famous("Petersen")?.girth()?, Some(5));
489 ///
490 /// let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
491 /// assert_eq!(path.girth()?, None);
492 /// // Loops and multi-edges do not form cycles here.
493 /// let multi = Graph::from_edges(&[(0, 0), (0, 1), (0, 1)], 2, false)?;
494 /// assert_eq!(multi.girth()?, None);
495 /// # Ok::<(), igraph::Error>(())
496 /// ```
497 ///
498 /// See also [`Graph::find_cycle`] (any cycle, respecting directions) and
499 /// [`Graph::minimum_cycle_basis`] (a basis of short cycles).
500 pub fn girth(&self) -> Result<Option<usize>> {
501 let mut girth = 0.0;
502 with_multi_cache_guard(self, || {
503 igraph_call!(igraph_girth(self, &mut girth, ptr::null_mut()))
504 })?;
505 Ok(girth.is_finite().then_some(girth as usize))
506 }
507
508 /// Like [`girth`](Self::girth), but also returns the vertex ids of one
509 /// shortest cycle of length at least 3, in cycle order (consecutive
510 /// vertices, and the last and the first, are adjacent); `None` if there
511 /// is no such cycle.
512 ///
513 /// Binds [`igraph_girth`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_girth).
514 ///
515 /// # Examples
516 /// ```
517 /// use igraph::prelude::*;
518 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (1, 3)], 4, false)?;
519 /// let (girth, cycle) = g.girth_with_cycle()?.unwrap();
520 /// assert_eq!(girth, 3);
521 /// assert_eq!(cycle.len(), 3);
522 /// assert!(g.is_clique(&cycle, false)?); // a triangle
523 /// # Ok::<(), igraph::Error>(())
524 /// ```
525 pub fn girth_with_cycle(&self) -> Result<Option<(usize, Vec<VertexId>)>> {
526 let mut girth = 0.0;
527 let mut circle = VectorInt::new();
528 with_multi_cache_guard(self, || {
529 igraph_call!(igraph_girth(self, &mut girth, &mut circle))
530 })?;
531 Ok(girth.is_finite().then(|| (girth as usize, circle.into())))
532 }
533
534 /// Whether the graph has at least one self-loop.
535 ///
536 /// The result is cached in the graph, so repeated calls are `O(1)`.
537 ///
538 /// Binds [`igraph_has_loop`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_has_loop).
539 /// Time complexity: `O(|E|)`.
540 ///
541 /// # Examples
542 /// ```
543 /// use igraph::prelude::*;
544 /// let mut g = Graph::from_edges(&[(0, 1), (1, 1)], 2, false)?;
545 /// assert!(g.has_loop()?);
546 /// g.simplify(true, true)?;
547 /// assert!(!g.has_loop()?);
548 /// # Ok::<(), igraph::Error>(())
549 /// ```
550 ///
551 /// See also [`count_loops`](Self::count_loops), [`is_loop`](Self::is_loop)
552 /// and [`Graph::simplify`].
553 pub fn has_loop(&self) -> Result<bool> {
554 let mut res = false;
555 igraph_call!(igraph_has_loop(self, &mut res))?;
556 Ok(res)
557 }
558
559 /// Whether the graph has at least one multi-edge (two or more edges
560 /// with the same endpoints, and the same direction in directed graphs).
561 ///
562 /// The result is cached in the graph, so repeated calls are `O(1)`.
563 ///
564 /// Binds [`igraph_has_multiple`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_has_multiple).
565 /// Time complexity: `O(|E| d)`, `d` the average degree.
566 ///
567 /// # Examples
568 /// ```
569 /// use igraph::prelude::*;
570 /// let mut g = Graph::from_edges(&[(0, 1), (1, 0)], 2, true)?;
571 /// assert!(!g.has_multiple()?); // opposite directions
572 /// g.add_edge(0, 1)?;
573 /// assert!(g.has_multiple()?);
574 /// # Ok::<(), igraph::Error>(())
575 /// ```
576 pub fn has_multiple(&self) -> Result<bool> {
577 let mut res = false;
578 igraph_call!(igraph_has_multiple(self, &mut res))?;
579 Ok(res)
580 }
581
582 /// The number of self-loops in the graph.
583 ///
584 /// Binds [`igraph_count_loops`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_count_loops).
585 /// Time complexity: `O(|E|)`.
586 pub fn count_loops(&self) -> Result<usize> {
587 let mut res = 0;
588 igraph_call!(igraph_count_loops(self, &mut res))?;
589 Ok(res as usize)
590 }
591
592 /// For each selected edge, whether it is a self-loop.
593 ///
594 /// Binds [`igraph_is_loop`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_loop).
595 /// Time complexity: `O(e)`, the number of edges to check.
596 ///
597 /// # Examples
598 /// ```
599 /// use igraph::prelude::*;
600 /// let g = Graph::from_edges(&[(0, 0), (0, 1), (1, 1)], 2, false)?;
601 /// assert_eq!(g.is_loop(..)?, vec![true, false, true]);
602 /// assert_eq!(g.count_loops()?, 2);
603 /// # Ok::<(), igraph::Error>(())
604 /// ```
605 pub fn is_loop<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<Vec<bool>> {
606 let es = edges.into().to_raw()?;
607 let mut res = VectorBool::new();
608 igraph_call!(igraph_is_loop(self, &mut res, es.get()))?;
609 Ok(res.into())
610 }
611
612 /// For each selected edge, whether it is a multi-edge.
613 ///
614 /// Only the *second and further* occurrences of parallel edges are
615 /// flagged, so that deleting all the flagged edges yields a graph
616 /// without multi-edges (as [`Graph::simplify`] does). In undirected
617 /// graphs `(a, b)` and `(b, a)` are parallel; in directed ones they are not.
618 ///
619 /// Binds [`igraph_is_multiple`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_multiple).
620 /// Time complexity: `O(e d)`.
621 ///
622 /// # Examples
623 /// ```
624 /// use igraph::prelude::*;
625 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 1), (0, 1), (1, 0)], 3, true)?;
626 /// assert_eq!(g.is_multiple(..)?, vec![false, false, false, true, false]);
627 /// # Ok::<(), igraph::Error>(())
628 /// ```
629 pub fn is_multiple<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<Vec<bool>> {
630 let es = edges.into().to_raw()?;
631 let mut res = VectorBool::new();
632 igraph_call!(igraph_is_multiple(self, &mut res, es.get()))?;
633 Ok(res.into())
634 }
635
636 /// For each selected edge, whether it is *mutual*, i.e. whether the
637 /// reversed edge exists as well.
638 ///
639 /// `loops` decides whether directed self-loops count as mutual. In
640 /// undirected graphs every edge is mutual. Multiplicities are not
641 /// considered: two `(a, b)` edges and one `(b, a)` edge are all mutual.
642 ///
643 /// Binds [`igraph_is_mutual`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_mutual).
644 /// Time complexity: `O(n log d)`, `n` the number of edges to check.
645 ///
646 /// # Examples
647 /// ```
648 /// use igraph::prelude::*;
649 /// let g = Graph::from_edges(&[(0, 1), (1, 0), (1, 2), (2, 2)], 3, true)?;
650 /// assert_eq!(g.is_mutual(.., true)?, vec![true, true, false, true]);
651 /// assert_eq!(g.is_mutual(.., false)?, vec![true, true, false, false]);
652 /// # Ok::<(), igraph::Error>(())
653 /// ```
654 pub fn is_mutual<'a>(
655 &self,
656 edges: impl Into<EdgeSelector<'a>>,
657 loops: bool,
658 ) -> Result<Vec<bool>> {
659 let es = edges.into().to_raw()?;
660 let mut res = VectorBool::new();
661 igraph_call!(igraph_is_mutual(self, &mut res, es.get(), loops))?;
662 Ok(res.into())
663 }
664
665 /// Whether the graph has at least one mutual edge pair (see
666 /// [`is_mutual`](Self::is_mutual)).
667 ///
668 /// Undirected graphs have mutual edges exactly when they have edges. A
669 /// directed graph without mutual edges (and loops) is an *oriented graph*.
670 ///
671 /// Binds [`igraph_has_mutual`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_has_mutual).
672 /// Time complexity: `O(|E| log d)`.
673 ///
674 /// # Examples
675 /// ```
676 /// use igraph::prelude::*;
677 /// let oriented = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true)?;
678 /// assert!(!oriented.has_mutual(true)?);
679 /// let with_loop = Graph::from_edges(&[(0, 1), (1, 1)], 2, true)?;
680 /// assert!(with_loop.has_mutual(true)?); // the loop counts as mutual
681 /// assert!(!with_loop.has_mutual(false)?);
682 /// # Ok::<(), igraph::Error>(())
683 /// ```
684 pub fn has_mutual(&self, loops: bool) -> Result<bool> {
685 let mut res = false;
686 igraph_call!(igraph_has_mutual(self, &mut res, loops))?;
687 Ok(res)
688 }
689
690 /// Whether the graph is simple, i.e. it has no self-loops and no
691 /// multi-edges.
692 ///
693 /// With `directed = false`, edge directions are ignored, so a directed
694 /// graph with a mutual edge pair is considered non-simple. Ignored for
695 /// undirected graphs.
696 ///
697 /// Binds [`igraph_is_simple`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_simple).
698 /// Time complexity: `O(|V| + |E|)`.
699 ///
700 /// # Examples
701 /// ```
702 /// use igraph::prelude::*;
703 /// let g = Graph::from_edges(&[(0, 1), (1, 0)], 2, true)?;
704 /// assert!(g.is_simple(true)?);
705 /// assert!(!g.is_simple(false)?);
706 /// # Ok::<(), igraph::Error>(())
707 /// ```
708 pub fn is_simple(&self, directed: bool) -> Result<bool> {
709 let mut res = false;
710 igraph_call!(igraph_is_simple(self, &mut res, directed))?;
711 Ok(res)
712 }
713
714 /// Whether the graph is a tree.
715 ///
716 /// An undirected graph is a tree if it is connected and has no cycles.
717 /// For directed graphs `mode` selects the test: [`NeighborMode::Out`]
718 /// for out-trees (arborescences, edges pointing away from the root),
719 /// [`NeighborMode::In`] for in-trees, [`NeighborMode::All`] to ignore
720 /// directions. The null graph is *not* a tree.
721 ///
722 /// Binds [`igraph_is_tree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_tree).
723 /// Time complexity: `O(|V| + |E|)`.
724 ///
725 /// # Examples
726 /// ```
727 /// use igraph::prelude::*;
728 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (2, 3)], 4, true)?;
729 /// assert!(g.is_tree(NeighborMode::Out)?);
730 /// assert!(!g.is_tree(NeighborMode::In)?);
731 /// assert_eq!(g.tree_root(NeighborMode::Out)?, Some(0));
732 /// // A complete binary tree on 15 vertices.
733 /// assert!(Graph::kary_tree(15, 2, TreeMode::Out)?.is_tree(NeighborMode::Out)?);
734 /// # Ok::<(), igraph::Error>(())
735 /// ```
736 ///
737 /// See also [`Graph::kary_tree`] and [`Graph::tree_game`] to build trees,
738 /// and [`Graph::to_prufer`] to encode them.
739 pub fn is_tree(&self, mode: NeighborMode) -> Result<bool> {
740 let mut res = false;
741 igraph_call!(igraph_is_tree(self, &mut res, ptr::null_mut(), mode.into()))?;
742 Ok(res)
743 }
744
745 /// The root of the graph if it is a tree (see [`is_tree`](Self::is_tree)),
746 /// `None` otherwise.
747 ///
748 /// For out-trees (in-trees) the root is the unique vertex of zero in-degree
749 /// (out-degree); with [`NeighborMode::All`] or in undirected graphs any
750 /// vertex can be the root, and vertex 0 is returned.
751 ///
752 /// Binds [`igraph_is_tree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_tree).
753 pub fn tree_root(&self, mode: NeighborMode) -> Result<Option<VertexId>> {
754 let mut res = false;
755 let mut root = -1;
756 igraph_call!(igraph_is_tree(self, &mut res, &mut root, mode.into()))?;
757 Ok(res.then_some(root))
758 }
759
760 /// Whether the graph is a forest, i.e. every connected component is a
761 /// tree (equivalently, there are no undirected cycles).
762 ///
763 /// `mode` has the same meaning as in [`is_tree`](Self::is_tree). The
764 /// null graph *is* a forest. The result is cached for undirected tests.
765 ///
766 /// Binds [`igraph_is_forest`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_forest).
767 /// Time complexity: `O(|V| + |E|)`.
768 pub fn is_forest(&self, mode: NeighborMode) -> Result<bool> {
769 let mut res = false;
770 igraph_call!(igraph_is_forest(
771 self,
772 &mut res,
773 ptr::null_mut(),
774 mode.into()
775 ))?;
776 Ok(res)
777 }
778
779 /// The roots of the trees if the graph is a forest (see
780 /// [`is_forest`](Self::is_forest)), `None` otherwise.
781 ///
782 /// With [`NeighborMode::All`] or in undirected graphs, one vertex per
783 /// component is returned (the smallest id); for out- (in-) forests the
784 /// roots are the vertices of zero in- (out-) degree.
785 ///
786 /// Binds [`igraph_is_forest`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_forest).
787 ///
788 /// # Examples
789 /// ```
790 /// use igraph::prelude::*;
791 /// let g = Graph::from_edges(&[(0, 1), (2, 3), (2, 4)], 6, false)?;
792 /// assert_eq!(g.forest_roots(NeighborMode::All)?, Some(vec![0, 2, 5]));
793 /// # Ok::<(), igraph::Error>(())
794 /// ```
795 pub fn forest_roots(&self, mode: NeighborMode) -> Result<Option<Vec<VertexId>>> {
796 let mut res = false;
797 let mut roots = VectorInt::new();
798 igraph_call!(igraph_is_forest(self, &mut res, &mut roots, mode.into()))?;
799 Ok(res.then(|| roots.into()))
800 }
801
802 /// Whether the graph has no cycles, taking edge directions into account:
803 /// for directed graphs this is the same as being a DAG.
804 ///
805 /// Self-loops are cycles. The result is cached in the graph.
806 ///
807 /// Binds [`igraph_is_acyclic`](https://igraph.org/c/html/latest/igraph-Cycles.html#igraph_is_acyclic).
808 /// Time complexity: `O(|V| + |E|)`.
809 ///
810 /// # Examples
811 /// ```
812 /// use igraph::prelude::*;
813 /// let dag = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true)?;
814 /// assert!(dag.is_acyclic()?);
815 /// assert!(!dag.is_forest(NeighborMode::All)?); // but it has an undirected cycle
816 /// # Ok::<(), igraph::Error>(())
817 /// ```
818 ///
819 /// See also [`Graph::is_dag`] (false for undirected graphs),
820 /// [`Graph::topological_sorting`] and [`Graph::find_cycle`].
821 pub fn is_acyclic(&self) -> Result<bool> {
822 let mut res = false;
823 igraph_call!(igraph_is_acyclic(self, &mut res))?;
824 Ok(res)
825 }
826
827 /// The maximum degree among the selected vertices (0 if the selection
828 /// is empty).
829 ///
830 /// `mode` selects out-, in- or total degree (ignored for undirected
831 /// graphs); `loops` how self-loops are counted.
832 ///
833 /// Binds [`igraph_maxdegree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_maxdegree).
834 /// Time complexity: `O(v)` when loops are counted, `O(v d)` otherwise.
835 ///
836 /// # Errors
837 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) for invalid vertices.
838 ///
839 /// # Examples
840 /// ```
841 /// use igraph::prelude::*;
842 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (3, 0), (1, 1)], 4, true)?;
843 /// assert_eq!(g.maxdegree(.., NeighborMode::Out, Loops::Twice)?, 2);
844 /// assert_eq!(g.maxdegree(.., NeighborMode::All, Loops::Twice)?, 3);
845 /// assert_eq!(g.maxdegree(&[1], NeighborMode::All, Loops::Twice)?, 3); // loop counted twice
846 /// assert_eq!(g.maxdegree(&[1], NeighborMode::All, Loops::None)?, 1);
847 /// # Ok::<(), igraph::Error>(())
848 /// ```
849 ///
850 /// See also [`Graph::degree`] for the individual degrees.
851 pub fn maxdegree<'a>(
852 &self,
853 vertices: impl Into<VertexSelector<'a>>,
854 mode: NeighborMode,
855 loops: Loops,
856 ) -> Result<i64> {
857 let vs = vertices.into().to_raw()?;
858 let mut res = 0;
859 igraph_call!(igraph_maxdegree(
860 self,
861 &mut res,
862 vs.get(),
863 mode.into(),
864 loops.into()
865 ))?;
866 Ok(res)
867 }
868
869 /// The mean degree of the graph.
870 ///
871 /// In directed graphs the mean out-degree equals the mean in-degree, and
872 /// that is what is returned (i.e. `|E| / |V|`); in undirected graphs it is
873 /// `2|E| / |V|`. With `loops = false`, self-loops are ignored. For the
874 /// null graph the result is NaN.
875 ///
876 /// Binds [`igraph_mean_degree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_mean_degree).
877 /// Time complexity: `O(1)` with loops, `O(|E|)` without.
878 ///
879 /// # Examples
880 /// ```
881 /// use igraph::prelude::*;
882 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 2)], 3, false)?;
883 /// assert_eq!(g.mean_degree(true)?, 2.0);
884 /// assert!((g.mean_degree(false)? - 4.0 / 3.0).abs() < 1e-12);
885 /// assert!(Graph::new(0, false).mean_degree(true)?.is_nan());
886 /// # Ok::<(), igraph::Error>(())
887 /// ```
888 pub fn mean_degree(&self, loops: bool) -> Result<f64> {
889 let mut res = 0.0;
890 igraph_call!(igraph_mean_degree(self, &mut res, loops))?;
891 Ok(res)
892 }
893
894 /// The reciprocity of a directed graph.
895 ///
896 /// With [`Reciprocity::Default`] it is the probability that the reverse
897 /// of a randomly chosen edge is also in the graph,
898 /// `1 - Σ_ij |A_ij - A_ji| / (2 Σ_ij A_ij)` (in multigraphs each parallel
899 /// edge needs its own reverse). With [`Reciprocity::Ratio`] it is the
900 /// number of mutually connected (unordered) vertex pairs divided by the
901 /// number of connected pairs.
902 ///
903 /// `ignore_loops` excludes self-loops from the count; otherwise they
904 /// count as mutual. Undirected graphs always give 1; directed graphs
905 /// without edges give NaN.
906 ///
907 /// Binds [`igraph_reciprocity`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_reciprocity).
908 /// Time complexity: `O(|V| + |E|)`.
909 ///
910 /// # Examples
911 /// ```
912 /// use igraph::prelude::*;
913 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 1)], 3, true)?;
914 /// assert!((g.reciprocity(false, Reciprocity::Default)? - 2.0 / 3.0).abs() < 1e-15);
915 /// assert_eq!(g.reciprocity(false, Reciprocity::Ratio)?, 0.5);
916 /// # Ok::<(), igraph::Error>(())
917 /// ```
918 ///
919 /// See also [`is_mutual`](Self::is_mutual) for the individual edges.
920 pub fn reciprocity(&self, ignore_loops: bool, mode: Reciprocity) -> Result<f64> {
921 let mut res = 0.0;
922 igraph_call!(igraph_reciprocity(
923 self,
924 &mut res,
925 ignore_loops,
926 mode.into()
927 ))?;
928 Ok(res)
929 }
930
931 /// The strength (weighted degree) of the selected vertices: the sum of
932 /// the weights of the incident edges.
933 ///
934 /// Without weights this is the ordinary degree (as `f64`). `mode` and
935 /// `loops` are as in [`degree`](Graph::degree).
936 ///
937 /// Binds [`igraph_strength`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_strength).
938 /// Time complexity: `O(|V| + |E|)`.
939 ///
940 /// # Examples
941 /// ```
942 /// use igraph::prelude::*;
943 /// let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, true)?;
944 /// let w = [1.5, 2.0, 4.0];
945 /// assert_eq!(g.strength(.., NeighborMode::Out, Loops::Twice, Some(&w))?, vec![3.5, 4.0, 0.0]);
946 /// assert_eq!(g.strength(.., NeighborMode::In, Loops::Twice, Some(&w))?, vec![0.0, 1.5, 6.0]);
947 /// assert_eq!(g.strength(.., NeighborMode::All, Loops::Twice, None)?, vec![2.0, 2.0, 2.0]);
948 /// # Ok::<(), igraph::Error>(())
949 /// ```
950 pub fn strength<'a>(
951 &self,
952 vertices: impl Into<VertexSelector<'a>>,
953 mode: NeighborMode,
954 loops: Loops,
955 weights: Option<&[f64]>,
956 ) -> Result<Vec<f64>> {
957 check_weights(self, weights)?;
958 let vs = vertices.into().to_raw()?;
959 let w = weights.map(Vector::view);
960 let mut res = Vector::new();
961 igraph_call!(igraph_strength(
962 self,
963 &mut res,
964 vs.get(),
965 mode.into(),
966 loops.into(),
967 weights_ptr(&w)
968 ))?;
969 Ok(res.into())
970 }
971
972 /// The selected vertices sorted by degree.
973 ///
974 /// `mode` and `loops` define the degree, `order` the sort direction.
975 /// With `only_indices = true` the result contains positions within the
976 /// selection instead of vertex ids (this makes no difference when all
977 /// vertices are selected).
978 ///
979 /// Binds [`igraph_sort_vertex_ids_by_degree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_sort_vertex_ids_by_degree).
980 ///
981 /// # Examples
982 /// ```
983 /// use igraph::prelude::*;
984 /// // A star with center 2.
985 /// let g = Graph::from_edges(&[(2, 0), (2, 1), (2, 3), (3, 4)], 5, false)?;
986 /// let hubs = g.sort_vertex_ids_by_degree(.., NeighborMode::All, Loops::Twice, Order::Descending, false)?;
987 /// assert_eq!(hubs[0], 2);
988 /// assert_eq!(hubs[1], 3);
989 /// # Ok::<(), igraph::Error>(())
990 /// ```
991 ///
992 /// Sorting by increasing degree gives a natural vertex order for
993 /// [`rich_club_sequence`](Self::rich_club_sequence).
994 pub fn sort_vertex_ids_by_degree<'a>(
995 &self,
996 vertices: impl Into<VertexSelector<'a>>,
997 mode: NeighborMode,
998 loops: Loops,
999 order: Order,
1000 only_indices: bool,
1001 ) -> Result<Vec<i64>> {
1002 let vs = vertices.into().to_raw()?;
1003 let mut res = VectorInt::new();
1004 igraph_call!(igraph_sort_vertex_ids_by_degree(
1005 self,
1006 &mut res,
1007 vs.get(),
1008 mode.into(),
1009 loops.into(),
1010 order.into(),
1011 only_indices
1012 ))?;
1013 Ok(res.into())
1014 }
1015
1016 /// Whether the graph is *perfect*: the chromatic number of every induced
1017 /// subgraph equals the size of its largest clique.
1018 ///
1019 /// The check is based on the strong perfect graph theorem (Chudnovsky,
1020 /// Robertson, Seymour and Thomas). It may build the complement graph,
1021 /// consuming a lot of memory on large graphs. Worst-case exponential.
1022 ///
1023 /// Binds [`igraph_is_perfect`](https://igraph.org/c/html/latest/igraph-Coloring.html#igraph_is_perfect).
1024 ///
1025 /// # Errors
1026 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if the graph
1027 /// is directed or not simple.
1028 ///
1029 /// # Examples
1030 /// ```
1031 /// use igraph::prelude::*;
1032 /// let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false)?;
1033 /// assert!(!c5.is_perfect()?); // an odd hole
1034 /// let c6 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], 6, false)?;
1035 /// assert!(c6.is_perfect()?); // bipartite
1036 /// assert!(!Graph::famous("Chvatal")?.is_perfect()?);
1037 /// # Ok::<(), igraph::Error>(())
1038 /// ```
1039 ///
1040 /// See also [`is_chordal`](Self::is_chordal) (chordal graphs are perfect)
1041 /// and [`Graph::is_bipartite`] (so are bipartite graphs); for perfect
1042 /// graphs [`Graph::clique_number`] equals the chromatic number.
1043 pub fn is_perfect(&self) -> Result<bool> {
1044 let mut res = false;
1045 igraph_call!(igraph_is_perfect(self, &mut res))?;
1046 Ok(res)
1047 }
1048
1049 // ------------------------------------------------------------------
1050 // Structural properties
1051 // ------------------------------------------------------------------
1052
1053 /// Whether the graph is complete: all pairs of distinct vertices are
1054 /// adjacent (in both directions, for directed graphs).
1055 ///
1056 /// Self-loops and multi-edges are ignored. The null graph and the
1057 /// singleton graph are complete.
1058 ///
1059 /// Binds [`igraph_is_complete`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_is_complete).
1060 /// Time complexity: `O(|V| + |E|)`.
1061 ///
1062 /// # Examples
1063 /// ```
1064 /// use igraph::prelude::*;
1065 /// assert!(Graph::full(5, true, false)?.is_complete()?);
1066 /// let one_way = Graph::from_edges(&[(0, 1)], 2, true)?;
1067 /// assert!(!one_way.is_complete()?); // the edge 1 -> 0 is missing
1068 /// # Ok::<(), igraph::Error>(())
1069 /// ```
1070 pub fn is_complete(&self) -> Result<bool> {
1071 let mut res = false;
1072 igraph_call!(igraph_is_complete(self, &mut res))?;
1073 Ok(res)
1074 }
1075
1076 /// Whether the candidate vertices form a clique (all pairs adjacent).
1077 ///
1078 /// With `directed = true` in a directed graph, both directions are
1079 /// required between every pair. Empty and singleton sets are cliques.
1080 ///
1081 /// Binds [`igraph_is_clique`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_is_clique).
1082 /// Time complexity: `O(n² log d)`, `n` the size of the candidate set.
1083 ///
1084 /// # Examples
1085 /// ```
1086 /// use igraph::prelude::*;
1087 /// let karate = Graph::famous("Zachary")?;
1088 /// assert!(karate.is_clique(&[0, 1, 2, 3, 7], false)?);
1089 /// // A largest clique found by the cliques module is, of course, a clique.
1090 /// let largest = &karate.largest_cliques()?[0];
1091 /// assert!(karate.is_clique(largest, false)?);
1092 /// # Ok::<(), igraph::Error>(())
1093 /// ```
1094 ///
1095 /// See also [`Graph::maximal_cliques`] and [`Graph::largest_cliques`] to
1096 /// find cliques.
1097 pub fn is_clique<'a>(
1098 &self,
1099 candidate: impl Into<VertexSelector<'a>>,
1100 directed: bool,
1101 ) -> Result<bool> {
1102 let vs = candidate.into().to_raw()?;
1103 let mut res = false;
1104 igraph_call!(igraph_is_clique(self, vs.get(), directed, &mut res))?;
1105 Ok(res)
1106 }
1107
1108 /// Whether the candidate vertices form an independent set (no pair is
1109 /// adjacent). Empty and singleton sets are independent.
1110 ///
1111 /// Self-loops are ignored: a vertex with a loop can still be part of an
1112 /// independent set.
1113 ///
1114 /// Binds [`igraph_is_independent_vertex_set`](https://igraph.org/c/html/latest/igraph-Cliques.html#igraph_is_independent_vertex_set).
1115 /// Time complexity: `O(n² log d)`.
1116 ///
1117 /// # Examples
1118 /// ```
1119 /// use igraph::prelude::*;
1120 /// let c6 = Graph::ring(6, false, false, true)?;
1121 /// assert!(c6.is_independent_vertex_set(&[0, 2, 4])?);
1122 /// assert!(!c6.is_independent_vertex_set(&[0, 1])?);
1123 /// # Ok::<(), igraph::Error>(())
1124 /// ```
1125 ///
1126 /// See also [`Graph::independence_number`] and
1127 /// [`Graph::largest_independent_vertex_sets`].
1128 pub fn is_independent_vertex_set<'a>(
1129 &self,
1130 candidate: impl Into<VertexSelector<'a>>,
1131 ) -> Result<bool> {
1132 let vs = candidate.into().to_raw()?;
1133 let mut res = false;
1134 igraph_call!(igraph_is_independent_vertex_set(self, vs.get(), &mut res))?;
1135 Ok(res)
1136 }
1137
1138 /// The edge ids of a minimum weight spanning tree (a spanning forest if
1139 /// the graph is disconnected).
1140 ///
1141 /// Edge directions are ignored. Without `weights` (or with
1142 /// [`MstAlgorithm::Unweighted`]) an arbitrary spanning tree is returned.
1143 /// The result is deterministic. To get a *maximum* spanning tree, negate
1144 /// the weights. Weights must not be NaN.
1145 ///
1146 /// Binds [`igraph_minimum_spanning_tree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_minimum_spanning_tree).
1147 ///
1148 /// # Examples
1149 /// ```
1150 /// use igraph::prelude::*;
1151 /// // A square with a heavy edge 3-0 and a light diagonal.
1152 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false)?;
1153 /// let w = [1.0, 2.0, 1.0, 9.0, 0.5];
1154 /// let mut tree = g.minimum_spanning_tree(Some(&w), MstAlgorithm::Kruskal)?;
1155 /// tree.sort();
1156 /// assert_eq!(tree, vec![0, 2, 4]);
1157 /// // Keep only the tree edges (and all the vertices).
1158 /// let t = g.subgraph_from_edges(&tree, false)?;
1159 /// assert!(t.is_tree(NeighborMode::All)?);
1160 /// # Ok::<(), igraph::Error>(())
1161 /// ```
1162 ///
1163 /// See also [`random_spanning_tree`](Self::random_spanning_tree) and
1164 /// [`Graph::subgraph_from_edges`] to turn the edge ids into a graph.
1165 pub fn minimum_spanning_tree(
1166 &self,
1167 weights: Option<&[f64]>,
1168 method: MstAlgorithm,
1169 ) -> Result<Vec<EdgeId>> {
1170 check_weights(self, weights)?;
1171 let w = weights.map(Vector::view);
1172 let mut res = VectorInt::new();
1173 igraph_call!(igraph_minimum_spanning_tree(
1174 self,
1175 &mut res,
1176 weights_ptr(&w),
1177 method.into()
1178 ))?;
1179 Ok(res.into())
1180 }
1181
1182 /// The edge ids of a spanning tree sampled uniformly at random, via a
1183 /// loop-erased random walk (Wilson's algorithm).
1184 ///
1185 /// Edge directions are ignored; edge multiplicities affect the sampling
1186 /// frequency. With `vertex = Some(v)` only the component of `v` is
1187 /// spanned; with `None`, a random spanning forest of all components is
1188 /// generated. Draws from the calling thread's default random number
1189 /// generator: seed it with [`rng::seed`](crate::rng::seed) for
1190 /// reproducible results (each thread has its own generator).
1191 ///
1192 /// The number of distinct spanning trees of a connected graph is given by
1193 /// Kirchhoff's matrix-tree theorem from the eigenvalues of
1194 /// [`get_laplacian`](Self::get_laplacian): it is the product of the
1195 /// non-zero ones divided by the number of vertices.
1196 ///
1197 /// # Errors
1198 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) if
1199 /// `vertex` is not a valid vertex id.
1200 ///
1201 /// Binds [`igraph_random_spanning_tree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_random_spanning_tree).
1202 ///
1203 /// # Examples
1204 /// ```
1205 /// use igraph::prelude::*;
1206 /// rng::seed(7)?;
1207 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4)], 5, false)?;
1208 /// assert_eq!(g.random_spanning_tree(None)?.len(), 3); // forest: 2 + 1 edges
1209 /// assert_eq!(g.random_spanning_tree(Some(3))?.len(), 1); // only 3-4
1210 ///
1211 /// // Same seed, same tree.
1212 /// rng::seed(7)?;
1213 /// let a = g.random_spanning_tree(None)?;
1214 /// rng::seed(7)?;
1215 /// assert_eq!(g.random_spanning_tree(None)?, a);
1216 /// # Ok::<(), igraph::Error>(())
1217 /// ```
1218 pub fn random_spanning_tree(&self, vertex: Option<VertexId>) -> Result<Vec<EdgeId>> {
1219 if let Some(v) = vertex.filter(|&v| v < 0) {
1220 return Err(Error::new(
1221 crate::ErrorKind::InvalidVertexId,
1222 format!("invalid vertex id {v}"),
1223 ));
1224 }
1225 let mut res = VectorInt::new();
1226 igraph_call!(igraph_random_spanning_tree(
1227 self,
1228 &mut res,
1229 vertex.unwrap_or(-1)
1230 ))?;
1231 Ok(res.into())
1232 }
1233
1234 /// The vertices reachable from `vertex` (itself included).
1235 ///
1236 /// [`NeighborMode::Out`] follows edges, [`NeighborMode::In`] follows them
1237 /// backwards, [`NeighborMode::All`] ignores directions (which is *not*
1238 /// the union of the other two). In undirected graphs this is the
1239 /// connected component of `vertex`. The vertices are listed in BFS order,
1240 /// starting with `vertex`.
1241 ///
1242 /// Binds [`igraph_subcomponent`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_subcomponent).
1243 /// Time complexity: `O(|V| + |E|)`.
1244 ///
1245 /// # Examples
1246 /// ```
1247 /// use igraph::prelude::*;
1248 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (3, 1)], 4, true)?;
1249 /// assert_eq!(g.subcomponent(1, NeighborMode::Out)?, vec![1, 2]);
1250 /// let mut up = g.subcomponent(1, NeighborMode::In)?;
1251 /// up.sort();
1252 /// assert_eq!(up, vec![0, 1, 3]);
1253 /// # Ok::<(), igraph::Error>(())
1254 /// ```
1255 ///
1256 /// # Errors
1257 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) for
1258 /// an invalid `vertex`.
1259 ///
1260 /// See also [`Graph::connected_components`] (all components at once),
1261 /// [`Graph::count_reachable`] and [`Graph::bfs`].
1262 pub fn subcomponent(&self, vertex: VertexId, mode: NeighborMode) -> Result<Vec<VertexId>> {
1263 let mut res = VectorInt::new();
1264 igraph_call!(igraph_subcomponent(self, &mut res, vertex, mode.into()))?;
1265 Ok(res.into())
1266 }
1267
1268 /// Unfolds the graph into a tree (or forest) by a breadth-first search
1269 /// from `roots`, replicating vertices each time they are reached again.
1270 ///
1271 /// Every edge of the graph appears exactly once in the result: non-tree
1272 /// edges lead to fresh copies of their endpoints. `mode` controls which
1273 /// edges the search follows in directed graphs. Give one root per
1274 /// component to unfold every component.
1275 ///
1276 /// Binds [`igraph_unfold_tree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_unfold_tree).
1277 /// Time complexity: `O(|V| + |E|)`.
1278 ///
1279 /// # Examples
1280 /// ```
1281 /// use igraph::prelude::*;
1282 /// // Unfolding a triangle from vertex 0: the closing edge copies a vertex.
1283 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
1284 /// let unfolded = g.unfold_tree(NeighborMode::All, &[0])?;
1285 /// assert_eq!((unfolded.tree.vcount(), unfolded.tree.ecount()), (4, 3));
1286 /// assert!(unfolded.tree.is_tree(NeighborMode::All)?);
1287 /// assert_eq!(&unfolded.vertex_index[..3], &[0, 1, 2]);
1288 /// # Ok::<(), igraph::Error>(())
1289 /// ```
1290 ///
1291 /// # Errors
1292 /// [`ErrorKind::InvalidVertexId`](crate::ErrorKind::InvalidVertexId) for
1293 /// an invalid root.
1294 pub fn unfold_tree(&self, mode: NeighborMode, roots: &[VertexId]) -> Result<UnfoldedTree> {
1295 let r = VectorInt::view(roots);
1296 let mut index = VectorInt::new();
1297 let tree = Graph::init_with(|t| unsafe {
1298 igraph_unfold_tree(self, t, mode.into(), r.as_ptr(), &mut index)
1299 })?;
1300 Ok(UnfoldedTree {
1301 tree,
1302 vertex_index: index.into(),
1303 })
1304 }
1305
1306 /// Maximum cardinality search (Tarjan and Yannakakis, 1984).
1307 ///
1308 /// Assigns a rank to every vertex so that visiting vertices by
1309 /// decreasing rank always picks the one with the most already visited
1310 /// neighbors. A graph is chordal iff any two higher-ranked neighbors of
1311 /// every vertex are adjacent. Edge directions are ignored.
1312 ///
1313 /// Mutual edges `u -> v`, `v -> u` of a directed graph count as a single
1314 /// undirected edge. igraph 1.0.1 can get this wrong when its property
1315 /// cache already records "no multi-edges" (a girth of 2, or heap
1316 /// corruption in the chordality code), so for directed graphs this
1317 /// wrapper clears the cache around the C call.
1318 ///
1319 /// Binds [`igraph_maximum_cardinality_search`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_maximum_cardinality_search).
1320 /// Time complexity: `O(|V| + |E|)`.
1321 ///
1322 /// # Examples
1323 /// ```
1324 /// use igraph::prelude::*;
1325 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false)?;
1326 /// let mcs = g.maximum_cardinality_search()?;
1327 /// for (v, &rank) in mcs.alpha.iter().enumerate() {
1328 /// assert_eq!(mcs.alpham1[rank as usize], v as i64);
1329 /// }
1330 /// // The ranks can be reused by the chordality test.
1331 /// let c = g.is_chordal_with(Some(&mcs.alpha), Some(&mcs.alpham1))?;
1332 /// assert!(c.is_chordal);
1333 /// # Ok::<(), igraph::Error>(())
1334 /// ```
1335 pub fn maximum_cardinality_search(&self) -> Result<CardinalitySearch> {
1336 let mut alpha = VectorInt::new();
1337 let mut alpham1 = VectorInt::new();
1338 with_multi_cache_guard(self, || {
1339 igraph_call!(igraph_maximum_cardinality_search(
1340 self,
1341 &mut alpha,
1342 &mut alpham1
1343 ))
1344 })?;
1345 Ok(CardinalitySearch {
1346 alpha: alpha.into(),
1347 alpham1: alpham1.into(),
1348 })
1349 }
1350
1351 /// Whether the graph is chordal: every cycle of four or more vertices
1352 /// has a chord (equivalently, every induced cycle is a triangle).
1353 ///
1354 /// Edge directions are ignored. See [`is_chordal_with`](Self::is_chordal_with)
1355 /// to also get the fill-in.
1356 ///
1357 /// Mutual edges `u -> v`, `v -> u` of a directed graph count as a single
1358 /// undirected edge. igraph 1.0.1 can get this wrong when its property
1359 /// cache already records "no multi-edges" (a girth of 2, or heap
1360 /// corruption in the chordality code), so for directed graphs this
1361 /// wrapper clears the cache around the C call.
1362 ///
1363 /// Binds [`igraph_is_chordal`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_chordal).
1364 /// Time complexity: linear.
1365 ///
1366 /// # Examples
1367 /// ```
1368 /// use igraph::prelude::*;
1369 /// let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false)?;
1370 /// assert!(!square.is_chordal()?);
1371 /// let c = square.is_chordal_with(None, None)?;
1372 /// assert_eq!(c.fill_in.len(), 1); // one diagonal suffices
1373 /// assert!(c.triangulated.is_chordal()?);
1374 /// # Ok::<(), igraph::Error>(())
1375 /// ```
1376 pub fn is_chordal(&self) -> Result<bool> {
1377 let mut res = false;
1378 with_multi_cache_guard(self, || {
1379 igraph_call!(igraph_is_chordal(
1380 self,
1381 ptr::null(),
1382 ptr::null(),
1383 &mut res,
1384 ptr::null_mut(),
1385 ptr::null_mut()
1386 ))
1387 })?;
1388 Ok(res)
1389 }
1390
1391 /// Chordality test that also returns the fill-in (chordal completion)
1392 /// and the triangulated graph.
1393 ///
1394 /// `alpha` and/or `alpham1` may be supplied from a previous
1395 /// [`maximum_cardinality_search`](Self::maximum_cardinality_search) on
1396 /// the same graph (if only one is given, the other is its inverse);
1397 /// with `None`, the search is performed internally. The fill-in is not
1398 /// necessarily minimal.
1399 ///
1400 /// Binds [`igraph_is_chordal`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_is_chordal).
1401 ///
1402 /// igraph 1.0.0 and 1.0.1 only check the lengths of `alpha` and `alpham1`
1403 /// and then index with their values: this wrapper also checks that they
1404 /// are permutations, so that bad input can never cause out-of-bounds reads.
1405 ///
1406 /// # Errors
1407 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if `alpha`
1408 /// or `alpham1` is not a permutation of `0..vcount`, or if both are given
1409 /// and they are not inverse of each other.
1410 ///
1411 /// # Examples
1412 /// ```
1413 /// use igraph::prelude::*;
1414 /// // A 5-cycle needs two chords to become chordal.
1415 /// let c5 = Graph::ring(5, false, false, true)?;
1416 /// let c = c5.is_chordal_with(None, None)?;
1417 /// assert!(!c.is_chordal);
1418 /// assert_eq!(c.fill_in.len(), 2);
1419 /// assert_eq!(c.triangulated.ecount(), 7);
1420 /// assert!(c.triangulated.is_chordal()?);
1421 /// # Ok::<(), igraph::Error>(())
1422 /// ```
1423 pub fn is_chordal_with(
1424 &self,
1425 alpha: Option<&[i64]>,
1426 alpham1: Option<&[VertexId]>,
1427 ) -> Result<Chordality> {
1428 // igraph 1.0.0 and 1.0.1 only check the lengths and then index with
1429 // the values: validate them here so that bad input can never read
1430 // out of bounds.
1431 let n = self.vcount();
1432 for (name, perm) in [("alpha", alpha), ("alpham1", alpham1)] {
1433 if let Some(p) = perm
1434 && !is_permutation(p, n)
1435 {
1436 return Err(Error::invalid(format!(
1437 "{name} must be a permutation of 0..{n}"
1438 )));
1439 }
1440 }
1441 if let (Some(a), Some(am1)) = (alpha, alpham1)
1442 && a.iter()
1443 .enumerate()
1444 .any(|(v, &r)| am1[r as usize] != v as i64)
1445 {
1446 return Err(Error::invalid("alpham1 must be the inverse of alpha"));
1447 }
1448 let a = alpha.map(VectorInt::view);
1449 let am1 = alpham1.map(VectorInt::view);
1450 let ap = a.as_ref().map_or(ptr::null(), |v| v.as_ptr());
1451 let am1p = am1.as_ref().map_or(ptr::null(), |v| v.as_ptr());
1452 let mut chordal = false;
1453 let mut fill_in = VectorInt::new();
1454 let triangulated = with_multi_cache_guard(self, || {
1455 Graph::init_with(|t| unsafe {
1456 igraph_is_chordal(self, ap, am1p, &mut chordal, &mut fill_in, t)
1457 })
1458 })?;
1459 Ok(Chordality {
1460 is_chordal: chordal,
1461 fill_in: fill_in
1462 .as_chunks::<2>()
1463 .0
1464 .iter()
1465 .map(|&[a, b]| (a, b))
1466 .collect(),
1467 triangulated,
1468 })
1469 }
1470
1471 /// Average degree of the neighbors of each selected vertex (`knn`), and
1472 /// its average as a function of the vertex degree (`knnk`).
1473 ///
1474 /// `mode` chooses which neighbors are considered and
1475 /// `neighbor_degree_mode` which of their degrees is averaged (both
1476 /// ignored for undirected graphs). With `weights`, the weighted average
1477 /// `k_nn,u = 1/s_u Σ_v w_uv k_v` of Barrat et al. (PNAS 2004) is computed,
1478 /// `s_u` being the strength of `u`, and `knnk` averages `knn` weighted by
1479 /// the strengths. Isolated vertices (with weights: vertices of zero
1480 /// strength) get NaN, as do degrees that don't occur in `knnk`.
1481 ///
1482 /// Binds [`igraph_avg_nearest_neighbor_degree`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_avg_nearest_neighbor_degree).
1483 /// Time complexity: `O(|V| + |E|)`.
1484 ///
1485 /// # Examples
1486 /// ```
1487 /// use igraph::prelude::*;
1488 /// // In a star, the center's neighbors have degree 1, the leaves' neighbor degree 3.
1489 /// let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
1490 /// let nd = star.avg_nearest_neighbor_degree(.., NeighborMode::All, NeighborMode::All, None)?;
1491 /// assert_eq!(nd.knn, vec![1.0, 3.0, 3.0, 3.0]);
1492 /// assert_eq!(nd.knnk[0], 3.0); // degree-1 vertices
1493 /// assert_eq!(nd.knnk[2], 1.0); // degree-3 vertices
1494 /// # Ok::<(), igraph::Error>(())
1495 /// ```
1496 ///
1497 /// See also [`degree_correlation_vector`](Self::degree_correlation_vector)
1498 /// (the same `k_nn(k)`, averaged over edges, with more mode choices) and
1499 /// [`Graph::assortativity_degree`], which summarizes degree correlations
1500 /// in a single number: a decreasing `k_nn(k)` goes with a negative
1501 /// assortativity.
1502 pub fn avg_nearest_neighbor_degree<'a>(
1503 &self,
1504 vertices: impl Into<VertexSelector<'a>>,
1505 mode: NeighborMode,
1506 neighbor_degree_mode: NeighborMode,
1507 weights: Option<&[f64]>,
1508 ) -> Result<NeighborDegree> {
1509 check_weights(self, weights)?;
1510 let vs = vertices.into().to_raw()?;
1511 let w = weights.map(Vector::view);
1512 let mut knn = Vector::new();
1513 let mut knnk = Vector::new();
1514 igraph_call!(igraph_avg_nearest_neighbor_degree(
1515 self,
1516 vs.get(),
1517 mode.into(),
1518 neighbor_degree_mode.into(),
1519 &mut knn,
1520 &mut knnk,
1521 weights_ptr(&w)
1522 ))?;
1523 Ok(NeighborDegree {
1524 knn: knn.into(),
1525 knnk: knnk.into(),
1526 })
1527 }
1528
1529 /// The degree correlation function `k_nn(k)`: `result[k]` is the mean
1530 /// degree of the targets of edges whose source has degree `k` (NaN for
1531 /// degrees that don't occur). Unlike
1532 /// [`avg_nearest_neighbor_degree`](Self::avg_nearest_neighbor_degree),
1533 /// index 0 is for degree 0.
1534 ///
1535 /// The average is over all directed edges; undirected edges count as two
1536 /// reciprocal directed ones. `from_mode` and `to_mode` define the degree
1537 /// of sources and targets (out-in, out-out, in-in, in-out correlations).
1538 /// With `directed_neighbors = false`, directed edges are also treated as
1539 /// reciprocal pairs. With `weights`, weighted averages are computed.
1540 ///
1541 /// Binds [`igraph_degree_correlation_vector`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_degree_correlation_vector).
1542 /// Time complexity: `O(|V| + |E|)`.
1543 ///
1544 /// # Examples
1545 /// ```
1546 /// use igraph::prelude::*;
1547 /// let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
1548 /// let knnk = star.degree_correlation_vector(None, NeighborMode::All, NeighborMode::All, true)?;
1549 /// assert_eq!(knnk[1], 3.0);
1550 /// assert_eq!(knnk[3], 1.0);
1551 /// assert!(knnk[0].is_nan() && knnk[2].is_nan());
1552 /// # Ok::<(), igraph::Error>(())
1553 /// ```
1554 ///
1555 /// See also [`Graph::joint_degree_matrix`], from which `k_nn(k)` can be
1556 /// derived, and [`Graph::assortativity_degree`].
1557 pub fn degree_correlation_vector(
1558 &self,
1559 weights: Option<&[f64]>,
1560 from_mode: NeighborMode,
1561 to_mode: NeighborMode,
1562 directed_neighbors: bool,
1563 ) -> Result<Vec<f64>> {
1564 check_weights(self, weights)?;
1565 let w = weights.map(Vector::view);
1566 let mut res = Vector::new();
1567 igraph_call!(igraph_degree_correlation_vector(
1568 self,
1569 weights_ptr(&w),
1570 &mut res,
1571 from_mode.into(),
1572 to_mode.into(),
1573 directed_neighbors
1574 ))?;
1575 Ok(res.into())
1576 }
1577
1578 /// Density of the subgraphs left after removing vertices one by one in
1579 /// the given order (the *rich-club* sequence).
1580 ///
1581 /// `result[i]` is the density of the graph remaining after the first
1582 /// `i` vertices of `vertex_order` have been removed (so `result[0]` is the
1583 /// density of the whole graph). With `normalized = false` the remaining
1584 /// edge count (or total weight) is returned instead. `loops` decides
1585 /// whether self-loops are possible when computing the maximum number of
1586 /// edges (see [`density`](Self::density)); `directed = false` treats a
1587 /// directed graph as undirected. Removing vertices by increasing degree
1588 /// reveals whether high-degree vertices are densely interconnected.
1589 ///
1590 /// `loops` is ignored when `normalized` is `false`. If `loops` is `false`
1591 /// but the graph has self-loops, igraph emits a warning and still divides
1592 /// by the loop-free maximum, so densities may exceed 1.
1593 ///
1594 /// This function is marked *experimental* in igraph 1.0.0 and 1.0.1.
1595 ///
1596 /// Binds [`igraph_rich_club_sequence`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_rich_club_sequence).
1597 /// Time complexity: `O(|V| + |E|)`.
1598 ///
1599 /// # Errors
1600 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) if
1601 /// `vertex_order` is not a permutation of the vertex ids (wrong length,
1602 /// out-of-range or repeated entries) or the weights have the wrong length.
1603 ///
1604 /// # Examples
1605 /// ```
1606 /// use igraph::prelude::*;
1607 /// // A triangle with a pendant vertex 3 attached to 0; peel off the pendant first.
1608 /// let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (0, 3)], 4, false)?;
1609 /// let seq = g.rich_club_sequence(None, &[3, 0, 1, 2], true, false, false)?;
1610 /// assert!((seq[0] - 4.0 / 6.0).abs() < 1e-12);
1611 /// assert_eq!(seq[1], 1.0); // the triangle is a clique
1612 /// # Ok::<(), igraph::Error>(())
1613 /// ```
1614 ///
1615 /// A natural order removes vertices by increasing degree, see
1616 /// [`sort_vertex_ids_by_degree`](Self::sort_vertex_ids_by_degree).
1617 pub fn rich_club_sequence(
1618 &self,
1619 weights: Option<&[f64]>,
1620 vertex_order: &[VertexId],
1621 normalized: bool,
1622 loops: bool,
1623 directed: bool,
1624 ) -> Result<Vec<f64>> {
1625 check_weights(self, weights)?;
1626 let w = weights.map(Vector::view);
1627 let order = VectorInt::view(vertex_order);
1628 let mut res = Vector::new();
1629 igraph_call!(igraph_rich_club_sequence(
1630 self,
1631 weights_ptr(&w),
1632 &mut res,
1633 order.as_ptr(),
1634 normalized,
1635 loops,
1636 directed
1637 ))?;
1638 Ok(res.into())
1639 }
1640
1641 // ------------------------------------------------------------------
1642 // Spectral properties
1643 // ------------------------------------------------------------------
1644
1645 /// The (dense) Laplacian matrix of the graph.
1646 ///
1647 /// `L_ij = -A_ij` for `i ≠ j` and `L_ii = d_i - A_ii`, where `A` is the
1648 /// (possibly weighted) adjacency matrix and `d_i` the degree (strength).
1649 /// In directed graphs `mode` selects out-degrees (rows sum to zero),
1650 /// in-degrees (columns sum to zero), or ignores directions
1651 /// ([`NeighborMode::All`]). In undirected graphs `A_ii` is twice the
1652 /// number (weight) of self-loops. See [`LaplacianNormalization`] for the
1653 /// normalized variants. Weights must be non-negative and not NaN.
1654 ///
1655 /// For undirected graphs the unnormalized Laplacian is symmetric and
1656 /// positive semi-definite. Without weights (or with positive weights) the
1657 /// multiplicity of its zero eigenvalue is the number of connected
1658 /// components, and, without weights, (matrix-tree theorem) the number of
1659 /// spanning trees of a connected graph is the product of the non-zero
1660 /// eigenvalues divided by `|V|`.
1661 ///
1662 /// Binds [`igraph_get_laplacian`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_laplacian).
1663 /// Time complexity: `O(|V|²)`.
1664 ///
1665 /// # Errors
1666 /// [`ErrorKind::InvalidValue`](crate::ErrorKind::InvalidValue) for
1667 /// negative or NaN weights, a wrong number of weights, or a normalization
1668 /// that would divide by a zero degree of a non-isolated vertex (e.g.
1669 /// [`LaplacianNormalization::Symmetric`] with [`NeighborMode::Out`] and a
1670 /// vertex with in-edges only).
1671 ///
1672 /// # Examples
1673 /// ```
1674 /// use igraph::{prelude::*, structural::LaplacianNormalization};
1675 /// let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
1676 /// let l = path.get_laplacian(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
1677 /// assert_eq!(l.to_rows(), vec![
1678 /// vec![1.0, -1.0, 0.0],
1679 /// vec![-1.0, 2.0, -1.0],
1680 /// vec![0.0, -1.0, 1.0],
1681 /// ]);
1682 ///
1683 /// // Matrix-tree theorem: K4 has 4^(4-2) = 16 spanning trees.
1684 /// use igraph::linalg::{lapack_dsyevr, SymmetricRange};
1685 /// let k4 = Graph::full(4, false, false)?;
1686 /// let l = k4.get_laplacian(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
1687 /// let eigen = lapack_dsyevr(&l, &SymmetricRange::All, 1e-12)?;
1688 /// let trees: f64 = eigen.values[1..].iter().product::<f64>() / 4.0;
1689 /// assert!((trees - 16.0).abs() < 1e-9);
1690 /// # Ok::<(), igraph::Error>(())
1691 /// ```
1692 ///
1693 /// See also [`get_laplacian_sparse`](Self::get_laplacian_sparse),
1694 /// [`Graph::get_adjacency`], and [`Graph::laplacian_spectral_embedding`].
1695 pub fn get_laplacian(
1696 &self,
1697 mode: NeighborMode,
1698 normalization: LaplacianNormalization,
1699 weights: Option<&[f64]>,
1700 ) -> Result<Matrix> {
1701 check_weights(self, weights)?;
1702 let w = weights.map(Vector::view);
1703 let mut res = Matrix::new();
1704 igraph_call!(igraph_get_laplacian(
1705 self,
1706 &mut res,
1707 mode.into(),
1708 normalization.into(),
1709 weights_ptr(&w)
1710 ))?;
1711 Ok(res)
1712 }
1713
1714 /// The Laplacian matrix in sparse form, as sorted `(row, column, value)`
1715 /// triplets of its non-zero entries.
1716 ///
1717 /// Same definition as [`get_laplacian`](Self::get_laplacian); it takes
1718 /// `O(|V| + |E|)` time and memory, which makes it suitable for large
1719 /// sparse graphs. Duplicate entries produced by igraph (e.g. for
1720 /// multi-edges) are summed, and entries that sum to exactly zero are
1721 /// dropped. Use [`get_laplacian_sparsemat`](Self::get_laplacian_sparsemat)
1722 /// to get a [`SparseMat`] for the sparse linear algebra of
1723 /// [`linalg`](crate::linalg) instead.
1724 ///
1725 /// For directed graphs with [`NeighborMode::All`], the graph is treated
1726 /// as undirected, exactly like the dense variant does (the sparse C
1727 /// implementation of igraph 1.0.0 and 1.0.1 alone would not symmetrize
1728 /// the matrix).
1729 ///
1730 /// Binds [`igraph_get_laplacian_sparse`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_laplacian_sparse).
1731 ///
1732 /// # Errors
1733 /// As for [`get_laplacian`](Self::get_laplacian).
1734 ///
1735 /// # Examples
1736 /// ```
1737 /// use igraph::{prelude::*, structural::LaplacianNormalization};
1738 /// let g = Graph::from_edges(&[(0, 1)], 3, false)?;
1739 /// let l = g.get_laplacian_sparse(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
1740 /// assert_eq!(l, vec![(0, 0, 1.0), (0, 1, -1.0), (1, 0, -1.0), (1, 1, 1.0)]);
1741 /// # Ok::<(), igraph::Error>(())
1742 /// ```
1743 pub fn get_laplacian_sparse(
1744 &self,
1745 mode: NeighborMode,
1746 normalization: LaplacianNormalization,
1747 weights: Option<&[f64]>,
1748 ) -> Result<Vec<(VertexId, VertexId, f64)>> {
1749 let sparse = self.get_laplacian_sparsemat(mode, normalization, weights)?;
1750 if !sparse.is_triplet() {
1751 // igraph builds the matrix in triplet form; be defensive anyway.
1752 return Err(Error::new(
1753 crate::ErrorKind::Internal,
1754 "expected a sparse Laplacian in triplet form",
1755 ));
1756 }
1757 let el = sparse.getelements()?;
1758 let mut entries: BTreeMap<(VertexId, VertexId), f64> = BTreeMap::new();
1759 for ((&i, &j), &x) in el.i.iter().zip(&el.j).zip(&el.x) {
1760 *entries.entry((i, j)).or_insert(0.0) += x;
1761 }
1762 Ok(entries
1763 .into_iter()
1764 .filter(|&(_, x)| x != 0.0)
1765 .map(|((i, j), x)| (i, j, x))
1766 .collect())
1767 }
1768
1769 /// The Laplacian matrix as a [`SparseMat`] (in triplet form, possibly
1770 /// with duplicate entries, which sparse operations sum up).
1771 ///
1772 /// Same definition, and same handling of directed graphs with
1773 /// [`NeighborMode::All`], as [`get_laplacian_sparse`](Self::get_laplacian_sparse).
1774 /// The result can be fed to the sparse linear algebra of
1775 /// [`linalg`](crate::linalg), e.g. [`SparseMat::mul_vec`] or
1776 /// [`SparseMat::to_dense`].
1777 ///
1778 /// Binds [`igraph_get_laplacian_sparse`](https://igraph.org/c/html/latest/igraph-Structural.html#igraph_get_laplacian_sparse).
1779 ///
1780 /// # Errors
1781 /// As for [`get_laplacian`](Self::get_laplacian).
1782 ///
1783 /// # Examples
1784 /// ```
1785 /// use igraph::{prelude::*, structural::LaplacianNormalization};
1786 /// let path = Graph::ring(4, false, false, false)?;
1787 /// let l = path.get_laplacian_sparsemat(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
1788 /// // The Laplacian annihilates constant vectors...
1789 /// assert_eq!(l.mul_vec(&[1.0; 4])?, vec![0.0; 4]);
1790 /// // ...and x^T L x is the sum of (x_i - x_j)^2 over the edges.
1791 /// let x = [0.0, 1.0, 3.0, 6.0];
1792 /// let lx = l.mul_vec(&x)?;
1793 /// let quad: f64 = x.iter().zip(&lx).map(|(a, b)| a * b).sum();
1794 /// assert_eq!(quad, 1.0 + 4.0 + 9.0);
1795 /// # Ok::<(), igraph::Error>(())
1796 /// ```
1797 pub fn get_laplacian_sparsemat(
1798 &self,
1799 mode: NeighborMode,
1800 normalization: LaplacianNormalization,
1801 weights: Option<&[f64]>,
1802 ) -> Result<SparseMat> {
1803 check_weights(self, weights)?;
1804 // igraph 1.0.0 and 1.0.1's sparse variant does not symmetrize the
1805 // off-diagonal entries of directed graphs with `IGRAPH_ALL` (unlike
1806 // the dense one): ignore directions explicitly, keeping edge ids
1807 // (and weights).
1808 let undirected;
1809 let graph = if self.is_directed() && mode == NeighborMode::All {
1810 undirected = Graph::from_edges(&self.edge_list(), self.vcount(), false)?;
1811 &undirected
1812 } else {
1813 self
1814 };
1815 let w = weights.map(Vector::view);
1816 let n = self.vcount();
1817 let mut sparse = SparseMat::new(n, n)?;
1818 igraph_call!(igraph_get_laplacian_sparse(
1819 graph,
1820 &mut sparse,
1821 mode.into(),
1822 normalization.into(),
1823 weights_ptr(&w)
1824 ))?;
1825 Ok(sparse)
1826 }
1827}