Skip to main content

igraph_t

Struct igraph_t 

Source
pub struct igraph_t { /* private fields */ }

Implementations§

Source§

impl igraph_t

Source

pub fn setup()

Initializes igraph for the calling thread (see crate::error::ensure_init).

Calling it is optional: every safe wrapper does it automatically.

Source

pub fn init_with( f: impl FnOnce(*mut igraph_t) -> igraph_error_t, ) -> Result<Self>

Creates a graph by running an igraph constructor, i.e. a C function that initializes the igraph_t pointed to by its argument.

This is the building block of all the wrappers returning new graphs, and it is useful to call raw constructors not wrapped by this crate. f must either fail or fully initialize the graph; if it reports success without initializing it, ErrorKind::Internal is returned:

use igraph::{ffi, prelude::*};
let ring = Graph::init_with(|g| unsafe { ffi::igraph_ring(g, 5, false, false, true) }).unwrap();
assert_eq!(ring.ecount(), 5);
Source

pub fn new(num_vertices: usize, directed: bool) -> Self

Creates a graph with num_vertices isolated vertices (igraph_empty).

§Panics

If igraph fails to allocate the graph, like Vec does, or if num_vertices exceeds igraph’s maximum vertex count (see empty for the fallible version).

Source

pub fn empty(num_vertices: usize, directed: bool) -> Result<Self>

Creates a graph with num_vertices isolated vertices (igraph_empty).

§Errors

ErrorKind::InvalidValue if num_vertices does not fit in an i64; ErrorKind::Range if it exceeds igraph’s maximum vertex count, or ErrorKind::OutOfMemory.

Source

pub fn from_edges( edges: &[(VertexId, VertexId)], num_vertices: usize, directed: bool, ) -> Result<Self>

Creates a graph from a list of (from, to) edges (igraph_create).

The graph has max(num_vertices, largest id + 1) vertices; in an undirected graph (a, b) and (b, a) denote the same edge. Multi-edges and loops are kept; edge i is the i-th pair. See constructors for ready-made graphs, and Graph::read_graph_edgelist_from_str to parse an edge list.

use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 5, true).unwrap();
assert_eq!((g.vcount(), g.ecount()), (5, 2));
// The vertex count grows to fit the largest id.
let h = Graph::from_edges(&[(0, 7)], 0, false).unwrap();
assert_eq!(h.vcount(), 8);
§Errors

ErrorKind::InvalidVertexId for negative ids.

Source

pub fn from_flat_edges( edges: &[VertexId], num_vertices: usize, directed: bool, ) -> Result<Self>

Creates a graph from a flat edge list [from0, to0, from1, to1, ...] (igraph_create).

§Errors

ErrorKind::InvalidValue for an odd length or a num_vertices that does not fit in an i64, ErrorKind::InvalidVertexId for negative ids.

Source

pub fn vcount(&self) -> usize

Number of vertices (igraph_vcount), O(1).

Source

pub fn ecount(&self) -> usize

Number of edges (igraph_ecount), O(1).

Source

pub fn num_vertices(&self) -> usize

Number of vertices; alias of vcount.

Source

pub fn num_edges(&self) -> usize

Number of edges; alias of ecount.

Source

pub fn is_directed(&self) -> bool

Whether the graph is directed (igraph_is_directed).

Source

pub fn vertices(&self) -> Range<VertexId>

The range of all vertex ids, 0..vcount.

Source

pub fn edge_ids(&self) -> Range<EdgeId>

The range of all edge ids, 0..ecount.

Source

pub fn add_vertices(&mut self, n: usize) -> Result<()>

Adds n isolated vertices, with ids vcount..vcount + n (igraph_add_vertices).

§Errors

ErrorKind::InvalidValue if n does not fit in an i64; ErrorKind::Overflow or ErrorKind::Range if the new vertex count would overflow or exceed igraph’s maximum vertex count.

Source

pub fn add_edge(&mut self, from: VertexId, to: VertexId) -> Result<()>

Adds a single edge (igraph_add_edge); for many edges add_edges is much faster.

§Errors

ErrorKind::InvalidVertexId if an endpoint does not exist.

Source

pub fn add_edges(&mut self, edges: &[(VertexId, VertexId)]) -> Result<()>

Adds the given (from, to) edges, with ids ecount.. in order (igraph_add_edges).

§Errors

ErrorKind::InvalidVertexId if an endpoint does not exist (then no edge is added).

Source

pub fn add_edges_from_slice( &mut self, edges: &[(VertexId, VertexId)], ) -> Result<()>

Adds the given (from, to) edges; alias of add_edges.

Source

pub fn add_edges_from_vector(&mut self, edges: &[VertexId]) -> Result<()>

Adds the edges of a flat list [from0, to0, from1, to1, ...] (igraph_add_edges).

§Errors

ErrorKind::InvalidValue for an odd length, ErrorKind::InvalidVertexId for missing vertices.

Source

pub fn delete_edges<'a>( &mut self, edges: impl Into<EdgeSelector<'a>>, ) -> Result<()>

Removes the selected edges (igraph_delete_edges); the remaining edges keep their relative order and are renumbered to stay consecutive.

§Errors

ErrorKind::InvalidEdgeId for invalid edges.

Source

pub fn delete_vertices<'a>( &mut self, vertices: impl Into<VertexSelector<'a>>, ) -> Result<()>

Removes the selected vertices and their incident edges (igraph_delete_vertices); the remaining vertices and edges are renumbered to stay consecutive (see delete_vertices_map for the mapping). To keep the original graph, build the complementary Graph::induced_subgraph instead.

§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn delete_vertices_map<'a>( &mut self, vertices: impl Into<VertexSelector<'a>>, ) -> Result<(Vec<VertexId>, Vec<VertexId>)>

Removes the selected vertices and their incident edges, returning (map, invmap) (igraph_delete_vertices_map): map[old] is the new id of vertex old, or -1 if it was deleted, and invmap[new] is the old id of vertex new.

use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let (map, invmap) = g.delete_vertices_map(&[1]).unwrap();
assert_eq!(map, vec![0, -1, 1, 2]);
assert_eq!(invmap, vec![0, 2, 3]);
assert_eq!(g.edge_list(), vec![(1, 2)]);
§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn neighbors( &self, vertex: VertexId, mode: NeighborMode, ) -> Result<Vec<VertexId>>

Neighbors of a vertex, sorted, counting loop edges twice and keeping multi-edges, so that the result has as many entries as the degree (igraph_neighbors with IGRAPH_LOOPS_TWICE and IGRAPH_MULTIPLE); see neighbors_with for other conventions.

For many queries on the same graph, an AdjList is faster; for the vertices within a given distance, see Graph::neighborhood.

use igraph::prelude::*;
let star = Graph::star(4, StarMode::Undirected, 0).unwrap();
assert_eq!(star.neighbors(0, NeighborMode::All).unwrap(), vec![1, 2, 3]);
assert_eq!(star.neighbors(2, NeighborMode::All).unwrap(), vec![0]);
§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

Source

pub fn neighbors_with( &self, vertex: VertexId, mode: NeighborMode, loops: Loops, multiple: bool, ) -> Result<Vec<VertexId>>

Neighbors of a vertex with full control over loops and multi-edges (igraph_neighbors). The result is sorted.

mode selects out-, in- or all neighbors in directed graphs (it is ignored for undirected ones); loops says whether a loop edge makes the vertex its own neighbor zero, one or two times; with multiple = false each neighbor appears once however many parallel edges lead to it.

use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 0), (0, 1), (0, 1)], 2, false).unwrap();
assert_eq!(g.neighbors_with(0, NeighborMode::All, Loops::Twice, true).unwrap(), vec![0, 0, 1, 1]);
assert_eq!(g.neighbors_with(0, NeighborMode::All, Loops::None, false).unwrap(), vec![1]);
§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

Source

pub fn incident( &self, vertex: VertexId, mode: NeighborMode, loops: Loops, ) -> Result<Vec<EdgeId>>

Ids of the edges incident to a vertex, ordered like the neighbors of neighbors_with (igraph_incident).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

Source

pub fn degree<'a>( &self, vertices: impl Into<VertexSelector<'a>>, mode: NeighborMode, loops: Loops, ) -> Result<Vec<igraph_int_t>>

Degrees of the selected vertices, in selector order (igraph_degree); loops says whether a loop edge counts zero, one or two times (two is the convention of the handshake lemma). mode selects out-, in- or total degrees in directed graphs.

See also Graph::strength (weighted degrees), Graph::maxdegree and Graph::mean_degree.

use igraph::prelude::*;
// A loop at 0 and a double edge 0 - 1.
let g = Graph::from_edges(&[(0, 0), (0, 1), (0, 1)], 2, false).unwrap();
assert_eq!(g.degree(.., NeighborMode::All, Loops::Twice).unwrap(), vec![4, 2]);
assert_eq!(g.degree(.., NeighborMode::All, Loops::Once).unwrap(), vec![3, 2]);
assert_eq!(g.degree(&[0], NeighborMode::All, Loops::None).unwrap(), vec![2]);
§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn degree_of( &self, vertex: VertexId, mode: NeighborMode, loops: Loops, ) -> Result<igraph_int_t>

Degree of a single vertex, with the conventions of degree but faster (O(1) when loops count twice, O(degree) otherwise) (igraph_degree_1).

use igraph::prelude::*;
let wheel = Graph::wheel(6, WheelMode::Undirected, 0).unwrap();
assert_eq!(wheel.degree_of(0, NeighborMode::All, Loops::Twice).unwrap(), 5);
assert_eq!(wheel.degree_of(3, NeighborMode::All, Loops::Twice).unwrap(), 3);
assert!(wheel.degree_of(6, NeighborMode::All, Loops::Twice).is_err());
§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

Source

pub fn edge(&self, edge: EdgeId) -> Result<(VertexId, VertexId)>

Endpoints (from, to) of an edge (igraph_edge); for undirected graphs from <= to.

§Errors

ErrorKind::InvalidEdgeId for an invalid edge.

Source

pub fn edges<'a>( &self, edges: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<(VertexId, VertexId)>>

Endpoints of the selected edges, in selector order (igraph_edges).

§Errors

ErrorKind::InvalidEdgeId for invalid edges.

Source

pub fn edge_list(&self) -> Vec<(VertexId, VertexId)>

All the edges as (from, to) pairs, in edge id order (for undirected graphs from <= to).

See edges_flat and Graph::get_edgelist for a flat list, and Graph::write_graph_edgelist_to_string for the text format.

Source

pub fn get_eid( &self, from: VertexId, to: VertexId, directed: bool, ) -> Result<Option<EdgeId>>

Id of an edge between two vertices, or None if they are not connected (igraph_get_eid). With directed = false, edge directions are ignored in directed graphs. With multi-edges, any one of them may be returned.

§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn get_eids( &self, pairs: &[(VertexId, VertexId)], directed: bool, ) -> Result<Vec<EdgeId>>

Ids of the edges connecting the given vertex pairs (igraph_get_eids); see get_eids_opt to tolerate missing edges.

§Errors

ErrorKind::InvalidValue if some pair is not connected, ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn get_all_eids_between( &self, from: VertexId, to: VertexId, directed: bool, ) -> Result<Vec<EdgeId>>

Ids of all the (multi-)edges between two vertices (igraph_get_all_eids_between).

§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn is_same_graph(&self, other: &Self) -> Result<bool>

Whether two graphs are the same labelled graph: same directedness, same number of vertices and precisely the same edges, regardless of their order (igraph_is_same_graph). It is what == on graphs computes. To compare graphs up to a relabeling of their vertices, use Graph::isomorphic.

use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (2, 1)], 3, false).unwrap();
let b = Graph::from_edges(&[(1, 2), (0, 1)], 3, false).unwrap();
let c = Graph::from_edges(&[(0, 2), (1, 2)], 3, false).unwrap(); // isomorphic only
assert!(a.is_same_graph(&b).unwrap());
assert!(!a.is_same_graph(&c).unwrap());
Source

pub fn select_vertices<'a>( &self, vertices: impl Into<VertexSelector<'a>>, ) -> Result<Vec<VertexId>>

Resolves a vertex selector into the list of vertex ids it denotes (igraph_vs_as_vector, undocumented in igraph_iterators.h).

§Errors

ErrorKind::InvalidVertexId (or ErrorKind::InvalidValue for ranges) if the selector names missing vertices.

Source

pub fn select_edges<'a>( &self, edges: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<EdgeId>>

Resolves an edge selector into the list of edge ids it denotes (igraph_es_as_vector).

§Errors

ErrorKind::InvalidEdgeId for missing edges, and ErrorKind::InvalidValue for vertex pairs that are not connected.

Source

pub fn invalidate_cache(&self)

Invalidates igraph’s internal cache of graph properties (igraph_invalidate_cache); only needed after unsafe raw mutation.

Source

pub fn try_clone(&self) -> Result<Self>

A deep copy of the graph, reporting allocation failures as an error instead of panicking like Clone::clone (igraph_copy).

Source

pub fn add_vertex(&mut self) -> Result<VertexId>

Adds one isolated vertex and returns its id (igraph_add_vertices).

use igraph::prelude::*;
let mut g = Graph::new(2, false);
let v = g.add_vertex().unwrap();
g.add_edge(0, v).unwrap();
assert_eq!((v, g.vcount()), (2, 3));
Source

pub fn add_edge_id(&mut self, from: VertexId, to: VertexId) -> Result<EdgeId>

Adds an edge and returns its id (edges are appended, so the new id is the previous edge count) (igraph_add_edge).

Source

pub fn has_edge( &self, from: VertexId, to: VertexId, directed: bool, ) -> Result<bool>

Whether some edge connects from and to (in this direction if directed is true and the graph is directed) (igraph_get_eid).

§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn get_eids_opt( &self, pairs: &[(VertexId, VertexId)], directed: bool, ) -> Result<Vec<Option<EdgeId>>>

Ids of the edges connecting the given vertex pairs, with None for the pairs that are not connected (igraph_get_eids with error = false).

use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert_eq!(g.get_eids_opt(&[(2, 1), (0, 2)], true).unwrap(), vec![Some(1), None]);
§Errors

ErrorKind::InvalidVertexId for invalid vertices.

Source

pub fn edges_flat<'a>( &self, edges: impl Into<EdgeSelector<'a>>, bycol: bool, ) -> Result<Vec<VertexId>>

Endpoints of the selected edges as a flat list; with bycol = false it is [from0, to0, from1, to1, ...], with bycol = true it is [from0, from1, ..., to0, to1, ...] (igraph_edges).

Source

pub fn edge_from(&self, edge: EdgeId) -> Result<VertexId>

The source vertex of an edge (IGRAPH_FROM).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge.

Source

pub fn edge_to(&self, edge: EdgeId) -> Result<VertexId>

The target vertex of an edge (IGRAPH_TO).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge.

Source

pub fn other_endpoint(&self, edge: EdgeId, vertex: VertexId) -> Result<VertexId>

The endpoint of edge opposite to vertex (IGRAPH_OTHER); for a loop edge it is vertex itself.

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, and ErrorKind::InvalidValue if vertex is not an endpoint of edge.

Source

pub fn vs_size<'a>( &self, vertices: impl Into<VertexSelector<'a>>, ) -> Result<usize>

Number of vertices in a vertex selector for this graph (igraph_vs_size).

This only counts, it does not validate: lists and ranges are counted by their length (duplicates included) even if they name missing vertices, and a single missing vertex counts as 0. Only the adjacent and non-adjacent selectors query the graph, and fail for a missing center. Use select_vertices to resolve and validate a selector.

use igraph::prelude::*;
let g = Graph::ring(5, false, false, true).unwrap();
assert_eq!(g.vs_size(..).unwrap(), 5);
assert_eq!(g.vs_size(&[1, 1, 4]).unwrap(), 3);
assert_eq!(g.vs_size(VertexSelector::non_adjacent(0, NeighborMode::All)).unwrap(), 3);
assert_eq!(g.vs_size(9).unwrap(), 0); // not validated
assert!(g.select_vertices(9).is_err());
§Errors

ErrorKind::InvalidVertexId for an adjacent or non-adjacent selector around a missing vertex.

Source

pub fn es_size<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<usize>

Number of edges in an edge selector for this graph (igraph_es_size).

Like vs_size, lists and ranges are counted by their length without validation (and a single missing edge counts as 0); incident, pair, path and all-between selectors query the graph. Use select_edges to resolve and validate a selector.

§Errors

ErrorKind::InvalidVertexId for selectors around missing vertices, ErrorKind::InvalidValue for pairs that are not connected.

Source

pub fn adjacent_vertices( &self, vertex: VertexId, mode: NeighborMode, loops: Loops, multiple: bool, ) -> Result<Vec<VertexId>>

Neighbors of the vertex through an adjacency selector (igraph_vs_adj resolved with igraph_vs_as_vector, which is undocumented in igraph_iterators.h), with full control of loops and multi-edges; the result equals neighbors_with with the same arguments. Unlike VertexSelector::Adjacent, which always ignores loops and multi-edges, the conventions are configurable.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

Source

pub fn incident_edges( &self, vertex: VertexId, mode: NeighborMode, loops: Loops, ) -> Result<Vec<EdgeId>>

Incident edges of the vertex with the given loop handling, in selector order (igraph_es_incident resolved with igraph_es_as_vector); unlike EdgeSelector::Incident, which lists every loop once, the loop counting mode is configurable. The result equals incident with the same arguments.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

Source

pub fn edges_iter( &self, ) -> impl Iterator<Item = (EdgeId, VertexId, VertexId)> + '_

Iterates over the edges as (id, from, to) triples, in id order.

use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
let e: Vec<_> = g.edges_iter().collect();
assert_eq!(e, vec![(0, 0, 1), (1, 1, 2)]);
Source§

impl igraph_t

Source

pub fn adjlist_init( &self, mode: NeighborMode, loops: Loops, multiple: bool, ) -> Result<AdjList>

Adjacency list of the graph: for each vertex, the sorted ids of its neighbors (igraph_adjlist_init).

The list is independent of the graph after creation: it reflects the graph at the time of the call and can be edited freely.

  • mode: in directed graphs, whether to list successors (NeighborMode::Out), predecessors (NeighborMode::In) or both (NeighborMode::All); ignored for undirected graphs.
  • loops: Loops::None drops self-loops; Loops::Once lists each loop edge once in the list of its vertex; Loops::Twice lists it twice, but only if the graph is undirected or mode is NeighborMode::All (otherwise it behaves as Once).
  • multiple: true keeps parallel edges, so a neighbor appears as many times as there are edges to it; false lists each neighbor once (with mode All this also merges a mutual pair u -> w, w -> u of a directed graph) and each kept self-loop once, or twice with Loops::Twice in an undirected graph or with mode All.

igraph 1.0.0 and 1.0.1 get multiple = false wrong when neighbors are collected in both directions (mode All, or an undirected graph): they mistake mutual pairs and single self-loops for multi-edges and record that in the graph’s property cache (so that Graph::has_multiple later answers true for a graph without multi-edges), and, if the cache already says “no multi-edges”, they list mutual pairs twice. This wrapper avoids both problems by collapsing such lists itself, with the semantics described above.

Loops::Twice with multiple = true is the fastest combination. The lists are currently sorted, but igraph does not guarantee this for the future: call AdjList::sort if you rely on it. The list of vertex v equals Graph::neighbors_with(v, mode, loops, multiple).

Time complexity: O(|V|+|E|).

See also Graph::lazy_adjlist_init (on-demand variant), Graph::inclist_init (edge ids instead of vertex ids), Graph::neighbors_with (one vertex) and Graph::get_adjacency (dense matrix).

§Examples
use igraph::prelude::*;
// An undirected graph with a double edge 0-1 and a loop on 2.
let g = Graph::from_edges(&[(0, 1), (0, 1), (1, 2), (2, 2)], 3, false).unwrap();
let full = g.adjlist_init(NeighborMode::All, Loops::Twice, true).unwrap();
assert_eq!(full.to_vecs(), vec![vec![1, 1], vec![0, 0, 2], vec![1, 2, 2]]);
let simple = g.adjlist_init(NeighborMode::All, Loops::None, false).unwrap();
assert_eq!(simple.to_vecs(), vec![vec![1], vec![0, 2], vec![1]]);

Binds igraph_adjlist_init.

Source

pub fn adjlist_init_complementer( &self, mode: NeighborMode, loops: Loops, ) -> Result<AdjList>

Adjacency list of the complementer graph, i.e. of the graph having exactly the edges missing from this one (igraph_adjlist_init_complementer).

Multi-edges of the input are ignored and the lists are sorted.

  • mode: which neighbors in the complementer to list for directed graphs (ignored for undirected ones);
  • loops: Loops::None never lists v among its own neighbors; Loops::Once lists it once if the graph has no loop on v; Loops::Twice lists it twice in that case when mode is NeighborMode::All, and behaves as Once otherwise.

Time complexity: O(|V|²+|E|).

See also Graph::complementer, which builds the complementer as a graph.

§Examples
use igraph::prelude::*;
// The complement of the path 0 - 1 - 2 - 3 is the path 1 - 3 - 0 - 2.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let c = g.adjlist_init_complementer(NeighborMode::All, Loops::None).unwrap();
assert_eq!(c.to_vecs(), vec![vec![2, 3], vec![3], vec![0], vec![0, 1]]);

Binds igraph_adjlist_init_complementer.

Source

pub fn adjlist_init_from_inclist(&self, inclist: &IncList) -> Result<AdjList>

Adjacency list consistent with an incidence list of this graph (igraph_adjlist_init_from_inclist).

Entry i of the list of vertex v is the other endpoint of the edge at entry i of inclist[v], so the two structures can be walked in lockstep (neighbor and connecting edge together). The result is independent of both the graph and the incidence list.

Time complexity: O(|V|+|E|).

§Errors
§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (2, 0)], 3, true).unwrap();
let il = g.inclist_init(NeighborMode::All, Loops::Twice).unwrap();
let al = g.adjlist_init_from_inclist(&il).unwrap();
for v in 0..3 {
    for (&e, &u) in il[v].iter().zip(&al[v]) {
        let (a, b) = g.edge(e).unwrap();
        assert!((a, b) == (v as i64, u) || (a, b) == (u, v as i64));
    }
}

Binds igraph_adjlist_init_from_inclist.

Source

pub fn adjlist( adjlist: &AdjList, mode: NeighborMode, duplicate: bool, ) -> Result<Graph>

Creates a graph from an adjacency list (igraph_adjlist), the inverse of Graph::adjlist_init.

The graph has one vertex per list.

  • mode: NeighborMode::All creates an undirected graph; NeighborMode::Out a directed graph where each list holds the successors of its vertex; NeighborMode::In a directed graph where each list holds the predecessors.
  • duplicate: for undirected graphs only, whether each edge is listed twice (in the lists of both endpoints, as produced by Graph::adjlist_init; a self-loop then appears twice in its list) or just once.

Converting a graph into an adjacency list, doing many structural edits on the lists and converting back costs O(|V|+|E|) overall, much less than repeated edge deletions and insertions on the graph.

Time complexity: O(|V|+|E|).

See also Graph::from_edges (from an edge list) and Graph::adjacency (from an adjacency matrix).

§Errors
  • ErrorKind::InvalidValue if duplicate is set (and mode is NeighborMode::All) but the edges are not correctly listed twice: u must appear in the list of w as many times as w appears in the list of u, and loops must be listed an even number of times (checked on the Rust side: igraph itself only detects some of these cases and may otherwise silently invent or drop edges);
  • ErrorKind::InvalidVertexId if a list contains an id that is not one of the list’s vertices (checked on the Rust side: igraph itself would silently add the missing vertices).
§Examples
use igraph::prelude::*;
use igraph::adjlist::AdjList;
// The undirected triangle, each edge listed from both of its ends.
let al = AdjList::from(vec![vec![1, 2], vec![0, 2], vec![0, 1]]);
let g = Graph::adjlist(&al, NeighborMode::All, true).unwrap();
assert!(!g.is_directed());
assert_eq!(g.ecount(), 3);
// Round trip.
assert_eq!(g.adjlist_init(NeighborMode::All, Loops::Twice, true).unwrap(), al);

Binds igraph_adjlist.

Source

pub fn inclist_init(&self, mode: NeighborMode, loops: Loops) -> Result<IncList>

Incidence list of the graph: for each vertex, the ids of its incident edges (igraph_inclist_init).

The list is independent of the graph after creation.

The list of vertex v holds the same ids as Graph::incident(v, mode, loops). Pair it with Graph::adjlist_init_from_inclist to get the matching neighbors.

Time complexity: O(|V|+|E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 2)], 3, false).unwrap();
let il = g.inclist_init(NeighborMode::All, Loops::Once).unwrap();
assert_eq!(il.to_vecs(), vec![vec![0], vec![0, 1], vec![1, 2]]);

Binds igraph_inclist_init.

Source

pub fn lazy_adjlist_init( &self, mode: NeighborMode, loops: Loops, multiple: bool, ) -> Result<LazyAdjList<'_>>

Lazy adjacency list of the graph (igraph_lazy_adjlist_init): the neighbors of a vertex are computed on its first get and then cached.

The arguments have the same meaning as in Graph::adjlist_init; the lists are sorted. The lazy list borrows the graph, which therefore cannot be modified while the list is alive. When igraph already knows (from its property cache, e.g. after Graph::has_loop or Graph::has_multiple) that the graph has no self-loops or no multi-edges, it skips the corresponding filtering: see LazyAdjList::loops and LazyAdjList::multiple. The lists are always the same as those of Graph::adjlist_init, whatever the state of the cache (this wrapper works around an igraph 1.0.0 and 1.0.1 bug that listed mutual pairs of a directed graph twice in mode All with multiple = false once the cache said “no multi-edges”).

Time complexity: O(|V|) for the initialization, O(d) for the first query of a vertex of degree d, O(1) afterwards.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, true).unwrap();
let mut lazy = g.lazy_adjlist_init(NeighborMode::In, Loops::Once, true).unwrap();
assert_eq!(lazy.len(), 3);
assert_eq!(lazy.get(2).unwrap(), &[0, 1]);
assert_eq!(lazy.get(0).unwrap(), &[] as &[i64]);

Binds igraph_lazy_adjlist_init.

Source

pub fn lazy_inclist_init( &self, mode: NeighborMode, loops: Loops, ) -> Result<LazyIncList<'_>>

Lazy incidence list of the graph (igraph_lazy_inclist_init): the incident edges of a vertex are computed on its first get and then cached.

The arguments have the same meaning as in Graph::inclist_init. The lazy list borrows the graph.

Time complexity: O(|V|) for the initialization, O(d) for the first query of a vertex of degree d, O(1) afterwards.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, true).unwrap();
let mut lazy = g.lazy_inclist_init(NeighborMode::Out, Loops::Once).unwrap();
assert_eq!(lazy.get(0).unwrap(), &[0, 1]);

Binds igraph_lazy_inclist_init.

Source§

impl igraph_t

Source

pub fn attribute_list(&self) -> Result<AttributeList>

Names and types of all the graph, vertex and edge attributes (igraph_cattribute_list).

Attributes are listed in creation order. A graph without attribute storage (created before enable) has an empty list.

Time complexity: O(Ag+Av+Ae), the total number of attributes.

§Examples
use igraph::prelude::*;
use igraph::attributes::AttributeType;

let mut g = Graph::new(2, false);
g.set_graph_attr_numeric("year", 2024.0).unwrap();
g.set_vertex_attr_str("label", 0, "a").unwrap();
let list = g.attribute_list().unwrap();
assert_eq!(list.graph, vec![("year".to_string(), AttributeType::Numeric)]);
assert_eq!(list.vertex, vec![("label".to_string(), AttributeType::String)]);
assert!(list.edge.is_empty());
Source

pub fn attribute_names(&self, kind: AttributeKind) -> Result<Vec<String>>

Names of the attributes of one kind, in creation order (from igraph_cattribute_list).

Source

pub fn attribute_type( &self, kind: AttributeKind, name: &str, ) -> Result<Option<AttributeType>>

The type of an attribute, or None if there is no such attribute (from igraph_cattribute_list).

use igraph::prelude::*;
use igraph::attributes::{AttributeKind, AttributeType};

let mut g = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
g.set_edge_attr_bool("bridge", 0, true).unwrap();
assert_eq!(g.attribute_type(AttributeKind::Edge, "bridge").unwrap(), Some(AttributeType::Boolean));
assert_eq!(g.attribute_type(AttributeKind::Vertex, "bridge").unwrap(), None);
Source

pub fn has_attribute(&self, kind: AttributeKind, name: &str) -> bool

Whether the graph has a graph, vertex or edge attribute called name (igraph_cattribute_has_attr).

Always false for graphs without attribute storage (and for names containing a NUL byte).

Time complexity: O(A), the number of attributes of that kind.

use igraph::prelude::*;
use igraph::attributes::AttributeKind;
let mut g = Graph::new(1, false);
g.set_vertex_attr_str("name", 0, "solo").unwrap();
assert!(g.has_attribute(AttributeKind::Vertex, "name"));
assert!(!g.has_attribute(AttributeKind::Graph, "name"));
Source

pub fn graph_attr_numeric(&self, name: &str) -> Result<f64>

The value of a numeric graph attribute (igraph_cattribute_GAN).

§Errors

ErrorKind::InvalidValue if the attribute does not exist or is not numeric (the C function would only emit a warning and return NaN).

use igraph::prelude::*;
let mut g = Graph::new(0, false);
g.set_graph_attr_numeric("density", 0.25).unwrap();
assert_eq!(g.graph_attr_numeric("density").unwrap(), 0.25);
assert_eq!(g.graph_attr_numeric("nope").unwrap_err().kind(), ErrorKind::InvalidValue);
Source

pub fn graph_attr_bool(&self, name: &str) -> Result<bool>

The value of a boolean graph attribute (igraph_cattribute_GAB).

§Errors

ErrorKind::InvalidValue if the attribute does not exist or is not boolean.

Source

pub fn graph_attr_str(&self, name: &str) -> Result<String>

The value of a string graph attribute, copied into a String (igraph_cattribute_GAS).

Invalid UTF-8 is replaced lossily.

§Errors

ErrorKind::InvalidValue if the attribute does not exist or is not a string.

Source

pub fn graph_attr(&self, name: &str) -> Result<AttributeValue>

The value of a graph attribute, whatever its type (GAN/GAB/GAS).

§Errors

ErrorKind::InvalidValue if the attribute does not exist.

Source

pub fn set_graph_attr_numeric(&mut self, name: &str, value: f64) -> Result<()>

Sets a numeric graph attribute, creating it if needed (igraph_cattribute_GAN_set).

Enables attributes (see enable) if they are not yet.

§Errors

ErrorKind::InvalidValue if the attribute exists with another type.

Source

pub fn set_graph_attr_bool(&mut self, name: &str, value: bool) -> Result<()>

Sets a boolean graph attribute, creating it if needed (igraph_cattribute_GAB_set).

§Errors

ErrorKind::InvalidValue if the attribute exists with another type.

Source

pub fn set_graph_attr_str(&mut self, name: &str, value: &str) -> Result<()>

Sets a string graph attribute (the value is copied), creating it if needed (igraph_cattribute_GAS_set).

§Errors

ErrorKind::InvalidValue if the attribute exists with another type, or if name or value contain a NUL byte.

Source

pub fn set_graph_attr( &mut self, name: &str, value: impl Into<AttributeValue>, ) -> Result<()>

Sets a graph attribute of any type (GAN_set/GAB_set/GAS_set).

§Errors

As the typed setters, e.g. set_graph_attr_numeric.

use igraph::prelude::*;
use igraph::attributes::AttributeValue;
let mut g = Graph::new(0, true);
g.set_graph_attr("name", "empty").unwrap();
g.set_graph_attr("order", 0).unwrap();
assert_eq!(g.graph_attr("name").unwrap(), AttributeValue::String("empty".into()));
assert_eq!(g.graph_attr("order").unwrap().as_f64(), Some(0.0));
Source

pub fn remove_graph_attr(&mut self, name: &str) -> bool

Removes a graph attribute, returning whether it existed (igraph_cattribute_remove_g).

Source

pub fn remove_all_attributes(&mut self, graph: bool, vertex: bool, edge: bool)

Removes all the graph, vertex and/or edge attributes (igraph_cattribute_remove_all).

use igraph::prelude::*;
let mut g = Graph::new(3, false);
g.set_graph_attr_str("title", "t").unwrap();
g.set_vertex_attr_numeric("x", 0, 1.0).unwrap();
g.remove_all_attributes(false, true, true);
let list = g.attribute_list().unwrap();
assert_eq!(list.graph.len(), 1);
assert!(list.vertex.is_empty());
Source

pub fn vertex_attr_numeric(&self, name: &str, vid: VertexId) -> Result<f64>

The value of a numeric vertex attribute for one vertex (igraph_cattribute_VAN).

Vertices that never had the attribute set hold NaN.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist or is not numeric.

use igraph::prelude::*;
let mut g = Graph::new(3, false);
g.set_vertex_attr_numeric("age", 1, 42.0).unwrap();
assert_eq!(g.vertex_attr_numeric("age", 1).unwrap(), 42.0);
assert!(g.vertex_attr_numeric("age", 0).unwrap().is_nan());
assert_eq!(g.vertex_attr_numeric("age", 3).unwrap_err().kind(), ErrorKind::InvalidVertexId);
Source

pub fn vertex_attr_bool(&self, name: &str, vid: VertexId) -> Result<bool>

The value of a boolean vertex attribute for one vertex (igraph_cattribute_VAB).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist or is not boolean.

Source

pub fn vertex_attr_str(&self, name: &str, vid: VertexId) -> Result<String>

The value of a string vertex attribute for one vertex, copied into a String (igraph_cattribute_VAS).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist or is not a string.

Source

pub fn vertex_attr(&self, name: &str, vid: VertexId) -> Result<AttributeValue>

The value of a vertex attribute of any type for one vertex (VAN/VAB/VAS).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist.

Source

pub fn vertex_attr_numeric_values<'a>( &self, name: &str, vids: impl Into<VertexSelector<'a>>, ) -> Result<Vec<f64>>

The values of a numeric vertex attribute for the selected vertices, in selector order (igraph_cattribute_VANV).

Use .. to select all the vertices.

Time complexity: O(v), the number of selected vertices.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist or is not numeric.

use igraph::prelude::*;
let mut g = Graph::new(4, false);
g.set_vertex_attr_numeric_values("x", &[0.0, 0.5, 1.0, 1.5]).unwrap();
assert_eq!(g.vertex_attr_numeric_values("x", vec![3, 0]).unwrap(), vec![1.5, 0.0]);
assert_eq!(g.vertex_attr_numeric_values("x", 1..3).unwrap(), vec![0.5, 1.0]);
Source

pub fn vertex_attr_bool_values<'a>( &self, name: &str, vids: impl Into<VertexSelector<'a>>, ) -> Result<Vec<bool>>

The values of a boolean vertex attribute for the selected vertices (igraph_cattribute_VABV).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist or is not boolean.

Source

pub fn vertex_attr_str_values<'a>( &self, name: &str, vids: impl Into<VertexSelector<'a>>, ) -> Result<Vec<String>>

The values of a string vertex attribute for the selected vertices (igraph_cattribute_VASV).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist or is not a string.

use igraph::prelude::*;
let mut g = Graph::famous("Krackhardt_Kite").unwrap();
let names = ["Andre", "Beverly", "Carol", "Diane", "Ed", "Fernando", "Garth", "Heather", "Ike", "Jane"];
g.set_vertex_attr_str_values("name", &names).unwrap();
// The names of Jane's neighbors (Jane is the tail of the kite).
let jane = VertexSelector::Adjacent { vertex: 9, mode: NeighborMode::All };
assert_eq!(g.vertex_attr_str_values("name", jane).unwrap(), vec!["Ike"]);
Source

pub fn vertex_attr_values<'a>( &self, name: &str, vids: impl Into<VertexSelector<'a>>, ) -> Result<AttributeValues>

The values of a vertex attribute of any type for the selected vertices (VANV/VABV/VASV).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute does not exist.

Source

pub fn set_vertex_attr_numeric( &mut self, name: &str, vid: VertexId, value: f64, ) -> Result<()>

Sets a numeric vertex attribute for one vertex (igraph_cattribute_VAN_set).

A new attribute is created for all the vertices, with NaN for the others. Enables attributes (see enable) if they are not yet.

Time complexity: O(n) if the attribute is new, O(1) otherwise.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute exists with another type.

Source

pub fn set_vertex_attr_bool( &mut self, name: &str, vid: VertexId, value: bool, ) -> Result<()>

Sets a boolean vertex attribute for one vertex (false for the others if new) (igraph_cattribute_VAB_set).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute exists with another type.

Source

pub fn set_vertex_attr_str( &mut self, name: &str, vid: VertexId, value: &str, ) -> Result<()>

Sets a string vertex attribute for one vertex ("" for the others if new) (igraph_cattribute_VAS_set).

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue if the attribute exists with another type or a string contains a NUL byte.

Source

pub fn set_vertex_attr( &mut self, name: &str, vid: VertexId, value: impl Into<AttributeValue>, ) -> Result<()>

Sets a vertex attribute of any type for one vertex (VAN_set/VAB_set/VAS_set).

§Errors

As the typed setters, e.g. set_vertex_attr_numeric.

use igraph::prelude::*;
let mut g = Graph::new(2, false);
g.set_vertex_attr("color", 0, "red").unwrap();
g.set_vertex_attr("size", 1, 3.5).unwrap();
assert_eq!(g.vertex_attr_str_values("color", ..).unwrap(), vec!["red", ""]);
Source

pub fn set_vertex_attr_numeric_values( &mut self, name: &str, values: &[f64], ) -> Result<()>

Sets a numeric vertex attribute for all the vertices at once (igraph_cattribute_VAN_setv).

values[i] is the value of vertex i.

§Errors

ErrorKind::InvalidValue if values.len() is not the number of vertices or the attribute exists with another type.

Source

pub fn set_vertex_attr_bool_values( &mut self, name: &str, values: &[bool], ) -> Result<()>

Sets a boolean vertex attribute for all the vertices at once (igraph_cattribute_VAB_setv).

§Errors

ErrorKind::InvalidValue if values.len() is not the number of vertices or the attribute exists with another type.

Source

pub fn set_vertex_attr_str_values<S: AsRef<str>>( &mut self, name: &str, values: &[S], ) -> Result<()>

Sets a string vertex attribute for all the vertices at once (igraph_cattribute_VAS_setv).

§Errors

ErrorKind::InvalidValue if values.len() is not the number of vertices, the attribute exists with another type or a string contains a NUL byte.

Source

pub fn set_vertex_attr_values( &mut self, name: &str, values: impl Into<AttributeValues>, ) -> Result<()>

Sets a vertex attribute of any type for all the vertices at once (VAN_setv/VAB_setv/VAS_setv).

§Errors

As the typed setters, e.g. set_vertex_attr_numeric_values.

use igraph::prelude::*;
let mut g = Graph::new(3, false);
g.set_vertex_attr_values("seen", vec![true, false, true]).unwrap();
assert_eq!(g.vertex_attr_bool_values("seen", ..).unwrap(), vec![true, false, true]);
assert!(g.set_vertex_attr_values("seen", vec![true]).is_err()); // wrong length
Source

pub fn remove_vertex_attr(&mut self, name: &str) -> bool

Removes a vertex attribute, returning whether it existed (igraph_cattribute_remove_v).

Once removed, the name can be reused for an attribute of another type.

use igraph::prelude::*;
let mut g = Graph::new(2, false);
g.set_vertex_attr_numeric("tmp", 0, 1.0).unwrap();
assert!(g.remove_vertex_attr("tmp"));
assert!(!g.remove_vertex_attr("tmp"));
g.set_vertex_attr_str("tmp", 0, "now a string").unwrap();
Source

pub fn edge_attr_numeric(&self, name: &str, eid: EdgeId) -> Result<f64>

The value of a numeric edge attribute for one edge (igraph_cattribute_EAN).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist or is not numeric.

Source

pub fn edge_attr_bool(&self, name: &str, eid: EdgeId) -> Result<bool>

The value of a boolean edge attribute for one edge (igraph_cattribute_EAB).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist or is not boolean.

Source

pub fn edge_attr_str(&self, name: &str, eid: EdgeId) -> Result<String>

The value of a string edge attribute for one edge (igraph_cattribute_EAS).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist or is not a string.

Source

pub fn edge_attr(&self, name: &str, eid: EdgeId) -> Result<AttributeValue>

The value of an edge attribute of any type for one edge (EAN/EAB/EAS).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist.

Source

pub fn edge_attr_numeric_values<'a>( &self, name: &str, eids: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<f64>>

The values of a numeric edge attribute for the selected edges, in selector order (igraph_cattribute_EANV).

This is the natural way to obtain a weight vector for the weighted algorithms of the crate: g.edge_attr_numeric_values("weight", ..) (see e.g. Graph::distances_dijkstra and Graph::strength).

Time complexity: O(e), the number of selected edges.

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist or is not numeric.

use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
g.set_edge_attr_numeric_values("weight", &[0.5, 2.0]).unwrap();
let weights = g.edge_attr_numeric_values("weight", ..).unwrap();
assert_eq!(weights.iter().sum::<f64>(), 2.5);
let d = g.distances_dijkstra(0, 2, Some(&weights), NeighborMode::All).unwrap();
assert_eq!(d[(0, 0)], 2.5);
Source

pub fn edge_attr_bool_values<'a>( &self, name: &str, eids: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<bool>>

The values of a boolean edge attribute for the selected edges (igraph_cattribute_EABV).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist or is not boolean.

Source

pub fn edge_attr_str_values<'a>( &self, name: &str, eids: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<String>>

The values of a string edge attribute for the selected edges (igraph_cattribute_EASV).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist or is not a string.

Source

pub fn edge_attr_values<'a>( &self, name: &str, eids: impl Into<EdgeSelector<'a>>, ) -> Result<AttributeValues>

The values of an edge attribute of any type for the selected edges (EANV/EABV/EASV).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute does not exist.

Source

pub fn set_edge_attr_numeric( &mut self, name: &str, eid: EdgeId, value: f64, ) -> Result<()>

Sets a numeric edge attribute for one edge (NaN for the others if new) (igraph_cattribute_EAN_set).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute exists with another type.

Source

pub fn set_edge_attr_bool( &mut self, name: &str, eid: EdgeId, value: bool, ) -> Result<()>

Sets a boolean edge attribute for one edge (false for the others if new) (igraph_cattribute_EAB_set).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute exists with another type.

Source

pub fn set_edge_attr_str( &mut self, name: &str, eid: EdgeId, value: &str, ) -> Result<()>

Sets a string edge attribute for one edge ("" for the others if new) (igraph_cattribute_EAS_set).

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue if the attribute exists with another type or a string contains a NUL byte.

Source

pub fn set_edge_attr( &mut self, name: &str, eid: EdgeId, value: impl Into<AttributeValue>, ) -> Result<()>

Sets an edge attribute of any type for one edge (EAN_set/EAB_set/EAS_set).

A new attribute gets the default value (NaN, false or "") on the other edges.

§Errors

As the typed setters, e.g. set_edge_attr_numeric.

use igraph::prelude::*;
use igraph::attributes::AttributeValue;
let mut g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
g.set_edge_attr("capacity", 1, 10).unwrap();
g.set_edge_attr("road", 0, true).unwrap();
assert_eq!(g.edge_attr("capacity", 1).unwrap(), AttributeValue::Numeric(10.0));
assert!(g.edge_attr_numeric("capacity", 0).unwrap().is_nan());
assert_eq!(g.edge_attr_bool_values("road", ..).unwrap(), vec![true, false]);
Source

pub fn set_edge_attr_numeric_values( &mut self, name: &str, values: &[f64], ) -> Result<()>

Sets a numeric edge attribute for all the edges at once (igraph_cattribute_EAN_setv).

values[i] is the value of edge i.

§Errors

ErrorKind::InvalidValue if values.len() is not the number of edges or the attribute exists with another type.

Source

pub fn set_edge_attr_bool_values( &mut self, name: &str, values: &[bool], ) -> Result<()>

Sets a boolean edge attribute for all the edges at once (igraph_cattribute_EAB_setv).

§Errors

ErrorKind::InvalidValue if values.len() is not the number of edges or the attribute exists with another type.

Source

pub fn set_edge_attr_str_values<S: AsRef<str>>( &mut self, name: &str, values: &[S], ) -> Result<()>

Sets a string edge attribute for all the edges at once (igraph_cattribute_EAS_setv).

§Errors

ErrorKind::InvalidValue if values.len() is not the number of edges, the attribute exists with another type or a string contains a NUL byte.

Source

pub fn set_edge_attr_values( &mut self, name: &str, values: impl Into<AttributeValues>, ) -> Result<()>

Sets an edge attribute of any type for all the edges at once (EAN_setv/EAB_setv/EAS_setv).

§Errors

As the typed setters, e.g. set_edge_attr_numeric_values.

Source

pub fn remove_edge_attr(&mut self, name: &str) -> bool

Removes an edge attribute, returning whether it existed (igraph_cattribute_remove_e).

Source

pub fn add_vertices_with_attributes( &mut self, n: usize, records: &[AttributeRecord], ) -> Result<()>

Adds n vertices together with their attribute values (igraph_add_vertices with an attribute record list).

Each record must be named and hold exactly n values. New attributes are created (with default values for the existing vertices); existing attributes not mentioned get their default value for the new vertices. Enables attributes if they are not yet.

§Errors

ErrorKind::InvalidValue if a record has the wrong length, no name, or a type different from the existing attribute with the same name.

use igraph::prelude::*;
use igraph::attributes::AttributeRecord;

let mut g = Graph::new(1, false);
g.set_vertex_attr_str("name", 0, "root").unwrap();
let names = AttributeRecord::string("name", &["left", "right"]).unwrap();
g.add_vertices_with_attributes(2, &[names]).unwrap();
assert_eq!(g.vertex_attr_str_values("name", ..).unwrap(), vec!["root", "left", "right"]);
Source

pub fn add_edges_with_attributes( &mut self, edges: &[(VertexId, VertexId)], records: &[AttributeRecord], ) -> Result<()>

Adds edges together with their attribute values (igraph_add_edges with an attribute record list).

Each record must be named and hold exactly edges.len() values.

§Errors

ErrorKind::InvalidVertexId for invalid endpoints, ErrorKind::InvalidValue for invalid records (see add_vertices_with_attributes).

use igraph::prelude::*;
use igraph::attributes::AttributeRecord;

let mut g = Graph::new(3, true);
let w = AttributeRecord::numeric("weight", &[1.5, 2.5]).unwrap();
g.add_edges_with_attributes(&[(0, 1), (1, 2)], &[w]).unwrap();
assert_eq!(g.edge_attr_numeric("weight", 1).unwrap(), 2.5);
Source

pub fn simplify_with_attributes( &mut self, remove_multiple: bool, remove_loops: bool, edge_comb: &AttributeCombination, ) -> Result<()>

Removes multi-edges and/or self-loops like Graph::simplify, combining the attributes of the merged edges according to edge_comb (igraph_simplify with an edge attribute combination).

When multi-edges are removed, igraph rebuilds the graph: each group of parallel edges becomes one edge whose attributes are computed from the values of the group (a group of a single edge is combined too, e.g. Sum keeps its value). Attributes the combination maps to Ignore or Default, and all of them for an empty combination, are dropped. The edge order may change.

edge_comb is not used, and the remaining edges keep all their attributes, when nothing has to be merged: if remove_multiple is false (loops are then deleted in place), or if igraph already knows that the graph has no multi-edges (e.g. it was simplified before), or nothing at all is to be removed. Graph and vertex attributes are always kept. Without attributes enabled this is exactly Graph::simplify.

Time complexity: O(|V|+|E|), plus the cost of the combinations.

§Errors

ErrorKind::AttributeCombination if a combination does not apply to the type of an attribute (e.g. summing strings), ErrorKind::Unimplemented for the numeric median.

§Examples
use igraph::prelude::*;
use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};

// Three calls between 0 and 1, one between 1 and 2.
let mut calls = Graph::from_edges(&[(0, 1), (1, 2), (0, 1), (0, 1)], 3, false).unwrap();
calls.set_edge_attr_numeric_values("minutes", &[5.0, 2.0, 1.0, 4.0]).unwrap();
let comb = AttributeCombination::all(Comb::Sum).unwrap();
calls.simplify_with_attributes(true, true, &comb).unwrap();
assert_eq!(calls.edge_list(), vec![(0, 1), (1, 2)]);
assert_eq!(calls.edge_attr_numeric_values("minutes", ..).unwrap(), vec![10.0, 2.0]);
Source

pub fn contract_vertices_with_attributes( &mut self, mapping: &[VertexId], vertex_comb: &AttributeCombination, ) -> Result<()>

Merges groups of vertices into single vertices like Graph::contract_vertices, combining their attributes according to vertex_comb (igraph_contract_vertices with a vertex attribute combination).

mapping[v] is the id of the vertex that v becomes; use consecutive ids starting at 0 to avoid creating isolated “orphan” vertices. No edge is removed (merged groups get self-loops and multi-edges: use simplify_with_attributes afterwards), and edge and graph attributes are kept unchanged.

Time complexity: O(|V|+|E|), plus the cost of the combinations.

§Errors

ErrorKind::InvalidValue if mapping does not have one entry per vertex or contains negative ids (or i64::MAX); the combination errors of simplify_with_attributes.

§Examples
use igraph::prelude::*;
use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};

// Four towns in two provinces.
let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
g.set_vertex_attr_numeric_values("population", &[10.0, 5.0, 7.0, 3.0]).unwrap();
g.set_vertex_attr_str_values("name", &["Firenze", "Prato", "Pisa", "Lucca"]).unwrap();
let comb = AttributeCombination::from_pairs(&[
    (Some("population"), Comb::Sum),
    (Some("name"), Comb::First),
]).unwrap();
g.contract_vertices_with_attributes(&[0, 0, 1, 1], &comb).unwrap();
assert_eq!(g.vertex_attr_numeric_values("population", ..).unwrap(), vec![15.0, 10.0]);
assert_eq!(g.vertex_attr_str_values("name", ..).unwrap(), vec!["Firenze", "Pisa"]);
assert_eq!(g.ecount(), 3); // two self-loops and the edge between the provinces
Source

pub fn to_undirected_with_attributes( &mut self, mode: ToUndirected, edge_comb: &AttributeCombination, ) -> Result<()>

Converts a directed graph to an undirected one like Graph::to_undirected, combining the attributes of the directed edges that become a single undirected edge according to edge_comb (igraph_to_undirected with an edge attribute combination).

With ToUndirected::Collapse all the edges between a pair of vertices are merged; with ToUndirected::Mutual each mutual pair u -> v, v -> u is merged into one edge (non-mutual edges are lost, loops are kept); with ToUndirected::Each every edge is kept together with its attributes and edge_comb is not used. Graph and vertex attributes are kept. Undirected graphs are left unchanged.

Time complexity: O(|V|+|E|), plus the cost of the combinations.

§Errors

The combination errors of simplify_with_attributes.

§Examples
use igraph::prelude::*;
use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};

// Messages sent in both directions between 0 and 1, and from 1 to 2.
let mut g = Graph::from_edges(&[(0, 1), (1, 0), (1, 2)], 3, true).unwrap();
g.set_edge_attr_numeric_values("messages", &[3.0, 4.0, 1.0]).unwrap();
let comb = AttributeCombination::all(Comb::Sum).unwrap();
g.to_undirected_with_attributes(ToUndirected::Collapse, &comb).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1), (1, 2)]);
assert_eq!(g.edge_attr_numeric_values("messages", ..).unwrap(), vec![7.0, 1.0]);
Source§

impl igraph_t

Source

pub fn is_bipartite(&self) -> Result<bool>

Whether the graph is bipartite, i.e. whether its vertices can be 2-colored so that no edge joins vertices of the same color.

Equivalently, the graph has no cycle of odd length; a graph with a self-loop is never bipartite. Edge directions are ignored. Use bipartite_types to also get a coloring.

Binds igraph_is_bipartite. Time complexity: O(|V|+|E|).

See also Graph::girth (a bipartite graph has even girth, or no cycle at all), Graph::is_bipartite_coloring (checks a given types vector instead of finding one) and Graph::vertex_coloring_greedy (colorings with more than two colors).

§Examples
use igraph::prelude::*;
let c6 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], 6, false).unwrap();
assert!(c6.is_bipartite().unwrap());
let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
assert!(!c5.is_bipartite().unwrap());
// The Heawood graph (incidence graph of the Fano plane) is bipartite,
// the Petersen graph is not (it has 5-cycles).
assert!(Graph::famous("Heawood").unwrap().is_bipartite().unwrap());
assert!(!Graph::famous("Petersen").unwrap().is_bipartite().unwrap());
Source

pub fn bipartite_types(&self) -> Result<Option<Vec<bool>>>

A 2-coloring (vertex types) witnessing that the graph is bipartite, or None if it is not.

The coloring is not unique in general: e.g. each connected component can be flipped independently. Binds igraph_is_bipartite. Time complexity: O(|V|+|E|).

The types can be fed directly to the other functions of this module, or to Graph::layout_bipartite to draw the graph in two rows.

§Examples
use igraph::prelude::*;
let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let types = path.bipartite_types().unwrap().unwrap();
assert_ne!(types[0], types[1]);
assert_ne!(types[1], types[2]);
// Draw it in two rows: `true` vertices at y = 0, `false` ones at y = 1.
let layout = path.layout_bipartite(&types, 1.0, 1.0, 100).unwrap();
for (v, &t) in types.iter().enumerate() {
    assert_eq!(layout[(v, 1)], if t { 0.0 } else { 1.0 });
}
Source

pub fn create_bipartite( types: &[bool], edges: &[(VertexId, VertexId)], directed: bool, ) -> Result<Graph>

Creates a bipartite graph from vertex types and edges, checking that every edge connects vertices of different types.

The graph has types.len() vertices; every endpoint in edges must be smaller than that. It is Graph::from_edges plus a bipartiteness check. BipartiteGraph::new does the same and keeps the types together with the graph.

Binds igraph_create_bipartite. Time complexity: O(|V|+|E|).

§Errors

ErrorKind::InvalidValue if an edge joins two vertices of the same type, ErrorKind::InvalidVertexId if an endpoint is out of range.

§Examples
use igraph::prelude::*;
let types = [false, true, false, true];
let g = Graph::create_bipartite(&types, &[(0, 1), (1, 2), (2, 3)], true).unwrap();
assert_eq!((g.vcount(), g.ecount(), g.is_directed()), (4, 3, true));
let err = Graph::create_bipartite(&types, &[(0, 2)], false).unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidValue);
Source

pub fn full_bipartite( n1: usize, n2: usize, directed: bool, mode: NeighborMode, ) -> Result<BipartiteGraph>

Creates the complete bipartite graph K(n1, n2).

The first n1 vertices have type false, the following n2 type true, and every vertex of the first kind is connected to every vertex of the second kind. In directed graphs, mode gives the edge directions: NeighborMode::Out from the first kind to the second, NeighborMode::In the opposite, NeighborMode::All mutual edges (so 2·n1·n2 edges). mode is ignored for undirected graphs.

Binds igraph_full_bipartite. Time complexity: O(|V|+|E|).

See also Graph::full_multipartite for complete k-partite graphs and Graph::realize_bipartite_degree_sequence for bipartite graphs with prescribed degrees.

§Examples
use igraph::prelude::*;
let k33 = Graph::full_bipartite(3, 3, false, NeighborMode::All).unwrap();
assert_eq!(k33.graph.ecount(), 9);
assert_eq!(k33.types, vec![false, false, false, true, true, true]);
let d = Graph::full_bipartite(2, 3, true, NeighborMode::All).unwrap();
assert_eq!(d.graph.ecount(), 12);
Source

pub fn biadjacency( biadjmatrix: &Matrix, directed: bool, mode: NeighborMode, multiple: bool, ) -> Result<BipartiteGraph>

Creates a bipartite graph from a bipartite adjacency matrix.

For an n × m matrix, the graph has n vertices of type false (the rows, ids 0..n) followed by m vertices of type true (the columns, ids n..n+m). If multiple is false, one edge is created for every non-zero element; if it is true, element (i, j) gives the number of edges between row i and column j (fractional parts are discarded; negative, infinite and NaN entries are an error). In directed graphs mode gives the directions: NeighborMode::Out from rows to columns, NeighborMode::In from columns to rows, NeighborMode::All mutual edges.

Binds igraph_biadjacency. Time complexity: O(n·m) plus the number of created edges.

See also Graph::adjacency for ordinary (square) adjacency matrices, and get_biadjacency for the inverse conversion.

§Errors

ErrorKind::InvalidValue if multiple is true and an entry is negative, not finite, or too large to be an edge count.

§Examples
use igraph::prelude::*;
let m = Matrix::from_rows(&[[0.0, 1.0, 2.0], [1.0, 0.0, 0.0]]).unwrap();
let b = Graph::biadjacency(&m, false, NeighborMode::All, true).unwrap();
assert_eq!(b.types, vec![false, false, true, true, true]);
let mut edges = b.graph.edge_list();
edges.sort();
assert_eq!(edges, vec![(0, 3), (0, 4), (0, 4), (1, 2)]);
Source

pub fn weighted_biadjacency( biadjmatrix: &Matrix, directed: bool, mode: NeighborMode, ) -> Result<WeightedBipartiteGraph>

Creates a weighted bipartite graph from a bipartite adjacency matrix.

Like biadjacency, but a single edge is created for every non-zero element, and the element becomes its weight (any real value, including negative, infinite and NaN ones). With NeighborMode::All in directed graphs, both edges of a mutual pair get the same weight.

Binds igraph_weighted_biadjacency. Time complexity: O(n·m).

§Examples
use igraph::prelude::*;
let m = Matrix::from_rows(&[[0.0, -4.5, 2.3], [-0.1, 0.0, 0.0]]).unwrap();
let w = Graph::weighted_biadjacency(&m, false, NeighborMode::All).unwrap();
assert_eq!(w.graph.ecount(), 3);
let mut weights = w.weights.clone();
weights.sort_by(f64::total_cmp);
assert_eq!(weights, vec![-4.5, -0.1, 2.3]);
Source

pub fn get_biadjacency( &self, types: &[bool], weights: Option<&[f64]>, ) -> Result<Biadjacency>

The bipartite adjacency matrix of the graph (the inverse of biadjacency).

Rows correspond to vertices of type false, columns to vertices of type true, both in increasing id order (their ids are returned in Biadjacency::row_ids and Biadjacency::col_ids). Element (i, j) is the number of edges between the two vertices, regardless of their direction, or the sum of their weights if weights is given. Edges within a class are ignored, with a warning.

Binds igraph_get_biadjacency. Time complexity: O(|E|).

See also Graph::get_adjacency for the full |V| × |V| adjacency matrix: the bipartite one is its off-diagonal block.

§Errors

ErrorKind::InvalidValue if types or weights have the wrong length.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (2, 1), (2, 3)], 4, false).unwrap();
let b = g.get_biadjacency(&[false, true, false, true], Some(&[1.0, 2.0, 5.0])).unwrap();
assert_eq!(b.matrix.to_rows(), vec![vec![1.0, 0.0], vec![2.0, 5.0]]);
assert_eq!((b.row_ids, b.col_ids), (vec![0, 2], vec![1, 3]));
Source

pub fn bipartite_projection_size( &self, types: &[bool], ) -> Result<ProjectionSize>

The number of vertices and edges of the two projections, without computing them.

Useful to estimate the memory needed by bipartite_projection on large graphs. The first projection is the one of the vertices of type false.

Binds igraph_bipartite_projection_size.

§Errors

ErrorKind::InvalidValue if types has the wrong length or an edge joins vertices of the same type.

§Examples
use igraph::prelude::*;
let k23 = Graph::full_bipartite(2, 3, false, NeighborMode::All).unwrap();
let s = k23.graph.bipartite_projection_size(&k23.types).unwrap();
assert_eq!((s.vcount1, s.ecount1, s.vcount2, s.ecount2), (2, 1, 3, 3));
Source

pub fn bipartite_projection( &self, types: &[bool], probe1: Option<VertexId>, ) -> Result<BipartiteProjection>

Both one-mode projections of a bipartite graph.

The projection onto a class has one vertex per vertex of that class (in increasing order of the original ids), and two vertices are connected if they have at least one common neighbor in the other class; the number of common neighbors is returned as the edge multiplicity. Edge directions are ignored and the projections are undirected simple graphs.

By default (probe1 = None), proj1 is the projection of the vertices of type false. If probe1 is Some(v), proj1 is the projection containing vertex v instead.

Binds igraph_bipartite_projection. Time complexity: O(|V|·d²+|E|), d being the average degree.

§Errors

ErrorKind::InvalidValue if types has the wrong length or an edge joins vertices of the same type; ErrorKind::InvalidVertexId if probe1 is not a vertex.

§Examples
use igraph::prelude::*;
// Two papers (0, 1) and three authors (2, 3, 4).
let types = [false, false, true, true, true];
let g = Graph::create_bipartite(&types, &[(0, 2), (0, 3), (1, 3), (1, 4)], false).unwrap();
let p = g.bipartite_projection(&types, None).unwrap();
// The papers share author 3.
assert_eq!(p.proj1.edge_list(), vec![(0, 1)]);
// Co-authorship: 2-3 and 3-4 (projected ids 0, 1, 2).
assert_eq!(p.proj2.edge_list(), vec![(0, 1), (1, 2)]);
assert_eq!(p.multiplicity2, vec![1, 1]);
Source

pub fn bipartite_projection_of( &self, types: &[bool], kind: bool, ) -> Result<(Graph, Vec<i64>)>

The one-mode projection onto the vertices of type kind, with its edge multiplicities.

It computes only one of the two projections of bipartite_projection, saving time and memory when the other one is not needed.

Binds igraph_bipartite_projection.

§Errors

ErrorKind::InvalidValue if types has the wrong length or an edge joins vertices of the same type.

§Examples
use igraph::prelude::*;
let star = Graph::full_bipartite(1, 4, false, NeighborMode::All).unwrap();
// The four leaves all share the center: they form a K4.
let (leaves, mult) = star.graph.bipartite_projection_of(&star.types, true).unwrap();
assert_eq!((leaves.vcount(), leaves.ecount()), (4, 6));
assert!(mult.iter().all(|&m| m == 1));
Source

pub fn bipartite_game_gnp( n1: usize, n2: usize, p: f64, options: &BipartiteGameOptions, ) -> Result<BipartiteGraph>

A random bipartite graph from the G(n1, n2, p) model.

Every possible edge between the n1 bottom vertices (type false, ids 0..n1) and the n2 top vertices (type true) is realized independently with probability p. When multi-edges are allowed (see BipartiteGameOptions), p is the expected number of edges between each pair and may exceed 1.

Binds igraph_bipartite_game_gnp. Uses the thread’s default random number generator (see crate::rng). Time complexity: O(|V|+|E|).

See also Graph::erdos_renyi_game_gnp, the one-mode version, and bipartite_game_gnm for a fixed number of edges.

Self-loops are impossible in a bipartite graph, so EdgeTypeSw::Loops behaves like EdgeTypeSw::Simple.

§Errors

ErrorKind::InvalidValue if p is NaN, infinite, negative, or larger than 1 without multi-edges; ErrorKind::Unimplemented if BipartiteGameOptions::edge_labeled is set without multi-edges (igraph 1.0.0 and 1.0.1 implement the edge-labeled G(n1, n2, p) model only for multigraphs).

§Examples
use igraph::prelude::*;
rng::seed(7).unwrap();
let b = Graph::bipartite_game_gnp(10, 20, 0.3, &Default::default()).unwrap();
assert_eq!(b.graph.vcount(), 30);
assert!(b.graph.is_bipartite().unwrap());
let full = Graph::bipartite_game_gnp(3, 4, 1.0, &Default::default()).unwrap();
assert_eq!(full.graph.ecount(), 12);
Source

pub fn bipartite_game_gnm( n1: usize, n2: usize, m: usize, options: &BipartiteGameOptions, ) -> Result<BipartiteGraph>

A uniformly random bipartite graph from the G(n1, n2, m) model: n1 bottom vertices (type false), n2 top vertices (type true) and exactly m edges.

With BipartiteGameOptions::edge_labeled, sampling is uniform over ordered edge lists rather than over graphs (this matters only when multi-edges are allowed).

Binds igraph_bipartite_game_gnm. Uses the thread’s default random number generator (see crate::rng). Time complexity: O(|V|+|E|).

See also Graph::erdos_renyi_game_gnm, the one-mode version, and bipartite_iea_game for a faster, non-uniform multigraph model.

§Errors

ErrorKind::InvalidValue if m is positive while n1 or n2 is zero, or, without multi-edges, if m is larger than the number of possible edges (n1·n2, or 2·n1·n2 for directed graphs with NeighborMode::All).

§Examples
use igraph::prelude::*;
rng::seed(1).unwrap();
let b = Graph::bipartite_game_gnm(5, 5, 12, &Default::default()).unwrap();
assert_eq!(b.graph.ecount(), 12);
assert!(Graph::bipartite_game_gnm(2, 2, 5, &Default::default()).is_err());
Source

pub fn bipartite_iea_game( n1: usize, n2: usize, m: usize, directed: bool, mode: NeighborMode, ) -> Result<BipartiteGraph>

A random bipartite multigraph by independent edge assignment (IEA): each of the m edges joins a uniformly random bottom–top pair, independently of the others.

There are n1 bottom vertices (type false) and n2 top vertices (type true). The resulting multigraphs are not uniformly sampled: a graph has probability proportional to 1 / ∏ A_ij!, so all simple graphs are equally likely. mode directs the edges of directed graphs as in BipartiteGameOptions::mode.

This model is experimental in igraph (1.0.0 and 1.0.1). It is the same as bipartite_game_gnm with multi-edges allowed and BipartiteGameOptions::edge_labeled set. Uses the thread’s default random number generator (see crate::rng). See also Graph::iea_game, the one-mode version.

Implements igraph_bipartite_iea_game through igraph_bipartite_game_gnm: in igraph 1.0.0 and 1.0.1 the C function forwards to the edge-unlabeled multigraph G(n1, n2, m) model by mistake, and so samples multigraphs uniformly instead of by independent edge assignment (unlike its one-mode counterpart igraph_iea_game). This wrapper calls the edge-labeled model directly, which is the documented IEA process. Time complexity: O(|V|+|E|).

§Examples
use igraph::prelude::*;
rng::seed(3).unwrap();
// 100 edges between 2 x 2 vertices: plenty of multi-edges.
let b = Graph::bipartite_iea_game(2, 2, 100, false, NeighborMode::Out).unwrap();
assert_eq!(b.graph.ecount(), 100);
assert!(b.graph.edge_list().iter().all(|&(u, v)| u < 2 && v >= 2));
Source

pub fn is_matching( &self, types: Option<&[bool]>, matching: &[VertexId], ) -> Result<bool>

Whether matching is a valid matching of the graph.

matching has one entry per vertex: the vertex it is matched to, or UNMATCHED (-1). It is valid if its length is the number of vertices, it is symmetric (matching[matching[i]] == i), and every matched pair is joined by an edge (directions are ignored). If types is given, matched vertices must also have different types. An invalid vector yields Ok(false), not an error.

Binds igraph_is_matching. Time complexity: O(|V|+|E|).

§Examples
use igraph::prelude::*;
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert!(path.is_matching(None, &[1, 0, 3, 2]).unwrap());
assert!(path.is_matching(None, &[-1, 2, 1, -1]).unwrap());
assert!(!path.is_matching(None, &[3, -1, -1, 0]).unwrap()); // no edge 0-3
Source

pub fn is_maximal_matching( &self, types: Option<&[bool]>, matching: &[VertexId], ) -> Result<bool>

Whether matching is a maximal matching of the graph: a valid matching (see is_matching) that cannot be extended, i.e. no two unmatched vertices are adjacent (if types is given, only edges joining vertices of different types count).

A maximal matching is not necessarily maximum (largest): on the path 0-1-2-3, matching only 1-2 is maximal but not maximum.

Binds igraph_is_maximal_matching. Time complexity: O(|V|+|E|).

§Examples
use igraph::prelude::*;
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert!(path.is_maximal_matching(None, &[-1, 2, 1, -1]).unwrap());
assert!(!path.is_maximal_matching(None, &[1, 0, -1, -1]).unwrap()); // 2-3 can be added
Source

pub fn maximum_bipartite_matching( &self, types: &[bool], weights: Option<&[f64]>, ) -> Result<BipartiteMatching>

A maximum matching of a bipartite graph: the largest set of edges no two of which share a vertex or, with weights, the matching of largest total weight.

Unweighted matchings are found with a push-relabel algorithm, in O(√|V|·|E|) time; weighted ones with the Hungarian algorithm, in O(|V|·|E|) time. The weighted algorithm is reliable only for integer weights; slacks of at most f64::EPSILON are considered zero (use maximum_bipartite_matching_eps to tune this tolerance). Edge directions are ignored.

A weighted maximum matching maximizes the total weight, not the number of pairs: edges of negative weight are never chosen, so BipartiteMatching::size may be smaller than in the unweighted case.

The size of an unweighted maximum matching equals the maximum flow from one class to the other with unit capacities (see Graph::maxflow_value) and, by König’s theorem, the size of a minimum vertex cover. Check a result with is_matching and is_maximal_matching.

Binds igraph_maximum_bipartite_matching.

§Errors

ErrorKind::InvalidValue if types or weights have the wrong length, an edge joins two vertices of the same type (self-loops included), or a weight is not finite. These are checked on the Rust side, because igraph’s own checks are incomplete (in igraph 1.0.0 and 1.0.1): it can silently return a wrong matching, or loop forever on infinite weights.

§Examples
use igraph::prelude::*;
// Weighted example from igraph's unit tests.
let types: Vec<bool> = (0..10).map(|i| i >= 5).collect();
let g = Graph::from_edges(&[(0, 8), (2, 7), (3, 7), (3, 8), (4, 5), (4, 9)], 10, false).unwrap();
let w = [8.0, 5.0, 9.0, 18.0, 20.0, 13.0];
let m = g.maximum_bipartite_matching(&types, Some(&w)).unwrap();
assert_eq!(m.weight, 43.0); // 2-7, 3-8 and 4-5: 5 + 18 + 20
assert_eq!(m.pairs(), vec![(2, 7), (3, 8), (4, 5)]);
// Vertices 0 and 3 compete for 8, 2 and 3 for 7: at most 3 pairs.
let m = g.maximum_bipartite_matching(&types, None).unwrap();
assert_eq!((m.size, m.weight), (3, 3.0));
Source

pub fn maximum_bipartite_matching_eps( &self, types: &[bool], weights: Option<&[f64]>, eps: f64, ) -> Result<BipartiteMatching>

Like maximum_bipartite_matching, with an explicit tolerance eps for the equality tests of the weighted algorithm (ignored if weights is None).

An edge is considered tight when its slack (the difference between the sum of the dual labels of its endpoints and its weight) is at most eps; a small positive value avoids the accumulation of rounding errors with fractional weights, while with integer weights eps = 0.0 is safe. A negative eps is clamped to zero by igraph (with a warning).

Binds igraph_maximum_bipartite_matching.

§Errors

As maximum_bipartite_matching; also ErrorKind::InvalidValue if eps is NaN (igraph would never terminate).

§Examples
use igraph::prelude::*;
let k22 = Graph::full_bipartite(2, 2, false, NeighborMode::All).unwrap();
// Edges 0-2, 0-3, 1-2, 1-3: the diagonal 0-2, 1-3 is the best.
let w = [0.5, 0.25, 0.25, 0.5];
let m = k22.graph.maximum_bipartite_matching_eps(&k22.types, Some(&w), 1e-9).unwrap();
assert_eq!(m.pairs(), vec![(0, 2), (1, 3)]);
assert_eq!(m.weight, 1.0);
Source§

impl igraph_t

Source

pub fn closeness<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: NeighborMode, weights: Option<&[f64]>, normalized: bool, ) -> Result<Vec<f64>>

Closeness centrality of the selected vertices (igraph_closeness).

The closeness of a vertex is the inverse of the mean distance to (or from) all other vertices; it measures how easily other vertices are reached from it. mode selects the paths in directed graphs: NeighborMode::Out uses distances from the vertex, NeighborMode::In distances to it and NeighborMode::All ignores directions. With weights, path lengths are the sums of edge weights.

If normalized is true the result is the inverse of the mean distance, otherwise the inverse of the sum of distances.

Closeness is meaningful for connected graphs only: in disconnected graphs igraph only considers reachable vertices (in undirected graphs this is closeness computed per component). Isolated vertices get NaN. Use closeness_reachability to detect disconnectedness, or consider harmonic_centrality.

Time complexity: O(n|E|) unweighted, O(n|E|log|V| + |V|) weighted, for n requested vertices.

See also distances for the underlying distance matrix and eccentricity for the largest (instead of the mean) distance from a vertex.

Binds igraph_closeness.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue for weights of the wrong length or containing NaN.

§Examples
use igraph::prelude::*;

// Path 0 - 1 - 2: the middle vertex is at distance 1 from both ends.
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert_eq!(g.closeness(.., NeighborMode::All, None, false).unwrap(), vec![1.0 / 3.0, 0.5, 1.0 / 3.0]);
assert_eq!(g.closeness(1, NeighborMode::All, None, true).unwrap(), vec![1.0]);
Source

pub fn closeness_cutoff<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: NeighborMode, weights: Option<&[f64]>, normalized: bool, cutoff: Option<f64>, ) -> Result<Vec<f64>>

Range-limited closeness centrality (igraph_closeness_cutoff).

Like closeness, but only shortest paths of length at most cutoff are considered (vertices farther away count as unreachable). None (or a negative cutoff) computes the exact closeness. Smaller cutoffs make the computation faster.

Binds igraph_closeness_cutoff.

§Errors

As for closeness.

§Examples
use igraph::prelude::*;

// Path 0 - 1 - 2 - 3: within distance 1, vertex 0 only reaches vertex 1.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let c = g.closeness_cutoff(0, NeighborMode::All, None, true, Some(1.0)).unwrap();
assert_eq!(c, vec![1.0]);
Source

pub fn closeness_reachability<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: NeighborMode, weights: Option<&[f64]>, normalized: bool, cutoff: Option<f64>, ) -> Result<Closeness>

Closeness centrality with reachability information (igraph_closeness_cutoff with all outputs).

Besides the (possibly range-limited, see closeness_cutoff) closeness scores, it returns the number of vertices reachable from each requested vertex and whether all vertices were reachable, see Closeness. These make it possible to compute the generalizations of closeness to disconnected graphs that rescale scores by the size of the reachable set.

Binds igraph_closeness_cutoff.

§Errors

As for closeness.

§Examples
use igraph::prelude::*;

// Two disjoint edges: closeness is computed within each component.
let g = Graph::from_edges(&[(0, 1), (2, 3)], 4, false).unwrap();
let c = g.closeness_reachability(.., NeighborMode::All, None, true, None).unwrap();
assert_eq!(c.scores, vec![1.0; 4]);
assert_eq!(c.reachable_count, vec![1; 4]);
assert!(!c.all_reachable);
Source

pub fn harmonic_centrality<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: NeighborMode, weights: Option<&[f64]>, normalized: bool, ) -> Result<Vec<f64>>

Harmonic centrality of the selected vertices (igraph_harmonic_centrality).

The harmonic centrality of a vertex is the sum (or, if normalized, the mean over the other |V| - 1 vertices) of the inverse distances to all other vertices; unreachable vertices contribute zero, which makes this measure well-behaved on disconnected graphs, unlike closeness. mode and weights are as in closeness.

References: M. Marchiori and V. Latora, Harmony in the small-world, Physica A 285 (2000); S. Vigna and P. Boldi, Axioms for Centrality, Internet Mathematics 10 (2014).

Time complexity: O(n|E|) unweighted, O(n|E|log|V| + |V|) weighted.

See also global_efficiency: the mean of the normalized harmonic centralities of all vertices is the global efficiency of the graph.

Binds igraph_harmonic_centrality.

§Errors

As for closeness.

§Examples
use igraph::prelude::*;

// Path 0 - 1 - 2: vertex 0 has 1/1 + 1/2 = 1.5.
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert_eq!(g.harmonic_centrality(.., NeighborMode::All, None, false).unwrap(), vec![1.5, 2.0, 1.5]);
Source

pub fn harmonic_centrality_cutoff<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: NeighborMode, weights: Option<&[f64]>, normalized: bool, cutoff: Option<f64>, ) -> Result<Vec<f64>>

Range-limited harmonic centrality (igraph_harmonic_centrality_cutoff).

Like harmonic_centrality, but vertices farther than cutoff contribute zero. None (or a negative value) computes the exact harmonic centrality. Note that normalization still divides by |V| - 1.

Binds igraph_harmonic_centrality_cutoff.

§Errors

As for closeness.

§Examples
use igraph::prelude::*;

// With cutoff 1 the harmonic centrality is just degree / (n - 1).
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let h = g.harmonic_centrality_cutoff(.., NeighborMode::All, None, true, Some(1.0)).unwrap();
assert_eq!(h, vec![0.5, 1.0, 0.5]);
Source

pub fn betweenness<'a>( &self, weights: Option<&[f64]>, vids: impl Into<VertexSelector<'a>>, directed: bool, normalized: bool, ) -> Result<Vec<f64>>

Betweenness centrality of the selected vertices (igraph_betweenness).

The betweenness of a vertex v is the number of shortest paths passing through it; when two vertices are joined by several shortest paths, only the fraction of them passing through v is counted (Brandes’ algorithm). With weights, weighted shortest paths are used. directed tells whether to follow edge directions (ignored for undirected graphs). If normalized, scores are divided by the number of vertex pairs: n(n-1) ordered pairs when directed paths are used, n(n-1)/2 unordered pairs otherwise (note: not the (n-1)(n-2) convention of some textbooks).

vids only selects which scores are returned: internally the betweenness of all vertices is computed. Time complexity: O(|V||E|).

Reference: U. Brandes, A faster algorithm for betweenness centrality, J. Math. Sociol. 25(2), 163–177 (2001).

See also get_all_shortest_paths to list the shortest paths that are being counted.

Binds igraph_betweenness.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue for invalid weights.

§Examples
use igraph::prelude::*;

// Path 0 - 1 - 2 - 3: the inner vertices each lie on 2 shortest paths.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert_eq!(g.betweenness(None, .., false, false).unwrap(), vec![0.0, 2.0, 2.0, 0.0]);
Source

pub fn betweenness_cutoff<'a>( &self, weights: Option<&[f64]>, vids: impl Into<VertexSelector<'a>>, directed: bool, normalized: bool, cutoff: Option<f64>, ) -> Result<Vec<f64>>

Range-limited betweenness centrality (igraph_betweenness_cutoff).

Like betweenness, but only shortest paths of length at most cutoff are counted. None (or a negative value) computes the exact betweenness.

Binds igraph_betweenness_cutoff.

§Errors

As for betweenness.

§Examples
use igraph::prelude::*;

// With cutoff 2 only the paths 0-1-2 and 1-2-3 pass through inner vertices.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert_eq!(g.betweenness_cutoff(None, .., false, false, Some(2.0)).unwrap(), vec![0.0, 1.0, 1.0, 0.0]);
Source

pub fn betweenness_subset<'a>( &self, weights: Option<&[f64]>, sources: impl Into<VertexSelector<'a>>, targets: impl Into<VertexSelector<'a>>, vids: impl Into<VertexSelector<'a>>, directed: bool, ) -> Result<Vec<f64>>

Betweenness restricted to paths between a set of sources and a set of targets (igraph_betweenness_subset).

Only the shortest paths starting in sources and ending in targets are counted. Scores are returned for vids. In undirected graphs each source-target pair contributes with weight 1/2, so that with sources == targets (where every pair is met from both ends) the result agrees with betweenness; in particular selecting all vertices as sources and targets gives the ordinary betweenness. Normalization is not implemented by igraph for this variant, so the scores are always raw path counts. Time complexity: O(|S||E|), S being the source set.

Binds igraph_betweenness_subset.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex in any of the selectors.

§Examples
use igraph::prelude::*;

// Directed path 0 -> 1 -> 2 -> 3, only paths from 0 to 3 count.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
assert_eq!(g.betweenness_subset(None, 0, 3, .., true).unwrap(), vec![0.0, 1.0, 1.0, 0.0]);
// Undirected, the single pair {0, 3} counts 1/2.
let u = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert_eq!(u.betweenness_subset(None, 0, 3, .., false).unwrap(), vec![0.0, 0.5, 0.5, 0.0]);
// With all vertices as sources and targets it is the usual betweenness.
assert_eq!(u.betweenness_subset(None, .., .., .., false).unwrap(), u.betweenness(None, .., false, false).unwrap());
Source

pub fn edge_betweenness<'a>( &self, weights: Option<&[f64]>, eids: impl Into<EdgeSelector<'a>>, directed: bool, normalized: bool, ) -> Result<Vec<f64>>

Betweenness centrality of the selected edges (igraph_edge_betweenness).

The betweenness of an edge is the number of shortest paths passing through it (fractionally, when there are several shortest paths). Parameters are as in betweenness; eids only selects the returned scores. Removing the edge with the highest betweenness is the basic step of the Girvan–Newman community detection method, available as community_edge_betweenness. Time complexity: O(|V||E|).

Binds igraph_edge_betweenness.

§Errors

ErrorKind::InvalidEdgeId for an invalid edge, ErrorKind::InvalidValue for invalid weights.

§Examples
use igraph::prelude::*;

// Two triangles joined by the bridge 2 - 3: all 9 cross pairs use it.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3), (3, 4), (4, 5), (3, 5)], 6, false).unwrap();
let eb = g.edge_betweenness(None, .., false, false).unwrap();
assert_eq!(eb[3], 9.0);
Source

pub fn edge_betweenness_cutoff<'a>( &self, weights: Option<&[f64]>, eids: impl Into<EdgeSelector<'a>>, directed: bool, normalized: bool, cutoff: Option<f64>, ) -> Result<Vec<f64>>

Range-limited edge betweenness (igraph_edge_betweenness_cutoff).

Like edge_betweenness, counting only shortest paths of length at most cutoff (None = no limit).

Binds igraph_edge_betweenness_cutoff.

§Errors

As for edge_betweenness.

§Examples
use igraph::prelude::*;

// With cutoff 1, every edge only carries the path between its endpoints.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert_eq!(g.edge_betweenness_cutoff(None, .., false, false, Some(1.0)).unwrap(), vec![1.0; 3]);
Source

pub fn edge_betweenness_subset<'a>( &self, weights: Option<&[f64]>, sources: impl Into<VertexSelector<'a>>, targets: impl Into<VertexSelector<'a>>, eids: impl Into<EdgeSelector<'a>>, directed: bool, ) -> Result<Vec<f64>>

Edge betweenness restricted to paths between a set of sources and a set of targets (igraph_edge_betweenness_subset).

Only shortest paths from sources to targets are counted; scores are returned for eids. As in betweenness_subset, in undirected graphs each source-target pair has weight 1/2. Normalization is not implemented by igraph for this variant. Time complexity: O(|S||E|).

Binds igraph_edge_betweenness_subset.

§Errors

ErrorKind::InvalidVertexId for an invalid source or target vertex.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
assert_eq!(g.edge_betweenness_subset(None, 0, 2, .., true).unwrap(), vec![1.0, 1.0, 0.0]);
Source

pub fn pagerank<'a>( &self, weights: Option<&[f64]>, vids: impl Into<VertexSelector<'a>>, options: &PageRankOptions, ) -> Result<EigenScores>

Google PageRank of the selected vertices (igraph_pagerank).

The PageRank of a vertex is the fraction of time a random walker spends on it. The walker follows out-edges with probabilities proportional to their weights (which must be non-negative), and at each step restarts from a uniformly random vertex with probability 1 - damping; it also restarts when stuck in a sink vertex. Scores of all vertices sum to one. In undirected graphs PageRank tends to be proportional to degree as the damping approaches 1, so it is mostly useful for directed graphs. See PageRankOptions for the damping, directedness and algorithm.

vids only selects the returned scores: all of them are computed anyway. Time complexity: usually O(|E|).

Reference: S. Brin and L. Page, The Anatomy of a Large-Scale Hypertextual Web Search Engine, WWW7 (1998).

Binds igraph_pagerank.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue for a damping outside [0, 1] (or NaN) or invalid (negative, NaN or infinite) weights. These are checked on the Rust side: igraph lets a NaN damping and infinite weights through, and then either aborts the process (ARPACK) or returns NaN scores (PRPACK). Huge finite weights are rescaled (PageRank does not depend on the scale of the weights).

§Examples
use igraph::prelude::*;
use igraph::centrality::PageRankOptions;

// A directed cycle: by symmetry every vertex has PageRank 1/4.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, true).unwrap();
let pr = g.pagerank(None, .., &PageRankOptions::default()).unwrap();
assert_eq!(pr.value, 1.0);
assert!(pr.scores.iter().all(|&p| (p - 0.25).abs() < 1e-12));
Source

pub fn personalized_pagerank<'a>( &self, weights: Option<&[f64]>, reset: Option<&[f64]>, vids: impl Into<VertexSelector<'a>>, options: &PageRankOptions, ) -> Result<EigenScores>

Personalized PageRank with an arbitrary restart distribution (igraph_personalized_pagerank).

Like pagerank, but when the random walker restarts (with probability 1 - damping, or when stuck in a sink), the new starting vertex is drawn from the distribution reset (one non-negative entry per vertex, not necessarily normalized) instead of uniformly. reset = None gives the ordinary PageRank.

Binds igraph_personalized_pagerank.

§Errors

As for pagerank; also ErrorKind::InvalidValue if reset has the wrong length, negative entries or sums to zero.

§Examples
use igraph::prelude::*;
use igraph::centrality::PageRankOptions;

// Always restarting from the leaf 0 of a path breaks its symmetry.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let reset = [1.0, 0.0, 0.0, 0.0];
let pr = g.personalized_pagerank(None, Some(&reset), .., &PageRankOptions::default()).unwrap();
assert!(pr.scores[0] > pr.scores[3] && pr.scores[1] > pr.scores[2]);
Source

pub fn personalized_pagerank_vs<'a>( &self, weights: Option<&[f64]>, reset_vids: impl Into<VertexSelector<'a>>, vids: impl Into<VertexSelector<'a>>, options: &PageRankOptions, ) -> Result<EigenScores>

Personalized PageRank restarting from a set of vertices (igraph_personalized_pagerank_vs).

Like personalized_pagerank, with the restart vertex chosen uniformly among reset_vids (duplicates count multiple times). Restarting always from a single vertex gives a “proximity to this vertex” measure, widely used for recommendations.

Binds igraph_personalized_pagerank_vs.

§Errors

As for pagerank; an empty or invalid reset_vids is an error too.

§Examples
use igraph::prelude::*;
use igraph::centrality::PageRankOptions;

let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let pr = g.personalized_pagerank_vs(None, 3, .., &PageRankOptions::default()).unwrap();
assert!(pr.scores[3] > pr.scores[0]);
Source

pub fn eigenvector_centrality( &self, mode: NeighborMode, weights: Option<&[f64]>, ) -> Result<EigenScores>

Eigenvector centrality of all vertices (igraph_eigenvector_centrality).

The eigenvector centrality of a vertex is proportional to the sum of the centralities of its neighbors: it is the eigenvector of the adjacency matrix belonging to the largest positive eigenvalue, which is non-negative when weights are non-negative. Scores are scaled so that the maximum is 1 (unless all are zero). In undirected graphs a self-loop counts twice on the diagonal; weights of parallel edges add up.

mode matters for directed graphs only: NeighborMode::Out (the standard choice) gives each vertex the sum of the centralities of the vertices pointing to it (left eigenvector); NeighborMode::In the sum over the vertices it points to; NeighborMode::All ignores directions.

The measure is meaningful only for (strongly) connected graphs: in a disconnected undirected graph all but one component typically get zeros (igraph emits a warning). Directed acyclic graphs have no positive eigenvalue: the returned value is then zero. For directed graphs, consider hub_and_authority_scores. Time complexity: usually O(|V| + |E|).

See also connected_components to split a disconnected graph first (and is_dag to detect the acyclic case), and eigen_adjacency for other eigenpairs of the adjacency matrix.

Binds igraph_eigenvector_centrality.

§Errors

ErrorKind::InvalidValue for invalid weights (wrong length, NaN or infinite: igraph does not check the latter and ARPACK would abort the process); ErrorKind::Arpack if the eigensolver fails. Weights so large that the iterations could overflow (absolute sum above 1e150) are divided by a power of two before the call, and the eigenvalue is scaled back (it may then be infinite, when it is not representable): the scores do not depend on the scale of the weights.

§Examples
use igraph::prelude::*;

// Weighted star with center 0 and weights 1..9 (igraph's example).
let edges: Vec<(i64, i64)> = (1..10).map(|i| (0, i)).collect();
let g = Graph::from_edges(&edges, 10, false).unwrap();
let w: Vec<f64> = (1..10).map(f64::from).collect();
let ec = g.eigenvector_centrality(NeighborMode::Out, Some(&w)).unwrap();
assert!((ec.value - 16.8819).abs() < 1e-4);
assert_eq!(ec.scores[0], 1.0);
assert!((ec.scores[1] - 0.0592349).abs() < 1e-6);
Source

pub fn hub_and_authority_scores( &self, weights: Option<&[f64]>, ) -> Result<HubAuthority>

Kleinberg’s hub and authority scores (HITS) (igraph_hub_and_authority_scores).

The authority score of a vertex is proportional to the sum of the hub scores of the vertices pointing to it, and its hub score to the sum of the authority scores of the vertices it points to. Hubs and authorities are the principal eigenvectors of A Aᵀ and Aᵀ A; igraph guarantees that the two returned vectors match (h = A a, a = Aᵀ h, up to scaling) even when the eigenvalue is degenerate. Both are scaled to have maximum 1. Edge weights should be non-negative (igraph warns otherwise).

In undirected graphs both vectors coincide with the eigenvector centrality (computed by it directly, with a warning) and value is the square of its eigenvalue. A graph without edges gives all-ones scores and value 0.

In extremely sparse graphs, where no single connected component dominates the graphs of A Aᵀ and Aᵀ A, the solution is not unique and many scores are zero: igraph then emits a warning (retrieve it with take_warnings) when more than 30% of the hub scores are zero (below 10 ε in absolute value), on directed graphs with at least 10 vertices. (In igraph 1.0.0 a rounding bug made it warn for any zero score; this was fixed in 1.0.1.)

Time complexity: usually O(|V|).

Reference: J. Kleinberg, Authoritative sources in a hyperlinked environment, J. ACM 46 (1999).

See also pagerank, another random-walk based ranking for directed graphs.

Binds igraph_hub_and_authority_scores.

§Errors

ErrorKind::InvalidValue for invalid weights (wrong length, NaN or infinite: igraph does not check the latter and ARPACK would abort the process); ErrorKind::Arpack if the eigensolver fails. Weights so large that the iterations could overflow (absolute sum above 1e150) are divided by a power of two before the call, and the eigenvalue is scaled back (it may then be infinite, when it is not representable): the scores do not depend on the scale of the weights.

§Examples
use igraph::prelude::*;

// Two pages linking to a third one: two perfect hubs, one authority.
let g = Graph::from_edges(&[(0, 2), (1, 2)], 3, true).unwrap();
let hits = g.hub_and_authority_scores(None).unwrap();
assert_eq!(hits.hubs, vec![1.0, 1.0, 0.0]);
assert_eq!(hits.authorities, vec![0.0, 0.0, 1.0]);
assert!((hits.value - 2.0).abs() < 1e-9);
Source

pub fn constraint<'a>( &self, vids: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, ) -> Result<Vec<f64>>

Burt’s constraint scores of the selected vertices (igraph_constraint).

Constraint measures how much a vertex’s ego network is closed: it is high when ego has few, or mutually strongly related (redundant), contacts, and low for vertices bridging structural holes. Formally C[i] = Σ_{j ≠ i} (p[i,j] + Σ_{q ≠ i,j} p[i,q] p[q,j])² over the neighbors j, with proportional tie strengths p[i,j] = (a[i,j] + a[j,i]) / Σ_k (a[i,k] + a[k,i]), a being the (weighted) adjacency matrix. It is undefined (NaN) for isolated vertices. Time complexity: O(|V| + |E| + n d²), d the average degree.

Reference: R. S. Burt, Structural holes and good ideas, American Journal of Sociology 110, 349–399 (2004).

See also transitivity_local_undirected, the local clustering coefficient, a related measure of ego-network closure.

Binds igraph_constraint.

§Errors

ErrorKind::InvalidVertexId for an invalid vertex, ErrorKind::InvalidValue for weights of the wrong length.

§Examples
use igraph::prelude::*;

// In a path, the end vertices are fully constrained by their only contact.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert_eq!(g.constraint(.., None).unwrap(), vec![1.0, 0.5, 0.5, 1.0]);
Source

pub fn convergence_degree(&self) -> Result<ConvergenceDegree>

Convergence degree of every edge (igraph_convergence_degree).

The input set of an edge is the set of vertices where the shortest paths passing through it originate, the output set where they terminate. The convergence degree is (|in| − |out|) / (|in| + |out|), in (-1, 1): positive values mark convergent edges (paths coming from many vertices and going to few), negative ones divergent edges. In undirected graphs the edge is oriented arbitrarily and the absolute value is reported. Time complexity: O(|V||E|).

Binds igraph_convergence_degree.

§Examples
use igraph::prelude::*;

// An in-star 1,2,3,4 -> 0 followed by 0 -> 5 (igraph's unit test).
let g = Graph::from_edges(&[(1, 0), (2, 0), (3, 0), (4, 0), (0, 5)], 6, true).unwrap();
let cd = g.convergence_degree().unwrap();
assert!((cd.result[4] - 2.0 / 3.0).abs() < 1e-9);
Source

pub fn centralization_degree( &self, mode: NeighborMode, loops: Loops, normalized: bool, ) -> Result<Centralization>

Degree centralization of the graph (igraph_centralization_degree).

Computes the degrees of all vertices (with mode for directed graphs and the loops counting convention, see Loops) and their centralization index, normalized by the theoretical maximum (the star) if normalized is true. Time complexity: O(|V| + |E|).

See also degree and maxdegree.

Binds igraph_centralization_degree.

§Examples
use igraph::prelude::*;

// A cycle is perfectly decentralized.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
let c = g.centralization_degree(NeighborMode::All, Loops::None, true).unwrap();
assert_eq!(c.scores, vec![2.0; 4]);
assert_eq!(c.centralization, 0.0);
assert_eq!(c.theoretical_max, 6.0);
Source

pub fn centralization_degree_tmax( &self, mode: NeighborMode, loops: Loops, ) -> Result<f64>

Theoretical maximum of degree centralization for graphs with the size and directedness of self (igraph_centralization_degree_tmax).

mode is ignored for undirected graphs. See the free function centralization_degree_tmax to specify the number of vertices directly.

Binds igraph_centralization_degree_tmax.

Source

pub fn centralization_betweenness( &self, directed: bool, normalized: bool, ) -> Result<Centralization>

Betweenness centralization of the graph (igraph_centralization_betweenness).

Computes the (unweighted) betweenness of all vertices, following directions if directed, and its centralization index, normalized by the theoretical maximum (the star) if normalized. Time complexity: O(|V||E|).

Binds igraph_centralization_betweenness.

§Examples
use igraph::prelude::*;

let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], 5, false).unwrap();
let c = star.centralization_betweenness(false, true).unwrap();
assert_eq!(c.centralization, 1.0);
Source

pub fn centralization_betweenness_tmax(&self, directed: bool) -> Result<f64>

Theoretical maximum of betweenness centralization for graphs with the size and directedness of self (igraph_centralization_betweenness_tmax).

directed is ignored for undirected graphs. See the free function centralization_betweenness_tmax to give the number of vertices.

Binds igraph_centralization_betweenness_tmax.

Source

pub fn centralization_closeness( &self, mode: NeighborMode, normalized: bool, ) -> Result<Centralization>

Closeness centralization of the graph (igraph_centralization_closeness).

Computes the (unweighted, normalized) closeness of all vertices, using mode for directed graphs, and its centralization index, normalized by the theoretical maximum (the star) if normalized. Time complexity: O(|V||E|).

Binds igraph_centralization_closeness.

§Examples
use igraph::prelude::*;

let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], 5, false).unwrap();
let c = star.centralization_closeness(NeighborMode::All, true).unwrap();
assert!((c.centralization - 1.0).abs() < 1e-12);
Source

pub fn centralization_closeness_tmax(&self, mode: NeighborMode) -> Result<f64>

Theoretical maximum of closeness centralization for graphs with the size and directedness of self (igraph_centralization_closeness_tmax).

mode is ignored for undirected graphs. See the free function centralization_closeness_tmax to give the number of vertices.

Binds igraph_centralization_closeness_tmax.

Source

pub fn centralization_eigenvector_centrality( &self, mode: NeighborMode, normalized: bool, ) -> Result<EigenvectorCentralization>

Eigenvector centralization of the graph (igraph_centralization_eigenvector_centrality).

Computes the (unweighted) eigenvector centrality of all vertices, scaled so that the maximum is 1, and its centralization index, normalized by the theoretical maximum if normalized. Note that eigenvector scores have no natural scale, so the centralization depends on the choice of scaling by the maximum (∞-norm). The most centralized undirected graph is a single edge (plus isolated vertices). mode is as in eigenvector_centrality.

Binds igraph_centralization_eigenvector_centrality.

§Errors

ErrorKind::Arpack if the eigensolver fails.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1)], 10, false).unwrap();
let c = g.centralization_eigenvector_centrality(NeighborMode::All, true).unwrap();
assert!((c.centralization - 1.0).abs() < 1e-9);
Source

pub fn centralization_eigenvector_centrality_tmax( &self, mode: NeighborMode, ) -> Result<f64>

Theoretical maximum of eigenvector centralization for graphs with the size and directedness of self (igraph_centralization_eigenvector_centrality_tmax).

mode is ignored for undirected graphs. See the free function centralization_eigenvector_centrality_tmax to give the number of vertices.

Binds igraph_centralization_eigenvector_centrality_tmax.

Source

pub fn local_scan_0( &self, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

Local scan statistic with k = 0 (igraph_local_scan_0).

By convention the 0-scan of a vertex is its degree (weights = None) or its strength (the sum of incident edge weights, as computed by strength). mode selects out-, in- or all edges in directed graphs.

Reference: C. E. Priebe et al., Scan Statistics on Enron Graphs, Comput. Math. Organ. Theory (2005).

Binds igraph_local_scan_0.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert_eq!(g.local_scan_0(None, NeighborMode::All).unwrap(), vec![1.0, 2.0, 1.0]);
assert_eq!(g.local_scan_0(Some(&[0.5, 2.0]), NeighborMode::All).unwrap(), vec![0.5, 2.5, 2.0]);
Source

pub fn local_scan_0_them( &self, them: &Graph, weights_them: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

“Them” local scan statistic with k = 0 (igraph_local_scan_0_them).

Neighborhoods are taken from self (“us”), but edges are counted (or their weights_them summed) in the graph them, which must have the same vertices and directedness: the result is, for every vertex, the number of them edges incident to it that also connect it in us. Useful to compare two snapshots of a network.

Binds igraph_local_scan_0_them.

§Errors

ErrorKind::InvalidValue if the two graphs differ in size or directedness, or weights_them does not have one entry per edge of them.

§Examples
use igraph::prelude::*;

let us = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let them = Graph::from_edges(&[(0, 1), (0, 2)], 3, false).unwrap();
assert_eq!(us.local_scan_0_them(&them, None, NeighborMode::All).unwrap(), vec![1.0, 1.0, 0.0]);
Source

pub fn local_scan_1_ecount( &self, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

Local scan statistic with k = 1 (igraph_local_scan_1_ecount).

For every vertex, the number of edges (or the sum of their weights) in the subgraph induced by its closed 1-neighborhood (the vertex and its neighbors along mode). For undirected simple graphs this is degree + number of triangles through the vertex (see count_adjacent_triangles).

Binds igraph_local_scan_1_ecount.

§Examples
use igraph::prelude::*;

// A triangle with a pendant vertex 3 attached to 2.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], 4, false).unwrap();
assert_eq!(g.local_scan_1_ecount(None, NeighborMode::All).unwrap(), vec![3.0, 3.0, 4.0, 1.0]);
Source

pub fn local_scan_1_ecount_them( &self, them: &Graph, weights_them: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

“Them” local scan statistic with k = 1 (igraph_local_scan_1_ecount_them).

For every vertex, the number of edges of them (or the sum of their weights_them) inside the closed 1-neighborhood of the vertex in self. The graphs must have the same vertices and directedness.

Binds igraph_local_scan_1_ecount_them.

§Errors

As for local_scan_0_them.

Source

pub fn local_scan_k_ecount( &self, k: usize, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

Local scan statistic for k-neighborhoods (igraph_local_scan_k_ecount).

For every vertex, the number of edges (or the sum of their weights) in the subgraph induced by the vertices within distance k along mode. k = 0 is special-cased to local_scan_0 (degree or strength), k = 1 to local_scan_1_ecount. The neighborhoods themselves are given by neighborhood with order = k.

Binds igraph_local_scan_k_ecount.

§Errors

ErrorKind::InvalidValue for weights of the wrong length.

§Examples
use igraph::prelude::*;

// In a path of 5 vertices, the 2-neighborhood of the center is everything.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
assert_eq!(g.local_scan_k_ecount(2, None, NeighborMode::All).unwrap(), vec![2.0, 3.0, 4.0, 3.0, 2.0]);
Source

pub fn local_scan_k_ecount_them( &self, them: &Graph, k: usize, weights_them: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

“Them” local scan statistic for k-neighborhoods (igraph_local_scan_k_ecount_them).

For every vertex, the number of edges of them (or the sum of their weights_them) inside the k-neighborhood of the vertex computed in self. The graphs must have the same vertices and directedness.

Binds igraph_local_scan_k_ecount_them.

§Errors

As for local_scan_0_them.

Source

pub fn local_scan_subset_ecount<S: AsRef<[VertexId]>>( &self, weights: Option<&[f64]>, subsets: &[S], ) -> Result<Vec<f64>>

Edge counts in the subgraphs induced by arbitrary vertex subsets (igraph_local_scan_subset_ecount).

Returns, for each subset, the number of edges (or the sum of their weights) of the subgraph it induces. Multi-edges and self-loops count (a loop counts once). Each subset should be a set: igraph does not deduplicate, so a repeated vertex makes its incident edges count more than once. Without weights, the count for a duplicate-free subset equals the edge count of the corresponding induced_subgraph. Time complexity: O(Σ_S Σ_{v ∈ S} deg(v)).

Binds igraph_local_scan_subset_ecount.

§Errors

ErrorKind::InvalidValue for an invalid vertex in a subset (igraph reports it as IGRAPH_EINVAL, not as an invalid vertex id) or for weights of the wrong length.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], 4, false).unwrap();
let counts = g.local_scan_subset_ecount(None, &[vec![0, 1, 2], vec![2, 3], vec![]]).unwrap();
assert_eq!(counts, vec![3.0, 1.0, 0.0]);
Source

pub fn local_scan_neighborhood_ecount<S: AsRef<[VertexId]>>( &self, weights: Option<&[f64]>, neighborhoods: &[S], ) -> Result<Vec<f64>>

👎Deprecated since 1.0.1:

deprecated in igraph 0.10: use local_scan_subset_ecount

Edge counts in precomputed neighborhoods, one per vertex (igraph_local_scan_neighborhood_ecount).

Like local_scan_subset_ecount, but neighborhoods must contain exactly one vertex set per vertex of the graph. The C documentation marks this function as deprecated in favor of igraph_local_scan_subset_ecount since igraph 0.10, hence the Rust #[deprecated] attribute.

Binds igraph_local_scan_neighborhood_ecount.

§Errors

ErrorKind::InvalidValue if the number of neighborhoods differs from the number of vertices, or as for local_scan_subset_ecount.

Source§

impl igraph_t

Source

pub fn cliques( &self, sizes: impl RangeBounds<usize>, max_results: Option<usize>, ) -> Result<Vec<Vec<VertexId>>>

Finds all cliques whose size lies in sizes.

Every clique is reported, not only the maximal ones: a triangle contributes three cliques of size 1, three of size 2 and one of size 3. The search stops after max_results cliques when that is Some. If you only need the size of the largest clique, use clique_number instead; to only list the maximal ones, use maximal_cliques. Edge directions, self-loops and multi-edges are ignored.

See also is_clique to test a given vertex set, and list_triangles for cliques of size 3 only.

The implementation uses the Cliquer library (version 1.21) by Sampo Niskanen and Patric R. J. Östergård. Time complexity: exponential.

Binds igraph_cliques.

§Errors

ErrorKind::InvalidValue if sizes is empty. ErrorKind::Failure if called from inside a cliques_callback closure (see the module docs).

§Examples
use igraph::prelude::*;
// K4 has C(4, 3) = 4 triangles.
let k4 = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false)
    .unwrap();
assert_eq!(k4.cliques(3..=3, None).unwrap().len(), 4);
// 15 = 2^4 - 1 non-empty cliques in total, but we only want two of them.
assert_eq!(k4.cliques(.., None).unwrap().len(), 15);
assert_eq!(k4.cliques(.., Some(2)).unwrap().len(), 2);
Source

pub fn cliques_callback<F>( &self, sizes: impl RangeBounds<usize>, f: F, ) -> Result<()>
where F: FnMut(&[VertexId]) -> ControlFlow<()>,

Calls f for each clique whose size lies in sizes, without storing them.

The closure receives the vertex ids of the clique (a borrowed slice, copy it if you want to keep it) and returns ControlFlow::Continue to go on or ControlFlow::Break to stop the search early; stopping is not an error. Cliques are produced in the same order as by cliques. A panic inside f stops the search and is propagated to the caller.

The closure must not start another Cliquer-based search (cliques, clique_size_hist, cliques_callback itself or the weighted clique functions): such nested calls return an ErrorKind::Failure error, because igraph keeps the state of the running search in per-thread globals. Other functions, e.g. maximal_cliques, are fine.

Binds igraph_cliques_callback.

§Errors

ErrorKind::InvalidValue if sizes is empty. ErrorKind::Failure if called from inside another cliques_callback closure.

§Examples
use igraph::prelude::*;
use std::ops::ControlFlow;
let k5 = Graph::from_edges(
    &[(0, 1), (0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)],
    5,
    false,
)
.unwrap();
// Find the first triangle containing vertex 4, then stop.
let mut found = None;
k5.cliques_callback(3..=3, |c| {
    if c.contains(&4) {
        found = Some(c.to_vec());
        ControlFlow::Break(())
    } else {
        ControlFlow::Continue(())
    }
})
.unwrap();
assert!(found.unwrap().contains(&4));
Source

pub fn clique_size_hist( &self, sizes: impl RangeBounds<usize>, ) -> Result<Vec<usize>>

Counts the cliques of each size.

Element i of the result is the number of cliques of size i + 1; sizes below the lower bound of sizes get a zero count, and the vector ends at the largest size found (so it is empty for the null graph). The counts are those of cliques, without storing the cliques: hist[0] is the number of vertices, hist[1] the number of adjacent vertex pairs and hist[2] the number of triangles (see count_triangles). Uses Cliquer; time complexity: exponential.

Binds igraph_clique_size_hist.

§Errors

ErrorKind::InvalidValue if sizes is empty. ErrorKind::Failure if called from inside a cliques_callback closure.

§Examples
use igraph::prelude::*;
// A triangle: 3 vertices, 3 edges, 1 triangle.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
assert_eq!(g.clique_size_hist(..).unwrap(), vec![3, 3, 1]);
assert_eq!(g.clique_size_hist(2..).unwrap(), vec![0, 3, 1]);
Source

pub fn largest_cliques(&self) -> Result<Vec<Vec<VertexId>>>

Finds all the largest (maximum) cliques.

A clique is largest if no other clique has more vertices. Largest cliques are always maximal, but a maximal clique need not be largest. The null graph has no cliques at all. The maximal cliques are enumerated with the same algorithm as maximal_cliques, keeping only the largest. All returned cliques have clique_number vertices.

Time complexity: O(3^(|V|/3)) in the worst case.

Binds igraph_largest_cliques.

§Examples
use igraph::prelude::*;
// The two 5-cliques of Zachary's karate club share the four leaders
// 0, 1, 2, 3.
let karate = Graph::famous("Zachary").unwrap();
let mut largest = karate.largest_cliques().unwrap();
largest.iter_mut().for_each(|c| c.sort());
largest.sort();
assert_eq!(largest, vec![vec![0, 1, 2, 3, 7], vec![0, 1, 2, 3, 13]]);
Source

pub fn clique_number(&self) -> Result<usize>

The clique number ω(G): the size of the largest clique.

It is 0 for the null graph and 1 for a graph without edges. It is a lower bound for the chromatic number, i.e. the number of colors of any proper coloring (see vertex_coloring_greedy); the two are equal for perfect graphs. Time complexity: O(3^(|V|/3)) in the worst case.

Binds igraph_clique_number.

§Examples
use igraph::prelude::*;
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert_eq!(path.clique_number().unwrap(), 2);
assert_eq!(Graph::new(0, false).clique_number().unwrap(), 0);
Source

pub fn maximal_cliques( &self, sizes: impl RangeBounds<usize>, max_results: Option<usize>, ) -> Result<Vec<Vec<VertexId>>>

Finds the maximal cliques whose size lies in sizes.

A maximal clique is not a proper subset of any other clique. Isolated vertices are maximal cliques of size 1. No guarantees are given about the order of the cliques or of the vertices inside them.

The implementation is the Bron–Kerbosch variant with degeneracy ordering by Eppstein, Löffler and Strash (2010, https://arxiv.org/abs/1006.5440). Time complexity: O(d (n − d) 3^(d/3)) in the worst case, where d is the degeneracy of the graph (typically small for sparse graphs; it is the largest value of coreness).

See also maximal_cliques_count, maximal_cliques_hist and maximal_cliques_callback to avoid storing the cliques, and maximal_independent_vertex_sets for the maximal cliques of the complementer graph.

Binds igraph_maximal_cliques.

§Errors

ErrorKind::InvalidValue if sizes is empty.

§Examples
use igraph::prelude::*;
// A "bowtie": triangles {0,1,2} and {2,3,4}.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
    .unwrap();
let mut cliques = g.maximal_cliques(.., None).unwrap();
cliques.iter_mut().for_each(|c| c.sort());
cliques.sort();
assert_eq!(cliques, vec![vec![0, 1, 2], vec![2, 3, 4]]);
Source

pub fn maximal_cliques_callback<F>( &self, sizes: impl RangeBounds<usize>, f: F, ) -> Result<()>
where F: FnMut(&[VertexId]) -> ControlFlow<()>,

Calls f for each maximal clique whose size lies in sizes.

This is the streaming version of maximal_cliques: the cliques are not stored, which is useful for graphs with a huge number of them. The closure receives a borrowed slice (copy it to keep it) and returns ControlFlow::Break to stop the search early, which is not an error. A panic inside f is propagated to the caller.

Binds igraph_maximal_cliques_callback.

§Errors

ErrorKind::InvalidValue if sizes is empty.

§Examples
use igraph::prelude::*;
use std::ops::ControlFlow;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let mut sizes = vec![];
g.maximal_cliques_callback(.., |c| {
    sizes.push(c.len());
    ControlFlow::Continue(())
})
.unwrap();
sizes.sort();
assert_eq!(sizes, vec![2, 3]);
Source

pub fn maximal_cliques_count( &self, sizes: impl RangeBounds<usize>, ) -> Result<usize>

Counts the maximal cliques whose size lies in sizes, without storing them.

Same algorithm and complexity as maximal_cliques.

Binds igraph_maximal_cliques_count.

§Errors

ErrorKind::InvalidValue if sizes is empty.

§Examples
use igraph::prelude::*;
// Each edge of a 5-cycle is a maximal clique.
let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
assert_eq!(c5.maximal_cliques_count(..).unwrap(), 5);
assert_eq!(c5.maximal_cliques_count(3..).unwrap(), 0);
Source

pub fn maximal_cliques_hist( &self, sizes: impl RangeBounds<usize>, ) -> Result<Vec<usize>>

Counts the maximal cliques of each size.

Element i of the result is the number of maximal cliques of size i + 1 (size-1 maximal cliques are the isolated vertices); sizes below the lower bound of sizes get a zero count, and the vector ends at the largest size found. The counts sum to maximal_cliques_count, and the length of the unrestricted histogram is the clique_number.

Binds igraph_maximal_cliques_hist.

§Errors

ErrorKind::InvalidValue if sizes is empty.

§Examples
use igraph::prelude::*;
// A triangle with a pendant edge, plus an isolated vertex.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 5, false).unwrap();
assert_eq!(g.maximal_cliques_hist(..).unwrap(), vec![1, 1, 1]);
assert_eq!(g.maximal_cliques_hist(2..).unwrap(), vec![0, 1, 1]);
Source

pub fn maximal_cliques_subset( &self, subset: &[VertexId], sizes: impl RangeBounds<usize>, max_results: Option<usize>, ) -> Result<Vec<Vec<VertexId>>>

Finds the maximal cliques reached from a subset of initial vertices.

The Eppstein–Löffler–Strash algorithm processes the vertices one by one in degeneracy order, each time listing the maximal cliques that contain that vertex and none of the vertices processed before it. This function only runs the outer loop over the vertices in subset: running it on the parts of a partition of the vertex set and concatenating the results yields every maximal clique exactly once, which makes it a building block for parallel enumeration.

Binds igraph_maximal_cliques_subset (without its optional file output, see write_maximal_cliques).

§Errors

ErrorKind::InvalidValue if sizes is empty, ErrorKind::InvalidVertexId if subset contains an invalid id.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
    .unwrap();
let all = g.maximal_cliques(.., None).unwrap().len();
let evens = g.maximal_cliques_subset(&[0, 2, 4], .., None).unwrap();
let odds = g.maximal_cliques_subset(&[1, 3], .., None).unwrap();
assert_eq!(evens.len() + odds.len(), all);
Source

pub fn write_maximal_cliques<W: Write + ?Sized>( &self, out: &mut W, sizes: impl RangeBounds<usize>, max_results: Option<usize>, ) -> Result<()>

Writes the maximal cliques whose size lies in sizes to out, one clique per line as space-separated vertex ids.

This mirrors the C function, which streams the cliques to a FILE * instead of storing them: igraph writes into an in-memory stream that is then copied into out.

Binds igraph_maximal_cliques_file.

§Errors

ErrorKind::InvalidValue if sizes is empty, ErrorKind::File if writing to out (or to the intermediate stream) fails.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 4, false).unwrap();
let mut out = Vec::new();
g.write_maximal_cliques(&mut out, 2.., None).unwrap();
let text = String::from_utf8(out).unwrap();
assert_eq!(text.lines().count(), 1); // the triangle; vertex 3 is too small
Source

pub fn weighted_cliques( &self, weights: Option<&[f64]>, options: &WeightedCliqueOptions, ) -> Result<Vec<Vec<VertexId>>>

Finds the cliques whose total vertex weight lies in a range.

The weight of a clique is the sum of the weights of its vertices. Only positive integer weights are supported: fractional weights (and weight bounds) are truncated to their integer part, with a warning. Weights must be finite, at least 1, and sum to at most i32::MAX (Cliquer computes with C ints). With weights = None every vertex weighs 1, so weights are sizes and this is cliques or maximal_cliques. See WeightedCliqueOptions for the range, maximality and limit.

Uses the Cliquer library. Time complexity: exponential.

Binds igraph_weighted_cliques.

§Errors

ErrorKind::InvalidValue if weights has the wrong length or an invalid weight (see above), if a bound is not finite, or if the weight range is empty (max_weight < 1 or max_weight < min_weight). ErrorKind::Failure if called from inside a cliques_callback closure (see the module docs).

§Examples
use igraph::{cliques::WeightedCliqueOptions, prelude::*};
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let w = [1.0, 1.0, 1.0, 10.0];
// Maximal cliques weighing at least 5: only the edge {2, 3} (weight 11).
let opts = WeightedCliqueOptions::default().with_maximal(true).with_min_weight(5.0);
let mut heavy = g.weighted_cliques(Some(&w), &opts).unwrap();
heavy[0].sort();
assert_eq!(heavy, vec![vec![2, 3]]);
Source

pub fn largest_weighted_cliques( &self, weights: Option<&[f64]>, ) -> Result<Vec<Vec<VertexId>>>

Finds the cliques of largest total vertex weight.

Only positive integer weights are supported (fractional weights are truncated). With weights = None this is largest_cliques. The total weight of every returned clique is the weighted_clique_number. A heaviest clique is always maximal, but it need not be a largest one.

Uses the Cliquer library when weights is given. Time complexity: exponential.

Binds igraph_largest_weighted_cliques.

§Errors

ErrorKind::InvalidValue if weights has the wrong length or a weight that is not finite, below 1, or makes the total exceed i32::MAX. ErrorKind::Failure if weights is given and this is called from inside a cliques_callback closure.

§Examples
use igraph::prelude::*;
// A triangle {0, 1, 2} of light vertices and a heavy edge {2, 3}.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let mut heaviest = g.largest_weighted_cliques(Some(&[1.0, 1.0, 1.0, 10.0])).unwrap();
heaviest[0].sort();
assert_eq!(heaviest, vec![vec![2, 3]]);
// Unweighted, the triangle wins.
assert_eq!(g.largest_weighted_cliques(None).unwrap().len(), 1);
assert_eq!(g.largest_weighted_cliques(None).unwrap()[0].len(), 3);
Source

pub fn weighted_clique_number(&self, weights: Option<&[f64]>) -> Result<f64>

The weighted clique number: the largest total vertex weight of a clique.

Only positive integer weights are supported (fractional weights are truncated). With weights = None this is the clique_number. It is 0 for the null graph. Uses the Cliquer library when weights is given. Time complexity: exponential.

Binds igraph_weighted_clique_number.

§Errors

ErrorKind::InvalidValue if weights has the wrong length or a weight that is not finite, below 1, or makes the total exceed i32::MAX. ErrorKind::Failure if weights is given and this is called from inside a cliques_callback closure.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
assert_eq!(g.weighted_clique_number(Some(&[1.0, 1.0, 1.0, 10.0])).unwrap(), 11.0);
assert_eq!(g.weighted_clique_number(None).unwrap(), 3.0);
Source

pub fn independent_vertex_sets( &self, sizes: impl RangeBounds<usize>, max_results: Option<usize>, ) -> Result<Vec<Vec<VertexId>>>

Finds all independent vertex sets whose size lies in sizes.

A vertex set is independent if no two of its vertices are adjacent. As with cliques, every such set is reported, not only the maximal ones. If you only need the size of the largest one, use independence_number. The independent sets of G are exactly the cliques of its complementer. Edge directions are ignored.

See also is_independent_vertex_set to test a given vertex set.

The implementation was ported from the Very Nauty Graph Library by Keith Briggs and uses the algorithm of Tsukiyama, Ide, Ariyoshi and Shirakawa (SIAM J. Computing 6:505–517, 1977).

Binds igraph_independent_vertex_sets.

§Errors

ErrorKind::InvalidValue if sizes is empty.

§Examples
use igraph::prelude::*;
// In the path 0-1-2-3 the independent pairs are {0,2}, {0,3} and {1,3}.
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let mut pairs = path.independent_vertex_sets(2..=2, None).unwrap();
pairs.iter_mut().for_each(|s| s.sort());
pairs.sort();
assert_eq!(pairs, vec![vec![0, 2], vec![0, 3], vec![1, 3]]);
Source

pub fn largest_independent_vertex_sets(&self) -> Result<Vec<Vec<VertexId>>>

Finds all the largest (maximum) independent vertex sets.

An independent set is largest if no other independent set has more vertices; its size is the independence number. Largest independent sets are always maximal (see maximal_independent_vertex_sets), but not conversely. Edge directions are ignored (with a warning).

Uses the algorithm of Tsukiyama et al. (1977), ported from the Very Nauty Graph Library.

Binds igraph_largest_independent_vertex_sets.

§Examples
use igraph::prelude::*;
// The Petersen graph has exactly five independent sets of size 4.
let petersen = Graph::famous("Petersen").unwrap();
let largest = petersen.largest_independent_vertex_sets().unwrap();
assert_eq!(largest.len(), 5);
assert!(largest.iter().all(|s| s.len() == 4));
assert!(petersen.is_independent_vertex_set(&largest[0]).unwrap());
Source

pub fn maximal_independent_vertex_sets( &self, sizes: impl RangeBounds<usize>, max_results: Option<usize>, ) -> Result<Vec<Vec<VertexId>>>

Finds the maximal independent vertex sets whose size lies in sizes.

A maximal independent set cannot be extended by adding any other vertex; equivalently it is an independent dominating set. Maximal independent sets of G are the maximal cliques of its complementer.

Uses the algorithm of Tsukiyama et al. (1977), as implemented by Kevin O’Neill and K. M. Briggs in the Very Nauty Graph Library. Edge directions are ignored (with a warning).

Binds igraph_maximal_independent_vertex_sets.

§Errors

ErrorKind::InvalidValue if sizes is empty.

§Examples
use igraph::prelude::*;
// In the star with center 0, the maximal independent sets are {0}
// and the set of all leaves.
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false).unwrap();
let mut sets = star.maximal_independent_vertex_sets(.., None).unwrap();
sets.iter_mut().for_each(|s| s.sort());
sets.sort();
assert_eq!(sets, vec![vec![0], vec![1, 2, 3]]);
// They are the maximal cliques of the complementer graph.
let comp = star.complementer(false).unwrap();
assert_eq!(comp.maximal_cliques_count(..).unwrap(), 2);
Source

pub fn independence_number(&self) -> Result<usize>

The independence number α(G): the size of the largest independent vertex set.

By definition α(G) = ω(complement of G) (see complementer). It is 0 for the null graph. Every color class of a proper vertex coloring is an independent set, so α(G) · χ(G) ≥ |V|. Edge directions are ignored (with a warning).

Binds igraph_independence_number.

§Examples
use igraph::prelude::*;
// α(C5) = 2: any three vertices of a pentagon contain an edge.
let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
assert_eq!(c5.independence_number().unwrap(), 2);
Source

pub fn vertex_coloring_greedy( &self, heuristic: ColoringGreedy, ) -> Result<Vec<i64>>

Computes a proper vertex coloring greedily.

Colors are the integers 0, 1, 2, ...; element v of the result is the color of vertex v, and adjacent vertices always get different colors. Vertices are colored one at a time, each receiving the smallest color not used by its already colored neighbors, in an order chosen by heuristic (see ColoringGreedy). The number of colors used is an upper bound on the chromatic number, not necessarily optimal; the clique_number is a lower bound. Edge directions and self-loops are ignored. Multi-edges never affect the validity of the coloring, but ColoringGreedy::ColoredNeighbors counts them with their multiplicity when choosing the next vertex (so the colors can differ from those of the simplified graph), while ColoringGreedy::DSatur ignores them. The algorithm is deterministic (it does not use the random number generator).

See also bipartite_types, which finds a 2-coloring whenever one exists.

Binds igraph_vertex_coloring_greedy.

§Examples
use igraph::{cliques::ColoringGreedy, prelude::*};
// An even cycle is bipartite: DSatur finds a 2-coloring.
let c6 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], 6, false)
    .unwrap();
let colors = c6.vertex_coloring_greedy(ColoringGreedy::DSatur).unwrap();
assert!(c6.is_vertex_coloring(&colors).unwrap());
assert_eq!(*colors.iter().max().unwrap(), 1);
Source

pub fn is_vertex_coloring(&self, colors: &[i64]) -> Result<bool>

Checks whether colors (one integer per vertex) is a proper vertex coloring, i.e. no edge joins two vertices of the same color.

Colors may be any integers (they need not be consecutive). Edge directions are ignored, and so are self-loops. Time complexity: O(|E|).

Binds igraph_is_vertex_coloring.

§Errors

ErrorKind::InvalidValue if colors.len() differs from the number of vertices.

§Examples
use igraph::prelude::*;
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
assert!(triangle.is_vertex_coloring(&[7, -1, 3]).unwrap());
assert!(!triangle.is_vertex_coloring(&[0, 1, 0]).unwrap());
Source

pub fn is_bipartite_coloring( &self, types: &[bool], ) -> Result<Option<NeighborMode>>

Checks whether types (one boolean per vertex) is a valid bipartite coloring, and if so reports the orientation of the edges.

Returns None if some edge joins two vertices of the same type (self-loops are ignored), otherwise Some(mode) where, for a directed graph, mode is NeighborMode::Out when all edges go from false to true vertices, NeighborMode::In when they all go from true to false, and NeighborMode::All when both directions occur. It is always NeighborMode::All for undirected graphs, and for directed graphs without non-loop edges. Time complexity: O(|E|).

See also bipartite_types to find such a coloring, and is_bipartite.

Binds igraph_is_bipartite_coloring.

§Errors

ErrorKind::InvalidValue if types.len() differs from the number of vertices.

§Examples
use igraph::prelude::*;
// Directed edges from "users" (false) to "items" (true).
let g = Graph::from_edges(&[(0, 2), (1, 2), (1, 3)], 4, true).unwrap();
let types = [false, false, true, true];
assert_eq!(g.is_bipartite_coloring(&types).unwrap(), Some(NeighborMode::Out));
assert_eq!(g.is_bipartite_coloring(&[false, true, true, false]).unwrap(), None);
Source

pub fn is_edge_coloring(&self, colors: &[i64]) -> Result<bool>

Checks whether colors (one integer per edge) is a proper edge coloring, i.e. no two edges sharing an endpoint have the same color.

A self-loop is not considered adjacent to itself, so graphs with self-loops can still be properly edge-colored. Time complexity: O(|V| d log d), where d is the maximum degree.

Binds igraph_is_edge_coloring.

§Errors

ErrorKind::InvalidValue if colors.len() differs from the number of edges.

§Examples
use igraph::prelude::*;
// The perfect matchings {01, 23}, {02, 13}, {03, 12} 3-edge-color K4.
let k4 = Graph::from_edges(&[(0, 1), (2, 3), (0, 2), (1, 3), (0, 3), (1, 2)], 4, false)
    .unwrap();
assert!(k4.is_edge_coloring(&[0, 0, 1, 1, 2, 2]).unwrap());
assert!(!k4.is_edge_coloring(&[0, 1, 0, 1, 2, 2]).unwrap());
Source§

impl igraph_t

Source

pub fn cocitation<'a>( &self, vids: impl Into<VertexSelector<'a>>, ) -> Result<Matrix>

Cocitation counts: res[(i, j)] is the number of vertices that cite (have an edge pointing to) both the i-th vertex of vids and vertex j.

The result has one row per vertex of vids, in the order given, and one column per vertex of the graph. In a simple graph the diagonal is zero: a vertex is not cocited with itself. Multi-edges count with multiplicity. For example, k -> a twice and k -> b once add 2 to (a, b). They also add 2 to the diagonal entry (a, a), because the two parallel edges form a pair. A self-loop of an undirected graph lists its vertex twice among its own neighbors, so it has the same effect as a double edge (a loop of a directed graph counts once). In an undirected graph every edge counts as a citation both ways, so the score is the number of common neighbors. Cocitation is symmetric, and it equals bibliographic coupling on the transposed graph. Off the diagonal, it is AᵀA, with A the adjacency matrix.

Binds igraph_cocitation. Time complexity: O(|V| d²), with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId (or ErrorKind::InvalidValue for a range) if vids names a missing vertex.

§Examples
use igraph::prelude::*;
// Papers 3 and 4 both cite papers 0 and 1; paper 4 also cites 2.
let g = Graph::from_edges(&[(3, 0), (3, 1), (4, 0), (4, 1), (4, 2)], 5, true)?;
let m = g.cocitation(&[0, 2])?;
assert_eq!(m.row(0), vec![0.0, 2.0, 1.0, 0.0, 0.0]);
assert_eq!(m.row(1), vec![1.0, 1.0, 0.0, 0.0, 0.0]);
Source

pub fn bibcoupling<'a>( &self, vids: impl Into<VertexSelector<'a>>, ) -> Result<Matrix>

Bibliographic coupling: res[(i, j)] is the number of vertices cited by both the i-th vertex of vids and vertex j.

The result has one row per vertex of vids, in the order given, and one column per vertex of the graph. The diagonal is zero in a simple graph; multi-edges (and undirected self-loops) count with multiplicity as in cocitation. In an undirected graph this is the same as the cocitation (the number of common neighbors). Off the diagonal, the matrix is AAᵀ.

Binds igraph_bibcoupling. Time complexity: O(|V| d²), with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId (or ErrorKind::InvalidValue for a range) if vids names a missing vertex.

§Examples
use igraph::prelude::*;
// Papers 3 and 4 share two references (0 and 1).
let g = Graph::from_edges(&[(3, 0), (3, 1), (4, 0), (4, 1), (4, 2)], 5, true)?;
let m = g.bibcoupling(4)?;
assert_eq!(m.row(0), vec![0.0, 0.0, 0.0, 2.0, 0.0]);
Source

pub fn similarity_inverse_log_weighted<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: NeighborMode, ) -> Result<Matrix>

Inverse log-weighted similarity (Adamic and Adar, 2003): the common neighbors of two vertices, each weighted by 1 / ln(degree).

The idea is that sharing a low-degree neighbor says more about two vertices than sharing a hub, which many vertices share by chance. mode chooses which neighbors are compared in a directed graph:

The result has one row per vertex of vids, in the order given, and one column per vertex of the graph. In a simple graph the self-similarities (the diagonal) are zero, and isolated vertices have zero similarity to all others. Multi-edges count with multiplicity, and an undirected loop makes a vertex its own neighbor twice (which also makes the diagonal nonzero). This can raise or lower a similarity, since it also raises the degrees used as weights, so consider simplifying the graph first.

Binds igraph_similarity_inverse_log_weighted. Time complexity: O(|V| d²), with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId (or ErrorKind::InvalidValue for a range) if vids names a missing vertex.

§Examples
use igraph::prelude::*;
// 0 and 1 share the neighbor 2, which has degree 3.
let g = Graph::from_edges(&[(0, 2), (1, 2), (2, 3)], 4, false)?;
let m = g.similarity_inverse_log_weighted(0, NeighborMode::All)?;
assert!((m[(0, 1)] - 1.0 / 3f64.ln()).abs() < 1e-12);
Source

pub fn similarity_jaccard<'a, 'b>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'b>>, mode: NeighborMode, loops: bool, ) -> Result<Matrix>

Jaccard similarity of every pair in from × to: the number of common neighbors divided by the number of vertices adjacent to at least one of the two.

res[(i, j)] is the similarity of the i-th vertex of from and the j-th vertex of to. mode selects out-, in- or all neighbors in directed graphs (ignored for undirected ones). Neighbor sets are compared, so loops and multi-edges are ignored. With loops = true, each vertex is added to its own neighbor set, which is the usual choice for link prediction (“closed neighborhoods”). A vertex has similarity 1 with itself. Two vertices with no neighbors at all have similarity 0.

Binds igraph_similarity_jaccard. When from and to are the same list with no repeated vertex, this calls that function. Otherwise it computes the pairs with igraph_similarity_jaccard_pairs, because the C function is wrong (and can write out of bounds) in that case (see the module docs). Time complexity: O(|from| |to| d), with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId (or ErrorKind::InvalidValue for a range) if a selector names a missing vertex.

§Examples
use igraph::prelude::*;
// A 4-cycle 0-1-2-3: opposite vertices have the same neighbors.
let g = Graph::ring(4, false, false, true)?;
let m = g.similarity_jaccard(.., .., NeighborMode::All, false)?;
assert_eq!(m[(0, 2)], 1.0);
assert_eq!(m[(0, 1)], 0.0);
// Rectangular: the rows are {0}, the columns {1, 2}.
let r = g.similarity_jaccard(0, &[1, 2], NeighborMode::All, true)?;
assert_eq!(r.shape(), (1, 2));
assert_eq!(r[(0, 1)], 0.5); // {0,1,3} vs {1,2,3}
Source

pub fn similarity_jaccard_pairs( &self, pairs: &[(VertexId, VertexId)], mode: NeighborMode, loops: bool, ) -> Result<Vec<f64>>

Jaccard similarity of each vertex pair in pairs, in the same order.

See similarity_jaccard for the meaning of mode and loops. A pair (v, v) has similarity 1.

Binds igraph_similarity_jaccard_pairs. Time complexity: O(n d) for n pairs, with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId if a pair names a missing vertex.

§Examples
use igraph::prelude::*;
// A star with center 0: all leaves have the same neighbor set {0}.
let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
let s = g.similarity_jaccard_pairs(&[(1, 2), (0, 1), (3, 3)], NeighborMode::All, false)?;
assert_eq!(s, vec![1.0, 0.0, 1.0]);
Source

pub fn similarity_jaccard_es<'a>( &self, es: impl Into<EdgeSelector<'a>>, mode: NeighborMode, loops: bool, ) -> Result<Vec<f64>>

Jaccard similarity of the two endpoints of each edge of es, in the order of the selector.

See similarity_jaccard for the meaning of mode and loops. A low value marks an edge between vertices with different neighborhoods, such as a “bridge” between communities.

Binds igraph_similarity_jaccard_es. Time complexity: O(n d) for n edges, with d the maximum degree.

§Errors

ErrorKind::InvalidEdgeId (or another kind, depending on the selector) if es names missing edges.

§Examples
use igraph::prelude::*;
// Two triangles joined by the edge 2-3.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)], 6, false)?;
let s = g.similarity_jaccard_es(.., NeighborMode::All, true)?;
// Inside a triangle: {0,1,2} vs {0,1,2,3} = 3/4; across: {0,1,2,3} vs {2,3,4,5} = 2/6.
assert_eq!(s[1], 0.75);
assert!((s[3] - 1.0 / 3.0).abs() < 1e-12);
Source

pub fn similarity_dice<'a, 'b>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'b>>, mode: NeighborMode, loops: bool, ) -> Result<Matrix>

Dice similarity of every pair in from × to: twice the number of common neighbors divided by the sum of the two neighbor-set sizes.

Dice and Jaccard similarities are related by D = 2J / (1 + J), so they rank pairs the same way. Everything else (shape, mode, loops, self-similarity 1, and the fallback to the pairs function when from and to differ) is as for similarity_jaccard.

Binds igraph_similarity_dice. Time complexity: O(|from| |to| d), with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId (or ErrorKind::InvalidValue for a range) if a selector names a missing vertex.

§Examples
use igraph::prelude::*;
let g = Graph::ring(4, false, false, true)?;
let m = g.similarity_dice(.., .., NeighborMode::All, true)?;
// {0,1,3} vs {0,1,2}: 2 * 2 / (3 + 3).
assert!((m[(0, 1)] - 2.0 / 3.0).abs() < 1e-12);
Source

pub fn similarity_dice_pairs( &self, pairs: &[(VertexId, VertexId)], mode: NeighborMode, loops: bool, ) -> Result<Vec<f64>>

Dice similarity of each vertex pair in pairs, in the same order.

See similarity_dice.

Binds igraph_similarity_dice_pairs. Time complexity: O(n d) for n pairs, with d the maximum degree.

§Errors

ErrorKind::InvalidVertexId if a pair names a missing vertex.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3)], 4, false)?;
// N(0) = {1, 2}, N(3) = {1}: 2 * 1 / (2 + 1).
let s = g.similarity_dice_pairs(&[(0, 3)], NeighborMode::All, false)?;
assert!((s[0] - 2.0 / 3.0).abs() < 1e-12);
Source

pub fn similarity_dice_es<'a>( &self, es: impl Into<EdgeSelector<'a>>, mode: NeighborMode, loops: bool, ) -> Result<Vec<f64>>

Dice similarity of the two endpoints of each edge of es, in the order of the selector.

See similarity_dice and similarity_jaccard_es.

Binds igraph_similarity_dice_es. Time complexity: O(n d) for n edges, with d the maximum degree.

§Errors

ErrorKind::InvalidEdgeId (or another kind, depending on the selector) if es names missing edges.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
// In a triangle, closed neighborhoods are all {0, 1, 2}.
assert_eq!(g.similarity_dice_es(.., NeighborMode::All, true)?, vec![1.0; 3]);
Source§

impl igraph_t

Source

pub fn coreness(&self, mode: NeighborMode) -> Result<Vec<i64>>

The coreness (k-core index) of every vertex.

The k-core of a graph is its maximal subgraph in which every vertex has degree at least k; the coreness of a vertex is the largest k such that it belongs to the k-core. For directed graphs mode selects in-cores (NeighborMode::In), out-cores (NeighborMode::Out) or the undirected version (NeighborMode::All); it is ignored for undirected graphs. Uses the O(|E|) algorithm of Batagelj and Zaversnik.

The coreness of a vertex never exceeds its degree; the vertices of coreness k or more induce the k-core, which can be extracted with Graph::induced_subgraph.

Binds igraph_coreness.

§Examples
use igraph::prelude::*;
// A triangle with a pendant vertex.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
assert_eq!(g.coreness(NeighborMode::All).unwrap(), vec![2, 2, 2, 1]);
Source

pub fn trussness(&self) -> Result<Vec<i64>>

The trussness of every edge.

A k-truss is a subgraph in which every edge lies in at least k − 2 triangles of the subgraph; the trussness of an edge is the largest k such that it belongs to a k-truss. To get the k-truss, keep the edges with trussness >= k. Loops are allowed, multigraphs are not. Time complexity: O(|E|^1.5) (Wang and Cheng, 2012).

Every edge has trussness at least 2, and more than 2 exactly when it lies in a triangle (see Graph::list_triangles); the edges of a k-clique (see Graph::maximal_cliques) have trussness at least k.

Binds igraph_trussness.

§Errors

ErrorKind::Unimplemented for multigraphs.

§Examples
use igraph::prelude::*;
// A 4-clique (every edge in 2 triangles) plus a pendant edge.
let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3), (3, 4)], 5, false)
    .unwrap();
assert_eq!(g.trussness().unwrap(), vec![4, 4, 4, 4, 4, 4, 2]);
Source

pub fn modularity( &self, membership: &[i64], weights: Option<&[f64]>, resolution: f64, directed: bool, ) -> Result<f64>

The modularity of a partition of the vertices.

Q = 1/(2m) Σ_ij (A_ij − γ k_i k_j / (2m)) δ(c_i, c_j), where m is the number of edges, A the adjacency matrix (with loops counted twice on the diagonal), k the degrees, γ the resolution (1 for the classical definition) and c the membership. With directed = true on a directed graph the Leicht–Newman version Q = 1/m Σ_ij (A_ij − γ k^out_i k^in_j / m) δ(c_i, c_j) is used. With weights, A, k and m are replaced by their weighted counterparts. For graphs without edges the modularity is NaN.

Community ids need not be contiguous (empty communities are allowed). Time complexity: O(|V| + |E|).

For non-negative ids and resolution = 1, this is the unnormalized nominal assortativity of the partition, see Graph::assortativity_nominal.

Binds igraph_modularity.

§Errors

ErrorKind::InvalidValue if membership or weights have a wrong length, a weight is negative, or resolution < 0.

§Examples
use igraph::prelude::*;
// Two triangles joined by an edge.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
let q = g.modularity(&[0, 0, 0, 1, 1, 1], None, 1.0, true).unwrap();
assert!((q - 5.0 / 14.0).abs() < 1e-12);
Source

pub fn modularity_matrix( &self, weights: Option<&[f64]>, resolution: f64, directed: bool, ) -> Result<Matrix>

The modularity matrix B_ij = A_ij − γ k_i k_j / (2m).

For directed graphs (and directed = true), B_ij = A_ij − γ k^out_i k^in_j / m. Loops of undirected graphs are counted twice in A; with weights, the weighted adjacency matrix and strengths are used. When there are no edges the result is undefined (NaNs). Then Q = 1/(2m) Σ_ij B_ij δ(c_i, c_j), see Graph::modularity. The adjacency part alone is Graph::get_adjacency (or this function with resolution = 0).

Binds igraph_modularity_matrix.

§Examples
use igraph::prelude::*;
let triangle = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, false).unwrap();
// With resolution 0 the modularity matrix is the adjacency matrix.
let b = triangle.modularity_matrix(None, 0.0, false).unwrap();
assert_eq!(b.to_rows(), vec![vec![0.0, 1.0, 1.0], vec![1.0, 0.0, 1.0], vec![1.0, 1.0, 0.0]]);
Source

pub fn community_multilevel( &self, weights: Option<&[f64]>, resolution: f64, ) -> Result<Multilevel>

Louvain community detection: multi-level greedy modularity optimization.

Initially each vertex is a community; vertices are then moved, in random order, to the neighboring community that increases modularity the most, until no move helps. Communities are then contracted into single vertices and the process restarts, until there is a single vertex or modularity cannot increase. Higher resolution values give more, smaller communities (1 is the classical modularity). Weights must be non-negative. The graph must be undirected. Near linear time on sparse graphs (Blondel et al., 2008).

The result contains the final membership and the membership and modularity after each level. For a directed graph, use Graph::community_leiden_simple (which supports directed modularity) or convert it first with Graph::to_undirected.

Binds igraph_community_multilevel.

§Errors

For directed graphs, negative weights or resolution < 0.

§Examples
use igraph::prelude::*;
// Two triangles joined by an edge.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
let res = g.community_multilevel(None, 1.0).unwrap();
assert_eq!(res.membership, vec![0, 0, 0, 1, 1, 1]);
assert_eq!(res.num_communities(), 2);
Source

pub fn community_leiden( &self, edge_weights: Option<&[f64]>, vertex_out_weights: Option<&[f64]>, vertex_in_weights: Option<&[f64]>, options: &LeidenOptions<'_>, ) -> Result<Leiden>

Leiden community detection with explicit vertex weights.

The Leiden algorithm (Traag, Waltman and van Eck, 2019) improves on Louvain by a refinement phase which guarantees well-connected communities. It maximizes 1/(2m) Σ_ij (A_ij − γ n_i n_j) δ(s_i, s_j) (directed: 1/m Σ_ij (A_ij − γ n^out_i n^in_j) δ(s_i, s_j)), where n are the vertex weights (vertex_out_weights, vertex_in_weights; None means all ones, and vertex_in_weights must be None for undirected graphs) and γ is LeidenOptions::resolution. With unit vertex weights this is the Constant Potts Model; with degrees as vertex weights and γ = 1/(2m) it is modularity (see Graph::community_leiden_simple for a more convenient interface). Edge weights may be negative.

Binds igraph_community_leiden.

§Errors

If a vector has a wrong length.

§Examples
use igraph::prelude::*;
use igraph::community::LeidenOptions;
let mut edges = vec![(0, 5)];
for base in [0, 5] {
    for i in 0..5 {
        for j in i + 1..5 { edges.push((base + i, base + j)); }
    }
}
let g = Graph::from_edges(&edges, 10, false).unwrap();
// Constant Potts Model with resolution 0.05, as in igraph's example.
let opts = LeidenOptions::default().with_resolution(0.05).with_iterations(Some(1));
let res = g.community_leiden(None, None, None, &opts).unwrap();
assert_eq!(res.nb_clusters, 2);
assert!((res.quality - 0.8929).abs() < 1e-4);
Source

pub fn community_leiden_simple( &self, weights: Option<&[f64]>, objective: LeidenObjective, options: &LeidenOptions<'_>, ) -> Result<Leiden>

Leiden community detection optimizing a chosen objective function.

A convenience interface to Graph::community_leiden which computes suitable vertex weights for LeidenObjective::Modularity (generalized modularity with resolution γ), LeidenObjective::Cpm (Constant Potts Model) or LeidenObjective::ErdosRenyi. Works on directed and undirected graphs. The reported quality is the value of the chosen objective. Near linear time on sparse graphs.

Binds igraph_community_leiden_simple.

§Errors

For negative weights with the modularity or ER objectives, or vectors of wrong length.

§Examples
use igraph::prelude::*;
use igraph::community::{LeidenObjective, LeidenOptions};
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(1).unwrap();
let res = g
    .community_leiden_simple(None, LeidenObjective::Modularity, &LeidenOptions::default())
    .unwrap();
assert_eq!(res.nb_clusters, 2);
let q = g.modularity(&res.membership, None, 1.0, true).unwrap();
assert!((res.quality - q).abs() < 1e-12);
Source

pub fn community_fastgreedy( &self, weights: Option<&[f64]>, ) -> Result<Dendrogram>

Greedy agglomerative modularity optimization (Clauset, Newman and Moore).

Starting from singletons, the pair of communities whose merge increases modularity the most is merged repeatedly, building a full dendrogram (with the improvements of Wakita and Tsurumi). The returned Dendrogram contains the merges, the modularity before and after each merge, and the membership with the highest modularity. The graph must not have multi-edges; weights must be non-negative. Time complexity: O(|E| + |V| log²|V|) typically.

Binds igraph_community_fastgreedy.

§Errors

For multigraphs (merge the parallel edges first with Graph::simplify, see also Graph::has_multiple) or invalid weights.

§Examples
use igraph::prelude::*;
// The example of igraph's documentation.
let g = Graph::from_edges(
    &[(0, 1), (1, 2), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)], 6, false).unwrap();
let weights = [10.0, 10.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let d = g.community_fastgreedy(Some(&weights)).unwrap();
assert_eq!(d.merges, vec![(1, 0), (2, 6), (3, 4), (8, 5), (9, 7)]);
Source

pub fn community_walktrap( &self, weights: Option<&[f64]>, steps: usize, ) -> Result<Dendrogram>

Walktrap community detection based on short random walks (Pons and Latapy).

Vertex similarity is measured by random walks of length steps (typically 3–8, 4 or 5 being a reasonable default); communities are merged agglomeratively (Ward’s method). Edge directions are ignored; weights must be positive. Isolated vertices are allowed. Time complexity: O(|V|² log|V|) typically.

Binds igraph_community_walktrap.

§Examples
use igraph::prelude::*;
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
let d = triangle.community_walktrap(None, 4).unwrap();
assert_eq!(d.merges, vec![(1, 2), (0, 3)]);
assert_eq!(d.membership, vec![0, 0, 0]);
Source

pub fn community_edge_betweenness( &self, weights: Option<&[f64]>, lengths: Option<&[f64]>, directed: bool, ) -> Result<EdgeBetweennessCommunities>

Girvan–Newman community detection by repeatedly removing the edge with the highest betweenness.

Betweenness is recomputed after each removal, until no edges remain; the resulting divisive hierarchy is returned as a dendrogram together with the removal order, the betweenness of each removed edge, the “bridges”, the modularity of each division and the best membership. With weights, the ratio betweenness / weight decides which edge to remove (strong edges are removed later), and weights are used for modularity. With lengths, shortest paths take edge lengths into account. For directed graphs directed selects directed betweenness and modularity (splits are into weakly connected components). The dendrogram is computed with Graph::community_eb_get_merges, so the order of the two ids within a merge may differ from igraph’s output when modularity and membership are not requested. Time complexity: O(|V| |E|²).

The first removed edge is the one with the highest Graph::edge_betweenness (divided by its weight) in the original graph.

Binds igraph_community_edge_betweenness.

§Errors

ErrorKind::InvalidValue if weights or lengths have a wrong length or contain invalid values.

§Examples
use igraph::prelude::*;
// The example of igraph's documentation.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (0, 3), (1, 3), (1, 4)], 5, false).unwrap();
let weights = [1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let res = g.community_edge_betweenness(Some(&weights), None, false).unwrap();
assert_eq!(res.removed_edges, vec![0, 1, 3, 4, 2, 5]);
assert_eq!(res.edge_betweenness, vec![2.0, 3.5, 6.0, 2.0, 1.0, 1.0]);
assert_eq!(res.merges, vec![(4, 1), (2, 0), (3, 5), (7, 6)]);
assert_eq!(res.bridges, vec![5, 4, 3, 2]);
Source

pub fn community_eb_get_merges( &self, directed: bool, edges: &[EdgeId], weights: Option<&[f64]>, ) -> Result<EdgeRemovalMerges>

Builds the dendrogram of a sequence of edge removals.

Given an order in which all the edges are removed (e.g. EdgeBetweennessCommunities::removed_edges, but any order works), the removal process is replayed backwards and each time two components get connected a merge is recorded (component ids below vcount are vertices, merged components are numbered from vcount). Modularity (weighted if weights is given, directed if directed) is computed for each division, and the best membership is returned. Time complexity: O(|E| + |V| log|V|).

Binds igraph_community_eb_get_merges.

§Errors

ErrorKind::InvalidEdgeId for an invalid edge id in edges, and ErrorKind::InvalidValue if edges is otherwise not a permutation of the edge ids.

§Examples
use igraph::prelude::*;
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
// Remove the middle edge first, then the outer ones.
let res = path.community_eb_get_merges(false, &[1, 0, 2], None).unwrap();
assert_eq!(res.dendrogram.merges, vec![(3, 2), (1, 0), (4, 5)]);
assert_eq!(res.dendrogram.membership, vec![0, 0, 1, 1]);
Source

pub fn community_leading_eigenvector( &self, weights: Option<&[f64]>, steps: Option<usize>, start: Option<&[i64]>, ) -> Result<LeadingEigenvector>

Newman’s leading eigenvector method (recursive spectral bisection).

Starting from the connected components (see Graph::connected_components) or from start, each community is split in two according to the signs of the leading eigenvector of its generalized modularity matrix, as long as this increases modularity, performing at most steps splits (None: as many as possible). The initial division into c components (or c start communities) counts as c − 1 steps, so at most max(steps, c − 1) + 1 communities are returned. Start community ids must lie in 0..vcount; communities with at most two vertices are never split. Edge directions are ignored. ARPACK is used with igraph’s default options. Time complexity: O(|E| + |V|² steps).

Binds igraph_community_leading_eigenvector.

§Errors

ErrorKind::InvalidValue if start or weights have a wrong length, start contains ids outside 0..vcount, or the weights contain NaN or infinite values or their absolute sum exceeds 1e150 (igraph does not check these, and ARPACK would abort the process); ErrorKind::Arpack if ARPACK fails.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
let res = g.community_leading_eigenvector(None, None, None).unwrap();
assert_eq!(res.num_communities(), 2);
assert!((res.modularity - 5.0 / 14.0).abs() < 1e-12);
Source

pub fn community_leading_eigenvector_with<F>( &self, weights: Option<&[f64]>, steps: Option<usize>, start: Option<&[i64]>, callback: F, ) -> Result<LeadingEigenvector>

Like Graph::community_leading_eigenvector, calling callback after each eigenvector computation.

The callback receives a LeadingEigenvectorStep describing the community being split, the eigenvalue and eigenvector, and can compute products with the community’s modularity matrix. Returning ControlFlow::Break stops the algorithm (the partition found so far is returned); a panic in the callback is propagated.

Binds igraph_community_leading_eigenvector with a callback.

§Examples
use igraph::prelude::*;
use std::ops::ControlFlow;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
let mut eigenvalues = vec![];
let res = g
    .community_leading_eigenvector_with(None, None, None, |step| {
        eigenvalues.push(step.eigenvalue());
        // B v = λ v
        let bv = step.multiply(step.eigenvector()).unwrap();
        for (x, v) in bv.iter().zip(step.eigenvector()) {
            assert!((x - step.eigenvalue() * v).abs() < 1e-6);
        }
        ControlFlow::Continue(())
    })
    .unwrap();
assert_eq!(eigenvalues, res.eigenvalues);
Source

pub fn community_spinglass( &self, weights: Option<&[f64]>, options: &SpinglassOptions, ) -> Result<Spinglass>

Spinglass community detection (Reichardt and Bornholdt).

Finds communities as the ground state of a Potts spin glass by simulated annealing, with at most SpinglassOptions::spins communities. The Neg implementation (Traag and Bruggeman) supports negative weights. Edge directions are ignored. The graph must be connected (check with Graph::is_connected; cluster each component separately otherwise). The result is random: seed the thread’s generator with rng::seed for reproducibility.

Binds igraph_community_spinglass.

§Errors

ErrorKind::InvalidValue for disconnected graphs, invalid parameters or invalid weights. Beyond igraph’s own checks (e.g. the temperatures must be both zero, or both positive with the starting one larger), the following are rejected on the Rust side, as igraph would loop forever or overflow on them: NaN or infinite weights, or weights whose absolute sum exceeds 1e150; a gamma outside [0, 1e150] (or NaN); with the Neg implementation, a gamma_minus of magnitude above 1e150 (or NaN); a cooling factor outside [0, 1) (or NaN); temperatures outside [0, 1e300]; and a number of spins outside 2..=i32::MAX.

§Examples
use igraph::prelude::*;
use igraph::community::SpinglassOptions;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(42).unwrap();
let res = g.community_spinglass(None, &SpinglassOptions::default()).unwrap();
assert_eq!(res.num_communities(), 2);
assert!((res.modularity - 5.0 / 14.0).abs() < 1e-9);
Source

pub fn community_spinglass_single( &self, weights: Option<&[f64]>, vertex: VertexId, options: &SpinglassOptions, ) -> Result<SpinglassSingle>

The spinglass community of a single vertex, without computing the whole partition.

Uses SpinglassOptions::spins, update_rule and gamma (the other options are ignored). Also returns the cohesion and adhesion indices of the community and the number (or weight) of its inner and outer edges. The graph must be connected.

Binds igraph_community_spinglass_single.

§Errors

ErrorKind::InvalidVertexId if vertex is not a vertex of the graph, and ErrorKind::InvalidValue for disconnected graphs or invalid parameters: spins outside 2..=i32::MAX, a gamma outside [0, 1e150] (or NaN), NaN or infinite weights (or weights whose absolute sum exceeds 1e150).

§Examples
use igraph::prelude::*;
use igraph::community::SpinglassOptions;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(42).unwrap();
let res = g.community_spinglass_single(None, 0, &SpinglassOptions::default()).unwrap();
let mut community = res.community.clone();
community.sort();
assert_eq!(community, vec![0, 1, 2]);
// Three edges inside the triangle, one (the bridge) leaving it.
assert_eq!((res.inner_links, res.outer_links), (3.0, 1.0));
Source

pub fn community_label_propagation( &self, weights: Option<&[f64]>, options: &LabelPropagationOptions<'_>, ) -> Result<Vec<i64>>

Label propagation community detection (Raghavan, Albert and Kumara; fast variant of Traag and Šubelj).

Every vertex repeatedly adopts the label that is dominant (highest total edge weight) among its neighbors, until all labels are dominant. See LabelPropagationOptions for directed propagation, initial and fixed labels and the variants. Weights must be non-negative. Ties are broken at random, so seed the thread’s generator for reproducible results. In directed graphs, labels circulate freely only within strongly connected components (see Graph::connected_components with Connectedness::Strong). Unlabeled vertices unreachable from labeled ones are labeled in an extra step (each such undirected component gets its own label). Time complexity: O(|V| + |E|) per iteration.

Binds igraph_community_label_propagation.

§Errors

ErrorKind::InvalidValue for vectors of wrong length, negative or NaN weights, or initial labels outside 0..vcount (negative ones excepted).

§Examples
use igraph::prelude::*;
use igraph::community::LabelPropagationOptions;
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
// Fix the labels of both ends; the others are unlabeled.
let initial = [0, -1, -1, -1, 1];
let fixed = [true, false, false, false, true];
let opts = LabelPropagationOptions { initial: Some(&initial), fixed: Some(&fixed),
                                     ..Default::default() };
let m = path.community_label_propagation(None, &opts).unwrap();
// The fixed vertices keep distinct labels, and nobody gets a third one
// (ties are broken at random, so the boundary may vary).
assert_ne!(m[0], m[4]);
assert!(m.iter().all(|&l| l == m[0] || l == m[4]));
Source

pub fn community_infomap( &self, edge_weights: Option<&[f64]>, vertex_weights: Option<&[f64]>, options: &InfomapOptions, ) -> Result<Infomap>

Infomap community detection: minimizes the map equation, the expected description length of a random walk (Rosvall and Bergstrom).

The random walker follows out-edges proportionally to edge_weights (non-negative) and teleports with probability 0.15 to a vertex chosen proportionally to vertex_weights (positive). Edge directions are taken into account. The best of InfomapOptions::trials attempts is returned, with its code length (in bits). The attempts are random.

Binds igraph_community_infomap.

§Errors

ErrorKind::InvalidValue for weight vectors of wrong length or invalid weights or trials, and ErrorKind::Unimplemented if igraph was built without Infomap support (as documented since igraph 1.0.1).

§Examples
use igraph::prelude::*;
use igraph::community::InfomapOptions;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(7).unwrap();
let res = g.community_infomap(None, None, &InfomapOptions::default()).unwrap();
assert_eq!(res.num_communities(), 2);
assert!(res.codelength > 0.0);
Source

pub fn community_fluid_communities(&self, k: usize) -> Result<Vec<i64>>

Fluid communities: k “fluids” expand and contract on the graph until they reach an equilibrium (Parés et al., 2017).

The graph must be simple and connected; edge directions are ignored, weights are not supported. k must be positive and at most the number of vertices. The result is random (seed the thread’s generator for reproducibility). Time complexity: O(|E|).

Binds igraph_community_fluid_communities.

§Errors

For non-simple graphs (see Graph::is_simple), disconnected graphs (see Graph::is_connected) or an invalid k.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(3).unwrap();
let m = g.community_fluid_communities(2).unwrap();
assert_eq!(m.iter().filter(|&&c| c == m[0]).count(), 3);
Source

pub fn community_voronoi( &self, lengths: Option<&[f64]>, weights: Option<&[f64]>, mode: NeighborMode, radius: Option<f64>, ) -> Result<Voronoi>

Voronoi community detection (Deritei et al.; Molnár et al.). Experimental in igraph.

Generator vertices are chosen as those with the largest local relative density s m / (m + k) within radius (s being the strength of the vertex, m the number of edges within its first-order neighborhood and k the number of edges with a single endpoint in it), and every vertex is assigned to its closest generator (ties broken at random) using the edge lengths divided by the edge clustering coefficient (Graph::ecc), as in Graph::voronoi. weights are used to select the generators and compute modularity. mode selects distances from (NeighborMode::Out) or to (NeighborMode::In) the generators in directed graphs. With radius = None the radius maximizing modularity is chosen automatically; an explicit radius must be non-negative (larger radii give fewer communities). The graph must be simple.

Binds igraph_community_voronoi.

§Errors

ErrorKind::InvalidValue for a negative or NaN radius, vectors of wrong length, NaN or infinite lengths or weights, or non-simple graphs. With radius = None, the sum of the lengths times the number of vertices must also be at most 1e150 (igraph 1.0.1 aborts the process when its radius search overflows; this is checked here).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(42).unwrap();
let res = g.community_voronoi(None, None, NeighborMode::All, None).unwrap();
assert_eq!(res.num_communities(), 2);
assert_eq!(res.generators.len(), 2);
// Each generator lies in its own community, and the two triangles are found.
for (c, &v) in res.generators.iter().enumerate() {
    assert_eq!(res.membership[v as usize], c as i64);
}
assert!((res.modularity - 5.0 / 14.0).abs() < 1e-12);
Source

pub fn community_optimal_modularity( &self, weights: Option<&[f64]>, resolution: f64, ) -> Result<Clustering>

The partition with the highest possible modularity, by integer programming (Brandes et al., 2008) with GLPK.

Exact modularity maximization is NP-complete: graphs up to ~50 vertices are fine, a few hundred may be possible. Directed graphs are supported. resolution is the γ of Graph::modularity.

Binds igraph_community_optimal_modularity.

§Errors

ErrorKind::Unimplemented if igraph was built without GLPK.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
match g.community_optimal_modularity(None, 1.0) {
    Ok(best) => assert!((best.modularity - 5.0 / 14.0).abs() < 1e-9),
    Err(e) => assert_eq!(e.kind(), ErrorKind::Unimplemented), // no GLPK
}
Source§

impl igraph_t

Source

pub fn hrg_fit(&self, steps: usize) -> Result<Hrg>

Fits a hierarchical random graph model to the graph by Markov chain Monte Carlo (Clauset, Moore and Newman, 2008).

Runs steps MCMC steps, or, with steps = 0, until a convergence criterion is met (the average log-likelihood over 65536 steps stabilizes), starting from a random dendrogram. The returned model is the most likely dendrogram visited. Edge directions, multi-edges and loops are ignored; the graph needs at least 3 vertices. The chain is random: seed the thread’s generator for reproducible models.

With steps > 0, igraph (1.0.0 and 1.0.1) only records a dendrogram when a step improves on the likelihood of the random initial one; on tiny graphs this may never happen, and the binding then runs the chain to equilibrium instead, so that a valid model is always returned.

Binds igraph_hrg_fit.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(42).unwrap();
let hrg = g.hrg_fit(1000).unwrap();
assert_eq!(hrg.size(), 6);
assert!(hrg.prob().iter().all(|p| (0.0..=1.0).contains(p)));
Source

pub fn hrg_refit(&self, hrg: &mut Hrg, steps: usize) -> Result<()>

Continues fitting an HRG to the graph, starting the MCMC from hrg (updated in place). See Graph::hrg_fit.

hrg is replaced by the most likely dendrogram visited, and is left unchanged if no step improves on its likelihood (or on error). With steps = 0 the chain runs until convergence.

Binds igraph_hrg_fit with start = true.

§Errors

ErrorKind::InvalidValue if the size of hrg differs from the number of vertices.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(42).unwrap();
let mut hrg = g.hrg_fit(100).unwrap();
g.hrg_refit(&mut hrg, 1000).unwrap();
// Still a model of `g`: its internal nodes account for all the edges.
assert_eq!(hrg.size(), 6);
assert_eq!(hrg.edges().iter().sum::<i64>(), 7);
Source

pub fn hrg_consensus( &self, start: Option<&Hrg>, num_samples: usize, ) -> Result<HrgConsensus>

Consensus tree of the HRG models sampled for the graph.

Starting from start (or from a freshly fitted model when None), num_samples HRGs are sampled by MCMC and the splits present in the majority of them form the consensus tree (a forest when some splits are not supported by a majority: -1 marks its roots).

Binds igraph_hrg_consensus.

§Errors

ErrorKind::InvalidValue if start does not match the graph or the graph has fewer than 3 vertices.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(3).unwrap();
let cons = g.hrg_consensus(None, 10).unwrap();
// Vertices 0..6 come first, then the internal nodes of the consensus tree.
assert_eq!(cons.parents.len(), 6 + cons.weights.len());
assert!(cons.parents[..6].iter().all(|&p| p == -1 || p >= 6));
Source

pub fn hrg_predict( &self, start: Option<&Hrg>, num_samples: usize, num_bins: usize, ) -> Result<HrgPrediction>

Predicts missing edges with HRG models.

Samples num_samples HRGs (starting from start, or from a freshly fitted model when None) and estimates, for every non-adjacent vertex pair, the probability that the edge exists but was not observed. num_bins controls the resolution of the probabilities (e.g. 25); note that igraph keeps a histogram of num_bins + 1 values for every vertex pair, i.e. O(|V|² num_bins) memory. The candidates are the pairs of distinct, non-adjacent vertices, most likely first.

Binds igraph_hrg_predict.

§Errors

ErrorKind::InvalidValue if start does not match the graph, the graph has fewer than 3 or more than 46341 vertices (igraph counts the candidate pairs in a C int), or num_bins is zero.

§Examples
use igraph::prelude::*;
// A 4-cycle: the two diagonals are the only missing links.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
rng::seed(1).unwrap();
let pred = g.hrg_predict(None, 10, 10).unwrap();
let mut edges = pred.edges.clone();
edges.sort();
assert_eq!(edges, vec![(0, 2), (1, 3)]);
Source

pub fn hrg_game(hrg: &Hrg) -> Result<Graph>

Samples a graph from an HRG model (same as Hrg::sample); listed with the other random graph generators of games in the C API.

Binds igraph_hrg_game.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)
    .unwrap();
rng::seed(42).unwrap();
let hrg = g.hrg_fit(0).unwrap();
let sample = Graph::hrg_game(&hrg).unwrap();
assert_eq!(sample.vcount(), 6);
assert!(!sample.is_directed());
Source

pub fn from_hrg_dendrogram(hrg: &Hrg) -> Result<(Graph, Vec<f64>)>

The dendrogram of an HRG as a directed tree, with the probability of each tree vertex.

The tree has 2n − 1 vertices: 0..n are the leaves (the modeled vertices, probability NaN) and n + i is internal node i (with probability hrg.prob()[i]); edges point from parents to children. Draw it with a tree layout such as Graph::layout_reingold_tilford.

Binds igraph_from_hrg_dendrogram.

§Examples
use igraph::prelude::*;
use igraph::community::Hrg;
let tree = Graph::from_edges(&[(0, 3), (0, 1), (1, 4), (1, 2), (2, 5), (2, 6)], 7, true).unwrap();
let hrg = Hrg::create(&tree, &[1.0, 0.5, 0.0]).unwrap();
let (dendrogram, prob) = Graph::from_hrg_dendrogram(&hrg).unwrap();
assert_eq!((dendrogram.vcount(), dendrogram.ecount()), (7, 6));
assert!(dendrogram.is_tree(NeighborMode::Out).unwrap());
assert!(prob[..4].iter().all(|p| p.is_nan()));
assert_eq!(&prob[4..], hrg.prob());
Source§

impl igraph_t

Source

pub fn connected_components( &self, mode: Connectedness, ) -> Result<ConnectedComponents>

The (weakly or strongly) connected components of the graph.

With Connectedness::Weak edge directions are ignored; with Connectedness::Strong two vertices are in the same component when each is reachable from the other by a directed path. The mode is ignored for undirected graphs.

Strongly connected components are numbered in topological order: vertex v is reachable from u only if membership[u] <= membership[v]. Weak components are numbered in order of their smallest vertex id.

Time complexity: O(|V| + |E|).

See also is_connected for a (cached) yes/no answer, decompose to get the components as graphs, and Graph::subcomponent for the component of a single vertex.

Binds igraph_connected_components.

§Examples
use igraph::prelude::*;

// 0 -> 1 -> 2 -> 0 is a directed cycle, 3 only has an in-edge.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, true).unwrap();
let weak = g.connected_components(Connectedness::Weak).unwrap();
assert_eq!(weak.count, 1);
let strong = g.connected_components(Connectedness::Strong).unwrap();
assert_eq!(strong.count, 2);
assert!(strong.same_component(0, 2));
assert!(!strong.same_component(0, 3));
Source

pub fn is_connected(&self, mode: Connectedness) -> Result<bool>

Whether the graph is (weakly or strongly) connected.

A graph is connected when every vertex is reachable from every other; for directed graphs, Connectedness::Strong follows edge directions while Connectedness::Weak ignores them. The mode is ignored for undirected graphs. By definition the null graph (no vertices) is not connected, while the singleton graph is.

The result is cached inside the graph, so repeated calls without modifications are O(1); otherwise the time complexity is O(|V| + |E|).

See also is_biconnected for 2-vertex-connectedness, and Graph::vertex_connectivity / Graph::edge_connectivity for how connected a graph is.

Binds igraph_is_connected.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
assert!(g.is_connected(Connectedness::Weak).unwrap());
assert!(!g.is_connected(Connectedness::Strong).unwrap());
assert!(!Graph::new(0, false).is_connected(Connectedness::Weak).unwrap());
Source

pub fn decompose( &self, mode: Connectedness, max_components: Option<usize>, min_vertices: usize, ) -> Result<Vec<Graph>>

Splits the graph into one separate Graph per connected component.

max_components limits the number of returned graphs (None for no limit): the first ones found are kept (for weak components, in order of their smallest vertex id). Components with fewer than min_vertices vertices are skipped (e.g. 2 drops the isolated vertices). Vertex ids are renumbered in each component graph, preserving their relative order; directedness is preserved.

Time complexity: O(|V| + |E|).

Each graph is the subgraph induced by one component: use connected_components with Graph::induced_subgraph to extract only some of them, or to keep the mapping to the original vertex ids.

Binds igraph_decompose.

§Examples
use igraph::prelude::*;
// A triangle, a single edge and an isolated vertex.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4)], 6, false).unwrap();
let parts = g.decompose(Connectedness::Weak, None, 2).unwrap();
let sizes: Vec<_> = parts.iter().map(|p| (p.vcount(), p.ecount())).collect();
assert_eq!(sizes, vec![(3, 3), (2, 1)]);
Source

pub fn articulation_points(&self) -> Result<Vec<VertexId>>

The articulation points (cut vertices) of the graph.

A vertex is an articulation point if its removal increases the number of (weakly) connected components. Edge directions are ignored. The vertices are returned in no particular order.

A graph without articulation points is not necessarily biconnected: the null graph, the singleton graph and edgeless graphs have none either; use is_biconnected for that. Conversely K2, which igraph considers biconnected, has no articulation points. (The C documentation of 1.0.0 and 1.0.1 lists K2 among the counterexamples, but is_biconnected returns true for it.)

Time complexity: O(|V| + |E|).

See also biconnected_components, which also returns the articulation points, and bridges for the edge analogue.

Binds igraph_articulation_points.

§Examples
use igraph::prelude::*;
// A path 0-1-2-3: the inner vertices are cut vertices.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let mut ap = g.articulation_points().unwrap();
ap.sort();
assert_eq!(ap, vec![1, 2]);
Source

pub fn bridges(&self) -> Result<Vec<EdgeId>>

The bridges (cut edges) of the graph, as edge ids.

An edge is a bridge if its removal increases the number of (weakly) connected components. Edge directions are ignored. The edges are returned in no particular order. A multi-edge is never a bridge (its parallel copies keep the endpoints connected), and neither is a self-loop.

Time complexity: O(|V| + |E|).

A connected graph has a bridge exactly when its edge_connectivity is 1 (and it has more than one vertex).

Binds igraph_bridges.

§Examples
use igraph::prelude::*;
// A square 0-1-2-3 with a pendant edge 3-4 (edge id 4).
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (3, 4)], 5, false).unwrap();
assert_eq!(g.bridges().unwrap(), vec![4]);
Source

pub fn biconnected_components(&self) -> Result<BiconnectedComponents>

The biconnected components of the graph (edge directions are ignored).

A graph is biconnected if removing any single vertex leaves it connected; a biconnected component is a maximal biconnected subgraph. The components partition the (non-loop) edges of the graph, while a vertex can belong to several of them (the articulation points) or to none (isolated vertices). igraph considers the two-vertex complete graph K2 biconnected, but not single vertices, so a single biconnected component does not imply that the graph is biconnected (there may be isolated vertices): use is_biconnected for that. Self-loops belong to no component.

All outputs are computed: the number of components, a spanning tree of each, their edges and vertices, and the articulation points. Time complexity: O(|V| + |E|) for the trees alone, but igraph documents computing the vertex sets as quadratic and the edge sets as cubic in |V| in the worst case.

Binds igraph_biconnected_components.

§Examples
use igraph::prelude::*;
// Two triangles sharing vertex 2 ("bowtie").
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)
    .unwrap();
let bc = g.biconnected_components().unwrap();
assert_eq!(bc.count, 2);
assert_eq!(bc.articulation_points, vec![2]);
assert!(bc.components.iter().all(|c| c.len() == 3 && c.contains(&2)));
Source

pub fn is_biconnected(&self) -> Result<bool>

Whether the graph is biconnected (edge directions are ignored).

A graph is biconnected if removing any single vertex (and its incident edges) does not disconnect it. igraph does not consider the null and singleton graphs biconnected, but does consider K2 biconnected. For graphs with at least three vertices this is the same as having vertex_connectivity at least 2, but much cheaper to compute.

Time complexity: O(|V| + |E|).

Binds igraph_is_biconnected.

§Examples
use igraph::prelude::*;
let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
assert!(square.is_biconnected().unwrap());
let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert!(!path.is_biconnected().unwrap());
Source

pub fn bond_percolation( &self, edge_order: Option<&[EdgeId]>, ) -> Result<BondPercolation>

Bond (edge) percolation curve: the size of the giant component as the edges of the graph are added one by one to its vertices.

edge_order gives the order in which the edge ids are added and must not contain duplicates; it may list only a subset of the edges. With None a uniformly random order is used, drawn from the calling thread’s default random number generator (reproducible after rng::seed). Reversing both the order and the resulting giant_size gives the curve for edge removal. Edge directions are ignored; the vertices are only counted once they get an incident edge, so isolated vertices never contribute.

This function is marked experimental in igraph.

Time complexity: O(|V| + |E| α(|E|)), α being the inverse Ackermann function.

See also site_percolation for the vertex version, edgelist_percolation to percolate arbitrary vertex pairs, and connected_components for the component sizes of a fixed graph.

Binds igraph_bond_percolation.

§Errors

ErrorKind::InvalidEdgeId for an edge id out of range and ErrorKind::InvalidValue for duplicates in edge_order. Both are checked on the Rust side: igraph 1.0.0 and 1.0.1 size their duplicate-detection bitset by the length of edge_order rather than by the number of edges, so an unchecked partial order or out-of-range id would make them access memory out of bounds.

§Examples
use igraph::prelude::*;
// The 4-cycle 0-1-2-3-0, adding edges 0-1, 2-3, 1-2, 3-0.
let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
let p = c4.bond_percolation(Some(&[0, 2, 1, 3])).unwrap();
assert_eq!(p.giant_size, vec![2, 2, 4, 4]);
assert_eq!(p.vertex_count, vec![2, 4, 4, 4]);

// A random order is reproducible after seeding; the final point is
// always the whole (connected) graph.
let grid = Graph::square_lattice(&[5, 5], 1, false, false, None).unwrap();
rng::seed(7).unwrap();
let a = grid.bond_percolation(None).unwrap();
rng::seed(7).unwrap();
let b = grid.bond_percolation(None).unwrap();
assert_eq!(a, b);
assert_eq!(a.giant_size.last(), Some(&25));
Source

pub fn site_percolation( &self, vertex_order: Option<&[VertexId]>, ) -> Result<SitePercolation>

Site (vertex) percolation curve: the size of the giant component as the vertices of the graph are added one by one, together with the edges among the vertices added so far.

vertex_order gives the order in which vertices are added and must not contain duplicates; it may list only a subset of the vertices. With None a uniformly random order is used, drawn from the calling thread’s default random number generator exactly as igraph would (reproducible after rng::seed). Reversing both the order and the resulting giant_size gives the curve for vertex removal (an attack/failure scenario). Edge directions are ignored; multi-edges count with their multiplicity in edge_count.

This function is marked experimental in igraph.

Time complexity: O(|V| + |E| α(|E|)).

See also bond_percolation for the edge version and Graph::coreness or Graph::vertex_connectivity for other robustness measures.

Binds igraph_site_percolation.

§Self-loops

igraph 1.0.0 and 1.0.1 count every self-loop twice in edge_count (it lists loops twice among a vertex’s neighbors). This wrapper corrects the count, so a self-loop counts as one edge. To be able to do so with vertex_order = None, the random order is generated on the Rust side with the same random draws igraph makes internally.

§Errors

ErrorKind::InvalidVertexId for invalid vertex ids and ErrorKind::InvalidValue for duplicates in vertex_order.

§Examples
use igraph::prelude::*;
let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
let p = c4.site_percolation(Some(&[0, 2, 1, 3])).unwrap();
assert_eq!(p.giant_size, vec![1, 1, 3, 4]);
assert_eq!(p.edge_count, vec![0, 0, 2, 4]);

// Removing the hub of a star shatters it at once.
let star = Graph::star(6, StarMode::Undirected, 0).unwrap();
let attack = [0, 1, 2, 3, 4, 5]; // hub first
let build: Vec<i64> = attack.iter().rev().copied().collect();
let mut after = star.site_percolation(Some(&build)).unwrap().giant_size;
after.reverse(); // after[k]: largest component with k vertices removed
assert_eq!(after, vec![6, 1, 1, 1, 1, 1]);
Source

pub fn is_separator<'a>( &self, candidate: impl Into<VertexSelector<'a>>, ) -> Result<bool>

Whether removing the candidate vertices disconnects the graph.

A vertex set S is a separator if there are vertices u and v outside S, connected in the graph, such that every path between them passes through S. Edge directions are ignored and duplicate ids in candidate are ignored. Removing all vertices, or all but one, is never a separation, and the empty set is never a separator either, not even in a disconnected graph: what matters is whether removing the set separates some vertices that were connected before.

Time complexity: O(|V| + |E|).

See also minimum_size_separators to find the smallest separators, and Graph::st_vertex_connectivity for the size of the smallest set separating two given vertices.

Binds igraph_is_separator.

§Examples
use igraph::prelude::*;
// A star with center 0.
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false).unwrap();
assert!(star.is_separator(0).unwrap());
assert!(!star.is_separator(1).unwrap());
assert!(!star.is_separator(1..4).unwrap());
Source

pub fn is_minimal_separator<'a>( &self, candidate: impl Into<VertexSelector<'a>>, ) -> Result<bool>

Whether candidate is a minimal separator: a separator (see is_separator) none of whose proper subsets is a separator. Edge directions are ignored.

Time complexity: O(|V| + |E|).

Binds igraph_is_minimal_separator.

§Examples
use igraph::prelude::*;
// The path 0-1-2-3.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
assert!(g.is_minimal_separator(1).unwrap());
assert!(g.is_separator(&[1, 2]).unwrap());
assert!(!g.is_minimal_separator(&[1, 2]).unwrap());
Source

pub fn all_minimal_st_separators(&self) -> Result<Vec<Vec<VertexId>>>

All the vertex sets that are minimal (s, t) separators for some pair of vertices s, t.

Some returned sets are not minimal with respect to disconnecting the graph: in the graph 0-1-2-3-4-1 the sets {1}, {2, 4} and {1, 3} are returned, and {1, 3} is minimal for separating 2 from 4 although {1} alone disconnects the graph. Edge directions are ignored. Unlike minimum_size_separators, a disconnected graph is handled component by component (its separators are those of its components). Uses the algorithm of Berry, Bordat and Cogis (1999).

Time complexity: O(n |V|³), n being the number of separators.

Binds igraph_all_minimal_st_separators.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 1)], 5, false).unwrap();
let mut seps: Vec<Vec<i64>> = g
    .all_minimal_st_separators()
    .unwrap()
    .into_iter()
    .map(|mut s| { s.sort(); s })
    .collect();
seps.sort();
assert_eq!(seps, vec![vec![1], vec![1, 3], vec![2, 4]]);
Source

pub fn minimum_size_separators(&self) -> Result<Vec<Vec<VertexId>>>

All the vertex separators of minimum size.

A vertex set is a separator if its removal disconnects the graph. The graph must be undirected. A graph that is already disconnected has no separators (an empty list is returned), and neither do complete graphs. Each separator has exactly vertex_connectivity vertices. The separators are returned in arbitrary order. Uses the algorithm of Kanevsky (1993).

See also Graph::all_st_mincuts for the edge analogue between two given vertices.

Binds igraph_minimum_size_separators.

§Errors

ErrorKind::InvalidValue for directed graphs.

§Examples
use igraph::prelude::*;
// The 5-cycle: removing any two non-adjacent vertices disconnects it.
let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
let seps = c5.minimum_size_separators().unwrap();
assert_eq!(seps.len(), 5);
assert!(seps.iter().all(|s| s.len() == 2));
Source

pub fn cohesive_blocks(&self) -> Result<CohesiveBlocks>

The hierarchical cohesive block structure of the graph (Moody and White, 2003).

A vertex set is k-cohesive when the subgraph it induces has vertex connectivity at least k; it is maximally k-cohesive when no superset is. Cohesive blocking starts from the whole graph and recursively identifies the maximally l-cohesive subsets, l > k, of each k-cohesive block, yielding a tree of nested blocks rooted at the whole graph (block 0, with parent None).

The cohesion of each block is the vertex_connectivity of the subgraph it induces. The root block has cohesion 0 when the graph is disconnected; the null graph yields a single, empty root block.

The graph must be undirected and simple (see Graph::simplify).

See also Graph::coreness for the (cheaper, degree-based) k-core hierarchy, and Graph::cohesion for the cohesion of the whole graph.

Binds igraph_cohesive_blocks.

§Errors

ErrorKind::InvalidValue for directed or non-simple graphs.

§Examples
use igraph::prelude::*;
// Two 4-cliques (0..4 and 3..7) sharing vertex 3.
let mut edges = vec![];
for block in [[0, 1, 2, 3], [3, 4, 5, 6]] {
    for i in 0..4 {
        for j in i + 1..4 {
            edges.push((block[i], block[j]));
        }
    }
}
let g = Graph::from_edges(&edges, 7, false).unwrap();
let cb = g.cohesive_blocks().unwrap();
assert_eq!(cb.len(), 3);
assert_eq!(cb.cohesion, vec![1, 3, 3]);
assert_eq!(cb.parent, vec![None, Some(0), Some(0)]);
assert_eq!(cb.children(0), vec![1, 2]);
Source

pub fn reachability(&self, mode: NeighborMode) -> Result<Reachability>

Which vertices are reachable from each vertex.

The result groups vertices by the component they belong to (strongly connected components for directed graphs with NeighborMode::Out/NeighborMode::In, plain components otherwise), since vertices of one component reach the same set, and stores for each component the set of reachable vertices as booleans. With Out edges are followed along their direction, with In against it, with All directions are ignored; mode is ignored for undirected graphs. A vertex always reaches itself.

Time complexity: O(|C||V|/w + |V| + |E|), |C| being the number of components and w the machine word size.

See also count_reachable for the sizes only, transitive_closure for the same information as a graph, and Graph::subcomponent for the vertices reachable from a single vertex.

Binds igraph_reachability.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (3, 2)], 4, true).unwrap();
let r = g.reachability(NeighborMode::Out).unwrap();
assert!(r.is_reachable(0, 2));
assert!(!r.is_reachable(2, 0));
assert_eq!(r.reachable_from(3), vec![2, 3]);
Source

pub fn count_reachable(&self, mode: NeighborMode) -> Result<Vec<usize>>

The number of vertices reachable from each vertex, itself included.

mode has the same meaning as in reachability: with NeighborMode::In it counts the vertices that can reach each vertex.

Time complexity: O(|C||V|/w + |V| + |E|).

Equivalently, it is neighborhood_size with unlimited order and mindist = 0, but faster: the work is shared by all the vertices of a strongly connected component instead of running one breadth-first search per vertex.

Binds igraph_count_reachable.

§Examples
use igraph::prelude::*;
// The directed path 0 -> 1 -> 2.
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
assert_eq!(g.count_reachable(NeighborMode::Out).unwrap(), vec![3, 2, 1]);
assert_eq!(g.count_reachable(NeighborMode::In).unwrap(), vec![1, 2, 3]);
Source

pub fn transitive_closure(&self) -> Result<Graph>

The transitive closure of the graph.

The result has the same vertices and directedness, and an edge i -> j (i != j) exactly when j is reachable from i in the original graph; it is simple (no loops nor multi-edges). For undirected graphs every component becomes a clique.

Time complexity: O(|V|² + |E|).

For a bounded number of steps use Graph::graph_power (a new graph) or Graph::connect_neighborhood (in place).

Binds igraph_transitive_closure.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
let tc = g.transitive_closure().unwrap();
assert_eq!(tc.ecount(), 3);
assert!(tc.get_eid(0, 2, true).unwrap().is_some());
Source

pub fn neighborhood_size<'a>( &self, vids: impl Into<VertexSelector<'a>>, order: Option<usize>, mode: NeighborMode, mindist: usize, ) -> Result<Vec<usize>>

The sizes of the neighborhoods of the selected vertices.

The neighborhood of order k of a vertex contains the vertices at distance at most k from it: order 0 is the vertex itself, order 1 adds its neighbors, and so on; order = None means no limit (the whole reachable set). Vertices closer than mindist are not counted: mindist = 1 excludes the vertex itself, 2 its neighbors too, etc. With NeighborMode::Out paths follow edge directions, with NeighborMode::In they go against them, NeighborMode::All ignores them.

Time complexity: O(n d o), n being the number of selected vertices, d the average degree and o the order.

See also Graph::distances for the distances themselves and Graph::bfs for a full breadth-first traversal.

Binds igraph_neighborhood_size.

§Errors

Invalid vertex ids, or mindist > order.

§Examples
use igraph::prelude::*;
// The path 0-1-2-3-4.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
assert_eq!(g.neighborhood_size(.., Some(1), NeighborMode::All, 0).unwrap(), vec![2, 3, 3, 3, 2]);
// Vertices at distance exactly 2.
assert_eq!(g.neighborhood_size(.., Some(2), NeighborMode::All, 2).unwrap(), vec![1, 1, 2, 1, 1]);
Source

pub fn neighborhood<'a>( &self, vids: impl Into<VertexSelector<'a>>, order: Option<usize>, mode: NeighborMode, mindist: usize, ) -> Result<Vec<Vec<VertexId>>>

The neighborhoods of the selected vertices, as vertex lists.

See neighborhood_size for the meaning of order (None = unlimited), mode and mindist. Each list is in breadth-first order: the vertex itself first (unless excluded by mindist), then vertices at distance 1, 2, …

Time complexity: O(n d o).

See also Graph::connect_neighborhood, which adds an edge from each vertex to every member of its neighborhood.

Binds igraph_neighborhood.

§Errors

Invalid vertex ids, or mindist > order.

§Examples
use igraph::prelude::*;
// The directed path 0 -> 1 -> 2 -> 3.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
assert_eq!(g.neighborhood(1, None, NeighborMode::Out, 0).unwrap(), vec![vec![1, 2, 3]]);
assert_eq!(g.neighborhood(&[1, 3], Some(1), NeighborMode::In, 1).unwrap(), vec![vec![0], vec![2]]);
Source

pub fn neighborhood_graphs<'a>( &self, vids: impl Into<VertexSelector<'a>>, order: Option<usize>, mode: NeighborMode, mindist: usize, ) -> Result<Vec<Graph>>

The subgraphs induced by the neighborhoods of the selected vertices.

See neighborhood_size for the meaning of order (None = unlimited), mode and mindist. In each graph the vertices are renumbered consecutively preserving their relative order in the original graph (as in an induced subgraph), so the new id of an original vertex v is the number of neighborhood members with a smaller id than v. Each graph is thus the same as Graph::induced_subgraph of the corresponding neighborhood (an “ego network”).

Time complexity: O(n (|V| + |E|)).

Binds igraph_neighborhood_graphs.

§Errors

Invalid vertex ids, or mindist > order.

§Examples
use igraph::prelude::*;
// A star with center 0 and leaves 1..=4: its 1-neighborhoods.
let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], 5, false).unwrap();
let egos = g.neighborhood_graphs(&[0, 1], Some(1), NeighborMode::All, 0).unwrap();
assert_eq!((egos[0].vcount(), egos[0].ecount()), (5, 4));
assert_eq!((egos[1].vcount(), egos[1].ecount()), (2, 1));
Source§

impl igraph_t

Deterministic generators, see the module docs.

Source

pub fn adjacency( matrix: &Matrix, mode: Adjacency, loops: Loops, ) -> Result<Graph>

Creates a graph from an adjacency matrix.

Row/column i of the square matrix becomes vertex i; entries are edge counts (non-negative integers), interpreted according to mode (A[i][j] is the element in row i, column j):

loops says how the diagonal is read: Loops::None ignores it, Loops::Once reads it as the number of self-loops, Loops::Twice as twice that number (the usual degree convention for undirected graphs; odd values are an error). In the Adjacency::Directed, Adjacency::Upper and Adjacency::Lower modes Twice is treated as Once: a directed loop adds one to both the in- and the out-degree, and a triangle only holds the diagonal once. Edge ordering is not specified.

Binds igraph_adjacency. Time complexity: O(|V|²).

See also Graph::get_adjacency, the inverse conversion, and sparse_adjacency for large sparse graphs.

§Errors

ErrorKind::InvalidValue for a non-square matrix, entries that are not non-negative integers (negative, fractional, NaN or infinite; checked on the Rust side, since igraph would truncate them with an unchecked cast), a non-symmetric matrix with Adjacency::Undirected, or an odd diagonal with Loops::Twice in the modes that honour it.

§Examples
use igraph::prelude::*;
let a = Matrix::from_rows(&[[0.0, 1.0, 1.0], [1.0, 0.0, 0.0], [1.0, 0.0, 2.0]]).unwrap();
let g = Graph::adjacency(&a, Adjacency::Undirected, Loops::Twice).unwrap();
// Two edges plus one self-loop on vertex 2 (its diagonal entry counts it twice).
assert_eq!(g.ecount(), 3);
assert_eq!(g.degree_of(2, NeighborMode::All, Loops::Twice).unwrap(), 3);
// `get_adjacency` gives the matrix back (with the same loop convention).
assert_eq!(g.get_adjacency(GetAdjacency::Both, None, Loops::Twice).unwrap(), a);
Source

pub fn weighted_adjacency( matrix: &Matrix, mode: Adjacency, loops: Loops, ) -> Result<(Graph, Vec<f64>)>

Creates a weighted graph from a weighted adjacency matrix, returning the graph and its edge weights (indexed by edge id).

Zero entries mean “no edge” (negative weights are allowed). The modes are as in adjacency, except that at most one edge is created per vertex pair: Adjacency::Undirected requires a symmetric matrix, and Max/Min/Plus combine the two weights A[i][j] and A[j][i] into the weight of a single edge. For the diagonal, Loops::None ignores it, Loops::Once takes the entry as the loop weight and Loops::Twice as twice the loop weight (halving it; Twice is treated as Once in the Adjacency::Directed, Adjacency::Upper and Adjacency::Lower modes).

Binds igraph_weighted_adjacency. Time complexity: O(|V|²).

See also Graph::get_adjacency with weights, the inverse conversion.

§Errors

ErrorKind::InvalidValue for a non-square matrix, or a non-symmetric one with Adjacency::Undirected.

§Examples
use igraph::prelude::*;
let a = Matrix::from_rows(&[[0.0, 2.5, 0.0], [0.0, 0.0, -1.0], [4.0, 0.0, 0.0]]).unwrap();
let (g, w) = Graph::weighted_adjacency(&a, Adjacency::Directed, Loops::None).unwrap();
let mut edges: Vec<_> = g.edge_list().into_iter().zip(w).collect();
edges.sort_by_key(|e| e.0);
assert_eq!(edges, vec![((0, 1), 2.5), ((1, 2), -1.0), ((2, 0), 4.0)]);
Source

pub fn sparse_adjacency( n: usize, entries: &[(VertexId, VertexId, f64)], mode: Adjacency, loops: Loops, ) -> Result<Graph>

Creates a graph on n vertices from a sparse adjacency matrix given as (row, column, value) triplets.

This is the sparse counterpart of adjacency, with the same mode and loops semantics and the same result; entries not listed are zero, and repeated (row, column) pairs are summed. The triplets are summed up and compressed into igraph’s column-compressed sparse matrix before the call.

igraph 1.0.0 and 1.0.1 lose the entries below the diagonal whose mirror entry is absent in the Adjacency::Max mode (and in the Max/Min/Plus modes of the weighted variant), and their Adjacency::Undirected mode rejects an explicitly stored zero without a stored mirror as non-symmetric. This wrapper stores only the nonzero sums, plus explicit zeros at the missing mirror positions where needed, so that the result always agrees with the dense adjacency.

Binds igraph_sparse_adjacency. Time complexity: O(|E|), plus O(t log t) for summing up the t triplets.

See also Graph::get_adjacency_sparse, whose entries are exactly the triplets this function takes.

§Errors

ErrorKind::InvalidVertexId if a triplet lies outside the n × n matrix; ErrorKind::InvalidValue if a value (or the sum of the values given for one position) is not a non-negative integer; otherwise as adjacency.

§Examples
use igraph::prelude::*;
// A directed 1000-cycle, without ever allocating a dense 1000 x 1000 matrix.
let n = 1000;
let entries: Vec<_> = (0..n as i64).map(|i| (i, (i + 1) % n as i64, 1.0)).collect();
let g = Graph::sparse_adjacency(n, &entries, Adjacency::Directed, Loops::None).unwrap();
assert_eq!((g.vcount(), g.ecount()), (1000, 1000));
assert!(g.is_directed());

// Round trip through the sparse adjacency matrix of the conversion module.
let karate = Graph::famous("Zachary").unwrap();
let coo = karate.get_adjacency_sparse(GetAdjacency::Upper, None, Loops::Twice).unwrap();
let back = Graph::sparse_adjacency(34, &coo.entries, Adjacency::Upper, Loops::Twice).unwrap();
assert!(back.isomorphic(&karate).unwrap());
assert_eq!(back.ecount(), 78);
Source

pub fn sparse_weighted_adjacency( n: usize, entries: &[(VertexId, VertexId, f64)], mode: Adjacency, loops: Loops, ) -> Result<(Graph, Vec<f64>)>

Creates a weighted graph on n vertices from a sparse weighted adjacency matrix given as (row, column, weight) triplets, returning the graph and its edge weights.

The sparse counterpart of weighted_adjacency; repeated (row, column) pairs are summed, explicit zeros mean no edge, and the result agrees with the dense version (see the note about mirror entries in sparse_adjacency).

Binds igraph_sparse_weighted_adjacency. Time complexity: O(|E|), plus O(t log t) for summing up the t triplets.

§Errors

ErrorKind::InvalidVertexId if a triplet lies outside the n × n matrix; otherwise as weighted_adjacency (any real weight is accepted).

§Examples
use igraph::prelude::*;
let entries = [(0, 1, 0.5), (1, 0, 0.5), (1, 2, 3.0), (2, 1, 3.0)];
let (g, w) =
    Graph::sparse_weighted_adjacency(3, &entries, Adjacency::Undirected, Loops::None)
        .unwrap();
assert_eq!(g.ecount(), 2);
assert_eq!(w.iter().sum::<f64>(), 3.5);
Source

pub fn small(n: usize, directed: bool, edges: &[VertexId]) -> Result<Graph>

Shorthand to create a small graph from a flat edge list [from0, to0, from1, to1, ...], in the spirit of igraph_small.

The C function igraph_small is variadic and terminated by -1; in Rust a slice literal does the same job safely. The graph has max(n, largest id + 1) vertices. This is the same as Graph::from_flat_edges, provided for familiarity with the C API; Graph::from_edges takes (from, to) pairs instead.

Binds igraph_create (the non-variadic equivalent of igraph_small).

§Errors

ErrorKind::InvalidValue for an odd number of ids, ErrorKind::InvalidVertexId for negative ids.

§Examples
use igraph::prelude::*;
let bowtie = Graph::small(5, false, &[0, 1, 1, 2, 2, 0, 2, 3, 3, 4, 4, 2]).unwrap();
assert_eq!(bowtie.degree_of(2, NeighborMode::All, Loops::Twice).unwrap(), 4);
Source

pub fn star(n: usize, mode: StarMode, center: VertexId) -> Result<Graph>

Creates a star graph: vertex center connected to all the other n - 1 vertices.

mode chooses between an undirected star (StarMode::Undirected), edges pointing out of (StarMode::Out) or into (StarMode::In) the center, or mutual directed edges (StarMode::Mutual).

Binds igraph_star. Time complexity: O(|V|).

See also wheel, and Graph::layout_star to draw it.

The star on a single vertex is that vertex alone (igraph 1.0.0 and 1.0.1 return the null graph there; this wrapper adds the missing vertex).

§Errors

ErrorKind::InvalidValue if center is not a vertex of the graph, in particular always for n = 0.

§Examples
use igraph::prelude::*;
let s = Graph::star(5, StarMode::In, 2).unwrap();
assert_eq!(s.degree_of(2, NeighborMode::In, Loops::Twice).unwrap(), 4);
assert_eq!(s.degree_of(2, NeighborMode::Out, Loops::Twice).unwrap(), 0);
Source

pub fn wheel(n: usize, mode: WheelMode, center: VertexId) -> Result<Graph>

Creates a wheel graph: a star (the spokes) plus a cycle through the n - 1 non-center vertices (the rim).

mode orients the spokes like in star; in the directed modes the rim is a directed cycle (mutual for WheelMode::Mutual). Note that the wheels on 2 and 3 vertices are not simple (they contain a self-loop and a multi-edge, respectively).

Binds igraph_wheel. Time complexity: O(|V|).

The wheel on one vertex is the singleton graph.

§Errors

ErrorKind::InvalidValue if center is not a vertex of the graph (always for n = 0).

§Examples
use igraph::prelude::*;
let w = Graph::wheel(6, WheelMode::Undirected, 0).unwrap();
assert_eq!(w.ecount(), 10); // 5 spokes + 5 rim edges
Source

pub fn hypercube(dim: usize, directed: bool) -> Result<Graph>

The dim-dimensional hypercube graph Q_dim.

It has 2^dim vertices and dim * 2^(dim-1) edges; two vertices are adjacent when the binary representations of their ids differ in exactly one bit. Directed edges point from lower to higher ids.

Binds igraph_hypercube. Time complexity: O(2^dim).

See also square_lattice: Q_dim is the 2 x 2 x ... x 2 lattice.

§Errors

ErrorKind::InvalidValue if dim > 57, so that the edge count would not fit into half the range of igraph_int_t (smaller but still huge dimensions fail with an out-of-memory error).

§Examples
use igraph::prelude::*;
let q3 = Graph::hypercube(3, false).unwrap();
assert_eq!((q3.vcount(), q3.ecount()), (8, 12));
assert_eq!(q3.neighbors(0, NeighborMode::All).unwrap(), vec![1, 2, 4]);
assert!(q3.isomorphic(&Graph::famous("Cubical").unwrap()).unwrap());
Source

pub fn square_lattice( dims: &[usize], nei: usize, directed: bool, mutual: bool, periodic: Option<&[bool]>, ) -> Result<Graph>

Creates an arbitrary-dimensional square lattice (grid).

dims gives the size along each dimension (an empty slice gives the singleton graph). The vertex at position (i_1, i_2, ..., i_d) gets id i_1 + n_1 * i_2 + n_1 * n_2 * i_3 + .... Vertices within nei steps of each other are connected (nei = 1 is the usual grid). periodic, when given, must have one flag per dimension, making the lattice wrap around (a torus) along the flagged dimensions. When directed, edges point from lower to higher ids unless mutual (or periodicity) is set.

Binds igraph_square_lattice. Time complexity: O(|V| + |E|) for nei < 2.

See also Graph::layout_grid, which places a 2D lattice on its grid (pass Some(dims[0]) as the width), and triangular_lattice / hexagonal_lattice.

§Errors

ErrorKind::InvalidValue if periodic and dims have different lengths.

§Examples
use igraph::prelude::*;
// A 4x4 torus is 4-regular.
let torus = Graph::square_lattice(&[4, 4], 1, false, false, Some(&[true, true])).unwrap();
assert_eq!(torus.ecount(), 32);
let deg = torus.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap();
assert!(deg.iter().all(|&d| d == 4));
// Vertex (x, y) of a 5 x 3 grid has id x + 5 y, which `layout_grid` puts at (x, y).
let grid = Graph::square_lattice(&[5, 3], 1, false, false, None).unwrap();
let layout = grid.layout_grid(Some(5)).unwrap();
assert_eq!((layout[(7, 0)], layout[(7, 1)]), (2.0, 1.0));
Source

pub fn ring( n: usize, directed: bool, mutual: bool, circular: bool, ) -> Result<Graph>

Creates a cycle graph C_n (circular = true) or a path graph P_n (circular = false).

In directed graphs all edges follow the same orientation along the ring, or are mutual when mutual is set (ignored when undirected). For n = 1 or 2 the cycle is not simple (a self-loop, or two parallel edges).

Binds igraph_ring. Time complexity: O(|V|).

See also circulant for rings with chords, and Graph::layout_circle to draw it.

§Examples
use igraph::prelude::*;
let ring = Graph::ring(5, true, false, true).unwrap();
assert_eq!(ring.edge_list(), vec![(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]);
Source

pub fn path_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph>

The path graph P_n on n vertices: 0 - 1 - ... - (n-1).

A convenience form of ring with circular = false; mutual adds both directions in directed graphs.

Binds igraph_path_graph. Time complexity: O(|V|).

§Examples
use igraph::prelude::*;
let p = Graph::path_graph(4, true, true).unwrap();
assert_eq!(p.ecount(), 6); // 3 links, both directions
Source

pub fn cycle_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph>

The cycle graph C_n on n vertices.

A convenience form of ring with circular = true. For n = 1 or 2 the result has a self-loop or parallel edges.

Binds igraph_cycle_graph. Time complexity: O(|V|).

§Examples
use igraph::prelude::*;
let c = Graph::cycle_graph(7, false, false).unwrap();
assert_eq!((c.vcount(), c.ecount()), (7, 7));
Source

pub fn kary_tree(n: usize, children: usize, mode: TreeMode) -> Result<Graph>

Creates a children-ary tree on n vertices, filled level by level in breadth-first order (vertex i’s children are children*i + 1 ..= children*i + children).

For a complete tree with l levels below the root use n = (children^(l+1) - 1) / (children - 1). mode gives the edge orientation (parent → child for TreeMode::Out). n = 0 gives the null graph.

Binds igraph_kary_tree. Time complexity: O(|V| + |E|).

See also Graph::tree_game for uniformly random trees and Graph::layout_reingold_tilford to draw trees.

§Errors

ErrorKind::InvalidValue if children is zero.

§Examples
use igraph::prelude::*;
let t = Graph::kary_tree(7, 2, TreeMode::Out).unwrap(); // a complete binary tree
assert_eq!(t.neighbors(1, NeighborMode::Out).unwrap(), vec![3, 4]);
assert!(t.is_tree(NeighborMode::Out).unwrap());
Source

pub fn symmetric_tree(branches: &[usize], mode: TreeMode) -> Result<Graph>

Creates a symmetric tree where every vertex at distance d from the root has branches[d] children.

The tree has 1 + b_0 + b_0 b_1 + ... vertices, numbered in breadth-first order from the root 0.

Binds igraph_symmetric_tree. Time complexity: O(|V| + |E|).

An empty branches gives the singleton graph.

§Errors

ErrorKind::InvalidValue if a branch count is zero.

§Examples
use igraph::prelude::*;
let t = Graph::symmetric_tree(&[3, 2], TreeMode::Undirected).unwrap();
assert_eq!(t.vcount(), 1 + 3 + 3 * 2);
Source

pub fn regular_tree(h: usize, k: usize, mode: TreeMode) -> Result<Graph>

Creates a regular tree (Bethe lattice) of height h in which every non-leaf vertex has total degree k.

Unlike a kary_tree, the root has k children and the other internal vertices k - 1, so that all internal degrees are equal. h is the distance between the root and the leaves.

Binds igraph_regular_tree. Time complexity: O(|V| + |E|).

§Errors

ErrorKind::InvalidValue unless h >= 1 and k >= 2.

§Examples
use igraph::prelude::*;
let t = Graph::regular_tree(2, 3, TreeMode::Undirected).unwrap();
assert_eq!(t.vcount(), 1 + 3 + 3 * 2);
Source

pub fn tree_from_parent_vector( parents: &[VertexId], mode: TreeMode, ) -> Result<Graph>

Builds a tree or forest from a parent vector: parents[v] is the parent of vertex v, or a negative value if v is a root.

Such vectors are produced by BFS/DFS traversals, shortest path trees, dominator trees, etc. The graph has parents.len() vertices; with TreeMode::Out edges point from parents to children, with TreeMode::In from children to parents.

Binds igraph_tree_from_parent_vector. Time complexity: O(n).

See also Graph::bfs_simple and the other traversals of crate::visitor, whose parents give such vectors (map None to -1).

§Errors

ErrorKind::InvalidValue if the vector encodes a cycle or a self-loop, ErrorKind::InvalidVertexId for out-of-range parents.

§Examples
use igraph::prelude::*;
// Two trees: 0 <- {1, 2}, 2 <- 3, and the lone root 4.
let f = Graph::tree_from_parent_vector(&[-1, 0, 0, 2, -1], TreeMode::Out).unwrap();
assert_eq!(f.ecount(), 3);
assert_eq!(f.neighbors(0, NeighborMode::Out).unwrap(), vec![1, 2]);

// The BFS tree of the Petersen graph: 1 root, 3 children, 6 grandchildren.
let petersen = Graph::famous("Petersen").unwrap();
let bfs = petersen.bfs_simple(0, NeighborMode::All).unwrap();
let parents: Vec<i64> = bfs.parents.iter().map(|p| p.unwrap_or(-1)).collect();
let tree = Graph::tree_from_parent_vector(&parents, TreeMode::Out).unwrap();
assert!(tree.is_tree(NeighborMode::Out).unwrap());
assert_eq!(tree.ecount(), 9);
Source

pub fn from_prufer(prufer: &[VertexId]) -> Result<Graph>

Builds the labelled tree encoded by a Prüfer sequence.

A sequence of length n - 2 with entries in 0..n encodes a unique tree on n vertices (Cayley’s formula counts n^(n-2) of them); a vertex appears in the sequence exactly degree - 1 times.

Binds igraph_from_prufer. Time complexity: O(|V|).

See also Graph::to_prufer, the inverse conversion.

§Errors

ErrorKind::InvalidValue for an invalid sequence (entries out of range).

§Examples
use igraph::prelude::*;
let t = Graph::from_prufer(&[3, 3, 3]).unwrap(); // the star K_{1,4} centered at 3
assert_eq!(t.degree_of(3, NeighborMode::All, Loops::Twice).unwrap(), 4);
assert_eq!(t.to_prufer().unwrap(), vec![3, 3, 3]);
Source

pub fn full(n: usize, directed: bool, loops: bool) -> Result<Graph>

Creates the complete graph on n vertices.

Directed complete graphs have both i -> j and j -> i; with loops every vertex also gets a single self-loop.

Binds igraph_full. Time complexity: O(|V|²).

See also Graph::is_complete, Graph::complementer (the complement of K_n is the empty graph) and Graph::full_bipartite.

§Examples
use igraph::prelude::*;
assert_eq!(Graph::full(5, false, false).unwrap().ecount(), 10);
assert_eq!(Graph::full(5, true, false).unwrap().ecount(), 20);
assert_eq!(Graph::full(5, false, true).unwrap().ecount(), 15);
Source

pub fn full_multipartite( sizes: &[usize], directed: bool, mode: NeighborMode, ) -> Result<(Graph, Vec<i64>)>

Creates a complete multipartite graph with partitions of the given sizes, returning the graph and the partition index of each vertex.

Vertices are numbered partition by partition; every pair of vertices in different partitions is connected. In directed graphs, mode NeighborMode::Out points edges from lower to higher partitions, NeighborMode::In the opposite, and NeighborMode::All creates mutual edges.

Binds igraph_full_multipartite. Time complexity: O(|V| + |E|).

See also Graph::full_bipartite for two partitions with boolean vertex types, and turan.

§Examples
use igraph::prelude::*;
let (k233, types) =
    Graph::full_multipartite(&[2, 3, 3], false, NeighborMode::All).unwrap();
assert_eq!(k233.ecount(), 2 * 3 + 2 * 3 + 3 * 3);
assert_eq!(types, vec![0, 0, 1, 1, 1, 2, 2, 2]);
Source

pub fn turan(n: usize, r: usize) -> Result<(Graph, Vec<i64>)>

Creates the Turán graph T(n, r), returning the graph and the partition index of each vertex.

It is the complete r-partite graph on n vertices with partition sizes as equal as possible: by Turán’s theorem, the densest graph on n vertices without a clique of size r + 1. It is undirected; n = 0 gives the null graph, and r > n gives the complete graph.

Binds igraph_turan. Time complexity: O(|V| + |E|).

§Errors

ErrorKind::InvalidValue if r is zero.

§Examples
use igraph::prelude::*;
let (t, types) = Graph::turan(6, 3).unwrap(); // the octahedron K_{2,2,2}
assert_eq!(t.ecount(), 12);
assert_eq!(types, vec![0, 0, 1, 1, 2, 2]);
// Turán's theorem: no clique on r + 1 = 4 vertices.
assert_eq!(t.clique_number().unwrap(), 3);
Source

pub fn full_citation(n: usize, directed: bool) -> Result<Graph>

Creates a full citation graph: the complete directed acyclic graph in which i -> j is an edge exactly when j < i.

With directed = false it is just the complete graph.

Binds igraph_full_citation. Time complexity: O(|V|²).

§Examples
use igraph::prelude::*;
let g = Graph::full_citation(4, true).unwrap();
assert_eq!(g.neighbors(3, NeighborMode::Out).unwrap(), vec![0, 1, 2]);
assert!(g.neighbors(0, NeighborMode::Out).unwrap().is_empty());
Source

pub fn atlas(number: usize) -> Result<Graph>

Creates graph number number of An Atlas of Graphs (Read and Wilson, 1998).

The atlas holds all 1253 simple undirected unlabelled graphs on 0 to 7 vertices, ordered by number of vertices, then number of edges, then degree sequence (lexicographically, e.g. 111223 < 112222), then increasing number of automorphisms. Graphs on 0, 1, …, 7 vertices start at numbers 0, 1, 2, 4, 8, 19, 53 and 209.

Binds igraph_atlas. Time complexity: O(|V| + |E|).

See also Graph::isomorphic and Graph::canonical_permutation to locate a given small graph in the atlas.

§Errors

ErrorKind::InvalidValue if number > 1252.

§Examples
use igraph::prelude::*;
let k7 = Graph::atlas(1252).unwrap(); // the last one is K_7
assert_eq!((k7.vcount(), k7.ecount()), (7, 21));
Source

pub fn extended_chordal_ring<R: AsRef<[i64]>>( nodes: usize, w: &[R], directed: bool, ) -> Result<Graph>

Creates an extended chordal ring: a cycle on nodes vertices plus chords described by the rows of w.

For each row L of length p (all rows have the same length, which must divide nodes), vertex i is connected to vertex (i + L[i mod p]) mod nodes. Entries may be negative. The result is not simplified: duplicate chords (and chords along the cycle) produce multi-edges, and offsets that are multiples of nodes self-loops. Note that igraph’s definition differs from the one in Kotsis (1993).

Binds igraph_extended_chordal_ring. Time complexity: O(|V| + |E|).

§Errors

ErrorKind::InvalidValue if nodes < 3, rows have different lengths, or the row length does not divide nodes.

§Examples
use igraph::prelude::*;
// One row [3]: each vertex also links 3 steps ahead; on 6 vertices the
// three "diameters" appear twice, giving the utility graph K_{3,3} plus duplicates.
let g = Graph::extended_chordal_ring(6, &[[3]], false).unwrap();
assert_eq!(g.ecount(), 6 + 6);
Source

pub fn linegraph(&self) -> Result<Graph>

The line graph L(G) of this graph: one vertex per edge (edge i becomes vertex i).

For undirected graphs, two vertices of L(G) are adjacent when the corresponding edges share an endpoint (twice, if they share both endpoints in a multigraph; the single vertex of a self-loop counts as two endpoints, so a self-loop and an edge incident to it are joined twice). For directed graphs, e -> f is an edge when the target of e is the source of f. Self-loops are self-adjacent and get a single self-loop in L(G), in both cases.

Binds igraph_linegraph. Time complexity: O(|V| + |E|).

See also the other graph transformations of crate::operators.

§Examples
use igraph::prelude::*;
// The line graph of the star K_{1,4} is K_4.
let l = Graph::star(5, StarMode::Undirected, 0).unwrap().linegraph().unwrap();
assert_eq!((l.vcount(), l.ecount()), (4, 6));
Source

pub fn de_bruijn(m: usize, n: usize) -> Result<Graph>

The de Bruijn graph B(m, n): vertices are the m^n strings of length n over an alphabet of m letters, with an edge v -> w when w is obtained by dropping the first letter of v and appending one.

Vertex ids are the strings read as base-m numbers. Every vertex has in- and out-degree m (loops included), so the graph has m^(n+1) edges and is Eulerian; its Eulerian circuits spell de Bruijn sequences.

B(m, 0) is the singleton graph and B(0, n) for n > 0 the null graph.

Binds igraph_de_bruijn. Time complexity: O(|V| + |E|).

See also Graph::eulerian_cycle to spell a de Bruijn sequence (as in the example below) and kautz.

§Errors

ErrorKind::InvalidValue if the number of vertices m^n does not fit in an i64, and ErrorKind::Overflow if the m^(n+1) edges do not fit in igraph’s edge vector (both checked before calling igraph).

§Examples
use igraph::prelude::*;
let b = Graph::de_bruijn(2, 3).unwrap();
assert_eq!((b.vcount(), b.ecount()), (8, 16));
// "011" -> "110" and "111"
assert_eq!(b.neighbors(0b011, NeighborMode::Out).unwrap(), vec![0b110, 0b111]);

// An Eulerian circuit of B(2, 3) spells a de Bruijn sequence of order 4:
// each vertex appends its last letter, and all 16 4-bit words occur
// exactly once as cyclic substrings.
let walk = b.eulerian_cycle().unwrap();
let seq: Vec<i64> = walk.vertices[1..].iter().map(|v| v % 2).collect();
assert_eq!(seq.len(), 16);
let words: std::collections::BTreeSet<i64> = (0..16)
    .map(|i| (0..4).fold(0, |acc, j| 2 * acc + seq[(i + j) % 16]))
    .collect();
assert_eq!(words.len(), 16);
Source

pub fn kautz(m: usize, n: usize) -> Result<Graph>

The Kautz graph K(m, n): vertices are the strings of length n + 1 over an alphabet of m + 1 letters with no two equal consecutive letters; v -> w when w is v shifted by one letter.

It has (m+1) m^n vertices, each with in- and out-degree m, and no self-loops. Degenerate cases: K(m, 0) is the complete directed graph on m + 1 vertices, and K(0, n) for n > 0 is the null graph.

Binds igraph_kautz. Time complexity: roughly O(|V| + |E|).

§Errors

ErrorKind::InvalidValue if the graph would be too large (m^n or (m+1)^(n+1) does not fit in an i64; checked before calling igraph).

§Examples
use igraph::prelude::*;
let k = Graph::kautz(2, 1).unwrap();
assert_eq!((k.vcount(), k.ecount()), (6, 12));
Source

pub fn circulant(n: usize, shifts: &[i64], directed: bool) -> Result<Graph>

The circulant graph C_n(shifts): vertex j is connected to (j + s) mod n for every shift s.

Shifts may be negative; shifts that are multiples of n are ignored and no multi-edges or self-loops are created.

Binds igraph_circulant. Time complexity: O(|V| |shifts|).

See also extended_chordal_ring, which allows position-dependent chords and multi-edges, and Graph::k_regular_game for random regular graphs.

§Examples
use igraph::prelude::*;
// C_5(1, 2) is K_5.
assert_eq!(Graph::circulant(5, &[1, 2], false).unwrap().ecount(), 10);
Source

pub fn generalized_petersen(n: usize, k: usize) -> Result<Graph>

The generalized Petersen graph G(n, k): an outer n-cycle v_0 .. v_(n-1) (ids 0..n), an inner circulant u_i ~ u_(i+k mod n) (ids n..2n) and the spokes v_i ~ u_i.

It has 2n vertices and 3n edges and is cubic. G(5, 2) is the Petersen graph, G(4, 1) the cube, G(10, 3) the Desargues graph.

Binds igraph_generalized_petersen. Time complexity: O(|V|).

§Errors

ErrorKind::InvalidValue unless n >= 3 and 0 < k < n / 2.

§Examples
use igraph::prelude::*;
let desargues = Graph::generalized_petersen(10, 3).unwrap();
assert_eq!((desargues.vcount(), desargues.ecount()), (20, 30));
let petersen = Graph::generalized_petersen(5, 2).unwrap();
assert!(petersen.isomorphic(&Graph::famous("Petersen").unwrap()).unwrap());
Source

pub fn famous(name: impl AsRef<str>) -> Result<Graph>

Creates a named graph, such as "Petersen" or "Zachary".

The name is case insensitive; the supported graphs (and their sizes) are listed by FamousGraph, which can be passed directly. Some names have aliases in igraph: Dodecahedral, Icosahedral, Octahedral, Tetrahedral and Groetzsch.

Binds igraph_famous. Time complexity: O(|V| + |E|).

See also atlas for all small graphs, and crate::foreign to read graphs from files.

§Errors

ErrorKind::InvalidValue for an unknown name (or one containing a NUL byte).

§Examples
use igraph::{constructors::FamousGraph, prelude::*};
let karate = Graph::famous("zachary").unwrap();
assert_eq!((karate.vcount(), karate.ecount()), (34, 78));
let kite = Graph::famous(FamousGraph::KrackhardtKite).unwrap();
assert_eq!(kite.vcount(), 10);
assert_eq!(Graph::famous("Unicorn").unwrap_err().kind(), ErrorKind::InvalidValue);
// The Frucht graph is cubic but has no symmetry at all.
let frucht = Graph::famous(FamousGraph::Frucht).unwrap();
assert_eq!(frucht.count_automorphisms(None).unwrap(), 1.0);
Source

pub fn lcf(n: usize, shifts: &[i64], repeats: usize) -> Result<Graph>

Creates a graph from LCF (Lederberg–Coxeter–Frucht) notation [shifts]^repeats on n vertices.

The graph is the cycle 0 - 1 - ... - (n-1) - 0 plus, going around the cycle, a chord from vertex i to i + s for the shifts s repeated repeats times. Normally n = shifts.len() * repeats, and the result is a cubic Hamiltonian graph. The result is always simple: duplicate chords are merged and loops (shifts that are multiples of n) dropped. Shifts are taken modulo n, so any i64 is accepted; n = 0 gives the null graph.

Binds igraph_lcf (and covers the variadic igraph_lcf_small). Time complexity: O(|V| + |E|).

See also generalized_petersen and famous for other ways to build well-known cubic graphs.

§Examples
use igraph::prelude::*;
// The Heawood graph is [5, -5]^7.
let h = Graph::lcf(14, &[5, -5], 7).unwrap();
assert_eq!((h.vcount(), h.ecount()), (14, 21));
assert!(h.isomorphic(&Graph::famous("Heawood").unwrap()).unwrap());
Source

pub fn realize_degree_sequence( out_degrees: &[i64], in_degrees: Option<&[i64]>, allowed: impl Into<AllowedEdgeTypes>, method: RealizeDegseq, ) -> Result<Graph>

Builds a graph realizing the given degree sequence, deterministically.

With in_degrees = None an undirected graph with degrees out_degrees is created, otherwise a directed graph with the given out- and in-degrees. Simple graphs are built with the Havel–Hakimi (undirected) or Kleitman–Wang (directed) algorithm: repeatedly pick a vertex and connect all its stubs to the vertices with the largest remaining degrees. Multigraphs use an analogous one-edge-at-a-time procedure; with self-loops allowed, leftover stubs become loops on a single vertex.

allowed selects the kind of graph (directed graphs support only AllowedEdgeTypes::SIMPLE; AllowedEdgeTypes::LOOPS alone is not implemented). method selects the vertex order: RealizeDegseq::Smallest (smallest remaining degree first; in the undirected case it yields a connected graph whenever one exists, so it builds a tree from tree degrees), RealizeDegseq::Largest (strongly assortative, often disconnected) or RealizeDegseq::Index (in vertex order).

Binds igraph_realize_degree_sequence. Time complexity: O(V + α(V) E) for simple undirected graphs.

See also is_graphical, which only decides whether a realization exists (it takes the same AllowedEdgeTypes flags), and Graph::degree_sequence_game for random realizations.

§Errors

ErrorKind::InvalidValue if the sequence is not graphical for the requested kind of graph, or lengths or sums of the directed sequences differ; ErrorKind::Unimplemented for unsupported combinations.

§Examples
use igraph::{constructors::AllowedEdgeTypes, prelude::*};
let degrees = [3, 3, 2, 2, 2, 1, 1];
let g = Graph::realize_degree_sequence(
    &degrees, None, AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest,
).unwrap();
assert_eq!(g.degree(VertexSelector::All, NeighborMode::All, Loops::Twice).unwrap(), degrees);
// [3, 3] cannot be realized as a simple graph...
assert!(Graph::realize_degree_sequence(
    &[3, 3], None, AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest).is_err());
// ... but it can as a multigraph: three parallel edges.
let m = Graph::realize_degree_sequence(
    &[3, 3], None, AllowedEdgeTypes::MULTI, RealizeDegseq::Smallest).unwrap();
assert_eq!(m.ecount(), 3);
Source

pub fn realize_bipartite_degree_sequence( degrees1: &[i64], degrees2: &[i64], allowed: impl Into<AllowedEdgeTypes>, method: RealizeDegseq, ) -> Result<Graph>

Builds a bipartite graph realizing the bidegree sequence (degrees1, degrees2), deterministically.

Vertices 0..degrees1.len() form the first partition, followed by the second one. A Havel–Hakimi-like algorithm is used; allowed is AllowedEdgeTypes::SIMPLE or AllowedEdgeTypes::MULTI (a bipartite graph has no self-loops, so igraph ignores the loops flag: LOOPS acts as SIMPLE, ALL as MULTI), and method has the same meaning as in realize_degree_sequence (with RealizeDegseq::Smallest the result is connected whenever the sequence is potentially connected).

Binds igraph_realize_bipartite_degree_sequence.

See also is_bigraphical, which only decides whether a realization exists.

§Errors

ErrorKind::InvalidValue if the bidegree sequence cannot be realized.

§Examples
use igraph::{constructors::AllowedEdgeTypes, prelude::*};
// Three students, two projects: who works on what.
let g = Graph::realize_bipartite_degree_sequence(
    &[1, 2, 1], &[2, 2], AllowedEdgeTypes::SIMPLE, RealizeDegseq::Smallest,
).unwrap();
assert_eq!((g.vcount(), g.ecount()), (5, 4));
assert!(g.is_bipartite().unwrap());
Source

pub fn triangular_lattice( dims: &[usize], directed: bool, mutual: bool, ) -> Result<Graph>

Creates a triangular lattice of the given shape.

Vertices are points (i, j) connected to (i+1, j), (i, j+1) and (i-1, j+1) when present, so degrees are at most 6. dims of length 1 gives a triangle with dims[0] vertices per side, length 2 a “quasi-rectangle” with sides of dims[0] and dims[1] vertices, length 3 a hexagon with the given side lengths. Vertices are ordered row by row. This is the planar dual of hexagonal_lattice with the same dims.

Binds igraph_triangular_lattice. Time complexity: O(|V|).

§Errors

ErrorKind::InvalidValue unless dims has length 1, 2 or 3, or if a hexagon shape is so large that its row sizes overflow an i64 (checked before calling igraph).

§Examples
use igraph::prelude::*;
let t = Graph::triangular_lattice(&[5], false, false).unwrap();
assert_eq!((t.vcount(), t.ecount()), (15, 30));
Source

pub fn hexagonal_lattice( dims: &[usize], directed: bool, mutual: bool, ) -> Result<Graph>

Creates a hexagonal (honeycomb) lattice of the given shape.

dims is interpreted as in triangular_lattice, but counts hexagons: the 6-cycles of the result correspond one-to-one to the vertices of the triangular lattice with the same dims. Degrees are at most 3.

Binds igraph_hexagonal_lattice. Time complexity: O(|V|).

§Errors

ErrorKind::InvalidValue unless dims has length 1, 2 or 3, or if a hexagon shape is so large that its row sizes overflow an i64 (checked before calling igraph).

§Examples
use igraph::prelude::*;
let benzene = Graph::hexagonal_lattice(&[1], false, false).unwrap();
assert_eq!((benzene.vcount(), benzene.ecount()), (6, 6));
Source

pub fn mycielski_graph(k: usize) -> Result<Graph>

The Mycielski graph M_k: triangle-free with chromatic number k.

Obtained by iterating the Mycielski construction: M_0 is the null graph, M_1 a single vertex, M_2 an edge, M_3 the 5-cycle, M_4 the Grötzsch graph. For k > 1, M_k has 3 * 2^(k-2) - 1 vertices and (7 * 3^(k-2) + 1) / 2 - 3 * 2^(k-2) edges.

This function is marked experimental in igraph 1.0.x.

Binds igraph_mycielski_graph. Time complexity: O(3^k).

See also Graph::mycielskian, which applies the construction to an arbitrary graph.

§Examples
use igraph::prelude::*;
let m4 = Graph::mycielski_graph(4).unwrap();
assert_eq!((m4.vcount(), m4.ecount()), (11, 20));
assert!(m4.isomorphic(&Graph::famous("Grotzsch").unwrap()).unwrap());
assert_eq!(m4.count_triangles().unwrap(), 0.0);
Source§

impl igraph_t

Source

pub fn get_adjacency( &self, kind: GetAdjacency, weights: Option<&[f64]>, loops: Loops, ) -> Result<Matrix>

The (dense) adjacency matrix of the graph.

Entry (i, j) of the returned n × n Matrix is the number of edges from vertex i to vertex j or, when weights are given, the total weight of those edges (so multi-edges add up).

  • kind selects which part of the matrix is filled for undirected graphs: GetAdjacency::Upper (upper-right triangle only, each edge stored once), GetAdjacency::Lower (lower-left triangle) or GetAdjacency::Both (the full symmetric matrix). It is ignored for directed graphs.
  • weights: optional edge weights, one per edge; None means every edge has weight 1.
  • loops controls the diagonal: Loops::None ignores self-loops (zero diagonal), Loops::Once counts each loop once and Loops::Twice counts loops twice in undirected graphs (it counts edge stems, the convention that makes row sums equal to degrees). In directed graphs Twice behaves like Once.

Time complexity: O(|V|²).

Binds igraph_get_adjacency. See get_adjacency_sparse for large, sparse graphs.

§Errors

ErrorKind::InvalidValue if weights has the wrong length.

§Examples
use igraph::prelude::*;
// A triangle with a double edge 0-1 and a loop on vertex 2.
let g = Graph::from_edges(&[(0, 1), (0, 1), (1, 2), (2, 0), (2, 2)], 3, false).unwrap();
let a = g.get_adjacency(GetAdjacency::Both, None, Loops::Twice).unwrap();
assert_eq!(a.to_rows(), vec![
    vec![0.0, 2.0, 1.0],
    vec![2.0, 0.0, 1.0],
    vec![1.0, 1.0, 2.0],
]);
// Row sums are the degrees.
let degrees: Vec<f64> = a.rows().map(|r| r.iter().sum()).collect();
assert_eq!(degrees, vec![3.0, 3.0, 4.0]);

// Weighted, upper triangle, loops ignored.
let w = [0.5, 1.5, 2.0, 3.0, 9.0];
let a = g.get_adjacency(GetAdjacency::Upper, Some(&w), Loops::None).unwrap();
assert_eq!(a.to_rows(), vec![
    vec![0.0, 2.0, 3.0],
    vec![0.0, 0.0, 2.0],
    vec![0.0, 0.0, 0.0],
]);
Source

pub fn get_adjacency_sparse( &self, kind: GetAdjacency, weights: Option<&[f64]>, loops: Loops, ) -> Result<CooMatrix>

The adjacency matrix of the graph in sparse (coordinate) format.

Same semantics as get_adjacency (kind, weights and loops mean exactly the same), but only the non-zero entries are stored, so the memory use is O(|V| + |E|) instead of O(|V|²). The C result (an igraph_sparsemat_t) is read into a CooMatrix whose parallel entries (from multi-edges) are summed.

Binds igraph_get_adjacency_sparse.

§Errors

ErrorKind::InvalidValue if weights has the wrong length.

§Examples
use igraph::prelude::*;
// A star with 1000 leaves: 1001² ≈ 10⁶ dense entries, only 2000 stored.
let edges: Vec<(i64, i64)> = (1..=1000).map(|i| (0, i)).collect();
let star = Graph::from_edges(&edges, 1001, false).unwrap();
let a = star.get_adjacency_sparse(GetAdjacency::Both, None, Loops::Twice).unwrap();
assert_eq!(a.nnz(), 2000);
assert_eq!(a.row_sums()[0], 1000.0);
assert_eq!(a.get(7, 0), 1.0);
Source

pub fn get_stochastic( &self, column_wise: bool, weights: Option<&[f64]>, ) -> Result<Matrix>

The stochastic (random walk transition) matrix of the graph.

This is the adjacency matrix normalized so that each row sums to one (column_wise = false, a right-stochastic matrix) or each column sums to one (column_wise = true, left-stochastic). Row-wise, entry (i, j) is the probability that a random walker at i steps to j following the edge directions; the column-wise matrix relates to walks moving against the edge directions. Rows (columns) of vertices with zero out-strength (in-strength) are all zeros.

Undirected self-loops are counted twice, consistently with degrees. weights are optional edge weights (None: all 1), which make the transition probabilities proportional to the weights. They should be non-negative, otherwise the result is not a probability matrix.

A vertex whose incident edges all have weight zero has zero strength: its row (column) is all zeros, like for a vertex without edges. (The C function of igraph 1.0.0 and 1.0.1 would fill it with 0 / 0 = NaN; this wrapper clears those rows so that the dense and sparse versions agree, as the C documentation promises.)

With negative weights a vertex can also have zero strength because its weights cancel out; its row (column) is cleared as well, like in get_stochastic_sparse.

The strengths used for the normalization are those of Graph::strength with Loops::Twice (out-strengths row-wise, in-strengths column-wise). Time complexity: O(|V|²).

Binds igraph_get_stochastic. See also Graph::random_walk, which simulates this walk, and Graph::pagerank, its damped stationary distribution.

§Errors

ErrorKind::InvalidValue if weights has the wrong length.

§Examples
use igraph::prelude::*;
// Star 0-1, 0-2, 0-3: from the center, each leaf with probability 1/3.
let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false).unwrap();
let p = g.get_stochastic(false, None).unwrap();
assert_eq!(p.row(0), vec![0.0, 1.0 / 3.0, 1.0 / 3.0, 1.0 / 3.0]);
assert_eq!(p.row(2), vec![1.0, 0.0, 0.0, 0.0]);
// Column-wise it is the transpose, for an undirected graph.
assert_eq!(g.get_stochastic(true, None).unwrap(), p.transposed());
Source

pub fn get_stochastic_sparse( &self, column_wise: bool, weights: Option<&[f64]>, ) -> Result<CooMatrix>

The stochastic matrix of the graph in sparse (coordinate) format.

Same as get_stochastic, but computed in O(|V| + |E|) time and returned as a CooMatrix. Entries of zero-strength vertices (all incident weights zero) are stored as explicit 0.0 values; so are those of a vertex whose (mixed-sign) weights sum to zero, as igraph scales such a row (column) by zero.

Binds igraph_get_stochastic_sparse.

§Errors

ErrorKind::InvalidValue if weights has the wrong length.

§Examples
use igraph::prelude::*;
// Directed: 0 → 1 (weight 3), 0 → 2 (weight 1); vertices 1, 2 are sinks.
let g = Graph::from_edges(&[(0, 1), (0, 2)], 3, true).unwrap();
let p = g.get_stochastic_sparse(false, Some(&[3.0, 1.0])).unwrap();
assert_eq!(p.entries, vec![(0, 1, 0.75), (0, 2, 0.25)]);
assert_eq!(p.row_sums(), vec![1.0, 0.0, 0.0]);
Source

pub fn get_edgelist(&self, bycol: bool) -> Result<Vec<VertexId>>

The list of all edges as a flat vector, in edge id order.

With bycol = false the result is [from0, to0, from1, to1, …], the format accepted by Graph::from_flat_edges and Graph::add_edges_from_vector. With bycol = true it is “column-wise”: first all the sources, then all the targets, [from0, from1, …, to0, to1, …], i.e. edge e is res[e] → res[|E| + e]. For (from, to) pairs see also Graph::edge_list.

Time complexity: O(|E|).

Binds igraph_get_edgelist.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (3, 2)], 4, true).unwrap();
assert_eq!(g.get_edgelist(false).unwrap(), vec![0, 1, 1, 2, 3, 2]);
assert_eq!(g.get_edgelist(true).unwrap(), vec![0, 1, 3, 1, 2, 2]);
// Round trip.
let h = Graph::from_flat_edges(&g.get_edgelist(false).unwrap(), 4, true).unwrap();
assert!(g.is_same_graph(&h).unwrap());
Source

pub fn to_directed(&mut self, mode: ToDirected) -> Result<()>

Converts an undirected graph into a directed one, in place.

Does nothing if the graph is already directed. The vertex ids are kept; how edges are directed depends on mode:

  • ToDirected::Arbitrary: each edge becomes one directed edge with an arbitrary (implementation-defined) direction; edge ids are kept.
  • ToDirected::Mutual: each edge e becomes two directed edges, one per direction; the first |E| edges keep the ids and endpoint order of the original ones, edge |E| + e is the reverse of e.
  • ToDirected::Random: each edge gets a uniformly random direction (drawn from the calling thread’s default RNG, so reproducible after rng::seed).
  • ToDirected::Acyclic: each edge is directed from the smaller to the larger vertex id; without self-loops the result is a DAG (see Graph::is_dag), and the identity is a topological order.

Random and Acyclic keep the edge ids too.

Graph, vertex and edge attributes (if an attribute handler is installed) are kept; with Mutual both copies of an edge get its attributes. Time complexity: O(|V| + |E|).

Binds igraph_to_directed. See into_directed for a by-value variant.

§Examples
use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
g.to_directed(ToDirected::Mutual).unwrap();
assert!(g.is_directed());
assert_eq!(g.edge_list(), vec![(0, 1), (1, 2), (1, 0), (2, 1)]);
Source

pub fn into_directed(self, mode: ToDirected) -> Result<Graph>

Consumes the graph and returns its directed version, see to_directed.

Handy in builder chains:

use igraph::prelude::*;
let dag = Graph::from_edges(&[(2, 0), (1, 2), (0, 1)], 3, false)
    .and_then(|g| g.into_directed(ToDirected::Acyclic))
    .unwrap();
assert!(dag.edge_list().iter().all(|&(a, b)| a < b));
Source

pub fn to_undirected(&mut self, mode: ToUndirected) -> Result<()>

Converts a directed graph into an undirected one, in place.

Does nothing if the graph is already undirected. mode decides which undirected edges are created:

  • ToUndirected::Each: one undirected edge per directed edge; the number of edges (and their ids) is unchanged, so multi-edges may appear (e.g. from a mutual pair a → b, b → a).
  • ToUndirected::Collapse: one undirected edge per pair of vertices connected by at least one directed edge in either direction; no multi-edges are created.
  • ToUndirected::Mutual: one undirected edge per mutual pair of directed edges (a → b matched with b → a); unreciprocated edges are dropped, while self-loops are kept unconditionally. May create multi-edges when several mutual pairs join the same vertices.

Edge attributes (with the attribute handler on) are kept with Each and dropped with Collapse and Mutual, because the C edge_comb attribute combination argument is passed as NULL; use to_undirected_with_comb or to_undirected_with_attributes to combine the attributes of the merged edges instead. Graph and vertex attributes are always kept.

Graph::is_mutual and Graph::reciprocity tell which edges Mutual keeps. Time complexity: O(|V| + |E|).

Binds igraph_to_undirected. See into_undirected for a by-value variant.

§Examples
use igraph::prelude::*;
// 0 ⇄ 1 is mutual, 1 → 2 is not.
let edges = [(0, 1), (1, 0), (1, 2)];
let mut each = Graph::from_edges(&edges, 3, true).unwrap();
each.to_undirected(ToUndirected::Each).unwrap();
assert_eq!(each.ecount(), 3);

let mut collapse = Graph::from_edges(&edges, 3, true).unwrap();
collapse.to_undirected(ToUndirected::Collapse).unwrap();
assert_eq!(collapse.edge_list(), vec![(0, 1), (1, 2)]);

let mut mutual = Graph::from_edges(&edges, 3, true).unwrap();
mutual.to_undirected(ToUndirected::Mutual).unwrap();
assert_eq!(mutual.edge_list(), vec![(0, 1)]);
Source

pub fn to_undirected_with_comb( &mut self, mode: ToUndirected, edge_comb: &AttributeCombination, ) -> Result<()>

Converts a directed graph into an undirected one, in place, combining the edge attributes of the directed edges merged into each undirected edge according to edge_comb.

The structure of the result is the same as with to_undirected. With ToUndirected::Collapse and ToUndirected::Mutual, each new edge gets the attribute values combined over the directed edges it replaces (e.g. the sum of their weights, see AttributeCombination); with ToUndirected::Each edges are not merged and keep their own values. Without an attribute handler (see attributes::enable) this is the same as to_undirected.

Binds igraph_to_undirected with a non-null edge_comb.

§Errors

ErrorKind::AttributeCombination (or ErrorKind::Unimplemented) if the combination is not supported for the type of an attribute.

§Examples
use igraph::prelude::*;
use igraph::attributes::{AttributeCombination, AttributeCombinationType as Comb};

// Traffic between two cities, in each direction.
let mut g = Graph::from_edges(&[(0, 1), (1, 0), (1, 2)], 3, true).unwrap();
g.set_edge_attr_numeric_values("traffic", &[10.0, 7.0, 3.0]).unwrap();

let comb = AttributeCombination::from_pairs(&[(Some("traffic"), Comb::Sum)]).unwrap();
g.to_undirected_with_comb(ToUndirected::Collapse, &comb).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1), (1, 2)]);
assert_eq!(g.edge_attr_numeric_values("traffic", ..).unwrap(), vec![17.0, 3.0]);
Source

pub fn into_undirected(self, mode: ToUndirected) -> Result<Graph>

Consumes the graph and returns its undirected version, see to_undirected.

use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 0)], 2, true).unwrap();
let u = g.into_undirected(ToUndirected::Collapse).unwrap();
assert_eq!((u.is_directed(), u.ecount()), (false, 1));
Source

pub fn to_prufer(&self) -> Result<Vec<VertexId>>

The Prüfer sequence of a tree.

A labelled tree on n ≥ 2 vertices corresponds one-to-one to a sequence of n - 2 vertex ids in 0..n (Cayley’s formula nⁿ⁻² follows). The sequence is obtained by repeatedly removing the leaf with the smallest id and recording its neighbor. Each vertex appears exactly degree - 1 times, so leaves never appear.

The inverse operation is the constructor Graph::from_prufer; Graph::is_tree checks the precondition.

Binds igraph_to_prufer.

§Errors

ErrorKind::InvalidValue if the graph is not a tree (edge directions are ignored; the null graph is not a tree) or has fewer than two vertices.

§Examples
use igraph::prelude::*;
// A star centered at 3: the center appears n - 2 times.
let star = Graph::from_edges(&[(3, 0), (3, 1), (3, 2), (3, 4)], 5, false).unwrap();
assert_eq!(star.to_prufer().unwrap(), vec![3, 3, 3]);

// Round trip through the constructor.
let back = Graph::from_prufer(&[3, 3, 3]).unwrap();
assert!(back.is_same_graph(&star).unwrap());

// A cycle is not a tree.
let tri = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
assert_eq!(tri.to_prufer().unwrap_err().kind(), ErrorKind::InvalidValue);
Source§

impl igraph_t

Source

pub fn is_dag(&self) -> Result<bool>

Checks whether the graph is a directed acyclic graph (DAG).

A DAG is a directed graph without directed cycles (self-loops count as cycles here). Undirected graphs are never DAGs: this returns false for them. The result is cached inside the graph, so repeated calls without modifications in between take O(1) time.

Time complexity: O(|V|+|E|).

See also is_acyclic, which agrees with this function on directed graphs but also tests undirected graphs for cycles, and find_cycle, which returns a cycle proving that a graph is not a DAG.

Binds igraph_is_dag.

§Examples
use igraph::prelude::*;
let chain = Graph::from_edges(&[(0, 1), (1, 2)], 3, true)?;
assert!(chain.is_dag()?);
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true)?;
assert!(!triangle.is_dag()?);
// Undirected graphs are not DAGs, even when they are trees.
let undirected = Graph::from_edges(&[(0, 1)], 2, false)?;
assert!(!undirected.is_dag()?);
assert!(undirected.is_acyclic()?); // ... but they can be acyclic
Source

pub fn topological_sorting(&self, mode: NeighborMode) -> Result<Vec<VertexId>>

Computes a topological sorting of a directed acyclic graph.

A topological sorting is a linear ordering of the vertices in which every vertex comes before all the vertices it has edges to. Every DAG has at least one, and possibly many; one of them is returned.

With mode = NeighborMode::Out each vertex precedes its successors, so the sources (no incoming edges) come first; with NeighborMode::In each vertex precedes its predecessors, so the sinks come first. Self-loops are ignored.

Time complexity: O(|V|+|E|).

See also is_dag to test beforehand whether an order exists, feedback_arc_set to make a cyclic graph sortable, and transitive_closure for the full “must come before” relation.

Binds igraph_topological_sorting.

§Errors

ErrorKind::InvalidValue if the graph contains a cycle (other than self-loops), if the graph is undirected, or if mode is NeighborMode::All.

§Examples

The example graph from the igraph documentation (and Wikipedia):

use igraph::prelude::*;
let g = Graph::from_edges(
    &[(0, 3), (0, 4), (1, 3), (2, 4), (2, 7), (3, 5), (3, 6), (3, 7), (4, 6)],
    8,
    true,
)?;
assert_eq!(g.topological_sorting(NeighborMode::Out)?, vec![0, 1, 2, 3, 4, 5, 7, 6]);
assert_eq!(g.topological_sorting(NeighborMode::In)?, vec![5, 6, 7, 4, 3, 2, 0, 1]);
Source

pub fn find_cycle(&self, mode: NeighborMode) -> Result<Option<Cycle>>

Finds a single cycle of the graph, or None if the graph is acyclic.

mode selects how edge directions are considered in directed graphs: Out follows them, In follows them backwards (the returned cycle is then listed against the edge directions) and All ignores them. It is ignored for undirected graphs. Self-loops and multi-edges count as cycles of length 1 and 2.

igraph lists the vertices so that each edge enters the vertex at the same position; this wrapper rotates them by one so that the result follows the Cycle layout (edges[i] goes from vertices[i] to the next vertex), like simple_cycles does.

The cycle is not necessarily a shortest one: use girth_with_cycle for that (undirected, cycles of length at least 3), or simple_cycles to list all cycles. is_acyclic only answers whether a cycle exists.

Time complexity: O(|V|+|E|).

Binds igraph_find_cycle.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (2, 0)], 5, true)?;
let c = g.find_cycle(NeighborMode::Out)?.unwrap();
assert_eq!(c.vertices, vec![0, 1, 2]);
assert_eq!(c.edges, vec![0, 1, 4]); // 0 -> 1 -> 2 -> 0

let tree = Graph::from_edges(&[(0, 1), (0, 2)], 3, false)?;
assert_eq!(tree.find_cycle(NeighborMode::All)?, None);

// Any cycle is at least as long as the girth.
let petersen = Graph::famous("Petersen")?;
let c = petersen.find_cycle(NeighborMode::All)?.unwrap();
assert!(c.len() >= petersen.girth()?.unwrap());
Source

pub fn simple_cycles(&self, options: &SimpleCyclesOptions) -> Result<Vec<Cycle>>

Lists the simple cycles of the graph (Johnson’s algorithm).

A simple cycle is a closed walk without repeated vertices. Each cycle is reported once (in undirected graphs, the two traversal directions of a cycle count as one). Self-loops are cycles of length 1; two parallel edges give a cycle of length 2 when they can be traversed in opposite directions (so not two parallel arcs u -> v with NeighborMode::Out). The search can be restricted with SimpleCyclesOptions: direction handling, minimum and maximum cycle length and a cap on the number of results. The number of simple cycles can grow exponentially with the size of the graph: bound the lengths or the result count on large graphs, or use simple_cycles_callback to avoid storing them.

See also list_triangles for the cycles of length 3 only, find_cycle for a single cycle, and get_all_simple_paths for simple paths.

This function is experimental in igraph 1.0.

Reference: Johnson DB, Finding all the elementary circuits of a directed graph, SIAM J. Comput. 4(1):77-84 (1975).

Binds igraph_simple_cycles.

§Examples

A square with one diagonal has three cycles: two triangles and the outer square.

use igraph::{cycles::SimpleCyclesOptions, prelude::*};
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false)?;
let cycles = g.simple_cycles(&SimpleCyclesOptions::default())?;
let mut lengths: Vec<usize> = cycles.iter().map(|c| c.len()).collect();
lengths.sort();
assert_eq!(lengths, vec![3, 3, 4]);
// Only the triangles:
let triangles = g.simple_cycles(&SimpleCyclesOptions::default().with_max_length(3))?;
assert_eq!(triangles.len(), 2);
Source

pub fn simple_cycles_callback<F>( &self, options: &SimpleCyclesOptions, f: F, ) -> Result<()>
where F: FnMut(&[VertexId], &[EdgeId]) -> ControlFlow<()>,

Visits the simple cycles of the graph with a closure (Johnson’s algorithm), without storing them.

This is the streaming variant of simple_cycles, with the same semantics and options. For each cycle, f is called with its vertices and edges (see Cycle for their layout); it returns ControlFlow::Continue to keep searching or ControlFlow::Break to stop the search early (this is not an error). The search also stops after max_results cycles.

If f panics, the search is aborted and the panic is resumed in the caller once igraph has cleaned up.

This function is experimental in igraph 1.0.

Binds igraph_simple_cycles_callback.

§Examples

Is there a cycle through vertex 3 in the complete graph K5? Stop at the first one.

use igraph::{cycles::SimpleCyclesOptions, prelude::*};
use std::ops::ControlFlow;
let k5 = Graph::full(5, false, false)?;
let mut total = 0;
k5.simple_cycles_callback(&SimpleCyclesOptions::default(), |_, _| {
    total += 1;
    ControlFlow::Continue(())
})?;
assert_eq!(total, 37); // 10 triangles + 15 squares + 12 pentagons

let mut found = None;
k5.simple_cycles_callback(&SimpleCyclesOptions::default(), |vs, _| {
    if vs.contains(&3) {
        found = Some(vs.to_vec());
        return ControlFlow::Break(());
    }
    ControlFlow::Continue(())
})?;
assert!(found.unwrap().contains(&3));
Source

pub fn fundamental_cycles( &self, start: Option<VertexId>, bfs_cutoff: Option<usize>, ) -> Result<Vec<Vec<EdgeId>>>

Computes a fundamental cycle basis, from breadth-first search trees.

Every edge not in a BFS spanning forest closes exactly one cycle with the tree edges: these fundamental cycles form a basis of the cycle space, whose dimension is the cyclomatic number |E| - |V| + c (c being the number of connected components). Each cycle is returned as a list of edge ids, in cycle order. Edge directions are ignored; multi-edges and self-loops are supported.

  • start: None returns a complete basis; Some(v) returns only the fundamental cycles of the BFS tree rooted at v, i.e. of the (weakly) connected component of v.
  • bfs_cutoff: None returns a complete basis; Some(k) limits the BFS depth, so that only cycles of length at most 2k + 1 are found.

Time complexity: O(|V|+|E|). This function is experimental in igraph 1.0. (The C function also takes a weights argument, currently unused by igraph, so it is not exposed.)

See also minimum_cycle_basis for a basis of shortest total length, and connected_components for c.

Binds igraph_fundamental_cycles.

§Errors

ErrorKind::InvalidVertexId if start is not a vertex of the graph.

§Examples
use igraph::prelude::*;
// Two triangles sharing the edge 0-2: the cycle space has dimension 5 - 4 + 1 = 2.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 0)], 4, false)?;
let basis = g.fundamental_cycles(None, None)?;
assert_eq!(basis.len(), 2);

// In general: one basis cycle per edge outside a spanning forest.
let karate = Graph::famous("Zachary")?;
let c = karate.connected_components(Connectedness::Weak)?.count;
let dim = karate.ecount() - karate.vcount() + c;
assert_eq!(karate.fundamental_cycles(None, None)?.len(), dim); // 78 - 34 + 1
Source

pub fn minimum_cycle_basis( &self, options: &MinimumCycleBasisOptions, ) -> Result<Vec<Vec<EdgeId>>>

Computes a minimum weight cycle basis (modified Horton algorithm).

A minimum cycle basis is a basis of the cycle space whose total length is as small as possible; its cycles are returned as edge-id lists, sorted by increasing length. Edge directions are ignored; multi-edges and self-loops are supported.

The search is tuned by MinimumCycleBasisOptions: an optional BFS cutoff trading exactness for speed, whether a cut-off basis should still be completed, and whether cycles are listed in cycle order.

This function is experimental in igraph 1.0. (The C function also takes a weights argument, currently unused by igraph, so it is not exposed.)

For a simple graph with at least one cycle, the first (shortest) basis cycle has the length of the girth. See also fundamental_cycles, a faster but usually longer basis.

Reference: Horton JD, A polynomial-time algorithm to find the shortest cycle basis of a graph, SIAM J. Comput. 16(2):358-366 (1987).

Binds igraph_minimum_cycle_basis.

§Examples
use igraph::{cycles::MinimumCycleBasisOptions, prelude::*};
// A square with a diagonal: the minimum basis is made of the two triangles.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false)?;
let basis = g.minimum_cycle_basis(&MinimumCycleBasisOptions::default())?;
assert_eq!(basis.iter().map(Vec::len).collect::<Vec<_>>(), vec![3, 3]);
// The outer square is the sum (symmetric difference) of the two
// triangles: the shared diagonal, edge 4, cancels out.
let mut parity = [false; 5];
for &e in basis.concat().iter() {
    parity[e as usize] ^= true;
}
assert_eq!(parity, [true, true, true, true, false]);

// The Petersen graph has girth 5: its 15 - 10 + 1 = 6 basis cycles are pentagons.
let petersen = Graph::famous("Petersen")?;
let basis = petersen.minimum_cycle_basis(&MinimumCycleBasisOptions::default())?;
assert_eq!(basis.iter().map(Vec::len).collect::<Vec<_>>(), vec![5; 6]);
Source

pub fn feedback_arc_set( &self, weights: Option<&[f64]>, algo: FasAlgorithm, ) -> Result<Vec<EdgeId>>

Finds a feedback arc set: edges whose removal makes the graph acyclic.

One is usually interested in a minimum feedback arc set, i.e. one of smallest total weight (weights, one per edge, or None for unit weights; weights are meant to be non-negative). For undirected graphs this is easy: the complement of a maximum weight spanning forest (and algo is ignored). For directed graphs the problem is NP-complete and algo selects the method:

  • FasAlgorithm::ExactIp: exact minimum via integer programming, picking the best available formulation (currently ExactIpCg). Exponential in the worst case.
  • FasAlgorithm::ExactIpCg: exact, set-cover formulation with incremental cycle (constraint) generation.
  • FasAlgorithm::ExactIpTi: exact, topological-order formulation with triangle inequalities; usually much slower.
  • FasAlgorithm::ApproxEades: the linear time O(|E|) heuristic of Eades, Lin and Smyth (1993), returning fewer than |E|/2 - |V|/6 edges.

References: Eades P, Lin X, Smyth WF, Inf. Proc. Letters 47(6):319-323 (1993); Baharev A et al., ACM J. Exp. Algorithmics 26:1-28 (2021).

Time complexity: depends on algo (see above).

See also feedback_vertex_set for the vertex version, and, for undirected graphs, minimum_spanning_tree: the edges not in a maximum weight spanning forest form a minimum feedback arc set. Delete the returned edges at once with delete_edges.

Binds igraph_feedback_arc_set.

§Errors

ErrorKind::InvalidValue if the weight vector has the wrong length or invalid values; ErrorKind::Unimplemented for the exact methods when igraph was built without GLPK.

§Examples

The graph of igraph’s own example program:

use igraph::prelude::*;
let edges = [(0, 1), (1, 2), (2, 0), (2, 3), (2, 4), (0, 4), (4, 3), (5, 0), (6, 5)];
let g = Graph::from_edges(&edges, 7, true)?;
assert_eq!(g.feedback_arc_set(None, FasAlgorithm::ExactIp)?, vec![0]);
// Make edge 0 expensive to cut: another edge of the triangle goes.
let w = [3.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0];
let fas = g.feedback_arc_set(Some(&w), FasAlgorithm::ExactIp)?;
assert!(fas == [1] || fas == [2]);
Source

pub fn feedback_vertex_set( &self, vertex_weights: Option<&[f64]>, algo: FvsAlgorithm, ) -> Result<Vec<VertexId>>

Finds a minimum feedback vertex set: vertices whose removal makes the graph acyclic.

Minimizes the total vertex weight (vertex_weights, one per vertex, or None for unit weights). The problem is NP-complete both for directed and undirected graphs; the only method, FvsAlgorithm::ExactIp, uses integer programming with incremental cycle generation (like FasAlgorithm::ExactIpCg) and is exponential in the worst case. Self-loops count as cycles, so looped vertices are always included.

Time complexity: exponential in the worst case. See also feedback_arc_set, and delete_vertices or induced_subgraph to remove the vertices.

Binds igraph_feedback_vertex_set.

§Errors

ErrorKind::InvalidValue if the weight vector has the wrong length or invalid values; ErrorKind::Unimplemented when igraph was built without GLPK.

§Examples
use igraph::prelude::*;
// Two triangles sharing vertex 0 (a "bowtie"): removing 0 breaks both.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (0, 3), (3, 4), (4, 0)], 5, false)?;
assert_eq!(g.feedback_vertex_set(None, FvsAlgorithm::ExactIp)?, vec![0]);

// The Petersen graph needs three vertices removed to become a forest.
let mut petersen = Graph::famous("Petersen")?;
let fvs = petersen.feedback_vertex_set(None, FvsAlgorithm::ExactIp)?;
assert_eq!(fvs.len(), 3);
petersen.delete_vertices(&fvs)?;
assert!(petersen.is_forest(NeighborMode::All)?);
Source

pub fn is_eulerian(&self) -> Result<EulerianStatus>

Checks whether the graph has an Eulerian path and/or an Eulerian cycle.

An Eulerian path traverses every edge exactly once; an Eulerian cycle is a closed one. By Euler’s theorem, a connected (ignoring isolated vertices) undirected graph has an Eulerian cycle iff all degrees are even, and a path iff at most two degrees are odd. A directed graph whose edges lie in a single weakly connected component has an Eulerian cycle iff every in-degree equals the out-degree, and a path iff this holds except for at most one start vertex (out-degree one larger) and one end vertex (in-degree one larger). A graph without edges trivially has both.

Time complexity: O(|V|+|E|).

See also degree and is_connected, the ingredients of Euler’s theorem, and eulerian_path / eulerian_cycle to construct the walks.

Binds igraph_is_eulerian.

§Examples

The seven bridges of Königsberg have no Eulerian path:

use igraph::prelude::*;
// 0: island Kneiphof, 1: north bank, 2: south bank, 3: east island Lomse
let bridges = [(0, 1), (0, 1), (0, 2), (0, 2), (0, 3), (1, 3), (2, 3)];
let konigsberg = Graph::from_edges(&bridges, 4, false)?;
let status = konigsberg.is_eulerian()?;
assert!(!status.has_path && !status.has_cycle);
Source

pub fn eulerian_path(&self) -> Result<EulerianWalk>

Finds an Eulerian path, traversing every edge exactly once (Hierholzer’s algorithm).

If the graph has no edges, an empty walk is returned. When the graph also has an Eulerian cycle, the returned path is necessarily closed (an Eulerian path with distinct ends exists only when exactly two vertices have odd degree, or unbalanced in/out-degrees if directed).

Time complexity: O(|V|+|E|).

Binds igraph_eulerian_path.

§Errors

ErrorKind::NoSolution if the graph has no Eulerian path (check first with is_eulerian).

§Examples

Drawing the “house of Santa Claus” without lifting the pen:

use igraph::prelude::*;
// square 0-1-2-3, both diagonals, and the roof 2-4-3
let house = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 2), (1, 3), (2, 4), (4, 3)];
let g = Graph::from_edges(&house, 5, false)?;
let walk = g.eulerian_path()?;
assert_eq!(walk.edges.len(), 8);
assert_eq!(walk.vertices.len(), 9);
// It must start and end at the two odd-degree corners 0 and 1.
let ends = [walk.vertices[0], walk.vertices[8]];
assert!(ends == [0, 1] || ends == [1, 0]);
Source

pub fn eulerian_cycle(&self) -> Result<EulerianWalk>

Finds an Eulerian cycle, a closed walk traversing every edge exactly once (Hierholzer’s algorithm).

The first and last returned vertices coincide. If the graph has no edges, an empty walk is returned.

Time complexity: O(|V|+|E|).

Binds igraph_eulerian_cycle.

§Errors

ErrorKind::NoSolution if the graph has no Eulerian cycle.

§Examples
use igraph::prelude::*;
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
let c = triangle.eulerian_cycle()?;
assert_eq!(c.edges, vec![0, 1, 2]);
assert_eq!(c.vertices, vec![0, 1, 2, 0]);

let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
assert_eq!(path.eulerian_cycle().unwrap_err().kind(), ErrorKind::NoSolution);

// An Eulerian cycle of the binary de Bruijn graph B(2, 2) traverses all
// 8 three-bit words once: reading one bit per step gives a de Bruijn
// sequence, which contains every 3-bit word (cyclically) exactly once.
let b = Graph::de_bruijn(2, 2)?;
let tour = b.eulerian_cycle()?;
let bits: Vec<i64> = tour.vertices[1..].iter().map(|v| v & 1).collect();
let mut words: Vec<i64> =
    (0..8).map(|i| 4 * bits[i] + 2 * bits[(i + 1) % 8] + bits[(i + 2) % 8]).collect();
words.sort();
assert_eq!(words, (0..8).collect::<Vec<_>>());
Source§

impl igraph_t

Source

pub fn maxflow( &self, source: VertexId, target: VertexId, capacity: Option<&[f64]>, ) -> Result<MaxFlow>

Maximum flow between source and target, together with a minimum cut certifying its optimality.

Uses the push-relabel algorithm of Goldberg and Tarjan (J. ACM 35(4), 1988). A flow assigns to each edge a non-negative amount not larger than its capacity, and conserves the flow at every vertex other than the source and the target; its value is the net flow entering the target. The result contains the value, the flow on every edge, the edges of the corresponding minimum cut and the two sides of that cut (the first containing the source, the second the target).

Works on directed and undirected graphs; in undirected graphs the sign of MaxFlow::flow tells the direction (see its docs). capacity gives one non-negative capacity per edge; None means that every edge has capacity 1.

Time complexity: O(|V|³), usually much faster in practice.

See also Graph::residual_graph to inspect the leftover capacities, and Graph::maximum_bipartite_matching for the matching problem that is usually solved as a unit-capacity flow.

Binds igraph_maxflow.

§Errors

ErrorKind::InvalidVertexId for invalid source/target, ErrorKind::InvalidValue if they coincide, or if the capacity vector has the wrong length or a negative, NaN or infinite entry.

§Examples

The example of igraph’s flow2.c:

use igraph::prelude::*;

let g = Graph::from_edges(
    &[(0, 1), (1, 2), (2, 3), (0, 5), (5, 4), (4, 3), (3, 0)], 6, true)?;
let capacity = [3.0, 1.0, 2.0, 10.0, 1.0, 3.0, 2.0];
let mf = g.maxflow(0, 2, Some(&capacity))?;
assert_eq!(mf.value, 1.0);
assert_eq!(mf.flow, vec![1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
assert_eq!(mf.partition, vec![0, 1, 3, 4, 5]);
assert_eq!(mf.partition2, vec![2]);
assert_eq!(mf.cut, vec![1]); // the edge 1 -> 2
assert!(mf.stats.bfs_runs >= 1);
Source

pub fn maxflow_value( &self, source: VertexId, target: VertexId, capacity: Option<&[f64]>, ) -> Result<f64>

Value of the maximum flow between source and target.

Same algorithm as Graph::maxflow, but only the value is computed. By the max-flow min-cut theorem this equals Graph::st_mincut_value. capacity: None means unit capacities.

Time complexity: O(|V|³).

Binds igraph_maxflow_value.

§Errors

As for Graph::maxflow.

§Examples
use igraph::prelude::*;

// Two parallel routes from 0 to 3, each able to carry one unit.
let g = Graph::from_edges(&[(0, 1), (1, 3), (0, 2), (2, 3)], 4, true)?;
assert_eq!(g.maxflow_value(0, 3, None)?, 2.0);
assert_eq!(g.maxflow_value(0, 3, Some(&[5.0, 1.0, 2.0, 7.0]))?, 3.0);
Source

pub fn maxflow_value_with_stats( &self, source: VertexId, target: VertexId, capacity: Option<&[f64]>, ) -> Result<(f64, MaxflowStats)>

Value of the maximum flow between source and target, together with the operation counts of the push-relabel solver.

See Graph::maxflow_value and MaxflowStats.

Binds igraph_maxflow_value.

§Errors

As for Graph::maxflow.

§Examples
use igraph::prelude::*;

let g = Graph::famous("Zachary")?;
let (value, stats) = g.maxflow_value_with_stats(0, 33, None)?;
assert_eq!(value, 10.0);
assert!(stats.pushes > 0);
assert!(stats.bfs_runs >= 1); // the initial global relabelling
Source

pub fn st_mincut( &self, source: VertexId, target: VertexId, capacity: Option<&[f64]>, ) -> Result<Cut>

Minimum cut between a source and a target vertex.

Finds the edge set of smallest total capacity whose removal eliminates all (directed, in directed graphs) paths from source to target, together with the two sides of the cut (the first contains the source, the second the target). Computed with Graph::maxflow. capacity: None means unit capacities.

Binds igraph_st_mincut.

§Errors

As for Graph::maxflow.

§Examples
use igraph::prelude::*;

// A "barbell": two triangles joined by the bridge 2 - 3.
let g = Graph::from_edges(
    &[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 5), (5, 3)], 6, false)?;
let cut = g.st_mincut(0, 5, None)?;
assert_eq!(cut.value, 1.0);
assert_eq!(cut.cut, vec![3]);
assert_eq!(cut.partition, vec![0, 1, 2]);
assert_eq!(cut.partition2, vec![3, 4, 5]);
Source

pub fn st_mincut_value( &self, source: VertexId, target: VertexId, capacity: Option<&[f64]>, ) -> Result<f64>

Value of the minimum cut between a source and a target vertex.

The minimum total capacity of edges to remove in order to eliminate all paths from source to target (directed paths in directed graphs). Equal to Graph::maxflow_value by the max-flow min-cut theorem. capacity: None means unit capacities.

Time complexity: O(|V|³).

Binds igraph_st_mincut_value.

§Errors

As for Graph::maxflow.

§Examples

The network of igraph’s igraph_st_mincut_value.c unit test:

use igraph::prelude::*;

let g = Graph::from_edges(
    &[(0, 1), (0, 2), (1, 2), (1, 3), (2, 4), (3, 4), (3, 5), (4, 5)], 6, true)?;
let capacity = [5.0, 2.0, 2.0, 3.0, 4.0, 1.0, 2.0, 5.0];
assert_eq!(g.st_mincut_value(0, 5, Some(&capacity))?, 7.0);
assert_eq!(g.maxflow_value(0, 5, Some(&capacity))?, 7.0);
Source

pub fn mincut(&self, capacity: Option<&[f64]>) -> Result<Cut>

Minimum cut of the whole graph.

The set of edges of minimum total capacity whose removal disconnects the graph (makes it not strongly connected, for directed graphs), with the two resulting vertex sides. Undirected graphs use the Stoer–Wagner algorithm (J. ACM 44, 1997), in O(|V||E| + |V|² log |V|); directed graphs compute 2|V| − 2 maximum flows, O(|V|⁴). capacity: None means unit capacities.

If the graph is already disconnected the value is 0 and the cut empty. Degenerate graphs follow igraph: with a single vertex (or a directed graph with no vertices) the value is f64::INFINITY and the cut empty, while an undirected graph with no vertices counts as disconnected (value 0, everything empty).

Note: for directed graphs igraph_mincut discards the error code of its internal max-flow computations (still the case in igraph 1.0.0 and 1.0.1). The only such errors not already excluded by the argument checks of this wrapper are out-of-memory conditions.

See also Graph::edge_connectivity (the unit-capacity value) and Graph::bridges (all the edges forming a cut of size one).

Binds igraph_mincut.

§Errors

ErrorKind::InvalidValue if the capacity vector has the wrong length or an entry that is negative, NaN or infinite.

§Examples

The weighted example of igraph’s igraph_mincut.c:

use igraph::prelude::*;

let g = Graph::from_edges(&[
    (0, 1), (0, 4), (1, 2), (1, 4), (1, 5), (2, 3),
    (2, 6), (3, 6), (3, 7), (4, 5), (5, 6), (6, 7),
], 8, false)?;
let w = [2.0, 3.0, 3.0, 2.0, 2.0, 4.0, 2.0, 2.0, 2.0, 3.0, 1.0, 3.0];
let cut = g.mincut(Some(&w))?;
assert_eq!(cut.value, 4.0);
assert_eq!(cut.partition, vec![2, 3, 6, 7]);
assert_eq!(cut.partition2, vec![0, 1, 4, 5]);
assert_eq!(cut.cut, vec![2, 10]); // 1-2 and 5-6
Source

pub fn mincut_value(&self, capacity: Option<&[f64]>) -> Result<f64>

Value of the minimum cut of the whole graph.

The minimum total capacity of edges whose removal makes the graph not strongly connected (0 if it is already disconnected). Uses Stoer–Wagner for undirected graphs, O(log|V| · |V|²), and maximum flows from a fixed vertex in both directions for directed graphs, O(|V|⁴). For a single vertex, or a directed graph without vertices, the result is f64::INFINITY; an undirected graph without vertices counts as disconnected and gives 0. capacity: None means unit capacities.

Binds igraph_mincut_value.

§Errors

ErrorKind::InvalidValue if the capacity vector has the wrong length or an entry that is negative, NaN or infinite.

§Examples
use igraph::prelude::*;

// A directed cycle is strongly connected, but a single edge breaks it.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true)?;
assert_eq!(g.mincut_value(None)?, 1.0);
assert_eq!(g.mincut_value(Some(&[4.0, 2.5, 3.0]))?, 2.5);
Source

pub fn st_vertex_connectivity( &self, source: VertexId, target: VertexId, neighbors: VconnNei, ) -> Result<i64>

Vertex connectivity of a pair of vertices.

The minimum number of vertices whose deletion eliminates all paths from source to target (directed paths in directed graphs). When the two vertices are not adjacent this equals the number of internally vertex-disjoint paths between them (Menger’s theorem).

Adjacent vertices cannot be separated by removing vertices; neighbors decides what happens then: VconnNei::Error fails with an error, VconnNei::Negative returns −1, VconnNei::NumberOfNodes returns the number of vertices, and VconnNei::Ignore ignores the direct edges and counts the vertices needed to break every other path. This is why the result is signed.

Time complexity: O(|V|³).

See also Graph::vertex_disjoint_paths (which counts the direct edges too) and Graph::is_separator to check a candidate vertex set.

Binds igraph_st_vertex_connectivity.

§Errors

ErrorKind::InvalidVertexId for invalid ids, ErrorKind::InvalidValue if source == target or the vertices are adjacent and neighbors is VconnNei::Error.

§Examples
use igraph::prelude::*;

// In the complete graph K6, two adjacent vertices are joined by 4 other
// paths of length two.
let k6 = Graph::full(6, false, false)?;
assert_eq!(k6.st_vertex_connectivity(0, 1, VconnNei::Ignore)?, 4);
assert_eq!(k6.st_vertex_connectivity(0, 1, VconnNei::Negative)?, -1);
assert_eq!(k6.st_vertex_connectivity(0, 1, VconnNei::NumberOfNodes)?, 6);
assert!(k6.st_vertex_connectivity(0, 1, VconnNei::Error).is_err());
Source

pub fn vertex_connectivity(&self, checks: bool) -> Result<usize>

Vertex connectivity of the graph.

The minimum of the vertex connectivity over all pairs of vertices, i.e. the minimum number of vertices whose removal disconnects the graph (the vertex count minus one for complete graphs). It coincides with the group cohesion of White and Harary, see Graph::cohesion.

With checks = true igraph first performs cheap tests: a graph that is not (strongly) connected has connectivity 0, a graph with a vertex of degree 1 has connectivity 1, and a complete graph has n - 1. They are recommended as the general computation is expensive, O(|V|⁵).

See also Graph::articulation_points (a connected graph on at least three vertices has vertex connectivity 1 exactly when it has one) and Graph::minimum_size_separators, which lists every vertex set of this size whose removal disconnects the graph.

Binds igraph_vertex_connectivity.

§Examples
use igraph::prelude::*;

// A cycle survives the removal of any single vertex, but not of two.
let c = Graph::ring(5, false, false, true)?;
assert_eq!(c.vertex_connectivity(true)?, 2);
assert_eq!(c.vertex_connectivity(false)?, 2);
// The five minimum separators are the pairs of non-adjacent vertices.
assert_eq!(c.minimum_size_separators()?.len(), 5);
Source

pub fn st_edge_connectivity( &self, source: VertexId, target: VertexId, ) -> Result<usize>

Edge connectivity of a pair of vertices.

The minimum number of edges to delete in order to eliminate all paths from source to target (directed paths in directed graphs); it is the unit-capacity maximum flow between them, and equals Graph::edge_disjoint_paths.

Time complexity: O(|V|³).

Binds igraph_st_edge_connectivity.

§Errors

ErrorKind::InvalidVertexId for invalid ids, ErrorKind::InvalidValue if source == target.

§Examples
use igraph::prelude::*;

// Two vertices of a 4-cycle are joined by two edge-disjoint routes.
let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false)?;
assert_eq!(c.st_edge_connectivity(0, 2)?, 2);
Source

pub fn edge_connectivity(&self, checks: bool) -> Result<usize>

Edge connectivity of the graph.

The minimum of the edge connectivity over all pairs of vertices, i.e. the minimum number of edges whose removal disconnects the graph. It is the group adhesion of White and Harary, see Graph::adhesion. Graphs with at most one vertex have edge connectivity 0.

With checks = true igraph first checks connectivity (0 if the graph is not (strongly) connected) and minimum degree (1 if some vertex has degree 1), which is much cheaper than the full computation.

Time complexity: O(log|V| · |V|²) for undirected graphs, O(|V|⁴) for directed graphs.

See also Graph::bridges (a connected undirected graph has edge connectivity 1 exactly when it has a bridge) and Graph::mincut, which also returns a cut realising the minimum.

Binds igraph_edge_connectivity.

§Examples
use igraph::prelude::*;

// Two triangles sharing only vertex 2 ("bowtie"): no bridge, so edge
// connectivity 2, but vertex 2 is a cut vertex.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)?;
assert_eq!(g.edge_connectivity(true)?, 2);
assert_eq!(g.vertex_connectivity(true)?, 1);
assert!(g.bridges()?.is_empty());
assert_eq!(g.articulation_points()?, vec![2]);
Source

pub fn edge_disjoint_paths( &self, source: VertexId, target: VertexId, ) -> Result<usize>

Maximum number of edge-disjoint paths between two vertices.

Paths are edge-disjoint when they share no edge; directed paths are considered in directed graphs. The number equals the edge connectivity of the pair (see Graph::st_edge_connectivity) and is computed with maximum flows, in O(|V|³).

Binds igraph_edge_disjoint_paths.

§Errors

ErrorKind::InvalidVertexId for invalid ids, ErrorKind::Unimplemented if source == target.

§Examples
use igraph::prelude::*;

// Bowtie: both triangles pass through vertex 2, yet two edge-disjoint
// routes exist from 0 to 3.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3), (3, 4), (4, 2)], 5, false)?;
assert_eq!(g.edge_disjoint_paths(0, 3)?, 2);
assert_eq!(g.vertex_disjoint_paths(0, 3)?, 1);
Source

pub fn vertex_disjoint_paths( &self, source: VertexId, target: VertexId, ) -> Result<usize>

Maximum number of vertex-disjoint paths between two vertices.

Paths are vertex-disjoint when they share no vertex other than their endpoints; directed paths are considered in directed graphs. When source and target are not adjacent this is their vertex connectivity; every direct edge from source to target (either orientation in undirected graphs, parallel edges counted separately) contributes one extra path. Computed with maximum flows, O(|V|³).

Binds igraph_vertex_disjoint_paths.

§Errors

ErrorKind::InvalidVertexId for invalid ids, ErrorKind::Unimplemented if source == target.

§Examples
use igraph::prelude::*;

// Triangle: the direct edge plus the path through the third vertex.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
assert_eq!(g.vertex_disjoint_paths(0, 1)?, 2);
Source

pub fn adhesion(&self, checks: bool) -> Result<usize>

Graph adhesion: the edge connectivity with uniform edge weights.

Defined by White and Harary (Sociological Methodology 31, 2001); it is the same as Graph::edge_connectivity. checks enables the same cheap connectivity/degree shortcuts.

Time complexity: O(log|V| · |V|²) for undirected graphs, O(|V|⁴) for directed ones.

Binds igraph_adhesion.

§Examples
use igraph::prelude::*;

// Every vertex of the Petersen graph has three neighbours, and no
// smaller edge set disconnects it.
let petersen = Graph::famous("Petersen")?;
assert_eq!(petersen.adhesion(true)?, 3);
assert_eq!(petersen.adhesion(true)?, petersen.edge_connectivity(false)?);
Source

pub fn cohesion(&self, checks: bool) -> Result<usize>

Graph cohesion: the vertex connectivity of the graph.

Defined by White and Harary (Sociological Methodology 31, 2001); it is the same as Graph::vertex_connectivity. checks enables the same cheap connectivity/degree/completeness shortcuts.

Time complexity: O(|V|⁴), more like O(|V|²) in practice.

See also Graph::cohesive_blocks, the hierarchy of maximally cohesive subgraphs built on this measure.

Binds igraph_cohesion.

§Examples
use igraph::prelude::*;

// The 3-dimensional cube is 3-connected in both senses.
let cube = Graph::hypercube(3, false)?;
assert_eq!(cube.cohesion(true)?, 3);
assert_eq!(cube.adhesion(true)?, 3);
Source

pub fn even_tarjan_reduction(&self) -> Result<EvenTarjanReduction>

Even–Tarjan reduction: turns vertex cuts into edge cuts.

Builds a directed graph with 2 n vertices: each vertex i becomes i' = i and i'' = i + n, joined by an edge i' → i'' (these come first, capacity 1). Each original edge (i, j) becomes the two edges i'' → j' and j'' → i' (capacity n, standing for infinity). A minimum i'' → j' cut in the reduced graph then corresponds to a minimum vertex separator of i and j in the original graph (Even and Tarjan, SIAM J. Comput. 4(4), 1975; Kanevsky, Networks 23, 1993).

Directedness of the input is not checked; the reduction is normally applied to directed graphs.

Time complexity: O(|V| + |E|).

Binds igraph_even_tarjan_reduction.

§Examples
use igraph::prelude::*;

// Path 0 - 1 - 2: vertex 1 separates 0 from 2.
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
let et = g.even_tarjan_reduction()?;
assert_eq!((et.graph.vcount(), et.graph.ecount()), (6, 7));
assert_eq!(et.capacity, vec![1.0, 1.0, 1.0, 3.0, 3.0, 3.0, 3.0]);
// Flow from 0'' (= 3) to 2' (= 2): one vertex must be removed.
assert_eq!(et.graph.maxflow_value(3, 2, Some(&et.capacity))?, 1.0);
Source

pub fn residual_graph( &self, capacity: &[f64], flow: &[f64], ) -> Result<ResidualGraph>

Residual graph of a flow.

Given the edge capacity and a flow on each edge (e.g. MaxFlow::flow of a directed graph), returns the directed graph containing, in edge-id order, every edge whose flow is strictly below its capacity, together with its residual capacity capacity - flow. The vertex set is unchanged.

Binds igraph_residual_graph (undocumented in the C reference manual, see the Flows chapter).

§Errors

ErrorKind::InvalidValue if capacity or flow do not have one entry per edge, or if a capacity is negative, NaN or infinite.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true)?;
let capacity = [2.0, 1.0, 1.0];
let mf = g.maxflow(0, 2, Some(&capacity))?;
assert_eq!(mf.flow, vec![1.0, 1.0, 1.0]);
let res = g.residual_graph(&capacity, &mf.flow)?;
// Only 0 -> 1 is not saturated.
assert_eq!(res.graph.edge_list(), vec![(0, 1)]);
assert_eq!(res.capacity, vec![1.0]);
Source

pub fn reverse_residual_graph( &self, capacity: Option<&[f64]>, flow: &[f64], ) -> Result<Graph>

Reverse residual graph of a flow.

For every edge u → v of the input, in edge-id order, the result contains u → v if the edge carries a positive flow, and v → u if its flow is below its capacity. This is the graph used by Graph::all_st_mincuts to enumerate the minimum cuts. capacity: None means unit capacities.

Binds igraph_reverse_residual_graph (undocumented in the C reference manual, see the Flows chapter).

§Errors

ErrorKind::InvalidValue if capacity or flow do not have one entry per edge, or if a capacity is negative, NaN or infinite.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true)?;
let rr = g.reverse_residual_graph(Some(&[2.0, 1.0, 1.0]), &[1.0, 1.0, 1.0])?;
assert_eq!(rr.edge_list(), vec![(0, 1), (1, 0), (1, 2), (0, 2)]);
Source

pub fn dominator_tree( &self, root: VertexId, mode: NeighborMode, ) -> Result<DominatorTree>

Dominator tree of a flowgraph rooted at root.

In a directed graph where every vertex is reachable from root, a vertex v dominates w ≠ v if every path from the root to w passes through v. The immediate dominator idom(w) is the dominator of w dominated by all its other dominators; the edges idom(w) → w form a tree rooted at root, and v dominates w iff v is an ancestor of w in it. Implemented with the Lengauer–Tarjan algorithm (ACM TOPLAS 1, 1979), in O(|V| + |E| α(|E|, |V|)).

mode must be NeighborMode::Out or NeighborMode::In; with In all edges are followed backwards (post-dominators). Vertices not reachable from the root are reported in DominatorTree::leftout and are isolated in the tree.

See also Graph::subcomponent for plain reachability from the root.

Binds igraph_dominator_tree.

§Errors

ErrorKind::InvalidVertexId for an invalid root, ErrorKind::InvalidValue for undirected graphs or mode == NeighborMode::All.

§Examples

The example of igraph’s dominator_tree.c:

use igraph::prelude::*;

let g = Graph::from_edges(&[
    (0, 9), (1, 0), (1, 2), (2, 3), (2, 7), (3, 1), (4, 1), (4, 3),
    (5, 2), (5, 3), (5, 4), (5, 8), (6, 5), (6, 9), (8, 7),
], 10, true)?;
let dt = g.dominator_tree(9, NeighborMode::In)?;
assert_eq!(dt.dom[..3], [Some(9), Some(0), Some(3)]);
assert_eq!(dt.dom[9], None); // the root
assert_eq!(dt.leftout, vec![7, 8]);
assert_eq!(dt.dominators(2), vec![3, 1, 0, 9]);
Source

pub fn all_st_cuts(&self, source: VertexId, target: VertexId) -> Result<StCuts>

Lists all minimal edge cuts between source and target in a directed graph.

Following Provan and Shier (Algorithmica 15, 1996), an s-t cut here is a minimal set of edges whose removal leaves no directed path from source to target: supersets of a cut are not listed (on the path 0 → 1 → 2 the cuts are {0 → 1} and {1 → 2}, not both edges together). Every such cut is listed exactly once, both as its set of edges and as the vertex set X ∋ source generating it (the cut is the set of edges leaving X). Runs in O(n (|V| + |E|)) where n is the number of cuts — which can be exponential in the size of the graph.

When target is not reachable from source, or source == target, igraph lists no cuts at all (the result is empty, not an error).

Binds igraph_all_st_cuts.

§Errors

ErrorKind::Unimplemented for undirected graphs, ErrorKind::InvalidVertexId for invalid ids.

§Examples
use igraph::prelude::*;

// On a directed path, each edge alone is a cut.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true)?;
let all = g.all_st_cuts(0, 3)?;
assert_eq!(all.cuts, vec![vec![0], vec![1], vec![2]]);
assert_eq!(all.partition1s, vec![vec![0], vec![0, 1], vec![0, 1, 2]]);
Source

pub fn all_st_mincuts( &self, source: VertexId, target: VertexId, capacity: Option<&[f64]>, ) -> Result<StMinCuts>

Lists all minimum-capacity edge cuts between source and target in a directed graph.

Several cuts may share the minimum total capacity. Each is returned as its edge set and as the generating vertex set X ∋ source. Capacities must be strictly positive (None means unit capacities); integer capacities are recommended, as round-off may hide some cuts otherwise. Uses a maximum flow followed by the Provan–Shier enumeration on the reverse residual graph. When target is not reachable from source the value is 0 and no cut is listed.

Binds igraph_all_st_mincuts.

§Errors

ErrorKind::Unimplemented for undirected graphs, ErrorKind::InvalidVertexId for invalid ids, ErrorKind::InvalidValue if source == target, or for a capacity vector of the wrong length or with non-positive or non-finite entries.

§Examples
use igraph::prelude::*;

// Two parallel branches 1->2->4 and 1->3->4 between a single entry
// edge 0->1 and a single exit edge 4->5.
let g = Graph::from_edges(&[(0, 1), (1, 2), (1, 3), (2, 4), (3, 4), (4, 5)], 6, true)?;
let m = g.all_st_mincuts(0, 5, None)?;
assert_eq!(m.value, 1.0);
assert_eq!(m.cuts, vec![vec![0], vec![5]]);
Source

pub fn gomory_hu_tree(&self, capacity: Option<&[f64]>) -> Result<GomoryHuTree>

Gomory–Hu tree of an undirected graph.

A tree on the same vertices whose edges are annotated with flow values such that, for every pair (u, v), the maximum flow (minimum cut) between u and v in the original graph is the minimum annotation along the tree path from u to v (see GomoryHuTree::flow_between). All n (n - 1) / 2 pairwise flows are thus encoded by n - 1 numbers. Built with Gusfield’s algorithm (SIAM J. Comput. 19(1), 1990) using n - 1 max-flow computations, O(|V|⁴). capacity: None means unit capacities. The smallest annotation is the global minimum cut value (Graph::mincut_value).

Binds igraph_gomory_hu_tree.

§Errors

ErrorKind::InvalidValue for directed graphs or a capacity vector of the wrong length or with a negative, NaN or infinite entry.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2), (2, 3)], 4, false)?;
let gh = g.gomory_hu_tree(None)?;
assert_eq!(gh.tree.ecount(), 3);
assert_eq!(gh.flow_between(0, 1), Some(2.0)); // inside the triangle
assert_eq!(gh.flow_between(0, 3), Some(1.0)); // through the pendant edge
Source§

impl igraph_t

Source

pub fn read_graph( path: impl AsRef<Path>, format: GraphFormat, directed: bool, ) -> Result<Graph>

Reads a graph from a file in the given format, with default options.

directed is used by the formats that don’t encode directedness (edge list, NCOL, LGL, DIMACS, graph database, DL); Pajek, GraphML and GML files specify it themselves. Use GraphFormat::from_path to guess the format from the extension. The defaults are: no extra isolated vertices for edge lists, NcolLglOptions::default (plus directed) for NCOL/LGL, the first graph of a GraphML document; the problem data of a DIMACS file is discarded (use read_graph_dimacs_flow to get it).

§Errors

ErrorKind::Unimplemented if the format can’t be read (DOT, LEDA), plus the errors of the specific reader.

§Examples
use igraph::{foreign::GraphFormat, prelude::*};
let path = std::env::temp_dir().join(format!("igraph-doc-read-{}.net", std::process::id()));
std::fs::write(&path, "*Vertices 3\n*Edges\n1 2\n2 3\n").unwrap();
let format = GraphFormat::from_path(&path).unwrap();
let g = Graph::read_graph(&path, format, false).unwrap();
std::fs::remove_file(&path).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1), (1, 2)]);
Source

pub fn read_graph_edgelist( path: impl AsRef<Path>, n: usize, directed: bool, ) -> Result<Graph>

Reads an edge list file: an even number of non-negative integers (0-based vertex ids) separated by whitespace, conventionally one from to pair per line.

The graph has max(n, largest id + 1) vertices, so n = 0 is always safe; a larger n adds isolated vertices. See read_graph_ncol for files with symbolic vertex names. Time complexity: O(|V| + |E|).

Binds igraph_read_graph_edgelist.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for syntax errors (non-integers, odd number of ids…).

Source

pub fn read_graph_edgelist_from_str( text: &str, n: usize, directed: bool, ) -> Result<Graph>

Parses an edge list from a string, see read_graph_edgelist.

§Examples
use igraph::prelude::*;
let g = Graph::read_graph_edgelist_from_str("0 1\n1 2\n2 0\n", 5, false).unwrap();
assert_eq!((g.vcount(), g.ecount()), (5, 3)); // two isolated vertices from `n`
let bad = Graph::read_graph_edgelist_from_str("0 1 2", 0, false).unwrap_err();
assert_eq!(bad.kind(), ErrorKind::Parse);
Source

pub fn read_graph_ncol( path: impl AsRef<Path>, predefnames: &[&str], options: &NcolLglOptions, ) -> Result<Graph>

Reads an NCOL file, the symbolic weighted edge list format of the Large Graph Layout software.

Each line is name1 name2 [weight]: two vertex names without whitespace, optionally followed by a (possibly negative, possibly scientific notation) weight. Vertex ids are assigned in the order the names first appear, after the names listed in predefnames (which get ids 0..predefnames.len(); unknown names found in the file extend them, and duplicate predefined names are accepted, each with an igraph warning). An empty predefnames means none. Multi-edges and loops are accepted. Time complexity: O(|V| + |E| log |V|) ignoring parsing.

With the attribute handler on, the names are stored in the name vertex attribute and the weights in the weight edge attribute, as requested by options; otherwise they are discarded (module docs).

Binds igraph_read_graph_ncol.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for syntax errors.

Source

pub fn read_graph_ncol_from_str( text: &str, predefnames: &[&str], options: &NcolLglOptions, ) -> Result<Graph>

Parses an NCOL graph from a string, see read_graph_ncol.

§Examples
use igraph::{foreign::NcolLglOptions, prelude::*};
let text = "alice bob 2.5\nbob carol\ncarol alice -1e2\n";
// Fix the vertex ids through the predefined names: carol=0, bob=1, alice=2.
let g = Graph::read_graph_ncol_from_str(text, &["carol", "bob", "alice"], &NcolLglOptions::default())
    .unwrap();
assert_eq!(g.edge_list(), vec![(1, 2), (0, 1), (0, 2)]);
Source

pub fn read_graph_lgl( path: impl AsRef<Path>, options: &NcolLglOptions, ) -> Result<Graph>

Reads an LGL file (Large Graph Layout format).

The file is a sequence of blocks: a line # name introduces a vertex, and each following line other [weight] adds an edge from it to other, until the next # line. A # line with no following lines defines an isolated vertex. Vertex ids are assigned in the order the names first appear. Time complexity: O(|V| + |E| log |V|) ignoring parsing.

With the attribute handler on, the names are stored in the name vertex attribute and the weights in the weight edge attribute, as requested by options; otherwise they are discarded (module docs).

Binds igraph_read_graph_lgl.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for syntax errors.

Source

pub fn read_graph_lgl_from_str( text: &str, options: &NcolLglOptions, ) -> Result<Graph>

Parses an LGL graph from a string, see read_graph_lgl.

§Examples
use igraph::{foreign::NcolLglOptions, prelude::*};
let text = "# a\nb\nc 2.0\n# b\nc\n# lonely\n";
let g = Graph::read_graph_lgl_from_str(text, &NcolLglOptions::default()).unwrap();
assert_eq!(g.vcount(), 4);
assert_eq!(g.edge_list(), vec![(0, 1), (0, 2), (1, 2)]);
Source

pub fn read_graph_pajek(path: impl AsRef<Path>) -> Result<Graph>

Reads a Pajek .net file.

Only a subset of the format is supported: .paj project files, temporal networks, graphs mixing directed and undirected edges, permutations/hierarchies/clusters/vectors and multi-relational networks are not. *Arcs sections create a directed graph, *Edges an undirected one; *Arcslist/*Edgeslist and matrix sections are understood, as well as bipartite (two-mode) networks. Vertex ids in the file are 1-based. Time complexity: O(|V| + |E|).

With the attribute handler on, vertex labels become the name vertex attribute, coordinates x, y (and z), edge weights (including the entries of *Matrix sections) the weight edge attribute, and the other Pajek parameters get descriptive names (ic/c → color, bc → framecolor, x_fact → xfact, l → label, w → edgewidth, …; unknown ones are kept as string attributes). A parameter given for some elements only takes Pajek’s default elsewhere (e.g. color is LightOrange for vertices and MidnightBlue for edges; plain numbers such as the coordinates and weights are NaN). Two-mode networks get the boolean type vertex attribute (false for the first mode), as expected by the bipartite functions. Without the handler all of this is discarded (module docs).

Binds igraph_read_graph_pajek.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for syntax errors.

Source

pub fn read_graph_pajek_from_str(text: &str) -> Result<Graph>

Parses a Pajek graph from a string, see read_graph_pajek.

§Examples
use igraph::prelude::*;
let text = "*Vertices 4\n1 \"A\"\n2 \"B\"\n3 \"C\"\n4 \"D\"\n*Arcs\n1 2 0.5\n2 3\n3 1\n";
let g = Graph::read_graph_pajek_from_str(text).unwrap();
assert!(g.is_directed());
assert_eq!((g.vcount(), g.ecount()), (4, 3));
assert_eq!(g.edge(0).unwrap(), (0, 1));
Source

pub fn read_graph_graphml(path: impl AsRef<Path>, index: usize) -> Result<Graph>

Reads a GraphML file.

Only basic GraphML is supported: no nested graphs, no hyperedges. Directedness comes from the edgedefault attribute of the graph element. If the file contains several graphs, index selects which one to load (0 for the first); note that igraph 1.0.0 and 1.0.1 fail with “Graph index was too large” (ErrorKind::InvalidValue) for any index but 0, even when the document does contain more graphs. Vertices get ids in order of appearance, edges keep the document order.

With the attribute handler on, every <key> becomes a graph, vertex or edge attribute named after its attr.name (or its id if attr.name is missing): boolean keys give boolean attributes, int/long/float/double numeric ones, string string ones (UTF-8). Missing values take the key’s <default>, or NaN/""/false. The GraphML ids of the nodes are kept in the string id vertex attribute (unless a vertex key already defines an attribute named id, in which case igraph only warns). When the edges have ids, igraph 1.0.0 and 1.0.1 also create a string id edge attribute (unless an edge key defines one) but, because of a bug in src/io/graphml.c, fill it with the node ids, in order, instead of the edge ids (padded with "" when there are more edges than nodes): don’t rely on it. Without the handler all of this is discarded (module docs).

Binds igraph_read_graph_graphml.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for malformed files, ErrorKind::InvalidValue if index is too large, ErrorKind::Unimplemented if the igraph library was compiled without GraphML (libxml2) support.

Source

pub fn read_graph_graphml_from_str(text: &str, index: usize) -> Result<Graph>

Parses a GraphML document from a string, see read_graph_graphml.

§Examples
use igraph::{attributes, prelude::*};
attributes::enable().unwrap();
let xml = r#"<?xml version="1.0" encoding="UTF-8"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns">
  <key id="d0" for="node" attr.name="age" attr.type="int"><default>20</default></key>
  <key id="d1" for="edge" attr.name="since" attr.type="double"/>
  <graph edgedefault="directed">
    <node id="ann"><data key="d0">30</data></node>
    <node id="bob"/>
    <edge source="bob" target="ann"><data key="d1">2019</data></edge>
  </graph>
</graphml>"#;
let g = Graph::read_graph_graphml_from_str(xml, 0).unwrap();
assert!(g.is_directed());
assert_eq!(g.edge_list(), vec![(1, 0)]);
assert_eq!(g.vertex_attr_str_values("id", ..).unwrap(), ["ann", "bob"]);
assert_eq!(g.vertex_attr_numeric_values("age", ..).unwrap(), [30.0, 20.0]); // bob: default
assert_eq!(g.edge_attr_numeric("since", 0).unwrap(), 2019.0);
Source

pub fn read_graph_dimacs_flow( path: impl AsRef<Path>, directed: bool, ) -> Result<DimacsFlow>

Reads a DIMACS network flow file.

DIMACS is a line oriented format; the first character of each line gives its type: c comment, p the problem line (p max|edge <vertices> <edges>, before any node or arc line), n node lines and a arc lines (a from to capacity, max-flow problems) or e edge lines (e from to, edge problems). In max-flow problems exactly two node lines n <id> s|t mark the source and the target. In edge problems the labels are the 1-based vertex indices: n <id> <label> lines are meant to change them, but igraph 1.0.0 and 1.0.1 reject them with a parse error. Vertex ids in the file start from 1, the returned ids from 0. A max-flow file without the source (or target) n line is accepted, with None in DimacsProblem::Max. Time complexity: O(|V| + |E| + c), c being the file size. The capacities are returned in DimacsProblem::Max, never as attributes.

See Graph::maxflow and Graph::st_mincut to solve the problem.

Binds igraph_read_graph_dimacs_flow.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse or ErrorKind::InvalidValue for malformed files, including a source or target n line naming a vertex that doesn’t exist.

Source

pub fn read_graph_dimacs_flow_from_str( text: &str, directed: bool, ) -> Result<DimacsFlow>

Parses a DIMACS flow problem from a string, see read_graph_dimacs_flow.

§Examples
use igraph::{foreign::DimacsProblem, prelude::*};
let text = "c a tiny network\np max 3 2\nn 1 s\nn 3 t\na 1 2 4\na 2 3 7\n";
let flow = Graph::read_graph_dimacs_flow_from_str(text, true).unwrap();
assert_eq!(flow.problem_name, "max");
assert_eq!(flow.graph.edge_list(), vec![(0, 1), (1, 2)]);
let DimacsProblem::Max { source: Some(s), target: Some(t), capacity } = flow.problem else {
    panic!("a max-flow problem with both terminals was expected");
};
assert_eq!((s, t, capacity.as_slice()), (0, 2, &[4.0, 7.0][..]));
// Solve it: the bottleneck is the first arc.
assert_eq!(flow.graph.maxflow_value(s, t, Some(&capacity)).unwrap(), 4.0);
Source

pub fn read_graph_graphdb( path: impl AsRef<Path>, directed: bool, ) -> Result<Graph>

Reads a graph in the binary format of the ARG graph database (used to benchmark isomorphism algorithms).

The file is a sequence of 16-bit little-endian words: the number of vertices, then for each vertex the number of its out-edges followed by their (0-based) targets. Only unlabelled graphs are supported. Time complexity: O(|V| + |E|).

The database is a benchmark for isomorphism algorithms: see Graph::isomorphic and the rest of the isomorphism module.

Binds igraph_read_graph_graphdb.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for truncated files or trailing bytes.

Source

pub fn read_graph_graphdb_from_bytes( data: &[u8], directed: bool, ) -> Result<Graph>

Parses a graph in the binary graph database format from bytes, see read_graph_graphdb.

§Examples
use igraph::prelude::*;
// 4 vertices; 0 -> [2]; 1 -> [0]; 2 -> []; 3 -> [0, 2] (igraph's unit test file).
let words: [u16; 9] = [4, 1, 2, 1, 0, 0, 2, 0, 2];
let bytes: Vec<u8> = words.iter().flat_map(|w| w.to_le_bytes()).collect();
let g = Graph::read_graph_graphdb_from_bytes(&bytes, true).unwrap();
assert_eq!(g.edge_list(), vec![(0, 2), (1, 0), (3, 0), (3, 2)]);
Source

pub fn read_graph_gml(path: impl AsRef<Path>) -> Result<Graph>

Reads a GML file.

Any syntactically correct GML is parsed, but only a subset is used: the first graph record, its directed flag, node records (with their id) and edge records (with source and target); other top level records are ignored. inf, -inf and nan are accepted as reals (case insensitively). Time complexity: proportional to the file length.

With the attribute handler on, every field of simple type (integer, real, string) of the graph, node and edge records becomes a numeric or string attribute (including the numeric id of the nodes, and comment fields); composite fields (records) are ignored with a warning, and missing values are NaN or "". Only the quot, amp, apos, lt and gt entities are decoded. Without the handler all of this is discarded (module docs).

The C parser of igraph 1.0.0 and 1.0.1 is not reentrant (it uses static buffers), so this wrapper serializes GML reads across threads with a process-wide lock; all the other readers run fully in parallel.

Binds igraph_read_graph_gml.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for syntax errors and for structural problems: no graph record, duplicate or non-integer node ids, edges without source/target or referring to unknown node ids.

Source

pub fn read_graph_gml_from_str(text: &str) -> Result<Graph>

Parses a GML graph from a string, see read_graph_gml.

§Examples
use igraph::prelude::*;
let text = r#"graph [
  directed 0
  node [ id 10 label "x" ]
  node [ id 20 ]
  node [ id 30 ]
  edge [ source 10 target 20 ]
  edge [ source 30 target 10 weight 2.5 ]
]"#;
let g = Graph::read_graph_gml_from_str(text).unwrap();
assert!(!g.is_directed());
// GML ids are mapped to consecutive vertex ids in order of appearance.
assert_eq!(g.edge_list(), vec![(0, 1), (0, 2)]);
Source

pub fn read_graph_dl(path: impl AsRef<Path>, directed: bool) -> Result<Graph>

Reads a file in the DL format of UCINET.

All the forms of the format are supported: full matrix, edge list (format = edgelist1) and node list (format = nodelist1), with or without labels. Labels are case sensitive. With the attribute handler on, labels are stored in the name vertex attribute and edge values in the weight edge attribute; otherwise they are discarded (module docs). Time complexity: linear in the number of vertices and edges, quadratic in the number of vertices for the full matrix form.

Binds igraph_read_graph_dl.

§Errors

ErrorKind::File if the file can’t be opened, ErrorKind::Parse for syntax errors.

Source

pub fn read_graph_dl_from_str(text: &str, directed: bool) -> Result<Graph>

Parses a UCINET DL graph from a string, see read_graph_dl.

§Examples
use igraph::prelude::*;
// `fullmatrix1.dl` from igraph's examples.
let text = "DL N = 5\nData:\n0 1 1 1 1\n1 0 1 0 0\n1 1 0 0 1\n1 0 0 0 0\n1 0 1 0 0\n";
let g = Graph::read_graph_dl_from_str(text, true).unwrap();
assert_eq!((g.vcount(), g.ecount()), (5, 12));
assert_eq!(g.edge(0).unwrap(), (0, 1));
Source§

impl igraph_t

Source

pub fn write_graph( &self, path: impl AsRef<Path>, format: GraphFormat, ) -> Result<()>

Writes the graph to a file in the given format, with default options (NCOL/LGL/LEDA without the optional named attributes, GraphML without prefixes, GML with the default GmlWriteOptions, LGL without isolated vertices). GraphML, GML and DOT export all the attributes, and Pajek those naming Pajek parameters, when the attribute handler is on.

§Errors

ErrorKind::Unimplemented if the format can’t be written generically (graph database, DL, DIMACS), plus the errors of the specific writer.

§Examples
use igraph::{foreign::GraphFormat, prelude::*};
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let path = std::env::temp_dir().join(format!("igraph-doc-write-{}.dot", std::process::id()));
g.write_graph(&path, GraphFormat::from_path(&path).unwrap()).unwrap();
let dot = std::fs::read_to_string(&path).unwrap();
std::fs::remove_file(&path).unwrap();
assert!(dot.contains("graph {") && dot.contains("1 -- 0;") && dot.contains("2 -- 1;"));
Source

pub fn write_graph_edgelist(&self, path: impl AsRef<Path>) -> Result<()>

Writes the edge list of the graph to a file: one from to line per edge (0-based ids, a single space as separator). Lines are sorted by the first endpoint, so edge ids are preserved by a round trip only if the edges were already sorted that way; isolated vertices (with ids above the largest endpoint) are lost unless the vertex count is passed back to read_graph_edgelist. Time complexity: O(|E|).

Binds igraph_write_graph_edgelist.

§Errors

ErrorKind::File if the file can’t be created or written.

Source

pub fn write_graph_edgelist_to_string(&self) -> Result<String>

The edge list serialization as a string, see write_graph_edgelist.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(2, 1), (0, 1)], 3, true).unwrap();
// Sorted by the source vertex, not by edge id.
assert_eq!(g.write_graph_edgelist_to_string().unwrap(), "0 1\n2 1\n");
Source

pub fn write_graph_ncol( &self, path: impl AsRef<Path>, names: Option<&str>, weights: Option<&str>, ) -> Result<()>

Writes the graph to an NCOL file (see read_graph_ncol): one from to [weight] line per edge.

names is the name of a string vertex attribute to write instead of the vertex ids, weights the name of a numeric edge attribute to write as weights; None skips them. A requested attribute that does not exist (always the case without the attribute handler) is skipped with an igraph warning (module docs). NaN and infinite weights are written as NaN, Inf and -Inf, and read back as such. Names must be non-empty and contain no spaces or non-printable characters. The format can’t represent isolated vertices; multi-edges and loops are written (though they break the LGL software). Time complexity: O(|E|).

Binds igraph_write_graph_ncol.

§Errors

ErrorKind::File on I/O errors, ErrorKind::InvalidValue for attribute names with NUL bytes or invalid vertex names.

Source

pub fn write_graph_ncol_to_string( &self, names: Option<&str>, weights: Option<&str>, ) -> Result<String>

The NCOL serialization as a string, see write_graph_ncol.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 4, false).unwrap();
assert_eq!(g.write_graph_ncol_to_string(None, None).unwrap(), "0 1\n1 2\n");
Source

pub fn write_graph_lgl( &self, path: impl AsRef<Path>, names: Option<&str>, weights: Option<&str>, isolates: bool, ) -> Result<()>

Writes the graph to an LGL file (see read_graph_lgl).

Edges are grouped by their first endpoint: # from followed by one to [weight] line per edge. names/weights name a string vertex attribute and a numeric edge attribute to write (skipped with a warning when missing, as always without the attribute handler, see the module docs); names must be non-empty and contain no spaces, # or non-printable characters. With isolates = true isolated vertices are written as lone # v lines. Time complexity: O(|E|), or O(|V| + |E|) with isolates.

Binds igraph_write_graph_lgl.

§Errors

ErrorKind::File on I/O errors, ErrorKind::InvalidValue for attribute names with NUL bytes or invalid vertex names.

Source

pub fn write_graph_lgl_to_string( &self, names: Option<&str>, weights: Option<&str>, isolates: bool, ) -> Result<String>

The LGL serialization as a string, see write_graph_lgl.

§Examples
use igraph::prelude::*;
// The graph of igraph's `igraph_write_graph_lgl.c` example.
let g = Graph::from_edges(&[(0, 1), (1, 3), (1, 2), (2, 0), (4, 2), (3, 4)], 7, false).unwrap();
let lgl = g.write_graph_lgl_to_string(None, None, true).unwrap();
assert_eq!(lgl, "# 0\n1\n2\n# 1\n2\n3\n# 2\n4\n# 3\n4\n# 5\n# 6\n");
Source

pub fn write_graph_graphml(&self, path: impl AsRef<Path>) -> Result<()>

Writes the graph to a GraphML file (without attribute prefixes).

GraphML is an XML format, see the GraphML primer. Vertices are written as n0, n1, ..., edges in id order, and the edgedefault reflects the directedness. With the attribute handler on, all graph, vertex and edge attributes are written as typed <key>s (boolean, double or string; NaN values are omitted), so a GraphML round trip preserves them (module docs). Attribute names and string values should be UTF-8 (igraph copies the bytes as they are) and must not contain control characters other than tab, CR and LF. Use write_graph_graphml_with to control the attribute name prefixes. Time complexity: O(|V| + |E|).

This replaces the former, infallible write_graph_graphml(&self, &str) that leaked its file handle: the file is now always closed and errors are reported.

Binds igraph_write_graph_graphml.

§Errors

ErrorKind::File if the file can’t be created or written, ErrorKind::InvalidValue if an attribute name or string value contains a control character other than tab, CR and LF.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
let path = std::env::temp_dir().join(format!("igraph-doc-{}.graphml", std::process::id()));
g.write_graph_graphml(&path).unwrap();
let xml = std::fs::read_to_string(&path).unwrap();
std::fs::remove_file(&path).unwrap();
assert!(xml.contains(r#"edgedefault="directed""#));
assert!(xml.contains(r#"<edge source="n2" target="n0">"#));
Source

pub fn write_graph_graphml_with( &self, path: impl AsRef<Path>, prefixattr: bool, ) -> Result<()>

Writes the graph to a GraphML file; with prefixattr = true attribute names get a g_, v_ or e_ prefix to keep graph, vertex and edge attributes with the same name distinct. See write_graph_graphml.

Binds igraph_write_graph_graphml.

§Errors

As write_graph_graphml.

Source

pub fn write_graph_graphml_to_string(&self, prefixattr: bool) -> Result<String>

The GraphML serialization as a string, see write_graph_graphml_with.

§Examples
use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
// Setting an attribute turns on the attribute handler.
g.set_vertex_attr_str_values("name", &["ann", "bob"]).unwrap();
g.set_edge_attr_numeric("weight", 0, 2.5).unwrap();
let xml = g.write_graph_graphml_to_string(true).unwrap();
assert!(xml.contains(r#"<key id="v_name" for="node" attr.name="name" attr.type="string"/>"#));
assert!(xml.contains(r#"<data key="v_name">bob</data>"#));
assert!(xml.contains(r#"<data key="e_weight">2.5</data>"#));
// Reading it back restores the attributes (under their original names).
let h = Graph::read_graph_graphml_from_str(&xml, 0).unwrap();
assert_eq!(h.vertex_attr_str_values("name", ..).unwrap(), ["ann", "bob"]);
assert_eq!(h.edge_attr_numeric("weight", 0).unwrap(), 2.5);
Source

pub fn write_graph_pajek(&self, path: impl AsRef<Path>) -> Result<()>

Writes the graph to a Pajek .net file.

The format is meant for interoperability with the Pajek software, not for data exchange. Vertex ids are written 1-based; directed graphs use an *Arcs section, undirected ones *Edges. Vertex and edge parameters come from the attributes named as in read_graph_pajek (name as the label, x/y/z, color, edge weight, …; other attributes are discarded), hence are only written with the attribute handler on (module docs). A boolean type vertex attribute makes a two-mode (bipartite) file: since Pajek needs the vertices of the first mode first, the vertices are then reordered, and their ids change on a round trip (without a name attribute the labels are then the original 1-based ids). igraph never writes a UTF-8 byte-order mark. Time complexity: O(|V| + |E|).

Binds igraph_write_graph_pajek.

§Errors

ErrorKind::File if the file can’t be created or written.

Source

pub fn write_graph_pajek_to_string(&self) -> Result<String>

The Pajek serialization as a string, see write_graph_pajek.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
assert_eq!(g.write_graph_pajek_to_string().unwrap(), "*Vertices 3\n*Arcs\n1 2\n2 3\n3 1\n");
Source

pub fn write_graph_dimacs_flow( &self, path: impl AsRef<Path>, source: VertexId, target: VertexId, capacity: &[f64], ) -> Result<()>

Writes a maximum flow problem in DIMACS format (see read_graph_dimacs_flow): a comment, the p max line, the source and target n lines and one a from to capacity line per edge (1-based ids). capacity must have one entry per edge. Time complexity: O(|E|).

See Graph::maxflow to solve the instance directly.

Binds igraph_write_graph_dimacs_flow.

§Errors

ErrorKind::InvalidVertexId if source or target is not a vertex of the graph (igraph itself would write them unchecked), ErrorKind::InvalidValue if capacity.len() != ecount, ErrorKind::File on I/O errors.

Source

pub fn write_graph_dimacs_flow_to_string( &self, source: VertexId, target: VertexId, capacity: &[f64], ) -> Result<String>

The DIMACS serialization as a string, see write_graph_dimacs_flow.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
let text = g.write_graph_dimacs_flow_to_string(0, 2, &[4.0, 7.5]).unwrap();
assert_eq!(text, "c created by igraph\np max 3 2\nn 1 s\nn 3 t\na 1 2 4\na 2 3 7.5\n");
Source

pub fn write_graph_gml( &self, path: impl AsRef<Path>, options: &GmlWriteOptions<'_>, ) -> Result<()>

Writes the graph to a GML file.

The output lists the directed flag, one node [ id ... ] record per vertex and one edge [ source ... target ... ] record per edge. See GmlWriteOptions for the ids, the creator line and the entity encoding. Time complexity: proportional to the output size.

With the attribute handler on, numeric and string attributes are written too (booleans as 0/1, NaN values skipped, infinite values kept with a warning since they are not standard GML). Attribute names are reduced to their alphanumeric characters (prefixed with igraph if they don’t start with a letter); attributes whose name would clash with the GML structure (source/target on edges, directed, node and edge on the graph, id on vertices) are skipped with a warning. The id vertex attribute itself is never written as an attribute: a numeric one supplies the node ids (see GmlWriteOptions::ids), any other is dropped (module docs).

Binds igraph_write_graph_gml.

§Errors

ErrorKind::InvalidValue if options.ids doesn’t have one entry per vertex, or the creator contains a NUL byte; ErrorKind::File on I/O errors.

Source

pub fn write_graph_gml_to_string( &self, options: &GmlWriteOptions<'_>, ) -> Result<String>

The GML serialization as a string, see write_graph_gml.

§Examples
use igraph::{foreign::GmlWriteOptions, prelude::*};
let g = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
let opts = GmlWriteOptions::default().with_creator("").with_ids(&[10.0, 20.0]);
let gml = g.write_graph_gml_to_string(&opts).unwrap();
// igraph stores undirected edges with the larger endpoint first.
assert!(gml.contains("id 10") && gml.contains("source 20") && gml.contains("target 10"));
assert!(!gml.contains("Creator"));
Source

pub fn write_graph_dot(&self, path: impl AsRef<Path>) -> Result<()>

Writes the graph to a Graphviz DOT file.

The structure is written as a -- b; (a -> b; for directed graphs) lines after the list of vertices; igraph adds no layout or visualization information of its own. With the attribute handler on, the graph attributes are written in a graph [ ... ] block and every vertex and edge attribute in the [ ... ] block of its element (numbers as written by igraph, NaN included, booleans as 0/1 with a warning, strings and names quoted and escaped when needed). The format is meant for interoperability with Graphviz, not for data exchange. Time complexity: proportional to the output size.

See Graph::layout_circle and the layout module to compute coordinates to add as attributes (e.g. pos).

Binds igraph_write_graph_dot.

§Errors

ErrorKind::File if the file can’t be created or written.

Source

pub fn write_graph_dot_to_string(&self) -> Result<String>

The DOT serialization as a string, see write_graph_dot.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
let dot = g.write_graph_dot_to_string().unwrap();
assert!(dot.starts_with("/* Created by igraph"));
assert!(dot.contains("digraph {") && dot.contains("  0 -> 1;\n") && dot.contains("  1 -> 2;\n"));
Source

pub fn write_graph_leda( &self, path: impl AsRef<Path>, vertex_attr: Option<&str>, edge_attr: Option<&str>, ) -> Result<()>

Writes the graph in the LEDA native graph format.

Only the graph section is written: the vertices, then the edges with 1-based endpoints (and, for undirected graphs, a -2 direction flag). vertex_attr/edge_attr name one vertex and one edge attribute whose values are stored, with their LEDA type (double, string or bool) in the header; void is written for None and for attributes that don’t exist (with a warning; always the case without the attribute handler, see the module docs). Time complexity: O(|V| + |E|).

Binds igraph_write_graph_leda.

§Errors

ErrorKind::File on I/O errors, ErrorKind::InvalidValue for attribute names with NUL bytes or string values containing a newline.

Source

pub fn write_graph_leda_to_string( &self, vertex_attr: Option<&str>, edge_attr: Option<&str>, ) -> Result<String>

The LEDA serialization as a string, see write_graph_leda.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
let leda = g.write_graph_leda_to_string(None, None).unwrap();
assert_eq!(
    leda,
    "LEDA.GRAPH\nvoid\nvoid\n-1\n# Vertices\n3\n|{}|\n|{}|\n|{}|\n# Edges\n3\n\
     1 2 0 |{}|\n2 3 0 |{}|\n3 1 0 |{}|\n"
);
Source§

impl igraph_t

Source

pub fn erdos_renyi_game_gnm( num_vertices: usize, num_edges: usize, directed: bool, mode: impl Into<AllowedEdgeTypes>, edge_labeled: bool, ) -> Result<Graph>

Generates a uniformly random graph with a fixed number of vertices and edges: the Erdős–Rényi G(n, m) model.

Among all graphs on num_vertices vertices with exactly num_edges edges (and respecting allowed_edge_types, which accepts an EdgeTypeSw or an AllowedEdgeTypes), one is drawn uniformly at random. With edge_labeled = true the sampling is uniform over ordered edge lists instead (see Graph::iea_game); pass false for the classic model.

Time complexity: O(|V| + |E|).

See also Graph::erdos_renyi_game_gnp (independent edges) and Graph::bipartite_game_gnm (the bipartite analogue).

Binds igraph_erdos_renyi_game_gnm.

§Errors

ErrorKind::InvalidValue if num_edges is larger than the number of available vertex pairs (for simple graphs).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let g = Graph::erdos_renyi_game_gnm(10, 20, true, EdgeTypeSw::Simple, false).unwrap();
assert_eq!((g.vcount(), g.ecount(), g.is_directed()), (10, 20, true));
// Only 45 vertex pairs exist in a simple undirected graph on 10 vertices.
let err = Graph::erdos_renyi_game_gnm(10, 46, false, EdgeTypeSw::Simple, false).unwrap_err();
assert_eq!(err.kind(), ErrorKind::InvalidValue);
Source

pub fn erdos_renyi_game_gnp( num_vertices: usize, p: f64, directed: bool, allowed_edge_types: impl Into<AllowedEdgeTypes>, edge_labeled: bool, ) -> Result<Graph>

Generates a random graph where every vertex pair is connected independently with probability p: the Erdős–Rényi G(n, p) (or Gilbert) model.

When multi-edges are allowed, p is the expected number of edges between any vertex pair (multiplicities are geometric: P(m edges) = q (1 - q)^m with q = 1 / (1 + p)). The expected mean degree is p (n - 1) without self-loops, p (n + 1) for undirected graphs with self-loops and p n for directed ones with self-loops; set p = k / n for a mean degree of about k. edge_labeled = true samples uniformly from ordered edge lists instead; igraph implements this variant only when multi-edges are allowed. Use false for the classic model.

Time complexity: O(|V| + |E|).

See also Graph::erdos_renyi_game_gnm (fixed edge count), Graph::bipartite_game_gnp and Graph::mean_degree.

Binds igraph_erdos_renyi_game_gnp.

§Errors

ErrorKind::InvalidValue if p is not in [0, 1] for graphs without multi-edges (or is negative for multigraphs), or is NaN or infinite, or if num_vertices exceeds igraph’s maximum vertex count i64::MAX - 1 (checked on the Rust side: igraph 1.0.1 overflows computing n + 1 for loopy undirected graphs, and mishandles an infinite p); ErrorKind::Overflow if, with edge_labeled = true, the expected number of edges exceeds 2^56 (igraph 1.0.1 would overflow doubling the sampled edge count); ErrorKind::Unimplemented for edge_labeled = true without multi-edges.

§Examples
use igraph::prelude::*;

// p = 1 gives the complete graph, p = 0 the empty graph.
let k5 = Graph::erdos_renyi_game_gnp(5, 1.0, false, EdgeTypeSw::Simple, false).unwrap();
assert!(k5.is_same_graph(&Graph::full(5, false, false).unwrap()).unwrap());
let e5 = Graph::erdos_renyi_game_gnp(5, 0.0, false, EdgeTypeSw::Simple, false).unwrap();
assert_eq!(e5.ecount(), 0);

// Mean degree p (n - 1) ≈ 4.
rng::seed(42).unwrap();
let g = Graph::erdos_renyi_game_gnp(2001, 0.002, false, EdgeTypeSw::Simple, false).unwrap();
assert!((g.mean_degree(true).unwrap() - 4.0).abs() < 0.3);
Source

pub fn iea_game( num_vertices: usize, num_edges: usize, directed: bool, loops: bool, ) -> Result<Graph>

Generates a random multigraph by independent edge assignment (IEA): each of the num_edges edges is assigned to a uniformly random ordered vertex pair, independently of the others.

This is uniform sampling of edge-labeled graphs: all simple graphs have the same probability, while multigraphs are down-weighted by the factorials of their edge multiplicities. loops controls whether self-loops can be produced. This function is marked experimental in igraph.

Time complexity: O(|V| + |E|).

See also Graph::erdos_renyi_game_gnm with edge_labeled = true and Graph::bipartite_iea_game.

Binds igraph_iea_game.

§Examples
use igraph::prelude::*;

rng::seed(1).unwrap();
// Two vertices, 10 edges, no loops: all edges are parallel.
let g = Graph::iea_game(2, 10, false, false).unwrap();
assert!(!g.has_loop().unwrap());
assert_eq!(g.count_multiple(..).unwrap(), vec![10; 10]);
Source

pub fn barabasi_game( num_vertices: usize, options: &BarabasiOptions<'_>, ) -> Result<Graph>

Generates a graph by preferential attachment: the Barabási–Albert model and its variants (Price model, non-linear attachment).

Starting from a single vertex (or from start_from), vertices are added one at a time; each new vertex cites m (or outseq[i]) existing vertices, chosen with probability proportional to d^power + A, where d is the in-degree (or total degree with outpref, and always in undirected graphs). See BarabasiOptions for all the parameters and BarabasiAlgorithm for the sampling algorithms: Bag only supports power = 1, A = 1 and may create multi-edges, Psumtree creates simple graphs, PsumtreeMultiple allows multi-edges.

Time complexity: O(|V| + |E|).

See also Graph::barabasi_aging_game and Graph::recent_degree_game for variants of preferential attachment, and power_law_fit to estimate the exponent of the resulting degree distribution.

Binds igraph_barabasi_game.

§Errors

ErrorKind::InvalidValue for a non-positive A (negative A when the total degree is used), negative outseq entries or an outseq whose length differs from the number of new vertices, an empty starting graph or one with more than num_vertices vertices, power != 1 or A != 1 with BarabasiAlgorithm::Bag, or an undirected starting graph for a directed result without outpref.

§Examples
use igraph::prelude::*;
use igraph::games::BarabasiOptions;

rng::seed(42).unwrap();
let g = Graph::barabasi_game(1000, &BarabasiOptions::default().with_m(3)).unwrap();
// Vertex 1 cites vertex 0 once, vertex 2 cites 0 and 1, then 3 edges each.
assert_eq!(g.ecount(), 1 + 2 + 997 * 3);
// Hubs emerge: the maximum degree is far above the mean (about 6).
let max = g.maxdegree(.., NeighborMode::All, Loops::Twice).unwrap();
assert!(max > 30);
Source

pub fn barabasi_aging_game( num_vertices: usize, options: &BarabasiAgingOptions<'_>, ) -> Result<Graph>

Generates a graph by preferential attachment with aging of vertices.

Starting from one vertex, a new vertex is added in each step and connected to m existing ones, chosen with probability proportional to (deg_coef * k^pa_exp + zero_deg_appeal) * (age_coef * l^aging_exp + zero_age_appeal), where k is the (in-)degree and l the age of the vertex; the age is incremented every floor(n / aging_bins) + 1 steps. See BarabasiAgingOptions.

Time complexity: O((|V| + |V|/aging_bins) log |V| + |E|).

See also Graph::recent_degree_aging_game, where only recently gained edges count.

Binds igraph_barabasi_aging_game.

§Errors

ErrorKind::InvalidValue for zero aging_bins, negative appeal or coefficient terms, or an outseq whose length differs from the number of vertices.

§Examples
use igraph::prelude::*;
use igraph::games::BarabasiAgingOptions;

rng::seed(3).unwrap();
let opts = BarabasiAgingOptions { m: 2, aging_exp: -1.0, aging_bins: 10, ..Default::default() };
let g = Graph::barabasi_aging_game(50, &opts).unwrap();
assert_eq!(g.vcount(), 50);
assert_eq!(g.ecount(), 49 * 2);

// From igraph's unit tests: a very steep aging preference for young
// vertices makes every vertex cite its predecessor twice.
let young = BarabasiAgingOptions {
    m: 2, pa_exp: 0.0, aging_exp: -10.0, aging_bins: 6,
    zero_deg_appeal: 0.1, deg_coef: 0.1, ..Default::default()
};
let line = Graph::barabasi_aging_game(5, &young).unwrap();
let mut edges = line.edge_list();
edges.sort();
assert_eq!(edges, [(1, 0), (1, 0), (2, 1), (2, 1), (3, 2), (3, 2), (4, 3), (4, 3)]);
Source

pub fn recent_degree_game( num_vertices: usize, options: &RecentDegreeOptions<'_>, ) -> Result<Graph>

Generates a growing graph where the attractiveness of a vertex depends on the number of edges it gained recently.

In each of the num_vertices time steps a vertex is added, citing m (or outseq[i]) existing vertices chosen with probability proportional to k^power + zero_appeal, where k counts the edges gained in the last window steps. See RecentDegreeOptions.

Time complexity: O(|V| log |V| + |E|).

See also Graph::barabasi_game (the whole degree counts) and Graph::recent_degree_aging_game.

Binds igraph_recent_degree_game.

§Errors

ErrorKind::InvalidValue for a negative zero_appeal, or an outseq whose length differs from the number of vertices. A window longer than num_vertices is equivalent to num_vertices (no edge ever leaves it) and is clamped on the Rust side, since igraph 1.0.1 sizes its history queue from it without overflow checks.

§Examples
use igraph::{games::RecentDegreeOptions, prelude::*};

// From igraph's unit tests: a strong preference for recently cited
// vertices makes a star of double edges around vertex 0.
let opts = RecentDegreeOptions {
    power: 30.0, window: 100, m: 2, zero_appeal: 0.001, directed: true,
    ..Default::default()
};
let g = Graph::recent_degree_game(10, &opts).unwrap();
assert_eq!(g.ecount(), 18);
assert!(g.edge_list().iter().all(|&(_, to)| to == 0));
Source

pub fn recent_degree_aging_game( num_vertices: usize, options: &RecentDegreeAgingOptions<'_>, ) -> Result<Graph>

Preferential attachment based on the number of edges gained recently, with aging of vertices.

Like Graph::barabasi_aging_game, but the degree part counts only the edges gained in the last window steps: the attractiveness is (k^pa_exp + zero_appeal) * l^aging_exp. See RecentDegreeAgingOptions.

Time complexity: O((|V| + |V|/aging_bins) log |V| + |E|).

Binds igraph_recent_degree_aging_game.

§Errors

ErrorKind::InvalidValue for zero aging_bins, a negative zero_appeal, or an outseq whose length differs from the number of vertices. As in Graph::recent_degree_game, a window longer than num_vertices is clamped to it (an equivalent model).

§Examples
use igraph::{games::RecentDegreeAgingOptions, prelude::*};

// From igraph's unit tests: one citation per step yields a tree.
let opts = RecentDegreeAgingOptions {
    outpref: true, pa_exp: 2.0, aging_exp: 2.0, aging_bins: 5, window: 4,
    ..Default::default()
};
let g = Graph::recent_degree_aging_game(10, &opts).unwrap();
assert!(g.is_tree(NeighborMode::All).unwrap());
Source

pub fn growing_random_game( num_vertices: usize, m: usize, directed: bool, citation: bool, ) -> Result<Graph>

Generates a growing random graph.

Starting with one vertex, in each step a new vertex and m new edges are added. The endpoints of the edges are uniformly random vertices, unless citation is true, in which case every edge goes from the newest vertex to a uniformly chosen older one. Such graphs differ from non-growing random graphs (older vertices have higher degree). The result may contain multi-edges (and self-loops without citation).

Time complexity: O(|V| + |E|).

See also Graph::barabasi_game (growth with preferential instead of uniform attachment).

Binds igraph_growing_random_game.

§Examples
use igraph::prelude::*;

rng::seed(5).unwrap();
let g = Graph::growing_random_game(10, 2, true, true).unwrap();
assert_eq!(g.ecount(), 9 * 2);
// Citations always point back in time.
assert!(g.edge_list().iter().all(|&(from, to)| from > to));
Source

pub fn degree_sequence_game( out_degrees: &[i64], in_degrees: Option<&[i64]>, method: DegreeSequenceMethod, ) -> Result<Graph>

Generates a random graph with a prescribed degree sequence.

out_degrees is the degree sequence of an undirected graph (when in_degrees is None), or the out-degree sequence of a directed one. The DegreeSequenceMethod chooses the sampler:

  • Configuration: the configuration model; may create loops and multi-edges.
  • ConfigurationSimple: configuration model with rejection of non-simple results; uniform over simple graphs (can be slow).
  • FastHeurSimple: simple graphs, fast but not uniform.
  • EdgeSwitchingSimple: MCMC with degree-preserving edge switches; simple graphs.
  • VigerLatapy: undirected connected simple graphs, approximately uniform.

Time complexity: O(|V| + |E|) for Configuration and EdgeSwitchingSimple, unknown for the others.

See also Graph::realize_degree_sequence (a deterministic realization), is_graphical (is the sequence realizable at all?) and Graph::rewire (degree-preserving randomization of an existing graph).

Binds igraph_degree_sequence_game.

§Errors

ErrorKind::InvalidValue if the sequences are not realizable with the chosen method (e.g. odd degree sum, mismatched in/out sums or lengths, non-graphical sequence for the simple methods).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let degrees = [3, 3, 2, 2, 2, 1, 1];
let g = Graph::degree_sequence_game(&degrees, None, DegreeSequenceMethod::VigerLatapy).unwrap();
assert_eq!(g.degree(.., NeighborMode::All, Loops::Twice).unwrap(), degrees);
// The Viger–Latapy sampler produces connected simple graphs.
assert!(g.is_simple(true).unwrap());
assert!(g.is_connected(Connectedness::Weak).unwrap());
Source

pub fn k_regular_game( num_vertices: usize, k: usize, directed: bool, multiple: bool, ) -> Result<Graph>

Generates a random k-regular graph: every vertex has degree k (or out- and in-degree k in the directed case).

For undirected graphs, num_vertices * k must be even. With multiple = false the result is simple. The sampling is not uniform (it relies on Graph::degree_sequence_game).

Time complexity: O(|V| + |E|) if multiple is true, unknown otherwise.

See also Graph::ring and Graph::square_lattice for deterministic regular graphs.

Binds igraph_k_regular_game.

§Errors

ErrorKind::InvalidValue e.g. for an odd number of vertices and odd k in an undirected graph.

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let g = Graph::k_regular_game(10, 3, false, false).unwrap();
assert_eq!(g.degree(.., NeighborMode::All, Loops::Twice).unwrap(), vec![3; 10]);
assert!(Graph::k_regular_game(9, 3, false, false).is_err());
Source

pub fn static_fitness_game( num_edges: usize, fitness_out: &[f64], fitness_in: Option<&[f64]>, allowed_edge_types: impl Into<AllowedEdgeTypes>, ) -> Result<Graph>

Generates a non-growing random graph with edge probabilities proportional to vertex fitness scores.

The graph has fitness_out.len() vertices and exactly num_edges edges. Pairs (i, j) are drawn with probabilities proportional to the fitnesses (out-fitness of i and in-fitness of j for directed graphs, which are created when fitness_in is given) and connected unless forbidden by allowed_edge_types. The expected degrees are proportional to the fitnesses (exactly so when loops and multi-edges are allowed). This is the model of Goh, Kahng and Kim (2001).

Time complexity: O(|V| + |E| log |E|).

See also Graph::static_power_law_game (power-law fitnesses) and Graph::chung_lu_game (independent edges with prescribed expected degrees).

Binds igraph_static_fitness_game.

§Errors

ErrorKind::InvalidValue for negative or non-finite fitnesses, mismatched lengths, requesting edges when every fitness is zero, or more edges than the non-zero fitnesses allow in a graph without multi-edges. The fitnesses are validated on the Rust side: igraph 1.0.1 accepts a negative out- or in-fitness in the directed case (as long as the other vector is non-negative) and then creates edges to non-existent vertices, and loops forever on infinite or NaN fitnesses, or on finite ones whose sum is infinite (also rejected). ErrorKind::Overflow for more than i64::MAX / 2 edges (igraph 1.0.1 would overflow computing 2 * num_edges and abort).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// Vertex 3 has zero fitness: it stays isolated.
let g = Graph::static_fitness_game(3, &[1.0, 2.0, 3.0, 0.0], None, EdgeTypeSw::Simple).unwrap();
assert_eq!(g.ecount(), 3);
assert_eq!(g.degree_of(3, NeighborMode::All, Loops::Twice).unwrap(), 0);
Source

pub fn static_power_law_game( num_vertices: usize, num_edges: usize, exponent_out: f64, exponent_in: Option<f64>, allowed_edge_types: impl Into<AllowedEdgeTypes>, finite_size_correction: bool, ) -> Result<Graph>

Generates a non-growing random graph with expected power-law degree distributions.

Uses Graph::static_fitness_game with fitnesses i^(-alpha) for i = 1, ..., n, where alpha = 1 / (gamma - 1) and gamma is the exponent: vertex v (counting from zero) gets (n - v)^(-alpha), so the highest-numbered vertices are the hubs (the finite size correction adds a constant offset to i). Pass exponent_in = Some(gamma_in) for a directed graph (the in-fitnesses are shuffled to avoid in/out correlations), None for an undirected one. Exponents must be at least 2 (f64::INFINITY gives an Erdős–Rényi graph). finite_size_correction applies the correction of Cho et al. that reduces finite size effects for exponents below 3.

Time complexity: O(|V| + |E| log |E|).

See also power_law_fit to estimate the exponent back from the degrees.

Binds igraph_static_power_law_game.

§Errors

ErrorKind::InvalidValue for exponents below 2 (or NaN), or too many edges for a simple graph; ErrorKind::Overflow for more than i64::MAX / 2 edges.

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let g = Graph::static_power_law_game(1000, 2000, 2.5, None, EdgeTypeSw::Simple, true).unwrap();
assert_eq!((g.vcount(), g.ecount()), (1000, 2000));
assert!(Graph::static_power_law_game(10, 10, 1.5, None, EdgeTypeSw::Simple, false).is_err());
Source

pub fn chung_lu_game( out_weights: &[f64], in_weights: Option<&[f64]>, loops: bool, variant: ChungLuVariant, ) -> Result<Graph>

Samples a graph from the Chung–Lu model, i.e. with prescribed expected degrees.

Each pair i, j is connected independently with a probability depending on q_ij = w_i w_j / S, where w are the vertex weights (out-weights out_weights and in-weights in_weights in the directed case, created when in_weights is given) and S is their sum. The ChungLuVariant selects p_ij = min(q_ij, 1) (Original, where the expected degrees equal the weights when loops are allowed), q_ij / (1 + q_ij) (MaxEnt) or 1 - exp(-q_ij) (Nr). loops controls whether self-loops may be created. Experimental in igraph.

Time complexity: O(|V| + |E|).

See also Graph::static_fitness_game (fixed number of edges) and Graph::degree_sequence_game (exact degrees).

Binds igraph_chung_lu_game.

§Errors

ErrorKind::InvalidValue for negative or non-finite weights, or in/out weights with different lengths or sums. Also, on the Rust side, when the sum of the weights is infinite or the product of the largest out- and in-weight overflows: igraph 1.0.1 would compute NaN probabilities and loop forever.

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let weights = vec![2.0; 500];
let g = Graph::chung_lu_game(&weights, None, true, ChungLuVariant::Original).unwrap();
// With loops allowed, the expected degrees are exactly the weights.
assert!((g.mean_degree(true).unwrap() - 2.0).abs() < 0.3);
Source

pub fn watts_strogatz_game( dim: usize, size: usize, nei: usize, p: f64, allowed_edge_types: impl Into<AllowedEdgeTypes>, ) -> Result<Graph>

Generates a Watts–Strogatz small-world graph.

A periodic dim-dimensional lattice with size vertices along each dimension is built, every vertex is connected to its neighbors within nei steps, and then both endpoints of each edge are rewired with probability p (as in Graph::rewire_edges). Note that this differs from the original model, which rewires one endpoint only: for p = 1 the result is a G(n, m) graph.

Time complexity: O(|V| d^o + |E|), d average degree, o = nei.

See also Graph::square_lattice (the unrewired lattice), Graph::average_path_length and Graph::transitivity_avglocal_undirected (the two quantities of the small-world phenomenon).

Binds igraph_watts_strogatz_game.

§Errors

ErrorKind::InvalidValue if dim or size is zero, or p is not in [0, 1] (NaN included).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// A ring of 100 vertices, each linked to 2 neighbors on each side.
let g = Graph::watts_strogatz_game(1, 100, 2, 0.05, EdgeTypeSw::Simple).unwrap();
assert_eq!((g.vcount(), g.ecount()), (100, 200));
// Without rewiring, the 1-dimensional lattice with nei = 1 is a cycle.
let c = Graph::watts_strogatz_game(1, 10, 1, 0.0, EdgeTypeSw::Simple).unwrap();
assert!(c.isomorphic(&Graph::ring(10, false, false, true).unwrap()).unwrap());
Source

pub fn rewire_edges( &mut self, prob: f64, allowed_edge_types: impl Into<AllowedEdgeTypes>, ) -> Result<()>

Rewires the edges of the graph in place, with constant probability.

Each endpoint of each edge is moved to a uniformly random vertex with probability prob (in [0, 1]), respecting allowed_edge_types. The number of vertices and edges is preserved (and the directedness).

Time complexity: O(|V| + |E|).

Unlike Graph::rewire, this does not preserve the degrees.

Binds igraph_rewire_edges.

§Errors

ErrorKind::InvalidValue if prob is not in [0, 1] (NaN included), or if prob > 0 and the graph has a single vertex and some edges but self-loops are not allowed (there is no other vertex to move an endpoint to; igraph 1.0.1 crashes with a division by zero there, so the Rust side rejects it).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let mut g = Graph::ring(4, false, false, true).unwrap();
let before = g.edge_list();
g.rewire_edges(0.0, EdgeTypeSw::Simple).unwrap(); // probability 0: no change
assert_eq!(g.edge_list(), before);
g.rewire_edges(1.0, EdgeTypeSw::Simple).unwrap();
assert_eq!(g.ecount(), 4);
assert!(g.is_simple(true).unwrap());
Source

pub fn rewire_directed_edges( &mut self, prob: f64, loops: bool, mode: NeighborMode, ) -> Result<()>

Rewires one chosen endpoint of the directed edges in place, with constant probability.

With mode = Out the target of each edge is rewired (the out-degree sequence is preserved), with mode = In the source (the in-degree sequence is preserved); mode = All and undirected graphs fall back to Graph::rewire_edges. loops allows self-loops. The result may contain multi-edges.

Time complexity: O(|E|).

Binds igraph_rewire_directed_edges.

§Errors

ErrorKind::InvalidValue if prob is not in [0, 1] (NaN included), or if prob > 0, loops is false and the graph has a single vertex and some edges (igraph 1.0.1 crashes with a division by zero there, so the Rust side rejects it).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let mut g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2), (3, 0)], 4, true).unwrap();
let outdeg = g.degree(.., NeighborMode::Out, Loops::Twice).unwrap();
g.rewire_directed_edges(1.0, false, NeighborMode::Out).unwrap();
assert_eq!(g.degree(.., NeighborMode::Out, Loops::Twice).unwrap(), outdeg);
Source

pub fn forest_fire_game( num_vertices: usize, fw_prob: f64, bw_factor: f64, ambs: usize, directed: bool, ) -> Result<Graph>

Generates a growing network with the forest fire model of Leskovec, Kleinberg and Faloutsos.

Each new vertex cites ambs uniformly chosen ambassadors; then, for each cited vertex v, it “burns” (cites) a geometrically distributed number of v’s not-yet-cited out-neighbors (mean p / (1 - p), p = fw_prob) and in-neighbors (backward probability bw_factor * fw_prob), recursively. The model reproduces heavy tailed degrees, community structure, densification and shrinking diameters.

Time complexity: TODO in igraph (roughly proportional to the number of burned vertices).

Binds igraph_forest_fire_game.

§Errors

ErrorKind::InvalidValue unless 0 <= fw_prob < 1 and 0 <= bw_factor * fw_prob < 1 (NaN values are rejected on the Rust side, igraph would silently accept them).

§Examples
use igraph::prelude::*;

// With no burning and more ambassadors than vertices, every new vertex
// cites all the previous ones: a transitive tournament.
let g = Graph::forest_fire_game(5, 0.0, 0.0, 100, true).unwrap();
assert_eq!(g.ecount(), 10);
assert!(g.is_dag().unwrap());
Source

pub fn sbm_game( pref_matrix: &Matrix, block_sizes: &[i64], directed: bool, allowed_edge_types: impl Into<AllowedEdgeTypes>, ) -> Result<Graph>

Samples a graph from a stochastic block model (SBM).

Vertices are split into consecutive blocks of sizes block_sizes (vertex ids follow the block order); a vertex of block i and one of block j are connected with probability pref_matrix[(i, j)] (the expected edge multiplicity when multi-edges are allowed). The preference matrix must be square, and symmetric for undirected graphs.

Time complexity: O(|V| + |E| + k²), k the number of blocks.

See also Graph::hsbm_game (hierarchical version), Graph::preference_game (random block assignment) and community detection, e.g. Graph::community_multilevel, to recover the blocks.

Binds igraph_sbm_game.

§Errors

ErrorKind::InvalidValue for a non-square or (undirected) non-symmetric matrix, entries out of range (probabilities in [0, 1], or non-negative expected multiplicities with multi-edges) or NaN, or negative block sizes or a number of blocks not matching the matrix. With multi-edges, expected multiplicities that are infinite or above about 9e15 are also rejected on the Rust side (igraph 1.0.1 silently returns no edges for the former and loops forever on the latter); ErrorKind::Overflow if the block sizes overflow when summed (checked on the Rust side, before igraph sums them unchecked).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// Two cliques of 3 vertices, no edges in between.
let pref = Matrix::from_rows(&[[1.0, 0.0], [0.0, 1.0]]).unwrap();
let g = Graph::sbm_game(&pref, &[3, 3], false, EdgeTypeSw::Simple).unwrap();
assert_eq!(g.ecount(), 6);
assert!(g.edge_list().iter().all(|&(a, b)| (a < 3) == (b < 3)));
Source

pub fn hsbm_game( num_vertices: usize, block_size: usize, rho: &[f64], c: &Matrix, p: f64, ) -> Result<Graph>

Samples an undirected graph from the hierarchical stochastic block model.

The num_vertices vertices are split into blocks of block_size vertices (num_vertices / block_size must be an integer). Within each block, vertices form clusters with sizes given by the fractions rho (summing to 1, with rho[i] * block_size integral), and two vertices of clusters i and j of the same block are connected with probability c[(i, j)] (c square and symmetric). Vertices of different blocks are connected with probability p.

Binds igraph_hsbm_game.

§Errors

ErrorKind::InvalidValue if num_vertices or block_size is zero, block_size does not divide num_vertices, rho does not sum to one or gives non-integral cluster sizes, c is not a symmetric rho.len() × rho.len() matrix of probabilities, or p is not in [0, 1] (NaN values are rejected on the Rust side, igraph would silently accept them), or num_vertices is 2^53 or more (cluster sizes are computed in floating point).

§Examples
use igraph::prelude::*;

// One block, clusters of 6 and 4 vertices, only inter-cluster edges:
// the complete bipartite graph K(6, 4).
let c = Matrix::from_rows(&[[0.0, 1.0], [1.0, 0.0]]).unwrap();
let g = Graph::hsbm_game(10, 10, &[0.6, 0.4], &c, 0.0).unwrap();
assert_eq!(g.ecount(), 24);
Source

pub fn hsbm_list_game( num_vertices: usize, block_sizes: &[i64], rhos: &[Vec<f64>], cs: &[Matrix], p: f64, ) -> Result<Graph>

Hierarchical stochastic block model, general version with blocks of different shapes.

Like Graph::hsbm_game, but block b has block_sizes[b] vertices, cluster fractions rhos[b] and cluster connection matrix cs[b]. Vertices of different blocks are connected with probability p.

Binds igraph_hsbm_list_game.

§Errors

ErrorKind::InvalidValue if the three lists are empty or have different lengths, a block size is not positive, the sizes do not sum to num_vertices, a rho/c pair is invalid (as in Graph::hsbm_game), p is not in [0, 1], or num_vertices or a block size is 2^53 or more; ErrorKind::Overflow if the block sizes overflow when summed.

§Examples
use igraph::prelude::*;

// A block of 3 vertices forming a triangle, and a block of 4 whose two
// clusters of 2 are fully connected to each other: K3 + C4.
let one = Matrix::from_rows(&[[1.0]]).unwrap();
let cross = Matrix::from_rows(&[[0.0, 1.0], [1.0, 0.0]]).unwrap();
let g = Graph::hsbm_list_game(7, &[3, 4], &[vec![1.0], vec![0.5, 0.5]], &[one, cross], 0.0)
    .unwrap();
assert_eq!(g.ecount(), 3 + 4);
assert_eq!(g.connected_components(Connectedness::Weak).unwrap().count, 2);
Source

pub fn preference_game( num_vertices: usize, type_dist: Option<&[f64]>, fixed_sizes: bool, pref_matrix: &Matrix, directed: bool, loops: bool, ) -> Result<TypedGraph>

Generates a graph with vertex types and type-dependent connection preferences (a block model with random block assignment).

Each of the num_vertices vertices gets a type drawn from type_dist (uniform when None), then each vertex pair is connected with probability pref_matrix[(type_u, type_v)]. The number of types is the size of the square pref_matrix, whose entries must be probabilities in [0, 1]; it must be symmetric for undirected graphs. With fixed_sizes = true, type_dist gives the exact number of vertices of each type (whole numbers; equal groups when None, the first num_vertices % types groups getting one extra vertex), and the vertices are assigned to the types in order. loops allows self-loops.

Time complexity: O(|V| + |E|).

See also Graph::sbm_game (fixed, consecutive blocks) and Graph::asymmetric_preference_game.

Binds igraph_preference_game.

§Errors

ErrorKind::InvalidValue for an empty or non-square pref_matrix, entries outside [0, 1], a non-symmetric matrix for undirected graphs, a type_dist of the wrong length or with negative or non-finite entries, a random type_dist (fixed_sizes = false) without any positive entry, or fixed sizes that are not whole numbers summing to num_vertices. The finiteness, positivity and integrality checks are done on the Rust side: igraph 1.0.1 loops forever on infinite weights, aborts on all-zero weights and leaves vertex types uninitialized for fractional sizes.

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// Two fixed groups of 5, connected only across groups: bipartite.
let pref = Matrix::from_rows(&[[0.0, 1.0], [1.0, 0.0]]).unwrap();
let r = Graph::preference_game(10, Some(&[5.0, 5.0]), true, &pref, false, false).unwrap();
assert_eq!(r.graph.ecount(), 25);
assert_eq!(r.types.iter().filter(|&&t| t == 0).count(), 5);
assert!(r.graph.is_bipartite().unwrap());
Source

pub fn asymmetric_preference_game( num_vertices: usize, type_dist_matrix: Option<&Matrix>, pref_matrix: &Matrix, loops: bool, ) -> Result<AsymmetricTypedGraph>

Generates a directed graph with asymmetric vertex types and connection preferences.

Every vertex gets an out-type and an in-type, drawn from the joint distribution type_dist_matrix (independent uniform types when None); then each ordered pair (u, v) is connected with probability pref_matrix[(out_type_u, in_type_v)]. The numbers of out- and in-types are the numbers of rows and columns of pref_matrix. loops allows self-loops.

Time complexity: O(|V| + |E|).

Binds igraph_asymmetric_preference_game.

§Errors

ErrorKind::InvalidValue for an empty pref_matrix, entries outside [0, 1], or a type_dist_matrix of the wrong shape, with negative or non-finite entries or without a positive one (the last two are checked on the Rust side: igraph 1.0.1 loops forever on infinite weights and aborts on all-zero ones).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// Out-type 0 vertices link to every in-type 1 vertex, nothing else.
let pref = Matrix::from_rows(&[[0.0, 1.0], [0.0, 0.0]]).unwrap();
let r = Graph::asymmetric_preference_game(20, None, &pref, false).unwrap();
for (u, v) in r.graph.edge_list() {
    assert_eq!((r.out_types[u as usize], r.in_types[v as usize]), (0, 1));
}
Source

pub fn callaway_traits_game( num_vertices: usize, edges_per_step: usize, type_dist: Option<&[f64]>, pref_matrix: &Matrix, directed: bool, ) -> Result<TypedGraph>

Simulates a growing network with vertex types: the model of Callaway, Hopcroft, Kleinberg, Newman and Strogatz.

In each time step a vertex is added, with a type drawn from type_dist (uniform when None); then edges_per_step times, two uniformly random vertices are picked and connected with probability pref_matrix[(type_u, type_v)]. The number of types is the size of the square pref_matrix. The two endpoints are drawn independently (with replacement) among all vertices added so far, so the result may contain self-loops and multi-edges; no edges are attempted in the step that adds the first vertex, hence at most (n - 1) * edges_per_step edges are created.

Time complexity: O(|V| k log |V|), k = edges_per_step.

Binds igraph_callaway_traits_game.

§Errors

ErrorKind::InvalidValue for an empty or non-square pref_matrix, entries outside [0, 1], a non-symmetric matrix for undirected graphs, or a type_dist of the wrong length, with negative or non-finite entries or without positive ones (infinite weights, on which igraph 1.0.1 loops forever, are rejected on the Rust side).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// Two types that only connect among themselves.
let pref = Matrix::from_rows(&[[1.0, 0.0], [0.0, 1.0]]).unwrap();
let r = Graph::callaway_traits_game(50, 2, None, &pref, false).unwrap();
assert!(r.graph.ecount() <= 49 * 2);
assert!(r.graph.edge_list().iter().all(|&(u, v)| r.types[u as usize] == r.types[v as usize]));
Source

pub fn establishment_game( num_vertices: usize, k: usize, type_dist: Option<&[f64]>, pref_matrix: &Matrix, directed: bool, ) -> Result<TypedGraph>

Generates a graph with a simple growing model with vertex types (the establishment game).

In each time step a vertex with a random type (from type_dist, uniform when None) is added, and it tries to connect to k uniformly chosen existing vertices; each connection succeeds with probability pref_matrix[(type_new, type_old)]. The k candidates are distinct, so the result is simple; the first k vertices have too few predecessors and make no connection attempts. Edges point from the new vertex to the older one.

Time complexity: O(|V| k log |V|).

Binds igraph_establishment_game.

§Errors

ErrorKind::InvalidValue for an empty or non-square pref_matrix, entries outside [0, 1], a non-symmetric matrix for undirected graphs, or a type_dist of the wrong length, with negative or non-finite entries or without positive ones (infinite weights, on which igraph 1.0.1 loops forever, are rejected on the Rust side).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// A single type whose connections always succeed: after the first
// `k` vertices, every vertex cites exactly `k` older ones.
let always = Matrix::from_rows(&[[1.0]]).unwrap();
let r = Graph::establishment_game(20, 3, None, &always, true).unwrap();
assert_eq!(r.graph.ecount(), (20 - 3) * 3);
assert!(r.graph.is_simple(true).unwrap() && r.graph.is_dag().unwrap());
Source

pub fn grg_game( num_vertices: usize, radius: f64, torus: bool, ) -> Result<GeometricGraph>

Generates a geometric random graph.

num_vertices points are dropped uniformly in the unit square (on a torus when torus is true), and pairs strictly closer than radius are connected (so a zero, negative or NaN radius gives no edges). Vertices are numbered by increasing x coordinate. The coordinates are returned in the GeometricGraph.

Time complexity: less than O(|V|² + |E|).

See also Graph::nearest_neighbor_graph, which builds the same kind of graph (with a cutoff distance) from given points, and Graph::spatial_edge_lengths to obtain Euclidean edge weights.

Binds igraph_grg_game.

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let r = Graph::grg_game(100, 0.2, false).unwrap();
for (u, v) in r.graph.edge_list() {
    let (u, v) = (u as usize, v as usize);
    assert!((r.x[u] - r.x[v]).hypot(r.y[u] - r.y[v]) < 0.2);
}
Source

pub fn lastcit_game( num_vertices: usize, edges_per_node: usize, agebins: usize, preference: &[f64], directed: bool, ) -> Result<Graph>

Simulates a citation network where the attractiveness of a vertex depends on the time since it was last cited.

In each step a vertex is added and cites edges_per_node vertices. Time is binned into agebins bins of width n / agebins + 1; preference[b] is the attractiveness of vertices last cited b bins ago, and the last element (preference[agebins]) is that of vertices never cited, which must be positive. So preference has length agebins + 1. Multi-edges may appear when edges_per_node > 1.

Time complexity: O(|V| a + |E| log |V|), a = agebins.

Binds igraph_lastcit_game.

§Errors

ErrorKind::InvalidValue if agebins is zero, preference does not have agebins + 1 entries, has negative or non-finite entries, or its last entry is not positive, or num_vertices exceeds igraph’s maximum i64::MAX - 1.

§Examples
use igraph::prelude::*;

// Only never-cited vertices are attractive: a path.
let g = Graph::lastcit_game(9, 1, 1, &[0.0, 1.0], false).unwrap();
let path: Vec<_> = (0..8).map(|i| (i, i + 1)).collect();
let mut edges: Vec<_> = g.edge_list().into_iter().map(|(a, b)| (a.min(b), a.max(b))).collect();
edges.sort();
assert_eq!(edges, path);
Source

pub fn cited_type_game( types: &[i64], pref: &[f64], edges_per_step: usize, directed: bool, ) -> Result<Graph>

Simulates a citation network where the attractiveness of a vertex depends on its type (category).

The graph has types.len() vertices; vertex v has type types[v] (numbered from zero). In each step one vertex is added and cites edges_per_step older vertices, chosen with probability proportional to pref[type] (pref must cover all types). Multi-edges may appear. As long as no earlier vertex has a positive attractiveness, igraph lets the new vertex cite itself, i.e. it creates self-loops (unlike the other citation games, which then pick a uniformly random older vertex).

Time complexity: O((|V| + |E|) log |V|).

Binds igraph_cited_type_game.

§Errors

ErrorKind::InvalidValue for negative types, negative or non-finite preferences, or types not covered by pref (non-finite preferences, which igraph 1.0.1 accepts and then loops forever or creates only self-loops, and types >= pref.len() are rejected on the Rust side); ErrorKind::Overflow if 2 × types.len() × edges_per_step overflows an i64.

§Examples
use igraph::prelude::*;

// Only type 0 (vertex 0) is attractive: a star of double edges.
let g = Graph::cited_type_game(&[0, 1, 1, 1, 1], &[1.0, 0.0], 2, true).unwrap();
assert_eq!(g.ecount(), 8);
assert!(g.edge_list().iter().all(|&(_, to)| to == 0));

// Nobody is attractive: every vertex but the first cites itself.
let g = Graph::cited_type_game(&[0, 0, 0], &[0.0], 1, true).unwrap();
assert_eq!(g.edge_list(), [(1, 1), (2, 2)]);
Source

pub fn citing_cited_type_game( types: &[i64], pref: &Matrix, edges_per_step: usize, directed: bool, ) -> Result<Graph>

Simulates a citation network where the probability of a citation depends on the types of both the citing and the cited vertex.

Like Graph::cited_type_game, but pref is a square matrix: pref[(i, j)] is the attractiveness of a type j vertex for a citing vertex of type i.

Time complexity: O((|V| + |E|) log |V|).

Binds igraph_citing_cited_type_game.

The matrix must be exactly t × t, where t = max(types) + 1. While no earlier vertex is attractive for the citing type, a uniformly random older vertex is cited.

§Errors

ErrorKind::InvalidValue for negative types, a pref that is not t × t, or negative or non-finite preferences. Negative types and types >= pref.ncol() are rejected on the Rust side: igraph 1.0.1 does not check the former and reads out of bounds, and computes max(types) + 1 unchecked.

§Examples
use igraph::prelude::*;

// From igraph's unit tests: type i only cites type i - 1 (type 0
// cites type 2, but nobody of type 2 exists yet): a path.
let line = Matrix::from_rows(&[
    [0.0, 0.0, 1.0, 0.0, 0.0],
    [1.0, 0.0, 0.0, 0.0, 0.0],
    [0.0, 1.0, 0.0, 0.0, 0.0],
    [0.0, 0.0, 1.0, 0.0, 0.0],
    [0.0, 0.0, 0.0, 1.0, 0.0],
])
.unwrap();
let g = Graph::citing_cited_type_game(&[0, 1, 2, 3, 4], &line, 1, true).unwrap();
assert_eq!(g.edge_list(), [(1, 0), (2, 1), (3, 2), (4, 3)]);
Source

pub fn simple_interconnected_islands_game( islands_n: usize, islands_size: usize, islands_pin: f64, n_inter: usize, ) -> Result<Graph>

Generates a simple graph made of interconnected islands, each a G(n, p) random graph.

There are islands_n islands of islands_size vertices each (vertex ids are consecutive within an island); every possible edge inside an island is present with probability islands_pin, and exactly n_inter edges (at most islands_size²) join each pair of islands.

Time complexity: O(|V| + |E|).

See also Graph::sbm_game, a more general planted partition model.

Binds igraph_simple_interconnected_islands_game.

§Errors

ErrorKind::InvalidValue if islands_pin is not in [0, 1] or n_inter > islands_size² (the C documentation says larger values are clamped, but igraph 1.0.1 reports an error). A NaN islands_pin is rejected on the Rust side: igraph 1.0.1 accepts it and then aborts the process. ErrorKind::Overflow if islands_size², islands_n × islands_size or n_inter × islands_n × (islands_n - 1) overflows an i64 (igraph computes them unchecked).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
// 3 complete islands of 4 vertices, 2 bridges between each pair.
let g = Graph::simple_interconnected_islands_game(3, 4, 1.0, 2).unwrap();
assert_eq!(g.ecount(), 3 * 6 + 3 * 2);
Source

pub fn correlated_game( &self, corr: f64, p: f64, permutation: Option<&[i64]>, ) -> Result<Graph>

Generates a random graph correlated with this (simple) graph.

The adjacency matrix of self is perturbed so that the Pearson correlation between the old and new adjacency matrices is corr (in [0, 1]), for a graph of density p (in the open interval (0, 1), typically the density of self). The vertices of the result are then permuted by permutation (permutation[i] is the vertex of the original graph that becomes vertex i), if given. The result is directed when self is. For corr = 0 the result is simply a fresh G(n, p) graph and the permutation is ignored (only its length is checked).

See also Graph::permute_vertices (same permutation convention) and Graph::isomorphic; correlated pairs are the standard benchmark of graph matching.

Binds igraph_correlated_game.

§Errors

ErrorKind::InvalidValue if corr is not in [0, 1], p is not in (0, 1) (NaN included), self is not simple, or permutation does not have one entry per vertex or (when corr > 0) is not a permutation of 0..n.

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let g = Graph::erdos_renyi_game_gnp(30, 0.3, false, EdgeTypeSw::Simple, false).unwrap();
// Perfect correlation reproduces the graph...
let h = g.correlated_game(1.0, 0.3, None).unwrap();
assert!(g.is_same_graph(&h).unwrap());
// ... up to the requested relabelling.
let perm: Vec<i64> = (0..30).map(|i| (i + 1) % 30).collect();
let h = g.correlated_game(1.0, 0.3, Some(&perm)).unwrap();
assert!(h.is_same_graph(&g.permute_vertices(&perm).unwrap()).unwrap());
Source

pub fn correlated_pair_game( num_vertices: usize, corr: f64, p: f64, directed: bool, permutation: Option<&[i64]>, ) -> Result<(Graph, Graph)>

Generates a pair of correlated random graphs.

The first graph is a G(n, p) graph (simple), the second one is obtained from it with Graph::correlated_game with correlation corr and optional vertex permutation. This is exactly what igraph_correlated_pair_game does (drawing the same random numbers); it is implemented here by chaining the two steps because the C function leaks the first graph when the second step fails (still the case in igraph 1.0.0 and 1.0.1), which the Rust version avoids.

Binds igraph_correlated_pair_game.

§Errors

As for Graph::correlated_game; in particular p must lie in the open interval (0, 1) (the error surfaces after the first graph has been drawn, as in C).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let (a, b) = Graph::correlated_pair_game(20, 0.0, 0.5, false, None).unwrap();
assert_eq!((a.vcount(), b.vcount()), (20, 20));
// From igraph's unit tests: correlation 1 gives two identical graphs.
let (a, b) = Graph::correlated_pair_game(10, 1.0, 0.5, true, None).unwrap();
assert!(a.is_same_graph(&b).unwrap());
Source

pub fn tree_game( num_vertices: usize, directed: bool, method: RandomTreeMethod, ) -> Result<Graph>

Generates a uniformly random labelled tree on num_vertices vertices.

RandomTreeMethod::Prufer samples uniform Prüfer sequences (undirected trees only); RandomTreeMethod::Lerw runs a loop-erased random walk on the complete graph (Wilson’s algorithm). Directed trees are oriented away from the root. For num_vertices = 0 the null graph is returned.

See also Graph::is_tree and, for deterministic trees, Graph::kary_tree.

Binds igraph_tree_game.

§Errors

ErrorKind::InvalidValue for directed Prüfer trees with at least two vertices (the Prüfer method only supports undirected trees; for 0 or 1 vertices no method is run and the empty or single-vertex graph is returned).

§Examples
use igraph::prelude::*;

rng::seed(42).unwrap();
let t = Graph::tree_game(50, false, RandomTreeMethod::Lerw).unwrap();
assert!(t.is_tree(NeighborMode::All).unwrap());
let d = Graph::tree_game(50, true, RandomTreeMethod::Lerw).unwrap();
assert!(d.is_tree(NeighborMode::Out).unwrap()); // an out-tree
Source

pub fn dot_product_game(vecs: &Matrix, directed: bool) -> Result<Graph>

Generates a random dot product graph.

Each vertex has a latent position vector, a column of vecs; two vertices are connected with probability equal to the dot product of their vectors (negative products never produce an edge, products above one always do, with a warning).

Time complexity: O(n² m), n vertices, m the vector length.

See also sample_sphere_surface and sample_dirichlet, convenient ways of drawing latent positions, and the adjacency spectral embedding (Graph::adjacency_spectral_embedding), which estimates them back.

Binds igraph_dot_product_game.

§Examples
use igraph::prelude::*;

// Orthonormal positions for vertices 0,1 vs 2: 0-1 always, 2 alone.
let vecs = Matrix::from_rows(&[[1.0, 1.0, 0.0], [0.0, 0.0, 1.0]]).unwrap();
let g = Graph::dot_product_game(&vecs, false).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1)]);
Source§

impl igraph_t

Source

pub fn isomorphic(&self, other: &Graph) -> Result<bool>

Whether self and other are isomorphic (igraph_isomorphic).

The algorithm is chosen automatically:

  1. a directed and an undirected graph are an error;
  2. if either graph has multi-edges, both are simplified and colorized and compared with VF2;
  3. graphs with different vertex or edge counts are not isomorphic;
  4. small loop-free graphs supported by isoclass (directed with 3–4 vertices, undirected with 3–6) use precomputed O(1) data;
  5. otherwise Bliss is used.

Use isomorphic_vf2 or isomorphic_bliss to obtain a mapping. Time complexity: exponential in the worst case.

See also Graph::is_same_graph, which compares labeled graphs (identical vertex ids and edges), and canonical_form to test many graphs against each other.

Binds igraph_isomorphic.

§Errors

ErrorKind::InvalidValue when one graph is directed and the other is not.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let b = Graph::from_edges(&[(0, 2), (2, 1)], 3, false).unwrap();
let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
assert!(a.isomorphic(&b).unwrap());
assert!(!a.is_same_graph(&b).unwrap()); // different labels
assert!(!a.isomorphic(&c).unwrap());
// The Petersen graph is the generalized Petersen graph GP(5, 2).
let p = Graph::famous("Petersen").unwrap();
assert!(p.isomorphic(&Graph::generalized_petersen(5, 2).unwrap()).unwrap());
Source

pub fn subisomorphic(&self, pattern: &Graph) -> Result<bool>

Whether pattern is isomorphic to a (not necessarily induced) subgraph of self (igraph_subisomorphic).

self is the larger graph. Currently this always uses VF2, and so does not support non-simple graphs (self-loops are an error, multi-edges give wrong results). Time complexity: exponential.

See also subisomorphic_lad (note its reversed argument order), which is often faster and can look for induced subgraphs, and Graph::clique_number for the special case of complete patterns.

Binds igraph_subisomorphic.

§Errors

ErrorKind::InvalidValue when the graphs differ in directedness or have self-loops.

§Examples
use igraph::prelude::*;
let square = Graph::ring(4, false, false, true).unwrap();
let path3 = Graph::ring(3, false, false, false).unwrap();
let triangle = Graph::full(3, false, false).unwrap();
assert!(square.subisomorphic(&path3).unwrap());
assert!(!square.subisomorphic(&triangle).unwrap());
Source

pub fn simplify_and_colorize(&self) -> Result<ColorizedGraph>

Turns a multigraph into a colored simple graph (igraph_simplify_and_colorize).

Multi-edges are merged into single edges whose color is their multiplicity, and self-loops are removed, the color of each vertex being its number of self-loops. The result is suitable for the VF2 functions (which only support simple graphs but honour colors): two multigraphs are isomorphic iff their colorized versions are isomorphic as colored graphs.

See also Graph::simplify, which removes multi-edges and self-loops in place without recording them, and Graph::count_multiple.

Binds igraph_simplify_and_colorize.

§Examples
use igraph::prelude::*;
// A double edge 0-1 plus a loop on 1.
let g = Graph::from_edges(&[(0, 1), (0, 1), (1, 1)], 2, false).unwrap();
let c = g.simplify_and_colorize().unwrap();
assert_eq!(c.graph.ecount(), 1);
assert_eq!(c.vertex_color, vec![0, 1]);
assert_eq!(c.edge_color, vec![2]);
Source

pub fn isomorphic_vf2( &self, other: &Graph, opts: &mut Vf2Options<'_>, ) -> Result<Option<IsoMapping>>

Isomorphism test with VF2, returning a mapping (igraph_isomorphic_vf2).

Returns Some(mapping) if self and other are isomorphic under the colors and compatibility predicates of opts (see Vf2Options), None otherwise. Both graphs must be simple: self-loops are rejected with an error, multi-edges are not detected and give wrong results (use simplify_and_colorize first). Use subisomorphic_vf2 for subgraphs. Time complexity: exponential.

Binds igraph_isomorphic_vf2.

§Errors

ErrorKind::InvalidValue on directedness mismatch, self-loops, or color vectors of the wrong length.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
let h = Graph::from_edges(&[(2, 0), (1, 2)], 3, true).unwrap();
let m = g.isomorphic_vf2(&h, &mut Vf2Options::new()).unwrap().unwrap();
assert_eq!(m.map12, vec![1, 2, 0]); // 0->1->2 becomes 1->2->0
assert_eq!(m.map21, vec![2, 0, 1]);
Source

pub fn count_isomorphisms_vf2( &self, other: &Graph, opts: &mut Vf2Options<'_>, ) -> Result<usize>

Number of isomorphisms between self and other with VF2 (igraph_count_isomorphisms_vf2).

With other == self this is the number of automorphisms (but count_automorphisms is much faster). Time complexity: exponential.

Binds igraph_count_isomorphisms_vf2.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let triangle = Graph::full(3, false, false).unwrap();
assert_eq!(triangle.count_isomorphisms_vf2(&triangle, &mut Vf2Options::new()).unwrap(), 6);
Source

pub fn get_isomorphisms_vf2( &self, other: &Graph, opts: &mut Vf2Options<'_>, ) -> Result<Vec<Vec<VertexId>>>

All the isomorphisms between self and other with VF2 (igraph_get_isomorphisms_vf2).

Each mapping is given as map21, i.e. m[v] is the vertex of self matched to vertex v of other. The result is empty if the graphs are not isomorphic. Time complexity: exponential.

Binds igraph_get_isomorphisms_vf2.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let p = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let mut maps = p.get_isomorphisms_vf2(&p, &mut Vf2Options::new()).unwrap();
maps.sort();
assert_eq!(maps, vec![vec![0, 1, 2], vec![2, 1, 0]]);
Source

pub fn get_isomorphisms_vf2_callback( &self, other: &Graph, opts: &mut Vf2Options<'_>, handler: impl FnMut(&[VertexId], &[VertexId]) -> bool, ) -> Result<()>

Streams the isomorphisms between self and other to a closure (igraph_get_isomorphisms_vf2_callback).

handler(map12, map21) is called for each isomorphism found; return true to continue the search or false to stop it (which is not an error). A panic in the handler aborts the search and is resumed when this function returns. Time complexity: exponential.

Binds igraph_get_isomorphisms_vf2_callback.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let k4 = Graph::full(4, false, false).unwrap();
// Take the first three automorphisms of K4 only (out of 24).
let mut found = vec![];
k4.get_isomorphisms_vf2_callback(&k4, &mut Vf2Options::new(), |m12, _m21| {
    found.push(m12.to_vec());
    found.len() < 3
})
.unwrap();
assert_eq!(found.len(), 3);
Source

pub fn subisomorphic_vf2( &self, pattern: &Graph, opts: &mut Vf2Options<'_>, ) -> Result<Option<IsoMapping>>

Subgraph isomorphism test with VF2, returning a mapping (igraph_subisomorphic_vf2).

Decides whether pattern (the smaller graph) is isomorphic to a subgraph of self (the larger graph); the subgraph need not be induced. Returns Some(mapping) with mapping.map21[v] the vertex of self matched to vertex v of pattern, and mapping.map12[u] the vertex of pattern matched to u (or -1). Time complexity: exponential.

Binds igraph_subisomorphic_vf2.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let big = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
let m = big.subisomorphic_vf2(&triangle, &mut Vf2Options::new()).unwrap().unwrap();
let mut hit = m.map21.clone();
hit.sort();
assert_eq!(hit, vec![0, 1, 2]);
assert_eq!(m.map12[3], -1); // vertex 3 is not in the match
Source

pub fn count_subisomorphisms_vf2( &self, pattern: &Graph, opts: &mut Vf2Options<'_>, ) -> Result<usize>

Number of subgraph isomorphisms from pattern into self with VF2 (igraph_count_subisomorphisms_vf2).

Every embedding is counted, so each copy of pattern in self is counted as many times as pattern has automorphisms. Time complexity: exponential.

Binds igraph_count_subisomorphisms_vf2.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let k4 = Graph::full(4, false, false).unwrap();
let triangle = Graph::full(3, false, false).unwrap();
// 4 triangles in K4, each with 6 automorphisms.
assert_eq!(k4.count_subisomorphisms_vf2(&triangle, &mut Vf2Options::new()).unwrap(), 24);
Source

pub fn get_subisomorphisms_vf2( &self, pattern: &Graph, opts: &mut Vf2Options<'_>, ) -> Result<Vec<Vec<VertexId>>>

All the subgraph isomorphisms from pattern into self with VF2 (igraph_get_subisomorphisms_vf2).

Each mapping m is a map21: m[v] is the vertex of self matched to vertex v of pattern. Time complexity: exponential.

Binds igraph_get_subisomorphisms_vf2.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let star = Graph::star(4, StarMode::Undirected, 0).unwrap();
let edge = Graph::full(2, false, false).unwrap();
let maps = star.get_subisomorphisms_vf2(&edge, &mut Vf2Options::new()).unwrap();
assert_eq!(maps.len(), 6); // 3 edges x 2 orientations
Source

pub fn get_subisomorphisms_vf2_callback( &self, pattern: &Graph, opts: &mut Vf2Options<'_>, handler: impl FnMut(&[VertexId], &[VertexId]) -> bool, ) -> Result<()>

Streams the subgraph isomorphisms from pattern into self to a closure (igraph_get_subisomorphisms_vf2_callback).

handler(map12, map21) is called for each embedding found (see IsoMapping for the meaning of the maps); return true to go on or false to stop the search early. A panic in the handler aborts the search and is resumed when this function returns. Time complexity: exponential.

Binds igraph_get_subisomorphisms_vf2_callback.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::Vf2Options;
let c5 = Graph::ring(5, false, false, true).unwrap();
let p3 = Graph::ring(3, false, false, false).unwrap();
// Collect the middle vertices of the 2-paths of the pentagon.
let mut centers = std::collections::BTreeSet::new();
c5.get_subisomorphisms_vf2_callback(&p3, &mut Vf2Options::new(), |_, m21| {
    centers.insert(m21[1]);
    true
})
.unwrap();
assert_eq!(centers.len(), 5);
Source

pub fn subisomorphic_lad( &self, target: &Graph, domains: Option<&[Vec<VertexId>]>, induced: bool, ) -> Result<Option<Vec<VertexId>>>

Subgraph isomorphism with the LAD algorithm (igraph_subisomorphic_lad).

Note the argument order, which follows the C library: self is the (smaller) pattern and target the (larger) graph searched. Returns Some(map) with map[v] the vertex of target matched to vertex v of the pattern, or None if the pattern does not occur.

  • domains: optionally, for each pattern vertex, the list of target vertices it may be matched to (this is how LAD implements vertex colors); it must have one entry per pattern vertex.
  • induced: whether to look for induced subgraphs only (then non-edges of the pattern must map to non-edges).

Works for directed and undirected graphs without multi-edges. Time complexity: exponential.

Binds igraph_subisomorphic_lad.

§Errors

ErrorKind::InvalidValue if domains does not have one entry per pattern vertex or the graphs differ in directedness or have multi-edges, and ErrorKind::InvalidVertexId if a domain contains an id that is not a vertex of target.

§Examples
use igraph::prelude::*;
let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
let path3 = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let map = path3.subisomorphic_lad(&square, None, true).unwrap().unwrap();
assert_eq!(map.len(), 3);
// Force the pattern's middle vertex onto vertex 2 of the square.
let domains = vec![vec![0, 1, 2, 3], vec![2], vec![0, 1, 2, 3]];
let map = path3.subisomorphic_lad(&square, Some(&domains), true).unwrap().unwrap();
assert_eq!(map[1], 2);
Source

pub fn get_subisomorphisms_lad( &self, target: &Graph, domains: Option<&[Vec<VertexId>]>, induced: bool, ) -> Result<Vec<Vec<VertexId>>>

All the subgraph isomorphisms of the pattern self into target with the LAD algorithm (igraph_subisomorphic_lad with maps).

Same arguments as subisomorphic_lad; returns every mapping (pattern vertex → target vertex).

Binds igraph_subisomorphic_lad.

§Examples
use igraph::prelude::*;
let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
let path3 = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
// 4 induced 2-paths, each traversed in 2 directions.
assert_eq!(path3.get_subisomorphisms_lad(&square, None, true).unwrap().len(), 8);
Source

pub fn isomorphic_bliss( &self, other: &Graph, colors1: Option<&[i64]>, colors2: Option<&[i64]>, sh: BlissSh, ) -> Result<BlissIsomorphism>

Isomorphism test with Bliss, with mapping and statistics (igraph_isomorphic_bliss).

Both graphs are brought into canonical form with the splitting heuristic sh and compared. colors1 and colors2 are optional vertex colors (a colored vertex may only map to a vertex of the same color; if only one graph is colored, colors are ignored with a warning). Multi-edges are not supported (the result would be wrong), self-loops are. Time complexity: exponential, fast in practice.

Binds igraph_isomorphic_bliss.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::BlissSh;
let a = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let b = Graph::from_edges(&[(3, 1), (1, 0), (0, 2)], 4, false).unwrap();
let res = a.isomorphic_bliss(&b, None, None, BlissSh::Fl).unwrap();
assert!(res.is_isomorphic());
assert_eq!(res.info1.group_size, "2");
Source

pub fn canonical_permutation( &self, colors: Option<&[i64]>, ) -> Result<Vec<VertexId>>

The canonical labeling of the graph (igraph_canonical_permutation).

Returns labeling, where labeling[i] is the vertex of self that becomes vertex i of the canonical form (since igraph 1.0; this is the convention of igraph_permute_vertices, and the inverse of the convention of igraph 0.10). Use invert_permutation to get, for each vertex, its position in the canonical form. Relabeling two graphs by their canonical labelings yields identical graphs if and only if the graphs are isomorphic (see canonical_form). colors optionally assigns vertex colors, which must be preserved; colored graphs are isomorphic if and only if, in addition, their colors relabeled the same way (colors[labeling[i]]) are equal. Multi-edges are not supported. Uses Bliss with sensible defaults; see canonical_permutation_bliss to tune it.

Binds igraph_canonical_permutation.

§Examples
use igraph::prelude::*;
let g = Graph::star(4, StarMode::Undirected, 0).unwrap();
let mut sorted = g.canonical_permutation(None).unwrap();
sorted.sort();
assert_eq!(sorted, vec![0, 1, 2, 3]); // it is a permutation

// The canonical form is the graph relabeled by the labeling...
let labeling = g.canonical_permutation(None).unwrap();
let relabeled = g.permute_vertices(&labeling).unwrap();
assert!(relabeled.isomorphic(&g.canonical_form(None).unwrap()).unwrap());

// ... built by hand: vertex v goes to position pos[v].
let pos = igraph::isomorphism::invert_permutation(&labeling).unwrap();
let mut edges: Vec<(i64, i64)> = g
    .edge_list()
    .into_iter()
    .map(|(a, b)| {
        let (a, b) = (pos[a as usize], pos[b as usize]);
        (a.min(b), a.max(b))
    })
    .collect();
edges.sort();
let by_hand = Graph::from_edges(&edges, 4, false).unwrap();
assert!(by_hand == g.canonical_form(None).unwrap());
Source

pub fn canonical_permutation_bliss( &self, colors: Option<&[i64]>, sh: BlissSh, ) -> Result<(Vec<VertexId>, BlissInfo)>

The canonical labeling of the graph computed by Bliss with the given splitting heuristic, together with the Bliss statistics (igraph_canonical_permutation_bliss).

See canonical_permutation.

Binds igraph_canonical_permutation_bliss.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::BlissSh;
let k3 = Graph::full(3, false, false).unwrap();
let (labeling, info) = k3.canonical_permutation_bliss(None, BlissSh::Fsm).unwrap();
assert_eq!(labeling.len(), 3);
assert_eq!(info.group_size, "6");
Source

pub fn canonical_form(&self, colors: Option<&[i64]>) -> Result<Graph>

The canonical form of the graph: the graph with its vertices permuted by its canonical labeling.

Two uncolored graphs are isomorphic if and only if their canonical forms are identical, i.e. compare equal with == (Graph::is_same_graph). This makes canonical forms perfect keys to deduplicate graphs up to isomorphism. With colors, the returned graph does not carry the colors: two colored graphs are isomorphic (as colored graphs) if and only if their canonical forms are identical and so are their colors relabeled by the canonical labelings, i.e. colors[labeling[i]] for each i (see canonical_permutation). Multi-edges are not supported (encode them with simplify_and_colorize first).

Combines igraph_canonical_permutation and igraph_permute_vertices (see Graph::permute_vertices), then sorts the edges (and, when undirected, their endpoints) so that the result does not depend on the input edge order.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let b = Graph::from_edges(&[(2, 0), (0, 3), (3, 1)], 4, false).unwrap();
assert!(a.canonical_form(None).unwrap() == b.canonical_form(None).unwrap());
Source

pub fn count_automorphisms(&self, colors: Option<&[i64]>) -> Result<f64>

Number of automorphisms of the graph (igraph_count_automorphisms).

An automorphism is an isomorphism of the graph onto itself, i.e. a symmetry. The count is returned as an f64 because it can be huge; use count_automorphisms_bliss to get the exact value as a string. colors optionally restricts to color-preserving automorphisms. Multi-edges are not supported.

Binds igraph_count_automorphisms.

§Errors

ErrorKind::Overflow if the count does not fit into an f64.

§Examples
use igraph::prelude::*;
let star = Graph::star(5, StarMode::Undirected, 0).unwrap();
assert_eq!(star.count_automorphisms(None).unwrap(), 24.0); // 4!
// The Petersen graph's symmetry group is S5, of order 120.
assert_eq!(Graph::famous("Petersen").unwrap().count_automorphisms(None).unwrap(), 120.0);
// Coloring one leaf differently leaves 3! symmetries.
assert_eq!(star.count_automorphisms(Some(&[0, 1, 1, 1, 2])).unwrap(), 6.0);
Source

pub fn count_automorphisms_bliss( &self, colors: Option<&[i64]>, sh: BlissSh, ) -> Result<BlissInfo>

Number of automorphisms computed by Bliss, returned exactly in BlissInfo::group_size (igraph_count_automorphisms_bliss).

Same semantics as count_automorphisms (optional colors, no multi-edges), with the splitting heuristic sh chosen explicitly; the returned statistics also describe the search tree. Unlike the f64 count, the decimal group_size is exact for arbitrarily large groups.

Binds igraph_count_automorphisms_bliss.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::BlissSh;
let k23 = Graph::full(23, false, false).unwrap();
let info = k23.count_automorphisms_bliss(None, BlissSh::F).unwrap();
assert_eq!(info.group_size, "25852016738884976640000"); // 23!
Source

pub fn automorphism_group( &self, colors: Option<&[i64]>, ) -> Result<Vec<Vec<VertexId>>>

Generators of the automorphism group of the graph (igraph_automorphism_group).

Each generator is a permutation of the vertices (0-based). The set may not be minimal and depends on the algorithm’s internals; every automorphism is a product of generators. colors optionally restricts to color-preserving automorphisms. Multi-edges are not supported.

Binds igraph_automorphism_group.

§Examples
use igraph::prelude::*;
let path = Graph::ring(3, false, false, false).unwrap();
assert_eq!(path.automorphism_group(None).unwrap(), vec![vec![2, 1, 0]]);
Source

pub fn automorphism_group_bliss( &self, colors: Option<&[i64]>, sh: BlissSh, ) -> Result<(Vec<Vec<VertexId>>, BlissInfo)>

Generators of the automorphism group computed by Bliss with the given splitting heuristic, with statistics (igraph_automorphism_group_bliss).

See automorphism_group.

Binds igraph_automorphism_group_bliss.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::BlissSh;
let c4 = Graph::ring(4, false, false, true).unwrap();
let (gens, info) = c4.automorphism_group_bliss(None, BlissSh::Fs).unwrap();
assert_eq!(gens.len() as u64, info.nof_generators);
assert_eq!(info.group_size, "8");
Source

pub fn isoclass(&self) -> Result<usize>

The isomorphism class of a small graph (igraph_isoclass).

Graphs with the same number of vertices and directedness are isomorphic iff they have the same class. Classes are numbered from 0 (the empty graph) to graph_count(n, directed) - 1 (the complete graph). Supported: directed graphs with 3–4 vertices, undirected graphs with 3–6 vertices. Multi-edges and self-loops are ignored. Time complexity: O(|E|).

Binds igraph_isoclass.

§Errors

ErrorKind::Unimplemented for unsupported sizes.

§Examples
use igraph::prelude::*;
let empty = Graph::new(3, false);
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
assert_eq!(empty.isoclass().unwrap(), 0);
assert_eq!(triangle.isoclass().unwrap(), 3);
Source

pub fn isoclass_subgraph<'a>( &self, vids: impl Into<VertexSelector<'a>>, ) -> Result<usize>

The isomorphism class of the subgraph induced by vids (igraph_isoclass_subgraph).

Same numbering and size limits as isoclass; each vertex may appear at most once in vids. Time complexity: O((d+n)·n), d being the average degree and n the number of vertices.

Binds igraph_isoclass_subgraph.

§Errors

ErrorKind::InvalidVertexId for invalid vertices, ErrorKind::InvalidValue if a vertex is selected twice, and ErrorKind::Unimplemented for unsupported subgraph sizes.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
assert_eq!(g.isoclass_subgraph(&[0, 1, 2]).unwrap(), 3); // triangle
assert_eq!(g.isoclass_subgraph(&[1, 2, 3]).unwrap(), 2); // path
Source

pub fn isoclass_create( size: usize, number: usize, directed: bool, ) -> Result<Graph>

Creates the canonical representative graph of an isomorphism class (igraph_isoclass_create).

size is the number of vertices (3–4 directed, 3–6 undirected) and number the class, in 0..graph_count(size, directed). This is the inverse of isoclass. Time complexity: O(|V|+|E|). Use it to draw or inspect the shapes counted by motifs_randesu.

Binds igraph_isoclass_create.

§Errors

For unsupported sizes or out-of-range class numbers.

§Examples
use igraph::prelude::*;
// Class 15 of directed triads is the complete digraph.
let g = Graph::isoclass_create(3, 15, true).unwrap();
assert_eq!(g.ecount(), 6);
assert_eq!(g.isoclass().unwrap(), 15);
Source

pub fn motifs_randesu( &self, size: usize, cut_prob: Option<&[f64]>, ) -> Result<Vec<f64>>

The motif histogram of the graph with the RAND-ESU algorithm (igraph_motifs_randesu).

Motifs are small weakly connected induced subgraphs. Entry i of the result counts the induced subgraphs on size vertices of isomorphism class i; classes that are not (weakly) connected are reported as NaN, since they are not counted. Supported sizes: 3–4 (directed), 3–6 (undirected); directed motifs are counted in directed graphs.

cut_prob optionally gives, for each of the size levels of the search tree, the probability of cutting a branch (sampling à la FANMOD, Wernicke and Rasche 2006); None performs a complete search. Cutting uses igraph’s RNG: seed it with rng::seed for reproducible samples.

To assess the significance of motif counts, compare them with those of degree-preserving randomizations, e.g. from Graph::rewire. For size 3 in directed graphs, triad_census additionally counts the unconnected triads.

Binds igraph_motifs_randesu.

§Errors

For unsupported sizes, or if cut_prob does not have length size.

§Examples
use igraph::prelude::*;
// A triangle with a pendant vertex: two 2-paths and one triangle.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let hist = g.motifs_randesu(3, None).unwrap();
assert_eq!(&hist[2..], &[2.0, 1.0]);
Source

pub fn motifs_randesu_callback( &self, size: usize, cut_prob: Option<&[f64]>, f: impl FnMut(&[VertexId], usize) -> bool, ) -> Result<()>

Finds the motifs of the graph and streams them to a closure (igraph_motifs_randesu_callback).

f(vids, isoclass) is called for every motif (weakly connected induced subgraph on size vertices) found, with its vertices and its isomorphism class; return true to continue or false to stop the search. A panic in f aborts the search and is resumed when this function returns. See motifs_randesu for size and cut_prob.

Binds igraph_motifs_randesu_callback.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let mut triangles = vec![];
g.motifs_randesu_callback(3, None, |vids, class| {
    if class == 3 {
        let mut t = vids.to_vec();
        t.sort();
        triangles.push(t);
    }
    true
})
.unwrap();
assert_eq!(triangles, vec![vec![0, 1, 2]]);
Source

pub fn motifs_randesu_no( &self, size: usize, cut_prob: Option<&[f64]>, ) -> Result<f64>

Total number of motifs, i.e. of weakly connected induced subgraphs on size vertices (igraph_motifs_randesu_no).

Unlike motifs_randesu, it does not classify the subgraphs, so arbitrarily large sizes are supported. The result is an integer returned as f64 (to avoid overflow). See motifs_randesu for cut_prob.

Binds igraph_motifs_randesu_no.

§Errors

ErrorKind::InvalidValue if size < 3 or cut_prob does not have length size.

§Examples
use igraph::prelude::*;
let k10 = Graph::full(10, false, false).unwrap();
assert_eq!(k10.motifs_randesu_no(4, None).unwrap(), 210.0); // C(10, 4)
Source

pub fn motifs_randesu_estimate( &self, size: usize, cut_prob: Option<&[f64]>, sample: MotifSample<'_>, ) -> Result<f64>

Estimates the total number of motifs of the given size (igraph_motifs_randesu_estimate).

Takes a sample of vertices (MotifSample: either a number of distinct vertices drawn uniformly at random, or an explicit list), runs the RAND-ESU enumeration rooted at each sampled vertex v (which counts the connected induced subgraphs on size vertices whose smallest vertex id is v), and multiplies the total by vcount / sample_size. With every vertex in the sample the result equals motifs_randesu_no; otherwise it depends on the vertex numbering, even for very symmetric graphs. Useful for large graphs where counting all subgraphs is too slow. See motifs_randesu for cut_prob. The random sample is drawn from the calling thread’s RNG (rng::seed). The null graph has an estimate of 0 whatever the sample.

Binds igraph_motifs_randesu_estimate.

§Errors

ErrorKind::InvalidValue if size < 3, cut_prob does not have length size, or the sample is empty or larger than the graph (MotifSample::Random); ErrorKind::InvalidVertexId if MotifSample::Vertices has invalid vertices.

§Examples
use igraph::prelude::*;
use igraph::isomorphism::MotifSample;
let k10 = Graph::full(10, false, false).unwrap();
// Sampling every vertex gives the exact count, C(10, 3).
let all: Vec<i64> = (0..10).collect();
let est = k10.motifs_randesu_estimate(3, None, MotifSample::Vertices(&all)).unwrap();
assert_eq!(est, 120.0);
// A random sample of 5 vertices gives an estimate, reproducible
// once the (per-thread) RNG is seeded.
igraph::rng::seed(42).unwrap();
let est = k10.motifs_randesu_estimate(3, None, MotifSample::Random(5)).unwrap();
assert!(est > 0.0);
igraph::rng::seed(42).unwrap();
assert_eq!(k10.motifs_randesu_estimate(3, None, MotifSample::Random(5)).unwrap(), est);
Source

pub fn dyad_census(&self) -> Result<DyadCensus>

The dyad census of the graph (igraph_dyad_census), as defined by Holland and Leinhardt (1970).

Classifies each unordered pair of vertices as mutual (edges in both directions), asymmetric (in one direction only) or null (not connected). In undirected graphs every connected pair is mutual. Multi-edges and self-loops do not matter. Time complexity: O(|V|+|E|).

See also Graph::reciprocity: with the default mode it equals 2 mutual / (2 mutual + asymmetric) on simple graphs.

Binds igraph_dyad_census.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 0), (1, 2)], 4, true).unwrap();
let d = g.dyad_census().unwrap();
assert_eq!((d.mutual, d.asymmetric, d.null), (1.0, 1.0, 4.0));
Source

pub fn triad_census(&self) -> Result<TriadCensus>

The triad census of the graph (igraph_triad_census), as defined by Davis and Leinhardt (1972).

Classifies every vertex triple into one of the 16 types of directed triads, see TriadCensus. Intended for directed graphs: undirected edges are treated as mutual (and igraph emits a warning). Note that the order differs from the isoclass order of motifs_randesu.

Binds igraph_triad_census.

§Examples
use igraph::prelude::*;
let cycle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
let t = cycle.triad_census().unwrap();
assert_eq!(t["030C"], 1.0);
assert_eq!(t.total(), 1.0);
Source

pub fn count_adjacent_triangles<'a>( &self, vids: impl Into<VertexSelector<'a>>, ) -> Result<Vec<f64>>

Number of triangles each selected vertex is part of (igraph_count_adjacent_triangles).

Edge directions and multiplicities are ignored. Returned as f64 (to avoid overflow). Time complexity: O(d²·n), d the average degree of the n queried vertices.

See also Graph::transitivity_local_undirected, the local clustering coefficient t(v) / (d(v) (d(v) - 1) / 2).

Binds igraph_count_adjacent_triangles.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
assert_eq!(g.count_adjacent_triangles(..).unwrap(), vec![1.0, 1.0, 1.0, 0.0]);
Source

pub fn list_triangles(&self) -> Result<Vec<[VertexId; 3]>>

All the triangles of the graph, each listed once as a triple of vertex ids (igraph_list_triangles).

Edge directions and multi-edges are ignored. Time complexity: O(d²·n). See also Graph::cliques for complete subgraphs of any size.

Binds igraph_list_triangles.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let mut t = g.list_triangles().unwrap();
t[0].sort();
assert_eq!(t, vec![[0, 1, 2]]);
Source

pub fn count_triangles(&self) -> Result<f64>

Total number of triangles of the graph (igraph_count_triangles).

Edge directions, multiplicities and self-loops are ignored. Returned as f64 (to avoid overflow). Time complexity: O(|V|·d²).

See also Graph::transitivity_undirected, the global clustering coefficient 3 × triangles / connected triples.

Binds igraph_count_triangles.

§Examples
use igraph::prelude::*;
let k4 = Graph::full(4, false, false).unwrap();
assert_eq!(k4.count_triangles().unwrap(), 4.0);
// Zachary's karate club has 45 triangles.
assert_eq!(Graph::famous("Zachary").unwrap().count_triangles().unwrap(), 45.0);
Source

pub fn graphlets_candidate_basis( &self, weights: &[f64], ) -> Result<GraphletBasis>

The candidate basis of the graphlet decomposition (igraph_graphlets_candidate_basis).

First step of the graphlet decomposition (Azari Soufiani and Airoldi): the cliques of the graph thresholded at decreasing edge weights, with the highest threshold at which each was found. The graph must be simple; edge directions are ignored; weights (one per edge) are mandatory.

An edgeless graph has an empty basis.

Binds igraph_graphlets_candidate_basis.

§Errors

ErrorKind::InvalidValue if weights does not have one entry per edge or the graph is not simple (ignoring directions).

§Examples
use igraph::prelude::*;
// A heavy triangle attached to a light edge.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let basis = g.graphlets_candidate_basis(&[5.0, 5.0, 5.0, 1.0]).unwrap();
assert_eq!(basis.cliques.len(), basis.thresholds.len());
assert!(basis.cliques.iter().any(|c| c.len() == 3));
Source

pub fn graphlets_project( &self, weights: &[f64], cliques: &[Vec<VertexId>], start: Option<&[f64]>, niter: usize, ) -> Result<Vec<f64>>

Projects the graph on a graphlet basis (igraph_graphlets_project).

Second step of the graphlet decomposition: fits a weight (Mu) for each clique of cliques so that the sum of the cliques, weighted, explains the edge weights, with niter iterations of the expectation-maximization algorithm. Each clique of size n is normalized by n (n + 1) / 2, so that a clique whose edges all have weight w and belong to no other clique converges to w (n - 1) / (n + 1). start optionally provides the initial weights (one per clique), otherwise all ones are used. The graph need not be the one used to compute the basis, but must have matching vertex ids.

Binds igraph_graphlets_project.

§Errors

ErrorKind::InvalidValue if weights or start have the wrong length, a clique lists a vertex twice, or the graph is not simple; ErrorKind::InvalidVertexId if a clique contains an invalid vertex.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let w = [5.0, 5.0, 5.0, 1.0];
let mu = g.graphlets_project(&w, &[vec![0, 1, 2], vec![2, 3]], None, 100).unwrap();
// Fixed points w (n - 1) / (n + 1): 5 * 2/4 for the triangle, 1 * 1/3 for the edge.
assert!((mu[0] - 2.5).abs() < 1e-3 && (mu[1] - 1.0 / 3.0).abs() < 1e-3);
Source

pub fn graphlets(&self, weights: &[f64], niter: usize) -> Result<Graphlets>

The graphlet decomposition of a weighted graph (igraph_graphlets).

Models the graph as a union of potentially overlapping dense groups (cliques), each with a weight: runs graphlets_candidate_basis, then graphlets_project with niter iterations, and sorts the graphlets by decreasing weight. The graph must be simple; edge directions are ignored. An edgeless graph has no graphlets. See also the cliques module, e.g. Graph::maximal_cliques, for unweighted dense groups.

Binds igraph_graphlets.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
let gl = g.graphlets(&[5.0, 5.0, 5.0, 1.0], 1000).unwrap();
// The heaviest graphlet is the triangle.
let mut top = gl.cliques[0].clone();
top.sort();
assert_eq!(top, vec![0, 1, 2]);
assert!(gl.weights.windows(2).all(|w| w[0] >= w[1]));
Source§

impl igraph_t

Source

pub fn layout_random(&self) -> Result<Matrix>

Places the vertices uniformly at random in the square [-1, 1]².

Uses the calling thread’s default random number generator (see rng::seed). Time complexity: O(|V|). Binds igraph_layout_random.

§Examples
use igraph::prelude::*;
let g = Graph::new(10, false);
rng::seed(1).unwrap();
let l = g.layout_random().unwrap();
assert_eq!(l.shape(), (10, 2));
assert!(l.as_slice().iter().all(|c| (-1.0..=1.0).contains(c)));
rng::seed(1).unwrap();
assert_eq!(g.layout_random().unwrap(), l);
Source

pub fn layout_random_3d(&self) -> Result<Matrix>

Places the vertices uniformly at random in the cube [-1, 1]³.

The 3D version of layout_random. Time complexity: O(|V|). Binds igraph_layout_random_3d.

Source

pub fn layout_circle<'a>( &self, order: impl Into<VertexSelector<'a>>, ) -> Result<Matrix>

Places the vertices uniformly on the unit circle, in the given order.

The k-th vertex of order (out of m selected vertices) is placed at angle 2πk/m; vertices not in order are placed at the origin. Pass .. to place all vertices in increasing id order. This is the natural drawing of Graph::ring. Time complexity: O(|V|). Binds igraph_layout_circle.

§Errors

ErrorKind::InvalidVertexId if order contains invalid vertices.

§Examples
use igraph::prelude::*;
let g = Graph::new(4, false);
let l = g.layout_circle(..).unwrap();
assert!((l[(1, 0)] - 0.0).abs() < 1e-12 && (l[(1, 1)] - 1.0).abs() < 1e-12);
// Only two vertices on the circle, the others at the origin.
let l = g.layout_circle(&[3, 1]).unwrap();
assert_eq!(l.row(3), vec![1.0, 0.0]);
assert_eq!(l.row(0), vec![0.0, 0.0]);
Source

pub fn layout_star( &self, center: VertexId, order: Option<&[VertexId]>, ) -> Result<Matrix>

Star-like layout: center at the origin, the other vertices evenly spaced on the unit circle.

The edges are ignored. The non-center vertices are placed in the order given by order, which must be a permutation of all the vertices (including the center), or in increasing id order if None; the first one is at angle zero. center is ignored for the null graph. Time complexity: O(|V|). This is the natural drawing of Graph::star. Binds igraph_layout_star.

§Errors

ErrorKind::InvalidValue if center is not a vertex, or order is not a permutation of the vertices. (igraph itself only checks the length and the range of order: with a repeated vertex it would leave the rows of the missing vertices uninitialized, so this wrapper rejects such orders.)

§Examples
use igraph::prelude::*;
let g = Graph::star(5, StarMode::Undirected, 2).unwrap();
let l = g.layout_star(2, None).unwrap();
assert_eq!(l.row(2), vec![0.0, 0.0]);
assert_eq!(l.row(0), vec![1.0, 0.0]);
Source

pub fn layout_grid(&self, width: Option<usize>) -> Result<Matrix>

Places the vertices on a regular 2D grid, row by row.

Vertex i gets coordinates (i mod w, i div w), where the width w is width, or ceil(sqrt(n)) if None (Some(0) is the same as None). With width equal to the first dimension, this draws the vertices of a Graph::square_lattice at their lattice points. Time complexity: O(|V|). Binds igraph_layout_grid.

§Examples
use igraph::prelude::*;
let g = Graph::new(5, false);
let l = g.layout_grid(Some(2)).unwrap();
assert_eq!(l.to_rows(), vec![
    vec![0.0, 0.0], vec![1.0, 0.0], vec![0.0, 1.0], vec![1.0, 1.0], vec![0.0, 2.0],
]);
Source

pub fn layout_grid_3d( &self, width: Option<usize>, height: Option<usize>, ) -> Result<Matrix>

Places the vertices on a regular 3D grid, filling rows (along x), then layers (along y), then stacking layers along z.

width is the number of vertices in a row and height the number of rows in a layer; if both are None they are ceil(cbrt(n)), if one is None it is chosen so that layers are roughly square (Some(0) is the same as None). Time complexity: O(|V|). Binds igraph_layout_grid_3d.

§Examples
use igraph::prelude::*;
// 8 vertices: a 2 x 2 x 2 cube.
let l = Graph::new(8, false).layout_grid_3d(None, None).unwrap();
assert_eq!(l.row(0), vec![0.0, 0.0, 0.0]);
assert_eq!(l.row(3), vec![1.0, 1.0, 0.0]);
assert_eq!(l.row(7), vec![1.0, 1.0, 1.0]);
Source

pub fn layout_sphere(&self) -> Result<Matrix>

Places the vertices (more or less) uniformly on the unit sphere.

Vertices are placed along a spiral wrapped around the sphere, in increasing id order, so consecutive ids end up close to each other (Saff & Kuijlaars, 1997). Time complexity: O(|V|). Binds igraph_layout_sphere.

§Examples
use igraph::prelude::*;
let l = Graph::new(20, false).layout_sphere().unwrap();
for p in l.rows() {
    let r = (p[0] * p[0] + p[1] * p[1] + p[2] * p[2]).sqrt();
    assert!((r - 1.0).abs() < 1e-9);
}
Source

pub fn layout_fruchterman_reingold( &self, options: &FruchtermanReingoldOptions<'_>, ) -> Result<Matrix>

Force-directed layout in the plane with the Fruchterman–Reingold algorithm.

It simulates an attractive force f_a(d) = -w d² between connected vertices (w is the edge weight) and a repulsive force f_r(d) = 1/d between all pairs, so the equilibrium length of an isolated edge is w^(-1/3) (the C documentation of igraph 1.0.0 and 1.0.1 says 1/w^3, a typo). In disconnected graphs a weak attraction of weight n^(-3/2) between all pairs keeps the components close. The movement is limited by a temperature decreasing linearly to zero, and per-vertex coordinate bounds may be given. Uses the calling thread’s default random number generator for the random start. See FruchtermanReingoldOptions for all the parameters. Time complexity: O(|V|²) per iteration (less with the grid variant). Binds igraph_layout_fruchterman_reingold.

§Errors

ErrorKind::InvalidValue for non-positive weights, vectors of the wrong length, inconsistent bounds, NaN bounds, a lower bound of +inf or an upper bound of -inf (an infinite bound in the other direction means “no bound”), or a start matrix of the wrong shape or with NaN or infinite coordinates.

§Examples
use igraph::prelude::*;
use igraph::layout::FruchtermanReingoldOptions;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false).unwrap();
rng::seed(7).unwrap();
// Keep every vertex in the right half-plane.
let minx = [0.0; 4];
let opts = FruchtermanReingoldOptions { minx: Some(&minx), ..Default::default() };
let l = g.layout_fruchterman_reingold(&opts).unwrap();
assert!(l.column(0).iter().all(|&x| x >= 0.0));
Source

pub fn layout_fruchterman_reingold_3d( &self, options: &FruchtermanReingoldOptions<'_>, ) -> Result<Matrix>

Force-directed layout in 3D space with the Fruchterman–Reingold algorithm.

The 3D version of layout_fruchterman_reingold (the grid option is ignored, the minz/maxz bounds are used).

Caveat (igraph 1.0.0 and 1.0.1): on disconnected graphs the C code adds the z component of the repulsion acting on one vertex of each pair to its y displacement instead of its z displacement, so the forces are slightly unbalanced; connected graphs are not affected. Binds igraph_layout_fruchterman_reingold_3d.

§Errors

As for the 2D version.

Source

pub fn layout_kamada_kawai( &self, options: &KamadaKawaiOptions<'_>, ) -> Result<Matrix>

Force-directed layout in the plane with the Kamada–Kawai spring algorithm.

A spring is placed between every pair of vertices u, v, whose rest length is proportional to their (undirected, possibly weighted) graph distance d(u, v), namely sqrt(n) · d(u, v) / D where D is the largest finite distance (so the drawing has a size of about sqrt(n)), and whose stiffness kkconst / d(u, v)² decreases with that distance; the energy is then minimized one vertex at a time. Vertices in different components are treated as being at distance D. It works particularly well for lattice-like, locally connected graphs; memory is O(|V|²), so it is not suitable for large graphs. Without a start layout (and with no bounds) it starts from a circle of radius 0.36 sqrt(n), so the result is deterministic. The target distances are the ones Graph::distances would compute with NeighborMode::All. See KamadaKawaiOptions. Time complexity: O(|V|) per iteration after an O(|V|² log |V|) initialization. Binds igraph_layout_kamada_kawai.

§Errors

ErrorKind::InvalidValue for non-positive weights or kkconst, wrong lengths or shapes, NaN bounds, a lower bound of +inf or an upper bound of -inf, or NaN or infinite start coordinates.

§Examples
use igraph::prelude::*;
use igraph::layout::KamadaKawaiOptions;
// A path 0 - 1 - 2: drawn straight, with edges of length sqrt(3) / 2.
let g = Graph::ring(3, false, false, false).unwrap();
let l = g.layout_kamada_kawai(&KamadaKawaiOptions::default()).unwrap();
let d = |i: usize, j: usize| (l[(i, 0)] - l[(j, 0)]).hypot(l[(i, 1)] - l[(j, 1)]);
assert!((d(0, 2) - d(0, 1) - d(1, 2)).abs() < 1e-3);
assert!((d(0, 1) - 3f64.sqrt() / 2.0).abs() < 1e-3);
Source

pub fn layout_kamada_kawai_3d( &self, options: &KamadaKawaiOptions<'_>, ) -> Result<Matrix>

Force-directed layout in 3D space with the Kamada–Kawai spring algorithm.

The 3D version of layout_kamada_kawai; without a start layout (and with no bounds) it starts from a layout_sphere of radius 0.36 sqrt(n). Binds igraph_layout_kamada_kawai_3d.

§Errors

As for the 2D version.

Source

pub fn layout_lgl(&self, options: &LglOptions) -> Result<Matrix>

Force-directed layout for large (connected) graphs, inspired by the Large Graph Layout program.

The root is placed first, then its neighbors, then the second neighbors and so on (following a BFS); after each layer a Fruchterman–Reingold-style simulated annealing runs, computing repulsion only between vertices in nearby grid cells. The graph must be connected (igraph warns otherwise; lay out the pieces from Graph::decompose separately and merge them with layout_merge_dla instead). See LglOptions. Time complexity: ideally O(dia · maxiter · (|V| + |E|)). Binds igraph_layout_lgl.

§Errors

ErrorKind::InvalidValue if a parameter is not finite and positive (repulserad may be +inf), or if cellsize is so small compared to area that the grid would have more than max(2^28, 4 |V|) cells; ErrorKind::InvalidVertexId for an invalid root.

§Examples
use igraph::prelude::*;
use igraph::layout::LglOptions;
let g = Graph::kary_tree(40, 3, TreeMode::Undirected).unwrap();
rng::seed(5).unwrap();
let l = g.layout_lgl(&LglOptions { root: Some(0), ..Default::default() }).unwrap();
assert_eq!(l.shape(), (40, 2));
assert!(l.as_slice().iter().all(|x| x.is_finite()));
Source

pub fn layout_reingold_tilford( &self, mode: NeighborMode, roots: Option<&[VertexId]>, rootlevel: Option<&[i64]>, ) -> Result<Matrix>

Reingold–Tilford tree layout: parents centered above their children.

Vertices are placed in levels by distance from the root (y is the depth), and subtrees are packed as tightly as possible. If the graph is not a tree a BFS spanning tree is used. mode selects which edges are followed from a parent (NeighborMode::Out, In, or All, which is forced for undirected graphs). roots should reach every vertex (e.g. one per component): igraph hangs any unreachable vertex directly below the (single) root, or places it next to the roots when there are several. With several roots, igraph joins them under a hidden common root, so the roots are drawn at depth y = 1 and their children at y = 2. None (or empty) selects the roots automatically: in igraph 1.0.1 with RootChoice::Degree below 500 vertices and RootChoice::Eccentricity from 500 on (the C documentation states the opposite, the code does this; see roots_for_tree_layout for a controllable choice). rootlevel, only used with several given roots, gives the extra depth of each root, which is useful for forests. Trees are conveniently built with Graph::kary_tree; use Graph::is_tree to check whether the drawing shows every edge, and Graph::unfold_tree to get the tree itself when the graph has cycles. Binds igraph_layout_reingold_tilford.

§Errors

ErrorKind::InvalidVertexId for invalid roots, ErrorKind::InvalidValue if rootlevel has negative entries or a huge sum (the extra levels are added as vertices), or is non-empty with a length different from that of roots (when several roots are given).

§Examples
use igraph::prelude::*;
// A binary tree of depth 2 rooted at 0.
let g = Graph::kary_tree(7, 2, TreeMode::Undirected).unwrap();
let l = g.layout_reingold_tilford(NeighborMode::All, Some(&[0]), None).unwrap();
assert_eq!(l.column(1), &[0.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0]); // depths
assert_eq!(l[(0, 0)], (l[(1, 0)] + l[(2, 0)]) / 2.0); // root centered
Source

pub fn layout_reingold_tilford_circular( &self, mode: NeighborMode, roots: Option<&[VertexId]>, rootlevel: Option<&[i64]>, ) -> Result<Matrix>

Circular Reingold–Tilford tree layout: the root at the center and the levels on concentric circles.

Same parameters as layout_reingold_tilford; the distance from the origin is the depth of a vertex, and the horizontal positions of the plain layout are mapped linearly to angles, the leftmost vertex at angle 0 and the rightmost one at 2π (n - 1) / n. Binds igraph_layout_reingold_tilford_circular.

§Errors

As for layout_reingold_tilford.

§Examples
use igraph::prelude::*;
let g = Graph::kary_tree(13, 3, TreeMode::Undirected).unwrap();
let l = g.layout_reingold_tilford_circular(NeighborMode::All, Some(&[0]), None).unwrap();
let radius = |v: usize| l[(v, 0)].hypot(l[(v, 1)]);
assert_eq!(radius(0), 0.0);
assert!((radius(1) - 1.0).abs() < 1e-12 && (radius(12) - 2.0).abs() < 1e-12);
Source

pub fn roots_for_tree_layout( &self, mode: NeighborMode, heuristic: RootChoice, ) -> Result<Vec<VertexId>>

Chooses a minimal set of roots for a nice tree layout, such that all vertices are reachable from them.

For undirected graphs (or mode = All) one root is chosen per connected component; in directed mode one per strongly connected component without incoming (for Out) or outgoing (for In) edges (the components are those of Graph::connected_components). Within a component the root is chosen with the given RootChoice heuristic (RootChoice::Eccentricity relates to Graph::eccentricity). Typically used with layout_reingold_tilford. Binds igraph_roots_for_tree_layout.

§Examples
use igraph::prelude::*;
use igraph::layout::RootChoice;
// Two stars: centers 0 and 4.
let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (4, 5), (4, 6)], 7, false).unwrap();
let roots = g.roots_for_tree_layout(NeighborMode::All, RootChoice::Degree).unwrap();
assert_eq!(roots, vec![0, 4]);
Source

pub fn layout_sugiyama( &self, options: &SugiyamaOptions<'_>, ) -> Result<SugiyamaLayout>

Sugiyama layout for layered (directed acyclic) graphs, minimizing edge crossings.

Vertices of the same layer are placed on the same horizontal line (y = layer · vgap; empty layers are skipped by the crossing minimization but still take room vertically); their x coordinates follow the heuristic of Sugiyama, Tagawa and Toda (1981). Without given layers, igraph breaks cycles (via a feedback arc set) and computes a layering itself. Edges spanning several layers are routed through dummy vertices, whose positions are returned in SugiyamaLayout::routing. Disconnected components are placed side by side. See SugiyamaOptions. Related: Graph::feedback_arc_set (the edges reversed to break cycles), Graph::is_dag and Graph::topological_sorting. Binds igraph_layout_sugiyama.

§Errors

ErrorKind::InvalidValue if layers or weights have the wrong length, or layers contains a negative index.

§Examples
use igraph::prelude::*;
use igraph::layout::SugiyamaOptions;
// 0 -> 1 -> 2 plus a shortcut 0 -> 2 spanning two layers.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
let s = g.layout_sugiyama(&SugiyamaOptions::default()).unwrap();
assert_eq!(s.coords.column(1), &[0.0, 1.0, 2.0]);
assert_eq!(s.routing[2].nrow(), 1); // one bend for the shortcut
Source

pub fn layout_mds(&self, dist: Option<&Matrix>, dim: usize) -> Result<Matrix>

Places the vertices in a space of dimension dim with classical (Torgerson) multidimensional scaling.

The Euclidean distances of the layout approximate the given symmetric n × n distance matrix (symmetry is not checked, and its diagonal is ignored), or, if None, the undirected shortest path lengths. dim must be at least 2 and at most the number of vertices. Disconnected graphs are laid out per component and merged with layout_merge_dla, which only works for dim = 2. Vertices symmetric to each other (e.g. leaves of the same parent) may receive identical coordinates. Time complexity: usually around O(|V|² dim). The default distance matrix is the one of Graph::distances with NeighborMode::All. Binds igraph_layout_mds.

§Errors

ErrorKind::InvalidValue for a distance matrix of the wrong shape, dim out of range, or dim > 2 on a disconnected graph.

§Examples
use igraph::prelude::*;
// Three points on a line at 0, 1, 3: MDS recovers their distances.
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let d = Matrix::from_rows(&[[0.0, 1.0, 3.0], [1.0, 0.0, 2.0], [3.0, 2.0, 0.0]]).unwrap();
let l = g.layout_mds(Some(&d), 2).unwrap();
let dist = |i: usize, j: usize| (l[(i, 0)] - l[(j, 0)]).hypot(l[(i, 1)] - l[(j, 1)]);
assert!((dist(0, 2) - 3.0).abs() < 1e-9 && (dist(0, 1) - 1.0).abs() < 1e-9);
Source

pub fn layout_bipartite( &self, types: &[bool], hgap: f64, vgap: f64, maxiter: usize, ) -> Result<Matrix>

Simple two-row layout for bipartite graphs.

Vertices with type true are placed on the line y = 0, those with type false on y = vgap; positions within the rows are then optimized to reduce edge crossings with the Sugiyama heuristic, using hgap as the preferred minimum gap and at most maxiter iterations (100 is a reasonable default). Only the types matter, edges between vertices of the same type are allowed: a proper 2-coloring can be obtained with Graph::bipartite_types, and the bipartite constructors (e.g. Graph::full_bipartite) return the types with the graph. Binds igraph_layout_bipartite.

§Errors

ErrorKind::InvalidValue if types has the wrong length or hgap is negative.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 2), (1, 2), (1, 3)], 4, false).unwrap();
let l = g.layout_bipartite(&[false, false, true, true], 1.0, 1.0, 100).unwrap();
assert_eq!(l.column(1), &[1.0, 1.0, 0.0, 0.0]);
Source

pub fn layout_umap(&self, options: &UmapOptions<'_>) -> Result<Matrix>

Layout with Uniform Manifold Approximation and Projection (UMAP), in the plane. Experimental in igraph.

UMAP embeds a (typically sparse, e.g. k-nearest-neighbors) distance graph: distances are turned into exponentially decaying weights in [0, 1], and a stochastic gradient descent on the cross-entropy then places strongly connected vertices close together, while repelling unconnected ones beyond min_dist. Without distances all edges have the same weight. If distances_are_weights is set, the distances are used directly as weights (see layout_umap_compute_weights). A typical input is a k-nearest-neighbor graph of data points built with Graph::nearest_neighbor_graph, with the distances from Graph::spatial_edge_lengths. Uses the calling thread’s default random number generator. See UmapOptions. Binds igraph_layout_umap.

§Errors

ErrorKind::InvalidValue for invalid distances, a negative min_dist or a wrongly shaped or non-finite start.

Source

pub fn layout_umap_3d(&self, options: &UmapOptions<'_>) -> Result<Matrix>

Layout with UMAP in 3D space. Experimental in igraph.

The 3D version of layout_umap. Binds igraph_layout_umap_3d.

§Errors

As for the 2D version.

Source

pub fn layout_umap_compute_weights( &self, distances: Option<&[f64]>, ) -> Result<Vec<f64>>

Computes the UMAP edge weights from the edge distances. Experimental in igraph.

For each vertex a scale factor and a connectivity correction are computed, and distances become exponentially decaying weights in [0, 1]. The graph may be directed but must have no loops or multi-edges (pairs of opposite directed edges are allowed): such pairs are symmetrized with the fuzzy union W = W1 + W2 - W1 W2, stored on one of the two edges, while the other gets weight 0 (see Graph::is_simple and Graph::simplify). Loops are reported as errors, but multi-edges are not detected by igraph: all but one of the parallel edges silently get weight 0. Pass the result to layout_umap with distances_are_weights. None means that all edges have the same distance. Binds igraph_layout_umap_compute_weights.

§Errors

ErrorKind::InvalidValue for negative or NaN distances, a wrong length, or loops.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let w = g.layout_umap_compute_weights(Some(&[1.0, 2.0, 3.0])).unwrap();
assert_eq!(w.len(), 3);
assert!(w.iter().all(|&x| (0.0..=1.0).contains(&x)));
Source

pub fn layout_drl( &self, options: &DrlOptions, weights: Option<&[f64]>, initial: Option<&Matrix>, ) -> Result<Matrix>

The DrL (Distributed Recursive Layout) force-directed layout, in the plane.

Designed for large graphs, it runs a sequence of simulated annealing phases driven by the given DrlOptions (start from a DrlTemplate), cutting highly stressed edges in the late phases to produce less dense, clustered layouts (Martin et al., 2008). weights must be positive (None: unit weights); initial gives optional starting positions (n × 2). Binds igraph_layout_drl.

§Errors

ErrorKind::InvalidValue for negative damping multipliers, non-positive weights, weights above 1e20 (DrL computes in single precision and overflows; rescale such weights), NaN or infinite start coordinates, or wrong lengths.

§Examples
use igraph::prelude::*;
use igraph::layout::DrlOptions;
let edges: Vec<(i64, i64)> = (0..10).map(|i| (i, (i + 1) % 10)).collect();
let g = Graph::from_edges(&edges, 10, false).unwrap();
rng::seed(3).unwrap();
let l = g.layout_drl(&DrlOptions::default(), None, None).unwrap();
assert_eq!(l.shape(), (10, 2));
Source

pub fn layout_drl_3d( &self, options: &DrlOptions, weights: Option<&[f64]>, initial: Option<&Matrix>, ) -> Result<Matrix>

The DrL force-directed layout in 3D space.

The 3D version of layout_drl (initial, if given, must be n × 3). Binds igraph_layout_drl_3d.

§Errors

As for the 2D version.

Source

pub fn layout_graphopt(&self, options: &GraphoptOptions<'_>) -> Result<Matrix>

The graphopt force-directed layout (a port of Michael Schmuhl’s graphopt).

Vertices are charged particles repelling each other (Coulomb’s law, ignored beyond distance 500) and edges are springs (Hooke’s law); the physical system is simulated for niter steps, without simulated annealing, so a stable fixed point is not guaranteed. A layout can be refined by passing it back as initial. See GraphoptOptions. Time complexity: O(niter (|V|² + |E|)), or O(niter |E|) with zero charge. Binds igraph_layout_graphopt.

§Errors

ErrorKind::InvalidValue for a wrongly shaped or non-finite start.

§Examples

Pure springs of rest length 1 (no charge) stretch a squeezed path along its diagonal (values from igraph’s igraph_layout_graphopt unit test):

use igraph::prelude::*;
use igraph::layout::GraphoptOptions;
let g = Graph::ring(4, false, false, false).unwrap(); // the path 0 - 1 - 2 - 3
let start = Matrix::from_rows(&[[0.15, -0.15], [0.05, -0.05], [-0.05, 0.05], [-0.15, 0.15]]).unwrap();
let opts = GraphoptOptions {
    node_charge: 0.0,
    spring_length: 1.0,
    spring_constant: 10.0,
    initial: Some(&start),
    ..Default::default()
};
let l = g.layout_graphopt(&opts).unwrap();
assert!((l[(0, 0)] - 1.06066).abs() < 1e-5 && (l[(1, 1)] + 0.353553).abs() < 1e-5);
Source

pub fn layout_gem(&self, options: &GemOptions<'_>) -> Result<Matrix>

The GEM force-directed layout (Frick, Ludwig and Mehldau, 1994).

Vertices are updated one at a time in random order, each with its own local temperature adapted to detect oscillations and rotations; the algorithm stops when the global temperature drops below temp_min or after maxiter vertex updates. Edge directions are ignored. See GemOptions. Time complexity: O(t · n · (n + e)) for t steps. Binds igraph_layout_gem.

§Errors

ErrorKind::InvalidValue unless 0 < temp_min <= temp_init <= temp_max, or for a wrongly shaped or non-finite start.

§Examples
use igraph::prelude::*;
use igraph::layout::GemOptions;
let g = Graph::ring(8, false, false, true).unwrap();
rng::seed(11).unwrap();
let l = g.layout_gem(&GemOptions::default()).unwrap();
assert_eq!(l.shape(), (8, 2));
// Zero iterations keep the start.
let opts = GemOptions { maxiter: Some(0), initial: Some(&l), ..Default::default() };
assert_eq!(g.layout_gem(&opts).unwrap(), l);
Source

pub fn layout_davidson_harel( &self, options: &DavidsonHarelOptions<'_>, ) -> Result<Matrix>

The Davidson–Harel simulated annealing layout (1996).

Minimizes an energy combining node-node distances, distances from the border, edge lengths, edge crossings and node-edge distances, first with simulated annealing then with a fine tuning phase; coordinates are kept within the bounds of the layout rectangle. Edge directions are ignored. The energy weights are hard to tune in general; see DavidsonHarelOptions for defaults determined by experimentation. Time complexity: O(n² + m²) per annealing iteration, O(mn) per fine tuning iteration. Binds igraph_layout_davidson_harel.

§Errors

ErrorKind::InvalidValue if cool_fact is not in (0, 1), if maxiter + fineiter overflows a 64-bit integer, or for a wrongly shaped or non-finite start.

§Examples
use igraph::prelude::*;
use igraph::layout::DavidsonHarelOptions;
let g = Graph::full(10, false, false).unwrap();
rng::seed(42).unwrap();
let l = g.layout_davidson_harel(&DavidsonHarelOptions::default()).unwrap();
assert_eq!(l.shape(), (10, 2));
assert!(l.as_slice().iter().all(|x| x.abs() < 20.0));
Source

pub fn layout_align(&self, layout: &mut Matrix) -> Result<()>

Centers a layout on the origin and rotates it to align it with the coordinate axes, in place.

The principal axes are computed from the edge directions (weighted by squared edge lengths), or from the vertex positions if there are no edges of non-zero length. Useful after force-directed layouts; works in any dimension. Time complexity: O(|V| + |E|). Binds igraph_layout_align.

§Errors

ErrorKind::InvalidValue if the layout does not have one row per vertex, has zero columns (and the graph is not the null graph), or contains NaN or infinite coordinates.

§Examples
use igraph::prelude::*;
// A diagonal segment becomes horizontal and centered.
let g = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
let mut l = Matrix::from_rows(&[[1.0, 1.0], [3.0, 3.0]]).unwrap();
g.layout_align(&mut l).unwrap();
assert!(l[(0, 1)].abs() < 1e-9 && l[(1, 1)].abs() < 1e-9);
assert!((l[(0, 0)] + l[(1, 0)]).abs() < 1e-9);
Source§

impl igraph_t

Source

pub fn eigen_adjacency( &self, which: &EigenWhich, algorithm: EigenAlgorithm, options: &ArpackOptions, ) -> Result<SymmetricEigen>

Eigenvalues and eigenvectors of the adjacency matrix of an undirected graph (igraph_eigen_adjacency), computed with ARPACK (the only algorithm implemented by igraph 1.0.0 and 1.0.1, also chosen by EigenAlgorithm::Auto). Self-loops count once on the diagonal, multi-edges add up.

Supported choices: largest/smallest magnitude, largest/smallest algebraic.

Binds igraph_eigen_adjacency (see the linear algebra chapter).

ARPACK starts from a random vector drawn from the calling thread’s igraph RNG (rng::seed makes runs reproducible). On small graphs whose wanted eigenvalue is highly repeated (e.g. the eigenvalue -1 of a complete graph) it can stop with “maximum number of iterations reached” for some start vectors; LAPACK on the dense matrix is the robust choice there.

See also Graph::eigenvector_centrality (the scaled leading eigenvector, also for directed and weighted graphs) and Graph::get_adjacency to form the dense matrix for eigen_matrix_symmetric or lapack_dsyevr.

§Errors

For directed graphs and unsupported choices (ErrorKind::Unimplemented).

§Examples

The complete graph K_n has adjacency eigenvalues n - 1 (once) and -1.

use igraph::{linalg::*, prelude::*};
let k6 = Graph::full(6, false, false).unwrap();
let opts = ArpackOptions::default();
let e = k6.eigen_adjacency(&EigenWhich::LargestAlgebraic(1), EigenAlgorithm::Auto, &opts).unwrap();
assert!((e.values[0] - 5.0).abs() < 1e-10);
Source§

impl igraph_t

Source

pub fn adjacency_spectral_embedding( &self, no: usize, weights: Option<&[f64]>, which: EmbeddingWhich, scaled: bool, cvec: Option<&[f64]>, options: &ArpackOptions, ) -> Result<SpectralEmbedding>

Adjacency spectral embedding (igraph_adjacency_spectral_embedding).

Computes a no-dimensional Euclidean representation of the graph from the singular value decomposition of its adjacency matrix, A = U D V'. For undirected graphs X = U_no D^(1/2) (and Y = X); for directed graphs X = U_no D^(1/2) and Y = V_no D^(1/2). If the graph is a random dot product graph generated from latent position vectors in R^no, the embedding estimates these latent positions.

  • weights: optional edge weights.
  • which: which eigenvalues (singular values) to use.
  • scaled: return X, Y if true, U, V otherwise.
  • cvec: added to the diagonal of the adjacency matrix before the decomposition; either one value per vertex, a single value for all, or None for zero. A common choice is half the degrees.
  • options: ARPACK options; only tol and mxiter are used (igraph sets which, nev and ncv, and starts from a random vector of the calling thread’s RNG).

Binds igraph_adjacency_spectral_embedding.

See also dim_select to choose no from the singular values, Graph::get_adjacency for the matrix being decomposed, and Graph::layout_mds for a distance-based spectral layout.

§Errors

If no is zero or larger than the number of vertices, or the weight or cvec lengths are wrong.

§Examples

Two disjoint 4-cliques: the 2-dimensional embedding places the two groups on orthogonal axes.

use igraph::{linalg::*, prelude::*};
let k4 = Graph::full(4, false, false).unwrap();
let g = k4.disjoint_union(&k4).unwrap(); // vertices 0..4 and 4..8
let e = g
    .adjacency_spectral_embedding(2, None, EmbeddingWhich::LargestAlgebraic, true, None, &ArpackOptions::default())
    .unwrap();
assert!((e.d[0] - 3.0).abs() < 1e-8 && (e.d[1] - 3.0).abs() < 1e-8);
let dot = |a: usize, b: usize| e.x.row(a).iter().zip(e.x.row(b)).map(|(p, q)| p * q).sum::<f64>();
assert!(dot(0, 1) > 0.5);      // same clique: similar positions
assert!(dot(0, 5).abs() < 1e-8); // different cliques: orthogonal
Source

pub fn laplacian_spectral_embedding( &self, no: usize, weights: Option<&[f64]>, which: EmbeddingWhich, kind: LaplacianEmbeddingType, scaled: bool, options: &ArpackOptions, ) -> Result<SpectralEmbedding>

Laplacian spectral embedding (igraph_laplacian_spectral_embedding).

Like adjacency_spectral_embedding, but decomposes a Laplacian of the graph, chosen with kind (see LaplacianEmbeddingType; directed graphs need LaplacianEmbeddingType::OAP). Use EmbeddingWhich::SmallestAlgebraic with D - A for the classic spectral clustering / Fiedler vector embedding.

Binds igraph_laplacian_spectral_embedding.

The eigenvectors come from ARPACK, started from a random vector of the calling thread’s igraph RNG (seed it with rng::seed for reproducible runs). On tiny graphs with repeated eigenvalues ARPACK may miss an eigenvalue or fail to converge for some start vectors; there, diagonalize the dense Laplacian with lapack_dsyevr instead.

See also Graph::get_laplacian and Graph::get_laplacian_sparse (structural module) for the Laplacian matrices themselves; D - A is LaplacianNormalization::Unnormalized and I - D^-1/2 A D^-1/2 is LaplacianNormalization::Symmetric.

§Errors

As for the adjacency embedding, plus an invalid kind for the directedness of the graph, and — for the normalized Laplacians — a vertex with zero strength (isolated vertex; for directed graphs, zero in- or out-strength).

§Examples

Spectral bisection of Zachary’s karate club: the Fiedler vector (eigenvalue ~0.4685 of D - A, the algebraic connectivity) puts the instructor (vertex 0) and the administrator (vertex 33) on opposite sides.

use igraph::{linalg::*, prelude::*};
let g = Graph::famous("Zachary").unwrap();
let e = g
    .laplacian_spectral_embedding(
        2,
        None,
        EmbeddingWhich::SmallestAlgebraic,
        LaplacianEmbeddingType::DA,
        false,
        &ArpackOptions::default(),
    )
    .unwrap();
// One eigenvalue is 0 (connected graph), the other is the algebraic connectivity.
let fiedler = if e.d[0] > e.d[1] { 0 } else { 1 };
assert!(e.d[1 - fiedler].abs() < 1e-8);
assert!((e.d[fiedler] - 0.4685).abs() < 1e-4);
let f = e.x.column(fiedler);
assert!(f[0] * f[33] < 0.0);
Source§

impl igraph_t

Source

pub fn sir( &self, beta: f64, gamma: f64, num_simulations: usize, ) -> Result<Vec<SirRun>>

Runs num_simulations stochastic SIR (susceptible–infected–recovered) epidemics on the graph.

Each individual (vertex) is susceptible, infected or recovered; recovered individuals are immune. A susceptible vertex with n infected neighbors becomes infected at rate n * beta, an infected one recovers at rate gamma (both are rates of exponential distributions, so the model runs in continuous time, as a Gillespie simulation). Every simulation starts with a single, uniformly chosen, infected vertex and stops when no infected vertex is left. It uses the thread’s default random number generator: seed it with rng::seed for reproducible runs.

Edge directions are ignored (with a warning) for directed graphs, so a directed graph with a pair of opposite edges u → v, v → u counts as a multigraph and is rejected. Seeded runs are reproducible, also when several threads simulate at the same time: each thread has its own default generator.

See also Graph::famous and the random graph models of crate::games for contact networks, and set_interruption_handler to cancel long simulations.

Binds igraph_sir. Time complexity: O(num_simulations * (|V| + |E| log |V|)).

§Errors

ErrorKind::InvalidValue when the graph has no vertices or is not simple, when beta < 0, gamma <= 0 or num_simulations == 0. The computation can be stopped by an interruption handler (ErrorKind::Interrupted).

§Examples
use igraph::prelude::*;

rng::seed(42)?;
let ring = Graph::ring(10, false, false, true)?;
let runs = ring.sir(2.0, 1.0, 5)?;
assert_eq!(runs.len(), 5);
for run in &runs {
    for (_, s, i, r) in run.states() {
        assert_eq!(s + i + r, 10); // the population is conserved
    }
    assert_eq!(*run.infected.last().unwrap(), 0); // the epidemic dies out
}

Epidemics spread further on the karate club network when the infection rate grows:

use igraph::prelude::*;

rng::seed(1)?;
let club = Graph::famous("Zachary")?;
let mean_final_size = |beta: f64| -> igraph::Result<f64> {
    let runs = club.sir(beta, 1.0, 300)?;
    Ok(runs.iter().map(|r| r.final_size() as f64).sum::<f64>() / 300.0)
};
assert!(mean_final_size(0.05)? < mean_final_size(1.0)?);
Source§

impl igraph_t

Source

pub fn delaunay_graph(points: &Matrix) -> Result<Graph>

The Delaunay graph of a point set: two points are adjacent when they share an edge of the Delaunay triangulation (tetrahedralization, …) of the points.

points has one point per row, in any dimension d >= 1; vertex i of the result is the point in row i. The Delaunay graph is a supergraph of the Gabriel graph, itself a supergraph of the relative neighborhood graph and of the Euclidean minimum spanning tree. The computation relies on Qhull.

See also convex_hull_2d: in 2D, the outer boundary of the triangulation is the convex hull of the points.

Binds igraph_delaunay_graph (experimental). Time complexity: O(n log n) for d <= 3, and O(n^⌊d/2⌋ / ⌊d/2⌋!) in general.

§Errors

ErrorKind::InvalidValue for duplicate points, non-finite coordinates, zero-dimensional points, and (currently) for degenerate sets that do not span the space, such as d + 1 or more points all lying on a hyperplane.

§Examples
use igraph::prelude::*;
// A 3x3 square lattice (igraph's own test case).
let pts: Vec<[f64; 2]> =
    (0..9).map(|i| [(i / 3) as f64, (i % 3) as f64]).collect();
let g = Graph::delaunay_graph(&Matrix::from_rows(&pts)?)?;
assert_eq!(g.vcount(), 9);
// 12 lattice sides plus one diagonal in each of the 4 cells.
assert_eq!(g.ecount(), 16);
Source

pub fn lune_beta_skeleton(points: &Matrix, beta: f64) -> Result<Graph>

The lune-based β-skeleton of a point set.

Two points A and B are adjacent when no other point lies in their (closed) lune, a region whose size grows with beta: larger values of beta give sparser graphs. For beta >= 1 the lune is the intersection of the two balls of radius beta·|AB|/2 centered on the line AB and passing through A and B respectively; for beta < 1 it is the intersection of the two disks of radius |AB|/(2·beta) whose boundaries pass through both A and B. beta = 1 gives the Gabriel graph; beta = 2 is (almost, see relative_neighborhood_graph) the relative neighborhood graph. Values of beta < 1 are only supported in 2D, and are considerably slower.

§Correctness workarounds

igraph 1.0.0 and 1.0.1 under-estimate the search radius of the lune for beta > 2 and beta < 0.5, and then returns spurious edges (for beta < 0.5, even the complete graph). This wrapper returns the correct skeleton in these ranges: for beta > 2 it keeps the edges of beta_weighted_gabriel_graph whose threshold exceeds beta (same complexity; point sets with no more points than dimensions are tested pair by pair, as igraph does), for beta < 0.5 it tests every pair of points against every other point (O(n³)).

Binds igraph_lune_beta_skeleton (experimental). Time complexity: about O(n^⌊d/2⌋ log n).

§Errors

ErrorKind::InvalidValue unless beta is positive and finite, or for NaN or infinite coordinates; ErrorKind::Unimplemented for beta < 1 outside of 2D; and, for beta >= 1 with more points than dimensions (where the candidate edges come from the Delaunay graph), the errors of delaunay_graph, e.g. for duplicate points.

§Examples
use igraph::prelude::*;
// Two points and a third one slightly off their midpoint.
let pts = Matrix::from_rows(&[[0.0, 0.0], [2.0, 0.0], [1.0, 1.2]])?;
// For beta = 1 the third point is outside the disk with diameter 0-1...
assert!(Graph::lune_beta_skeleton(&pts, 1.0)?.get_eid(0, 1, false)?.is_some());
// ...but it is inside the fatter lune of beta = 2.
assert!(Graph::lune_beta_skeleton(&pts, 2.0)?.get_eid(0, 1, false)?.is_none());
Source

pub fn circle_beta_skeleton(points: &Matrix, beta: f64) -> Result<Graph>

The circle-based β-skeleton of a 2D point set.

For beta >= 1, A and B are adjacent when no other point lies in the union of the two disks of radius beta·|AB|/2 whose boundaries pass through both A and B; for beta < 1 the forbidden region is the intersection of the disks of radius |AB|/(2·beta), as for the lune-based skeleton. beta must be positive; larger values give sparser graphs, and values below 1 are considerably slower. For beta = 1 it coincides with the Gabriel graph.

For beta < 0.5 igraph 1.0.0 and 1.0.1 return spurious edges (see the lune-based skeleton): this wrapper then computes the correct skeleton by brute force, in O(n³).

Binds igraph_circle_beta_skeleton (experimental).

§Errors

ErrorKind::InvalidValue unless beta is positive and finite, or for NaN or infinite coordinates; ErrorKind::Unimplemented if the points are not two-dimensional.

Source

pub fn beta_weighted_gabriel_graph( points: &Matrix, max_beta: f64, ) -> Result<(Graph, Vec<f64>)>

The Gabriel graph together with, for each edge, the threshold β at which the edge disappears from the lune-based β-skeleton.

The edge e belongs to the lune β-skeleton exactly for 1 <= β < weights[e], so this single call summarizes all the skeletons with β >= 1. Edges that persist for arbitrarily large β, or beyond the max_beta cutoff, get the weight f64::INFINITY. A smaller max_beta makes the computation faster; pass f64::INFINITY for no cutoff.

Returns the graph and the weights, indexed by edge id.

Binds igraph_beta_weighted_gabriel_graph (experimental).

§Errors

ErrorKind::InvalidValue if max_beta is NaN, and the errors of delaunay_graph (which it uses even for tiny point sets: it needs more points than dimensions, and there must be no duplicate points).

§Examples
use igraph::prelude::*;
let pts = Matrix::from_rows(&[[0.0, 0.0], [2.0, 0.0], [1.0, 1.2]])?;
let (g, beta) = Graph::beta_weighted_gabriel_graph(&pts, f64::INFINITY)?;
assert_eq!(g.ecount(), 3);
let long = g.get_eid(0, 1, false)?.unwrap() as usize;
// Edge 0-1 leaves the lune skeleton at beta = 1.22 (the third point
// enters its lune), the other two sides at a much larger beta.
assert!((beta[long] - 1.22).abs() < 1e-9);
assert!(beta.iter().all(|&b| b >= beta[long]));
Source

pub fn gabriel_graph(points: &Matrix) -> Result<Graph>

The Gabriel graph of a point set: A and B are adjacent when no other point lies in the closed ball having the segment AB as a diameter.

The Gabriel graph is connected, planar in 2D, and it is the β-skeleton (lune- or circle-based) with β = 1. Any dimension is supported.

Binds igraph_gabriel_graph (experimental). Time complexity: about O(n^⌊d/2⌋ log n).

§Examples
use igraph::prelude::*;
// An obtuse triangle: the long side has the third point inside its
// diametral circle, so it is not a Gabriel edge.
let pts = Matrix::from_rows(&[[0.0, 0.0], [4.0, 0.0], [2.0, 0.5]])?;
let g = Graph::gabriel_graph(&pts)?;
assert_eq!(g.ecount(), 2);
assert_eq!(g.get_eid(0, 1, false)?, None);
Source

pub fn relative_neighborhood_graph(points: &Matrix) -> Result<Graph>

The relative neighborhood graph of a point set: A and B are adjacent unless some other point C is strictly closer to both of them than they are to each other (AC < AB and BC < AB).

It is always connected, and it is a supergraph of the Euclidean minimum spanning tree. Unlike the β = 2 lune skeleton (which uses non-strict inequalities and is triangle-free), it connects the three corners of an equilateral triangle.

Binds igraph_relative_neighborhood_graph (experimental). Time complexity: about O(n^⌊d/2⌋ log n).

See also Graph::minimum_spanning_tree: with the edge lengths as weights, the minimum spanning tree of this graph is the Euclidean minimum spanning tree of the points.

§Examples
use igraph::prelude::*;
// A 1x2 rectangle with its center: the center is closer to every
// corner than the corners to each other along the long sides.
let pts = Matrix::from_rows(&[[0.0, 0.0], [2.0, 0.0], [2.0, 1.0], [0.0, 1.0], [1.0, 0.5]])?;
let g = Graph::relative_neighborhood_graph(&pts)?;
let mut edges = g.edge_list();
edges.sort();
// The two short sides and the four spokes to the center.
assert_eq!(edges, vec![(0, 3), (0, 4), (1, 2), (1, 4), (2, 4), (3, 4)]);
Source

pub fn nearest_neighbor_graph( points: &Matrix, metric: Metric, k: Option<usize>, cutoff: Option<f64>, directed: bool, ) -> Result<Graph>

The k nearest neighbor graph of a point set.

Each point is connected to (at most) its k nearest other points according to metric, considering only points closer than cutoff. k = None means no limit on the number of neighbors, and cutoff = None no limit on the distance (with both None the result is complete). With directed, the edge i → j means that j is among the neighbors of i; otherwise i and j are connected when either chose the other (mutual choices give a single edge). Ties between equidistant neighbors are broken arbitrarily.

Binds igraph_nearest_neighbor_graph (experimental). Time complexity: O(n log n) (k-d tree).

See also Graph::grg_game, which samples random points in the unit square and connects those closer than a radius: the undirected graph built here with k = None and that radius as cutoff.

§Errors

ErrorKind::InvalidValue for zero-dimensional points, non-finite coordinates, or a negative or NaN cutoff.

§Examples
use igraph::{misc::Metric, prelude::*};
// Points on a line: 0, 1, 3, 7.
let pts = Matrix::from_rows(&[[0.0], [1.0], [3.0], [7.0]])?;
let g = Graph::nearest_neighbor_graph(&pts, Metric::Euclidean, Some(1), None, true)?;
let mut edges = g.edge_list();
edges.sort();
assert_eq!(edges, vec![(0, 1), (1, 0), (2, 1), (3, 2)]);
Source

pub fn spatial_edge_lengths( &self, points: &Matrix, metric: Metric, ) -> Result<Vec<f64>>

The length of each edge, computed from the coordinates of its endpoints with the given metric, indexed by edge id.

Row i of points holds the coordinates of vertex i, in any dimension. The lengths can be used as weights by path-length based functions, e.g. Graph::distances_dijkstra, Graph::minimum_spanning_tree, Graph::betweenness, Graph::closeness or Graph::voronoi.

Binds igraph_spatial_edge_lengths (experimental). Time complexity: O(|E| d).

§Errors

ErrorKind::InvalidValue if the number of rows differs from the number of vertices, or the points are zero-dimensional (a 0 × 0 matrix is accepted for the null graph).

§Examples
use igraph::{misc::Metric, prelude::*};
let g = Graph::from_edges(&[(0, 1), (0, 2)], 3, false)?;
let pts = Matrix::from_rows(&[[0.0, 0.0], [3.0, 4.0], [1.0, 1.0]])?;
assert_eq!(g.spatial_edge_lengths(&pts, Metric::Euclidean)?[0], 5.0);
assert_eq!(g.spatial_edge_lengths(&pts, Metric::Manhattan)?, vec![7.0, 2.0]);
Source§

impl igraph_t

Source

pub fn transitivity_undirected(&self, mode: TransitivityMode) -> Result<f64>

Global transitivity (clustering coefficient) of the graph.

The transitivity is the probability that two neighbors of a vertex are connected; more precisely, it is the ratio between the number of closed connected triples (three times the number of triangles) and the number of connected triples. Edge directions and multiplicities are ignored. This single number differs from the average local transitivity, which weights all vertices equally.

mode says what to return for graphs without connected triples: TransitivityMode::Nan gives NaN, TransitivityMode::Zero gives 0.

Binds igraph_transitivity_undirected. Reference: S. Wasserman and K. Faust, Social Network Analysis: Methods and Applications, Cambridge University Press (1994).

See also Graph::count_triangles: for a simple graph with degrees d_v, the transitivity is 3 T / Σ_v d_v (d_v - 1) / 2.

Time complexity: O(|V| d²), d being the average degree.

§Examples
use igraph::prelude::*;

// A triangle with a pendant edge: 3 closed triples out of 5.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false)?;
assert!((g.transitivity_undirected(TransitivityMode::Nan)? - 0.6).abs() < 1e-12);

// A path has connected triples but no triangle; a single edge has neither.
let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
assert_eq!(path.transitivity_undirected(TransitivityMode::Nan)?, 0.0);
let edge = Graph::from_edges(&[(0, 1)], 2, false)?;
assert!(edge.transitivity_undirected(TransitivityMode::Nan)?.is_nan());
assert_eq!(edge.transitivity_undirected(TransitivityMode::Zero)?, 0.0);
Source

pub fn transitivity_local_undirected<'a>( &self, vids: impl Into<VertexSelector<'a>>, mode: TransitivityMode, ) -> Result<Vec<f64>>

Local transitivity (clustering coefficient) of the selected vertices.

For each vertex, the fraction of pairs of its neighbors that are themselves connected (Watts–Strogatz clustering coefficient). Edge directions and multiplicities are ignored. Vertices with fewer than two neighbors get NaN with TransitivityMode::Nan and 0 with TransitivityMode::Zero. The result follows the order of vids.

Binds igraph_transitivity_local_undirected. Reference: D. J. Watts and S. Strogatz, Collective dynamics of small-world networks, Nature 393, 440–442 (1998).

See also Graph::count_adjacent_triangles: in a simple graph the local transitivity of v is t_v / (d_v (d_v - 1) / 2), t_v being the number of triangles through v.

Time complexity: O(n d²), n being the number of selected vertices and d the average degree.

§Errors

ErrorKind::InvalidVertexId if the selector contains a non-existent vertex.

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false)?;
let c = g.transitivity_local_undirected(.., TransitivityMode::Zero)?;
assert_eq!(c[..2], [1.0, 1.0]);
assert!((c[2] - 1.0 / 3.0).abs() < 1e-12);
assert_eq!(c[3], 0.0); // a leaf
Source

pub fn transitivity_avglocal_undirected( &self, mode: TransitivityMode, ) -> Result<f64>

Average local transitivity (average clustering coefficient).

The mean of the local transitivities of all vertices. Vertices with fewer than two neighbors are left out of the average with TransitivityMode::Nan (the result is NaN if no vertex has two neighbors), and counted as zero with TransitivityMode::Zero. Edge directions and multiplicities are ignored.

Binds igraph_transitivity_avglocal_undirected. Reference: D. J. Watts and S. Strogatz, Collective dynamics of small-world networks, Nature 393, 440–442 (1998). A small-world graph (e.g. Graph::watts_strogatz_game with a small rewiring probability) has a much higher average clustering than an Erdős–Rényi graph of the same density.

Time complexity: O(|V| d²).

§Examples
use igraph::prelude::*;

let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false)?;
// Local values: 1, 1, 1/3 and (leaf) NaN or 0.
let skip = g.transitivity_avglocal_undirected(TransitivityMode::Nan)?;
let zero = g.transitivity_avglocal_undirected(TransitivityMode::Zero)?;
assert!((skip - 7.0 / 9.0).abs() < 1e-12);
assert!((zero - 7.0 / 12.0).abs() < 1e-12);
Source

pub fn transitivity_barrat<'a>( &self, vids: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: TransitivityMode, ) -> Result<Vec<f64>>

Barrat’s weighted local transitivity of the selected vertices.

For a vertex i, every triangle i, j, h contributes the total weight w_ij + w_ih of the two triangle edges incident on i (in equation (5) each triangle appears twice, as the ordered pairs (j, h) and (h, j), with the mean weight (w_ij + w_ih) / 2); the sum is divided by s_i (k_i - 1), where s_i is the strength and k_i the degree of i (equation (5) of A. Barrat, M. Barthélemy, R. Pastor-Satorras and A. Vespignani, The architecture of complex weighted networks, PNAS 101, 3747 (2004)). With equal weights it coincides with the unweighted local transitivity.

Edge directions are ignored; the graph must not have multi-edges (for directed graphs, not even mutual pairs u -> v, v -> u, which become multi-edges once directions are ignored). If weights is None, igraph emits a warning and falls back to the unweighted local transitivity. When the denominator s_i (k_i - 1) is zero (fewer than two incident edges, or zero strength), TransitivityMode::Zero gives 0, while TransitivityMode::Nan performs the division: NaN (0 / 0), or ±∞ if a zero strength comes from weights of mixed signs around closed triangles.

Binds igraph_transitivity_barrat. See also Graph::strength for the s_i.

Time complexity: O(|V| d²).

§Errors

ErrorKind::InvalidValue if the weight vector has the wrong length or the graph has multi-edges (or mutual directed edges); ErrorKind::InvalidVertexId for invalid vertices.

§Examples
use igraph::prelude::*;

// igraph's unit test graph: two triangles 0-1-2 and 1-2-3, a tail 3-4, isolated 5.
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2), (1, 3), (2, 3), (3, 4)], 6, false)?;
let w = [-1.0, 0.0, 1.0, 2.0, 3.0, 4.0];
let t = g.transitivity_barrat(.., Some(&w), TransitivityMode::Zero)?;
let expected = [1.0, 0.75, 0.625, 0.277778, 0.0, 0.0];
for (a, b) in t.iter().zip(expected) {
    assert!((a - b).abs() < 1e-6);
}
Source

pub fn ecc<'a>( &self, eids: impl Into<EdgeSelector<'a>>, k: usize, offset: bool, normalize: bool, ) -> Result<Vec<f64>>

Edge clustering coefficient of the selected edges.

For an edge (i, j), let z be the number of k-cycles it belongs to and s the largest such number compatible with the degrees of its endpoints: s = min(d_i - 1, d_j - 1) for k = 3 and s = (d_i - 1)(d_j - 1) for k = 4. The coefficient is

C = (z + offset) / s      (normalize = true)
C =  z + offset           (normalize = false)

where offset is 1 if offset is true and 0 otherwise. The original definition of Radicchi et al. (PNAS 101, 2658 (2004)) uses offset = true, normalize = true; with offset = false the normalized value is at most 1, which for k = 3 is achieved by every edge of a complete graph. When normalizing, edges with s = 0 (an endpoint of degree 1, or a self-loop, which igraph assigns z = s = 0) get NaN without offset (0 / 0) and +∞ with it (1 / 0). Multiplicities are ignored when listing cycles but not in the degrees. The result follows the order of eids.

Only k = 3 and k = 4 are currently supported.

Binds igraph_ecc. See also Graph::list_triangles (for k = 3, the unnormalized, offset-free coefficient of an edge is the number of listed triangles containing it) and Graph::community_edge_betweenness, the other classic edge-removal criterion for divisive community detection.

Time complexity: O(|V| d log d + |E| d) for k = 3, O(|V| d log d + |E| d²) for k = 4.

§Errors

ErrorKind::InvalidValue if k < 3; ErrorKind::Unimplemented if k > 4; ErrorKind::InvalidEdgeId for invalid edges.

§Examples
use igraph::prelude::*;

// In K4 each edge is in 2 triangles, the most its degree-3 endpoints allow.
let k4 = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false)?;
assert!(k4.ecc(.., 3, false, true)?.iter().all(|&c| c == 1.0));
assert_eq!(k4.ecc(0, 3, true, false)?, vec![3.0]); // 2 triangles, plus one
Source

pub fn assortativity( &self, weights: Option<&[f64]>, values: &[f64], values_in: Option<&[f64]>, directed: bool, normalized: bool, ) -> Result<f64>

Assortativity coefficient based on numeric vertex values.

With normalized = true this is the Pearson correlation of the values x found at the two ends of the edges (Newman’s assortativity coefficient, in [-1, 1]); with normalized = false it is the covariance

cov(x_out, x_in) = 1/m Σ_ij (A_ij - k_i^out k_j^in / m) x_i x_j

For directed graphs (with directed = true) the value of the edge source is taken from values and the one of the target from values_in, if given (otherwise from values as well). Undirected graphs (and directed ones with directed = false) are treated as directed graphs with every edge reciprocated, so self-loops count twice; in that case values_in is ignored, with a warning if given. directed is ignored for undirected graphs.

When weights are given they act as edge multiplicities: m becomes the total weight and degrees become strengths.

Binds igraph_assortativity. See also Graph::assortativity_degree (values = degrees) and Graph::strength (weighted degrees as values). References: M. E. J. Newman, Mixing patterns in networks, Phys. Rev. E 67, 026126 (2003); Assortative mixing in networks, Phys. Rev. Lett. 89, 208701 (2002).

Time complexity: O(|E|).

§Errors

ErrorKind::InvalidValue if a value or weight vector has the wrong length.

§Examples
use igraph::prelude::*;

// A path 0-1-2-3 with values increasing along it: neighbors are alike.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false)?;
let r = g.assortativity(None, &[1.0, 2.0, 3.0, 4.0], None, false, true)?;
assert!(r > 0.0);
// Alternating values: every edge joins a "low" and a "high" vertex.
let r = g.assortativity(None, &[0.0, 1.0, 0.0, 1.0], None, false, true)?;
assert!((r + 1.0).abs() < 1e-12);
Source

pub fn assortativity_nominal( &self, types: &[igraph_int_t], directed: bool, normalized: bool, ) -> Result<f64>

Assortativity coefficient based on vertex categories.

types[v] is the (non-negative integer) category of vertex v. The normalized coefficient (normalized = true, the usual choice) is 1 when all edges stay within categories, -1 for a perfectly disassortative network, and asymptotically 0 for random connections. The unnormalized version equals the modularity of the partition into categories (with resolution 1):

Q = 1/m Σ_ij (A_ij - k_i^out k_j^in / m) δ(t_i, t_j)

and the normalized one is Q divided by its largest possible value 1 - 1/m² Σ_ij k_i^out k_j^in δ(t_i, t_j), i.e. Q / (1 - Σ_t a_t b_t) with a_t (b_t) the fraction of edges starting (ending) in category t. (The C documentation of 1.0.1 writes this denominator as 1/m Σ_ij (m - k_i^out k_j^in δ(t_i, t_j) / m), which is not what the code computes.)

directed says whether to consider edge directions (ignored for undirected graphs, which are treated as directed graphs with reciprocal edges, so self-loops count twice). The null graph gives NaN.

Weighted nominal assortativity is not implemented by igraph (1.0.0 and 1.0.1 fail with IGRAPH_UNIMPLEMENTED when weights are given), so this wrapper takes no weights; use Graph::modularity for the weighted unnormalized value.

Binds igraph_assortativity_nominal. Reference: M. E. J. Newman, Mixing patterns in networks, Phys. Rev. E 67, 026126 (2003).

Time complexity: O(|E| + t), t being the number of categories.

See also Graph::joint_type_distribution, the full mixing matrix of the categories.

§Errors

ErrorKind::InvalidValue if types does not have one entry per vertex or contains negative values.

§Examples
use igraph::prelude::*;

// Two triangles joined by one bridge; categories = triangles.
let g = Graph::from_edges(
    &[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false)?;
let r = g.assortativity_nominal(&[0, 0, 0, 1, 1, 1], false, true)?;
assert!(r > 0.7);
// A bipartite labeling of a bipartite graph is perfectly disassortative.
let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false)?;
let r = square.assortativity_nominal(&[0, 1, 0, 1], false, true)?;
assert!((r + 1.0).abs() < 1e-12);
Source

pub fn assortativity_degree(&self, directed: bool) -> Result<f64>

Degree assortativity: do high-degree vertices link to each other?

The assortativity coefficient with the vertex degrees as values, normalized (Pearson correlation of the degrees at the two ends of the edges). With directed = true on a directed graph, out-degrees are used for edge sources and in-degrees for edge targets; otherwise total degrees are used. Social networks tend to be assortative (> 0), technological and biological ones disassortative (< 0). For regular graphs the correlation is undefined and the result is NaN.

Binds igraph_assortativity_degree. Loops count twice in the degrees and multi-edges are counted with their multiplicity; the unnormalized covariance can be obtained with Graph::assortativity and Graph::strength values.

See also Graph::avg_nearest_neighbor_degree and Graph::degree_correlation_vector, which show the degree correlation as a function of the degree instead of summarizing it in one number.

Time complexity: O(|E| + |V|).

§Examples
use igraph::prelude::*;

// In a star, the hub is only linked to leaves: perfectly disassortative.
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], 5, false)?;
assert!((star.assortativity_degree(false)? + 1.0).abs() < 1e-12);
// A cycle is regular: the coefficient is undefined.
let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
assert!(c.assortativity_degree(false)?.is_nan());
Source

pub fn joint_degree_matrix( &self, weights: Option<&[f64]>, max_out_degree: Option<usize>, max_in_degree: Option<usize>, ) -> Result<Matrix>

Joint degree matrix: number (or total weight) of edges between degree classes.

Entry (i - 1, j - 1) of the result holds J_ij, the number of edges (or the total weight, if weights are given) between vertices of (out-)degree i and vertices of (in-)degree j. Each edge, self-loops included, is counted exactly once: for ordered degree pairs (i, j) in directed graphs, whose entries then sum to the number of edges m (or total weight), and for unordered pairs in undirected graphs, whose matrix is symmetric and whose upper triangle (diagonal included) sums to m (without limits; with limits, only the part that fits). J_ij / m is the probability that a random edge joins degrees i and j.

max_out_degree / max_in_degree set the number of rows / columns; None uses the largest (out-/in-)degree of the graph. Edges whose degree pair falls outside the matrix are not counted. Unlike joint_degree_distribution, there is no row or column for degree zero, and undirected same-degree connections are counted once instead of twice.

Binds igraph_joint_degree_matrix. It is a finer description of a network than its degree sequence: degree-preserving rewiring keeps the degrees but in general changes this matrix. See also joint_degree_distribution. Reference: I. Stanton and A. Pinar, Constructing and sampling graphs with a prescribed joint degree distribution, ACM J. Exp. Algorithmics 17, 3.5 (2012).

Time complexity: O(|E|).

§Errors

ErrorKind::InvalidValue if the weight vector has the wrong length or a limit does not fit an i64.

§Examples
use igraph::prelude::*;

// A star with 3 leaves: 3 edges between degree 3 and degree 1.
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
let j = star.joint_degree_matrix(None, None, None)?;
assert_eq!(j.to_rows(), vec![
    vec![0.0, 0.0, 3.0],
    vec![0.0, 0.0, 0.0],
    vec![3.0, 0.0, 0.0],
]);
Source

pub fn joint_degree_distribution( &self, weights: Option<&[f64]>, options: &JointDegreeDistributionOptions, ) -> Result<Matrix>

Joint degree distribution P_ij of connected vertex pairs.

Entry (i, j) is the probability that a randomly chosen ordered pair of connected vertices u -> v has degrees i (for u, computed with from_mode) and j (for v, computed with to_mode). An undirected graph behaves like the directed graph with all edges reciprocated. Without normalization the entries are connection counts (or total weights): without degree limits they sum to the number of edges of a directed graph (twice that with directed_neighbors = false) and to twice that of an undirected one. Rows and columns for degree 0 are included.

Related quantities: the degree correlation function is k_nn(k) = Σ_j j P_kj / Σ_j P_kj and the unnormalized degree assortativity is Σ_ij i j (P_ij - q_i r_j) with q and r the row and column sums. Compare with joint_degree_matrix, whose undirected diagonal is half of the unnormalized P_ii.

When connections are counted in both directions (undirected graphs, or directed_neighbors = false), each reverse connection v -> u contributes to entry (deg_from(v), deg_to(u)) if it falls within the matrix, and normalization divides by the total weight of the connections that fall within it. In the cases where igraph 1.0.0 and 1.0.1 would index out of bounds (non-square limits, or from_mode != to_mode without directed_neighbors), this wrapper computes the matrix in Rust with exactly these semantics.

Binds igraph_joint_degree_distribution. See also Graph::degree_correlation_vector, which computes k_nn(k) directly, and Graph::assortativity with degree values.

Time complexity: O(|E|).

§Errors

ErrorKind::InvalidValue if the weight vector has the wrong length or a degree limit is i64::MAX or more.

§Examples
use igraph::mixing::JointDegreeDistributionOptions;
use igraph::prelude::*;

let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
let p = star.joint_degree_distribution(None, &JointDegreeDistributionOptions::default())?;
assert_eq!(p.shape(), (4, 4));
// Half of the ordered pairs go hub -> leaf, half leaf -> hub.
assert_eq!(p[(3, 1)], 0.5);
assert_eq!(p[(1, 3)], 0.5);
Source

pub fn joint_type_distribution( &self, weights: Option<&[f64]>, from_types: &[igraph_int_t], to_types: Option<&[igraph_int_t]>, directed: bool, normalized: bool, ) -> Result<Matrix>

Mixing matrix of vertex categories.

Entry (i, j) is proportional to the probability that a randomly chosen ordered pair of connected vertices u -> v has from_types[u] = i and to_types[v] = j (to_types = None reuses from_types). Types must be non-negative integers; the matrix has one more row/column than the largest source/target type, so re-index sparse labels first. Undirected graphs (or directed = false) count each edge in both directions. With normalized = true the entries sum to 1; otherwise they are connection counts (or total weights). When connections are counted in both directions, the reverse connection v -> u of an edge contributes to (from_types[v], to_types[u]); with distinct to_types this case is computed in Rust, because igraph 1.0.0 and 1.0.1 would index out of bounds.

With a single normalized categorization M, row sums a and column sums b, the modularity of the partition is Q = Σ_i M_ii - Σ_i a_i b_i and the nominal assortativity is Q / (1 - Σ_i a_i b_i).

Binds igraph_joint_type_distribution.

Time complexity: O(|E|).

§Errors

ErrorKind::InvalidValue if the weight vector or a type vector has the wrong length, or a type vector contains negative values (checked on the Rust side for to_types too, which igraph 1.0.0 and 1.0.1 themselves forget to validate).

§Examples
use igraph::prelude::*;

// igraph's unit test: a small undirected multigraph with loops and 3 types.
let g = Graph::from_flat_edges(
    &[3, 0, 0, 3, 0, 2, 3, 1, 5, 5, 4, 2, 1, 1, 1, 1, 0, 1, 5, 1], 6, false)?;
let m = g.joint_type_distribution(None, &[0, 0, 1, 1, 2, 2], None, false, false)?;
assert_eq!(m.to_rows(), vec![
    vec![6.0, 4.0, 1.0],
    vec![4.0, 0.0, 1.0],
    vec![1.0, 1.0, 2.0],
]);
Source§

impl igraph_t

Source

pub fn disjoint_union(&self, other: &Graph) -> Result<Graph>

The disjoint union of two graphs.

The vertices of other are relabeled so that the two vertex sets are disjoint: vertex and edge ids of self are unchanged, while those of other are shifted by the vertex and edge counts of self. The result has |V1|+|V2| vertices and |E1|+|E2| edges.

See also decompose, which splits a graph back into its connected components.

Binds igraph_disjoint_union. Time complexity: O(|V1|+|V2|+|E1|+|E2|).

§Errors

ErrorKind::InvalidValue if the two graphs do not have the same directedness.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
let b = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let u = a.disjoint_union(&b).unwrap();
assert_eq!(u.edge_list(), vec![(0, 1), (2, 3), (3, 4)]);
Source

pub fn disjoint_union_many<'a>( graphs: impl IntoIterator<Item = &'a Graph>, ) -> Result<Graph>

The disjoint union of many graphs, laid side by side in the given order (see disjoint_union).

Vertex and edge ids of each operand are shifted by the total vertex and edge counts of the operands before it. With no operand at all the result is a directed graph with no vertices, as in igraph.

Binds igraph_disjoint_union_many. Time complexity: O(|V|+|E|) of the result.

§Errors

ErrorKind::InvalidValue if the graphs have mixed directedness.

§Examples
use igraph::prelude::*;
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
let three = Graph::disjoint_union_many([&triangle, &triangle, &triangle]).unwrap();
assert_eq!((three.vcount(), three.ecount()), (9, 9));
assert_eq!(three.edge(8).unwrap(), (6, 8));
Source

pub fn union(&self, other: &Graph) -> Result<Graph>

The union of two graphs on the same vertex ids: an edge is in the result if it is in at least one of the operands.

The result has as many vertices as the larger operand. Multi-edges are handled by multiplicity: if self has N edges between u and v and other has M, the union has max(N, M). Use union_map to also learn where each edge went.

Binds igraph_union. Time complexity: O(|V|+|E|) of the result.

§Errors

ErrorKind::InvalidValue if the two graphs do not have the same directedness.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let b = Graph::from_edges(&[(2, 1), (2, 3)], 4, false).unwrap();
let u = a.union(&b).unwrap();
assert_eq!(u.vcount(), 4);
assert_eq!(u.edge_list(), vec![(0, 1), (1, 2), (2, 3)]);
Source

pub fn union_map(&self, other: &Graph) -> Result<EdgeMapped>

Like union, also returning the edge maps.

In the returned EdgeMapped, edge_map1[e] is the id in the union of edge e of self (one entry per edge of self), and edge_map2 is the same for other.

Binds igraph_union.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
let b = Graph::from_edges(&[(1, 2), (2, 0)], 3, true).unwrap();
let u = a.union_map(&b).unwrap();
assert_eq!(u.graph.edge_list(), vec![(0, 1), (1, 2), (2, 0)]);
assert_eq!(u.edge_map1, vec![0, 1]);
assert_eq!(u.edge_map2, vec![1, 2]); // the shared edge 1->2 is edge 1
Source

pub fn union_many<'a>( graphs: impl IntoIterator<Item = &'a Graph>, ) -> Result<Graph>

The union of many graphs: an edge is in the result if it is in at least one operand, with the maximum of the multiplicities.

The result has as many vertices as the largest operand. With no operand at all the result is a directed graph with no vertices.

Binds igraph_union_many. Time complexity: O(|V|+|E|), |V| the vertex count of the largest graph and |E| the edge count of the result.

§Errors

ErrorKind::InvalidValue if the graphs have mixed directedness.

§Examples
use igraph::prelude::*;
// The three "spokes" of a star, as three single-edge graphs.
let spokes: Vec<Graph> =
    (1..4).map(|i| Graph::from_edges(&[(0, i)], 0, false).unwrap()).collect();
let star = Graph::union_many(&spokes).unwrap();
let mut edges = star.edge_list();
edges.sort();
assert_eq!(edges, vec![(0, 1), (0, 2), (0, 3)]);
Source

pub fn union_many_map<'a>( graphs: impl IntoIterator<Item = &'a Graph>, ) -> Result<EdgeMappedMany>

Like union_many, also returning, for each operand, the id in the union of each of its edges.

Binds igraph_union_many.

Source

pub fn intersection(&self, other: &Graph) -> Result<Graph>

The intersection of two graphs on the same vertex ids: an edge is in the result if it is in both operands.

The result has as many vertices as the larger operand. Multi-edges are handled by multiplicity: if self has N edges between u and v and other has M, the intersection has min(N, M).

Binds igraph_intersection. Time complexity: O(|V|+|E|), |E| the edge count of the smaller graph.

§Errors

ErrorKind::InvalidValue if the two graphs do not have the same directedness.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let b = Graph::from_edges(&[(2, 1), (3, 0), (3, 2)], 4, false).unwrap();
assert_eq!(a.intersection(&b).unwrap().edge_list(), vec![(1, 2), (2, 3)]);
Source

pub fn intersection_map(&self, other: &Graph) -> Result<EdgeMapped>

Like intersection, also returning the edge maps.

Note the direction of the maps, which differs from union_map: they are indexed by the edges of the result, i.e. edge_map1[e] is the id in self of edge e of the intersection, and edge_map2[e] its id in other. Both have as many entries as the intersection has edges.

Binds igraph_intersection.

§Examples

Taken from igraph’s igraph_intersection.c example:

use igraph::prelude::*;
let left = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 0, true).unwrap();
let right = Graph::from_edges(&[(1, 0), (5, 4), (1, 2), (3, 2)], 0, true).unwrap();
let isec = left.intersection_map(&right).unwrap();
assert_eq!(isec.graph.edge_list(), vec![(1, 2)]);
assert_eq!((isec.edge_map1, isec.edge_map2), (vec![1], vec![2]));
Source

pub fn intersection_many<'a>( graphs: impl IntoIterator<Item = &'a Graph>, ) -> Result<Graph>

The intersection of many graphs: an edge is in the result if it is in every operand, with the minimum of the multiplicities.

The result has as many vertices as the largest operand. With no operand at all the result is a directed graph with no vertices.

Binds igraph_intersection_many. Time complexity: O(|V|+|E|), |E| the edge count of the smallest graph.

§Errors

ErrorKind::InvalidValue if the graphs have mixed directedness.

§Examples
use igraph::prelude::*;
let a = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let b = Graph::from_edges(&[(0, 1), (1, 2)], 4, false).unwrap();
let c = Graph::from_edges(&[(1, 2), (0, 3)], 4, false).unwrap();
assert_eq!(Graph::intersection_many([&a, &b, &c]).unwrap().edge_list(), vec![(1, 2)]);
Source

pub fn intersection_many_map<'a>( graphs: impl IntoIterator<Item = &'a Graph>, ) -> Result<EdgeMappedMany>

Like intersection_many, also returning, for each operand, the id in the result of each of its edges (None for edges that are not in the intersection).

Unlike intersection_map, these maps are indexed by the edges of the operands.

Binds igraph_intersection_many.

Source

pub fn difference(&self, sub: &Graph) -> Result<Graph>

The difference of two graphs: the edges of self that are not in sub, on the vertex set of self.

Multi-edges are subtracted by multiplicity. The result always has the same number of vertices as self.

Binds igraph_difference. Time complexity: O(|V|+|E|), |V| the vertex count of the smaller graph and |E| the edge count of the result.

§Errors

ErrorKind::InvalidValue if the two graphs do not have the same directedness.

§Examples

From igraph’s igraph_difference.c example:

use igraph::prelude::*;
let orig = Graph::from_edges(&[(0, 1), (1, 2), (2, 1), (4, 5), (8, 9)], 0, true).unwrap();
let sub = Graph::from_edges(&[(0, 1), (5, 4), (2, 1), (6, 7)], 0, true).unwrap();
let diff = orig.difference(&sub).unwrap();
assert_eq!(diff.vcount(), 10);
assert_eq!(diff.edge_list(), vec![(1, 2), (4, 5), (8, 9)]);
Source

pub fn join(&self, other: &Graph) -> Result<Graph>

The join of two graphs: their disjoint union plus an edge between every vertex of self and every vertex of other.

The result has |V1|+|V2| vertices and |E1|+|E2|+|V1||V2| edges; for directed graphs both (v, u) and (u, v) are added, i.e. |E1|+|E2|+2|V1||V2| edges. Vertex ids of other are shifted by |V1|. For example, the join of an empty graph on m vertices and one on n vertices is the complete bipartite graph K(m,n).

See also Graph::full_bipartite and Graph::wheel, which build the classic joins (K(m,n), and a single vertex joined to a cycle) directly.

Binds igraph_join. Time complexity: O(|V1||V2|+|E1|+|E2|).

§Errors

ErrorKind::InvalidValue if the two graphs do not have the same directedness.

§Examples
use igraph::prelude::*;
// K(2,3) as the join of two edgeless graphs.
let k23 = Graph::new(2, false).join(&Graph::new(3, false)).unwrap();
assert_eq!(k23.ecount(), 6);
assert_eq!(k23.neighbors(0, NeighborMode::All).unwrap(), vec![2, 3, 4]);
assert_eq!(k23, Graph::full_bipartite(2, 3, false, NeighborMode::All).unwrap().graph);
Source

pub fn complementer(&self, loops: bool) -> Result<Graph>

The complement of the graph: all the edges that are not in self.

With loops = true self-loops are added to every vertex that has none. For directed graphs, edge directions are taken into account. Multi-edges in the input are treated like single edges.

See also Graph::full: the complement of the edgeless graph on n vertices is the complete graph K_n.

Binds igraph_complementer. Time complexity: O(|V|+|E1|+|E2|), |E1| and |E2| the edge counts of the graph and of its complement.

§Examples
use igraph::prelude::*;
// The path 0-1-2-3 is self-complementary: its complement is 2-0-3-1.
let p4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let mut comp = p4.complementer(false).unwrap().edge_list();
comp.sort();
assert_eq!(comp, vec![(0, 2), (0, 3), (1, 3)]);
Source

pub fn compose(&self, other: &Graph) -> Result<Graph>

The composition self ∘ other of two graphs, seen as binary relations.

The result contains an edge (i, j) for every vertex k such that self has an edge (i, k) and other has an edge (k, j): it may thus contain multi-edges (one per such k) and self-loops (e.g. (i, i) from i -> k in self and k -> i in other, which for undirected graphs happens for every edge the two operands share at k). The result has as many vertices as the larger operand.

Binds igraph_compose. Time complexity: O(|V| d1 d2), d1 and d2 the average degrees.

§Errors

ErrorKind::InvalidValue if the two graphs do not have the same directedness.

§Examples
use igraph::prelude::*;
// "parent of" composed with itself is "grandparent of".
let parent = Graph::from_edges(&[(0, 1), (1, 2), (1, 3), (3, 4)], 5, true).unwrap();
let mut grandparent = parent.compose(&parent).unwrap().edge_list();
grandparent.sort();
assert_eq!(grandparent, vec![(0, 2), (0, 3), (1, 4)]);
Source

pub fn compose_map(&self, other: &Graph) -> Result<EdgeMapped>

Like compose, also returning the edge maps: edge_map1[e] is the edge (i, k) of self, and edge_map2[e] the edge (k, j) of other, that generated edge e of the result.

Binds igraph_compose.

Source

pub fn contract_vertices(&mut self, mapping: &[VertexId]) -> Result<()>

Merges groups of vertices into single vertices, in place.

mapping[v] is the id, in the contracted graph, of the original vertex v (so mapping must have one entry per vertex). To avoid isolated “orphan” vertices, the new ids should be the consecutive integers 0..k; the contracted graph has max(mapping) + 1 vertices. No edge is removed: edges inside a group become self-loops and parallel connections between groups become multi-edges; call simplify afterwards to clean them up. Vertex attributes are discarded (edge and graph attributes are kept): use contract_vertices_with_attributes to combine the vertex attributes of each group instead.

A typical mapping is a community membership vector, e.g. from community_multilevel, which contracts every community to a single vertex. See also induced_subgraph to zoom into a group instead.

Binds igraph_contract_vertices. Time complexity: O(|V|+|E|).

§Errors

ErrorKind::InvalidValue if mapping does not have one entry per vertex or contains negative ids or VertexId::MAX (the vertex count max(mapping) + 1 would overflow).

§Examples
use igraph::prelude::*;
// Two triangles joined by an edge, contracted to two "super-vertices".
let mut g = Graph::from_edges(
    &[(0, 1), (1, 2), (2, 0), (3, 4), (4, 5), (5, 3), (2, 3)], 6, false,
).unwrap();
g.contract_vertices(&[0, 0, 0, 1, 1, 1]).unwrap();
assert_eq!((g.vcount(), g.ecount()), (2, 7));
g.simplify(true, true).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1)]);
Source

pub fn permute_vertices(&self, permutation: &[VertexId]) -> Result<Graph>

Relabels the vertices according to a permutation, returning a new graph.

permutation[i] is the id, in the original graph, of the vertex that becomes vertex i of the result. Edge ids are unchanged. Use it e.g. with a canonical permutation to obtain the canonical form of a graph.

See also canonical_permutation and canonical_form (isomorphism), which compute such a permutation and apply it.

Binds igraph_permute_vertices. Time complexity: O(|V|+|E|).

§Errors

ErrorKind::InvalidValue if permutation is not a permutation of 0..vcount.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, true).unwrap();
// New vertex 0 is old vertex 2, new 1 is old 0, new 2 is old 1.
let h = g.permute_vertices(&[2, 0, 1]).unwrap();
assert_eq!(h.edge_list(), vec![(1, 2), (2, 0)]);

// Relabeled copies share the same canonical form.
let canon = |g: &Graph| g.permute_vertices(&g.canonical_permutation(None).unwrap()).unwrap();
assert_eq!(canon(&g), canon(&h));
Source

pub fn connect_neighborhood( &mut self, order: usize, mode: NeighborMode, ) -> Result<()>

Connects every vertex to all the vertices reachable from it in at most order steps, in place.

Existing connections are not duplicated, and for undirected graphs a single edge is added per pair. For directed graphs mode tells how to search: with NeighborMode::Out each vertex u gets an edge u -> v to every v it reaches along directed paths; with NeighborMode::In every v reaching u gets an edge v -> u (so the new edges still follow the paths: the same set of edges is added as with Out, possibly in another order); NeighborMode::All ignores directions and adds a single edge u -> v with u < v per newly connected pair. Orders below 2 leave the graph unchanged. See graph_power for a non-mutating variant that also simplifies.

See also neighborhood (components), which lists the vertices within order steps without changing the graph.

Binds igraph_connect_neighborhood. Time complexity: O(|V| d^k), d the average degree and k the order.

§Errors

ErrorKind::InvalidValue if order does not fit in an igraph_int_t.

§Examples
use igraph::prelude::*;
// A ring of 6 vertices where everybody also knows the neighbors' neighbors.
let edges: Vec<(i64, i64)> = (0..6).map(|i| (i, (i + 1) % 6)).collect();
let mut g = Graph::from_edges(&edges, 6, false).unwrap();
g.connect_neighborhood(2, NeighborMode::All).unwrap();
assert_eq!(g.ecount(), 12);
assert_eq!(g.degree(.., NeighborMode::All, Loops::Twice).unwrap(), vec![4; 6]);
Source

pub fn graph_power(&self, order: usize, directed: bool) -> Result<Graph>

The order-th power of the graph.

It is a simple graph on the same vertices where u is connected to v if v is reachable from u in at most order steps. The zeroth power has no edges; the first power is the graph with multi-edges and loops removed. With directed = false edge directions are ignored and the result is undirected. Graph and vertex attributes are kept, edge attributes are discarded.

For directed inputs this wrapper clears igraph’s cached graph properties around the call, working around an igraph 1.0.1 bug that could otherwise return a double edge for a mutual pair u -> v, v -> u when directions are ignored, or abort the process on a later call (see the source for details). The result is always simple.

Binds igraph_graph_power. Time complexity: O(|V| d^k), d the average degree and k the order.

§Errors

ErrorKind::InvalidValue if order does not fit in an igraph_int_t.

§Examples
use igraph::prelude::*;
// The square of the path 0-1-2-3.
let p4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let sq = p4.graph_power(2, false).unwrap();
assert_eq!(sq.ecount(), 5); // everything but (0, 3)
assert_eq!(sq.get_eid(0, 3, false).unwrap(), None);
Source

pub fn rewire( &mut self, trials: usize, allowed: EdgeTypeSw, ) -> Result<RewiringStats>

Randomly rewires the graph in place, preserving its degree sequence.

Performs trials degree-preserving edge switches: two edges (a, b) and (c, d) are picked uniformly at random and replaced by (a, d) and (c, b), provided the result respects allowed: EdgeTypeSw::Simple forbids loops and multi-edges, EdgeTypeSw::Loops allows (single) self-loops. Multigraphs (EdgeTypeSw::Multi) are not supported yet by igraph. For directed graphs both in- and out-degrees are preserved. All attributes are lost.

Draws from the calling thread’s default random number generator (each thread has its own): seed it with rng::seed, or install a private generator with Rng::scoped, for reproducible results. Returns the number of switches actually performed.

See also rewire_edges, which rewires edge endpoints with a given probability (not preserving degrees), and degree_sequence_game, which samples a new graph with a prescribed degree sequence (games).

Binds igraph_rewire.

Graphs with fewer than two edges cannot be rewired: they are left unchanged and zero swaps are reported.

§Errors

ErrorKind::Unimplemented for EdgeTypeSw::Multi, which igraph does not support yet; ErrorKind::InvalidValue if trials does not fit in an igraph_int_t.

§Examples
use igraph::prelude::*;
rng::seed(42).unwrap();
let edges: Vec<(i64, i64)> = (0..10).map(|i| (i, (i + 1) % 10)).collect();
let mut g = Graph::from_edges(&edges, 10, false).unwrap();
let stats = g.rewire(100, EdgeTypeSw::Simple).unwrap();
assert!(stats.successful_swaps > 0);
// Still 2-regular.
assert_eq!(g.degree(.., NeighborMode::All, Loops::Twice).unwrap(), vec![2; 10]);
Source

pub fn simplify( &mut self, remove_multiple: bool, remove_loops: bool, ) -> Result<()>

Removes multi-edges and/or self-loops, in place.

With remove_multiple, parallel edges are merged into one; with remove_loops, self-loops are deleted. The edge order may change, even if the graph was already simple.

With the attribute handler on, graph and vertex attributes are always kept. Edge attributes are discarded whenever igraph rebuilds the edge set to merge multi-edges, i.e. when remove_multiple is true and igraph does not already know (from its property cache) that the graph has no multi-edges, even if no edge actually gets merged. When only loops are removed (or there is nothing to do) the remaining edges keep their attributes. Use simplify_with_attributes to combine the attributes of the merged edges instead (e.g. summing their weights), and contract_vertices_with_attributes for the analogous vertex operation.

See also is_simple, has_multiple and count_multiple (structural) to inspect a graph before simplifying it.

Binds igraph_simplify. Time complexity: O(|V|+|E|).

§Examples
use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 0), (1, 1), (1, 2), (1, 2)], 3, false).unwrap();
let mut keep_loops = g.clone();
keep_loops.simplify(true, false).unwrap();
assert_eq!(keep_loops.edge_list(), vec![(0, 1), (1, 1), (1, 2)]);
g.simplify(true, true).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1), (1, 2)]);
Source

pub fn induced_subgraph<'a>( &self, vids: impl Into<VertexSelector<'a>>, implementation: SubgraphImplementation, ) -> Result<Graph>

The subgraph induced by the selected vertices: those vertices and all the edges among them.

Duplicate vertices in the selector are considered once and the selection order is ignored: the subgraph keeps the vertices in increasing order of their original ids (so vertex i of the subgraph is the i-th smallest selected id). implementation picks the strategy: SubgraphImplementation::CopyAndDelete is best when keeping most of the graph, SubgraphImplementation::CreateFromScratch when extracting a small part; SubgraphImplementation::Auto chooses based on the ratio of the sizes. Use induced_subgraph_map to get the id correspondence.

See also delete_vertices (the in-place complement of this operation), neighborhood_graphs and decompose (components).

Binds igraph_induced_subgraph. Time complexity: O(|V|+|E|) of the original graph.

§Errors

ErrorKind::InvalidVertexId for invalid vertex ids.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false).unwrap();
let tri = g.induced_subgraph(&[2, 0, 1], SubgraphImplementation::Auto).unwrap();
assert_eq!(tri.edge_list(), vec![(0, 1), (1, 2), (0, 2)]);
Source

pub fn induced_subgraph_map<'a>( &self, vids: impl Into<VertexSelector<'a>>, implementation: SubgraphImplementation, ) -> Result<InducedSubgraph>

Like induced_subgraph, also returning the maps between the original vertex ids and the subgraph’s.

Binds igraph_induced_subgraph_map.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
let sub = g.induced_subgraph_map(&[3, 1, 2], SubgraphImplementation::CreateFromScratch).unwrap();
assert_eq!(sub.invmap, vec![1, 2, 3]);
assert_eq!(sub.map, vec![None, Some(0), Some(1), Some(2), None]);
assert_eq!(sub.graph.edge_list(), vec![(0, 1), (1, 2)]);
Source

pub fn induced_subgraph_edges<'a>( &self, vids: impl Into<VertexSelector<'a>>, ) -> Result<Vec<EdgeId>>

Ids of the edges of the subgraph induced by the selected vertices, i.e. of the edges having both endpoints in the selection.

Binds igraph_induced_subgraph_edges. Time complexity: O(mv log(nv)), nv the number of selected vertices and mv the sum of their degrees.

§Errors

ErrorKind::InvalidVertexId for invalid vertex ids.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false).unwrap();
let mut inside = g.induced_subgraph_edges(&[0, 1, 2]).unwrap();
inside.sort();
assert_eq!(inside, vec![0, 1, 4]);
Source

pub fn subgraph_from_edges<'a>( &self, eids: impl Into<EdgeSelector<'a>>, delete_vertices: bool, ) -> Result<Graph>

The subgraph made of the selected edges (and their endpoints).

Edge ids are reassigned consecutively, keeping the original order. With delete_vertices = true, vertices not incident to any selected edge are removed as well (and vertex ids reassigned in increasing order); otherwise the subgraph keeps all the vertices of self. Attributes are preserved.

Binds igraph_subgraph_from_edges. Time complexity: O(|V|+|E|) of the original graph.

§Errors

ErrorKind::InvalidEdgeId for invalid edge ids.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4)], 5, false).unwrap();
let kept = g.subgraph_from_edges(&[1, 2], true).unwrap();
assert_eq!((kept.vcount(), kept.edge_list()), (3, vec![(0, 1), (1, 2)]));
let spanning = g.subgraph_from_edges(&[1, 2], false).unwrap();
assert_eq!((spanning.vcount(), spanning.edge_list()), (5, vec![(1, 2), (2, 3)]));
Source

pub fn reverse_edges<'a>( &mut self, eids: impl Into<EdgeSelector<'a>>, ) -> Result<()>

Reverses the direction of the selected edges, in place.

Attributes and the order of vertices and edges are preserved. Pass .. to reverse all edges (this is O(1)); it is rarely needed, since most functions accept NeighborMode::In to walk edges backwards. Undirected graphs are left unchanged (and eids is then not even validated). An edge listed twice is reversed twice, i.e. it ends up with its original direction.

Binds igraph_reverse_edges. Time complexity: O(1) for all edges, O(|E|) otherwise.

§Errors

ErrorKind::InvalidEdgeId for invalid edge ids.

§Examples
use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
g.reverse_edges(1).unwrap();
assert_eq!(g.edge_list(), vec![(0, 1), (2, 1), (2, 3)]);
g.reverse_edges(..).unwrap();
assert_eq!(g.edge_list(), vec![(1, 0), (1, 2), (3, 2)]);
Source

pub fn product(&self, other: &Graph, kind: Product) -> Result<Graph>

A graph product of self and other (experimental in igraph).

The vertices of the product are the pairs (u, v) with u in self and v in other, numbered u * |V2| + v. Writing u ~ u' for adjacency, (u, v) is connected to (u', v') when:

Productconditionedges (undirected)
Cartesianu = u' and v ~ v', or u ~ u' and v = v'|V1||E2| + |V2||E1|
Lexicographicu = u' and v ~ v', or u ~ u'|V1||E2| + |V2|²|E1|
StrongCartesian or tensor condition|V1||E2| + |V2||E1| + 2|E1||E2|
Tensoru ~ u' and v ~ v'2|E1||E2|
Modularboth adjacent or both non-adjacent (simple graphs only)2|E1||E2| + 2|E1'||E2'|

In the directed case the factor 2 disappears. All these products are associative; the lexicographic one is not commutative.

See also Graph::square_lattice and Graph::hypercube: grids and hypercubes are iterated Cartesian products of paths, cycles and K2, built directly.

Binds igraph_product.

§Errors

ErrorKind::InvalidValue if the two graphs have different directedness, or are not simple for the modular product.

§Examples
use igraph::prelude::*;
// The 3x4 grid is the Cartesian product of two paths.
let p3 = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let p4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let grid = p3.product(&p4, Product::Cartesian).unwrap();
assert_eq!((grid.vcount(), grid.ecount()), (12, 3 * 3 + 4 * 2));
let lattice = Graph::square_lattice(&[4, 3], 1, false, false, None).unwrap();
assert!(grid.isomorphic(&lattice).unwrap());
Source

pub fn rooted_product(&self, other: &Graph, root: VertexId) -> Result<Graph>

The rooted product of self and other with root root in other (experimental in igraph).

A copy of other is attached to every vertex u of self, glued at its root: (u, v) is connected to (u', v') if u = u' and v ~ v', or u ~ u' and v = v' = root. Vertex ids follow the same u * |V2| + v convention as product; the result has |V1||E2| + |E1| edges.

Binds igraph_rooted_product. Time complexity: O(|V1||V2| + |V1||E2| + |E1|).

§Errors

ErrorKind::InvalidVertexId if root is not a vertex of other; ErrorKind::InvalidValue for mixed directedness.

§Examples
use igraph::prelude::*;
// A "comb": a path with a pendant edge hanging from each vertex.
let spine = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let tooth = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
let comb = spine.rooted_product(&tooth, 0).unwrap();
assert_eq!((comb.vcount(), comb.ecount()), (6, 5));
assert_eq!(comb.degree(.., NeighborMode::All, Loops::Twice).unwrap(), vec![2, 1, 3, 1, 2, 1]);
Source

pub fn mycielskian(&self, k: usize) -> Result<Graph>

The k-times iterated Mycielskian of the graph (experimental in igraph).

Mycielski’s construction increases the chromatic number by one while keeping the graph triangle-free. From G with vertices v_1..v_n it builds M(G): G itself, a copy u_i of every v_i, and a new vertex w; each u_i is joined to w and, for every edge (v_i, v_j), the edges (u_i, v_j) and (v_i, u_j) are added. So M(G) has 2n + 1 vertices and 3m + n edges; after k iterations there are (n + 1) 2^k - 1 vertices. The Mycielskian of the null graph is the singleton and that of the singleton is the 2-path, so that iterating from them yields the connected Mycielski graphs (the Grötzsch graph after 4 steps from the null graph).

See also Graph::mycielski_graph, which builds the Mycielski graphs M_k directly; M_k is the (k - 2)-times iterated Mycielskian of K2, up to relabeling.

Binds igraph_mycielskian. Time complexity: O(|V| 2^k + |E| 3^k).

§Errors

ErrorKind::InvalidValue if k does not fit in an igraph_int_t, and ErrorKind::Overflow if the result would be too large.

§Examples
use igraph::prelude::*;
// M(K2) is the 5-cycle, M(C5) is the Grötzsch graph.
let k2 = Graph::from_edges(&[(0, 1)], 2, false).unwrap();
let c5 = k2.mycielskian(1).unwrap();
assert_eq!((c5.vcount(), c5.ecount()), (5, 5));
let grotzsch = k2.mycielskian(2).unwrap();
assert_eq!((grotzsch.vcount(), grotzsch.ecount()), (11, 20));
assert!(grotzsch.isomorphic(&Graph::famous("Grotzsch").unwrap()).unwrap());
assert_eq!(grotzsch.girth().unwrap(), Some(4)); // still triangle-free
Source§

impl igraph_t

Source

pub fn diameter(&self) -> Result<f64>

The diameter of the graph: the length of its longest shortest path.

This is the backwards-compatible shorthand of diameter_with_path for the most common case: unweighted, following edge directions in directed graphs (directed = self.is_directed()), and, for disconnected graphs, returning the longest geodesic within a component (unconn = true). The diameter of the null graph is NaN.

See also girth, the length of the shortest cycle, and radius, the smallest eccentricity.

Binds igraph_diameter. Time complexity: O(|V| |E|).

§Examples
use igraph::prelude::*;
// A path on 5 vertices has diameter 4 ...
let path = Graph::path_graph(5, false, false).unwrap();
assert_eq!(path.diameter().unwrap(), 4.0);
// ... and closing it into a cycle halves it.
let mut cycle = path.clone();
cycle.add_edge(4, 0).unwrap();
assert_eq!(cycle.diameter().unwrap(), 2.0);
assert!(Graph::new(0, false).diameter().unwrap().is_nan());
Source

pub fn diameter_with_path( &self, weights: Option<&[f64]>, directed: bool, unconn: bool, ) -> Result<Diameter>

The (weighted) diameter of the graph together with its endpoints and one longest geodesic.

The diameter is the maximum eccentricity of the vertices, i.e. the length of the longest shortest path.

  • weights: optional edge lengths (Dijkstra’s algorithm is used when given); edges with positive infinite weight are ignored.
  • directed: whether to follow edge directions (ignored for undirected graphs).
  • unconn: for disconnected graphs, true returns the longest geodesic within a component, false returns INFINITY.

The null graph has diameter NaN; from/to are None and the path is empty when there is no diameter path.

Binds igraph_diameter. Time complexity: O(|V| |E|) unweighted, O(|V| |E| log |E|) weighted.

§Examples
use igraph::prelude::*;
// The directed path 0 → 1 → ... → 9 (a non-circular directed ring, as
// in igraph's own example).
let ring = Graph::ring(10, true, false, false).unwrap();
let d = ring.diameter_with_path(None, true, true).unwrap();
assert_eq!(d.length, 9.0);
assert_eq!((d.from, d.to), (Some(0), Some(9)));
assert_eq!(d.path.vertices, (0..10).collect::<Vec<_>>());
assert_eq!(d.path.edges, (0..9).collect::<Vec<_>>());
Source

pub fn pseudo_diameter( &self, weights: Option<&[f64]>, start: Option<VertexId>, directed: bool, unconn: bool, ) -> Result<PseudoDiameter>

An approximation (and lower bound) of the diameter, computed from a pseudo-peripheral vertex.

A pseudo-peripheral vertex v is such that for every vertex u as far away from v as possible, v is also as far away from u as possible. The search starts at start (a random vertex when None); in disconnected graphs the result refers to the component of the start vertex. directed and unconn have the same meaning as in diameter_with_path: with unconn = false a disconnected graph gives INFINITY and no endpoints. Returns NaN for the null graph. With start = None the start vertex is drawn from the calling thread’s default random number generator (seed it with rng::seed for reproducible results).

§Errors

ErrorKind::InvalidVertexId if start is not a vertex of the graph (negative ids included).

Binds igraph_pseudo_diameter. Time complexity: O(|V| |E| log |E|).

§Examples
use igraph::prelude::*;
let path = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let pd = path.pseudo_diameter(None, Some(1), false, true).unwrap();
// On trees the pseudo-diameter is exact.
assert_eq!(pd.length, 3.0);
Source

pub fn eccentricity<'a>( &self, vids: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<f64>>

Eccentricity of the selected vertices: the largest distance from (or to, depending on mode) each vertex to any vertex reachable from it.

Vertex pairs in different components are ignored, so isolated vertices have eccentricity zero. weights must be non-negative and not NaN; edges with infinite weight are ignored. The maximum eccentricity is the diameter, the minimum the radius. See also closeness, which averages the distances instead of taking their maximum.

Binds igraph_eccentricity. Time complexity: O(|V| |E| log |V| + |V|).

§Examples
use igraph::prelude::*;
// A star: the center has eccentricity 1, the leaves 2.
let star = Graph::star(4, StarMode::Undirected, 0).unwrap();
assert_eq!(star.eccentricity(.., None, NeighborMode::All).unwrap(), vec![1.0, 2.0, 2.0, 2.0]);
// Only some vertices, in the requested order.
assert_eq!(star.eccentricity(vec![3, 0], None, NeighborMode::All).unwrap(), vec![2.0, 1.0]);
Source

pub fn radius(&self, weights: Option<&[f64]>, mode: NeighborMode) -> Result<f64>

The radius of the graph: the smallest eccentricity of its vertices (NaN for the null graph).

Binds igraph_radius. Time complexity: O(|V| |E| log |V| + |V|).

§Examples
use igraph::prelude::*;
let path = Graph::path_graph(5, false, false).unwrap();
assert_eq!(path.radius(None, NeighborMode::All).unwrap(), 2.0);
// Weighted: stretching the first edge moves the center towards it.
let w = [10.0, 1.0, 1.0, 1.0];
assert_eq!(path.radius(Some(&w), NeighborMode::All).unwrap(), 10.0);
Source

pub fn graph_center( &self, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<VertexId>>

The center of the graph: the vertices of minimum eccentricity.

In disconnected graphs the minimum is taken across all components. This function is marked experimental in igraph.

Binds igraph_graph_center. Time complexity: O(|V| |E| log |V| + |V|).

§Examples
use igraph::prelude::*;
// The center of a path with an even number of vertices is its middle edge.
let path = Graph::path_graph(4, false, false).unwrap();
assert_eq!(path.graph_center(None, NeighborMode::All).unwrap(), vec![1, 2]);
Source

pub fn average_path_length( &self, weights: Option<&[f64]>, directed: bool, unconn: bool, ) -> Result<f64>

The average shortest path length over all ordered pairs of distinct vertices.

  • weights: optional non-negative edge lengths.
  • directed: whether to follow edge directions (ignored for undirected graphs).
  • unconn: if true, only pairs connected by a path are averaged; if false, disconnected graphs give INFINITY.

Returns NaN when no pair can be included (e.g. fewer than two vertices). See average_path_length_details to also obtain the number of disconnected pairs, and closeness for the per-vertex (inverse) averages.

Binds igraph_average_path_length. Time complexity: O(|V| |E| log |E| + |V|).

§Examples
use igraph::prelude::*;
// In a triangle every pair is adjacent.
let k3 = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false).unwrap();
assert_eq!(k3.average_path_length(None, false, true).unwrap(), 1.0);
// Path 0-1-2: distances 1, 1, 2 → average 4/3.
let p3 = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert!((p3.average_path_length(None, false, true).unwrap() - 4.0 / 3.0).abs() < 1e-12);
Source

pub fn average_path_length_details( &self, weights: Option<&[f64]>, directed: bool, unconn: bool, ) -> Result<AveragePathLength>

Like average_path_length, also returning the number of ordered vertex pairs (u, v) with v unreachable from u.

Binds igraph_average_path_length.

§Examples
use igraph::prelude::*;
// Two disjoint edges: 4 ordered connected pairs, 8 unconnected ones.
let g = Graph::from_edges(&[(0, 1), (2, 3)], 4, false).unwrap();
let apl = g.average_path_length_details(None, false, true).unwrap();
assert_eq!(apl.average, 1.0);
assert_eq!(apl.unconnected_pairs, 8.0);
Source

pub fn path_length_hist(&self, directed: bool) -> Result<PathLengthHistogram>

Histogram of the (unweighted) shortest path lengths between all vertex pairs.

counts[0] is the number of pairs at distance 1, counts[1] at distance 2, and so on. In undirected graphs (or with directed = false) each unordered pair is counted once; in directed graphs with directed = true both directions are counted.

Binds igraph_path_length_hist. Time complexity: O(|V| |E|).

§Examples
use igraph::prelude::*;
// A path on 4 vertices: 3 pairs at distance 1, 2 at distance 2, 1 at distance 3.
let p4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let h = p4.path_length_hist(false).unwrap();
assert_eq!(h.counts, vec![3.0, 2.0, 1.0]);
assert_eq!(h.unconnected, 0.0);
Source

pub fn global_efficiency( &self, weights: Option<&[f64]>, directed: bool, ) -> Result<f64>

The global efficiency of the network: the average of the inverse distances between all ordered pairs of distinct vertices, E = 1/(N(N-1)) Σ_{i≠j} 1/d_ij (Latora & Marchiori, 2001).

Unreachable pairs contribute zero, so, unlike the average path length, it is well defined for disconnected graphs; graphs with fewer than two vertices give NaN. It equals the mean of the normalized harmonic_centrality of the vertices.

Binds igraph_global_efficiency. Time complexity: O(|V| |E|) unweighted, O(|V| |E| log |E| + |V|) weighted.

§Examples
use igraph::prelude::*;
let k4 = Graph::full(4, false, false).unwrap();
assert_eq!(k4.global_efficiency(None, false).unwrap(), 1.0);
// Path 0-1-2: inverse distances 1, 1, 1/2 (each counted twice) → 5/6.
let p3 = Graph::path_graph(3, false, false).unwrap();
assert!((p3.global_efficiency(None, false).unwrap() - 5.0 / 6.0).abs() < 1e-12);
Source

pub fn local_efficiency<'a>( &self, vids: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, directed: bool, mode: NeighborMode, ) -> Result<Vec<f64>>

The local efficiency around each selected vertex.

The vertex is removed and the average inverse distance between its neighbors (through the rest of the network) is computed. Unreachable pairs contribute zero; vertices with fewer than two neighbors have local efficiency zero. mode selects which neighbors form the local neighborhood in directed graphs (NeighborMode::All is a sensible default), directed whether distances follow edge directions. It is a distance based analogue of the local clustering coefficient (transitivity_local_undirected).

Binds igraph_local_efficiency. Time complexity: O(|E|² log |E|) weighted, O(|E|²) unweighted.

§Examples
use igraph::prelude::*;
// In a 4-cycle the two neighbors of a vertex are at distance 2 once it is removed.
let c4 = Graph::cycle_graph(4, false, false).unwrap();
assert_eq!(c4.local_efficiency(.., None, false, NeighborMode::All).unwrap(), vec![0.5; 4]);
Source

pub fn average_local_efficiency( &self, weights: Option<&[f64]>, directed: bool, mode: NeighborMode, ) -> Result<f64>

The average of the local efficiencies of all vertices (zero for the null graph).

Binds igraph_average_local_efficiency. Time complexity: O(|E|² log |E|) weighted, O(|E|²) unweighted.

§Examples
use igraph::prelude::*;
let c4 = Graph::cycle_graph(4, false, false).unwrap();
assert_eq!(c4.average_local_efficiency(None, false, NeighborMode::All).unwrap(), 0.5);
Source§

impl igraph_t

Source

pub fn distances<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Matrix>

Shortest path lengths between the from and to vertices.

Row i of the result holds the distances from the i-th source to every target (INFINITY if unreachable). to must not contain duplicates. mode chooses outgoing (Out), incoming (In) or undirected (All) paths in directed graphs.

With weights = None a BFS is used; with weights, igraph picks the most suitable algorithm: Floyd–Warshall for dense all-pairs problems, Dijkstra for non-negative weights, and Bellman–Ford or Johnson when negative weights are present.

See also bfs (which also reports BFS distances), neighborhood (the vertices within a given distance) and closeness / harmonic_centrality, which summarize the rows of this matrix.

Binds igraph_distances. Time complexity (unweighted): O(n(|V| + |E|)) for n sources.

§Errors

ErrorKind::InvalidVertexId for invalid vertices, ErrorKind::NegativeCycle if negative weights form a negative cycle.

§Examples
use igraph::prelude::*;
let p = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
let d = p.distances(.., .., None, NeighborMode::All).unwrap();
assert_eq!(d.to_rows(), vec![vec![0.0, 1.0, 2.0], vec![1.0, 0.0, 1.0], vec![2.0, 1.0, 0.0]]);
// Only from vertex 0 to vertices 1 and 2:
let d = p.distances(0, vec![1, 2], None, NeighborMode::All).unwrap();
assert_eq!(d.to_rows(), vec![vec![1.0, 2.0]]);
Source

pub fn distances_cutoff<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, cutoff: Option<f64>, ) -> Result<Matrix>

Like distances, but paths longer than cutoff are ignored (their length is reported as INFINITY).

cutoff = None (or a negative value) means no cutoff. The search from each source stops at the cutoff, which may save much time. With weights this is the same as distances_dijkstra_cutoff.

§Errors

ErrorKind::InvalidValue for a NaN cutoff or invalid weights, ErrorKind::InvalidVertexId for invalid vertices.

Binds igraph_distances_cutoff. Time complexity: O(s |E| + |V|) for s sources (unweighted).

§Examples
use igraph::prelude::*;
let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let d = p.distances_cutoff(0, .., None, NeighborMode::All, Some(2.0)).unwrap();
assert_eq!(d.row(0), vec![0.0, 1.0, 2.0, f64::INFINITY]);
Source

pub fn distances_dijkstra<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Matrix>

Weighted shortest path lengths with Dijkstra’s algorithm (binary heap), run independently from each source.

Weights must be non-negative and not NaN; None falls back to the unweighted distances.

Binds igraph_distances_dijkstra. Time complexity: O(s |E| log |V| + |V|) for s sources.

§Errors

ErrorKind::InvalidValue for negative or NaN weights, or a weight vector of the wrong length.

§Examples
use igraph::prelude::*;
let tri = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, false).unwrap();
let d = tri.distances_dijkstra(0, 2, Some(&[1.0, 1.0, 5.0]), NeighborMode::All).unwrap();
assert_eq!(d[(0, 0)], 2.0);
Source

pub fn distances_dijkstra_cutoff<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, cutoff: Option<f64>, ) -> Result<Matrix>

Like distances_dijkstra, ignoring paths longer than cutoff (None or negative: no cutoff).

Binds igraph_distances_dijkstra_cutoff. Time complexity: at most O(s |E| log |V| + |V|); the cutoff limits the explored region.

§Examples
use igraph::prelude::*;
// Path 0 -2- 1 -2- 2 -2- 3: with cutoff 4 vertex 3 (at distance 6) is out of reach.
let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let d = p
    .distances_dijkstra_cutoff(0, .., Some(&[2.0; 3]), NeighborMode::All, Some(4.0))
    .unwrap();
assert_eq!(d.row(0), vec![0.0, 2.0, 4.0, f64::INFINITY]);
Source

pub fn distances_bellman_ford<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Matrix>

Weighted shortest path lengths with the Bellman–Ford algorithm, which allows negative weights (but no negative cycles).

If there are no negative weights, distances_dijkstra is faster.

Binds igraph_distances_bellman_ford. Time complexity: O(s |E| |V|) for s sources.

§Errors

ErrorKind::NegativeCycle when a negative cycle is reachable.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
let d = g.distances_bellman_ford(0, .., Some(&[2.0, -3.0, 1.0]), NeighborMode::Out).unwrap();
assert_eq!(d.row(0), vec![0.0, 2.0, -1.0]);
Source

pub fn distances_johnson<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Matrix>

Weighted shortest path lengths with Johnson’s algorithm: negative weights are allowed (directed graphs only, no negative cycles).

A single Bellman–Ford run reweights the edges to non-negative values, then Dijkstra is run from each source; this beats Bellman–Ford when there are many sources. Without negative weights Dijkstra is used directly; with None the unweighted algorithm is used. Undirected graphs with any negative weight are rejected, even when no negative edge is reachable from the sources, and so is NeighborMode::All combined with negative weights: igraph treats an undirected negative edge as a negative cycle.

Binds igraph_distances_johnson. Time complexity: O(s |V| log |V| + |V| |E|).

§Errors

ErrorKind::NegativeCycle when a negative cycle is reachable, and also for negative weights in an undirected graph or together with NeighborMode::All; ErrorKind::InvalidValue for NaN weights or a weight vector of the wrong length.

§Examples
use igraph::prelude::*;
// 0 → 1 → 2 with a negative "discount" edge 1 → 2.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
let w = [2.0, -3.0, 1.0];
let d = g.distances_johnson(.., .., Some(&w), NeighborMode::Out).unwrap();
assert_eq!(d.row(0), vec![0.0, 2.0, -1.0]);
// Following the edges backwards gives the transposed matrix.
let d_in = g.distances_johnson(.., .., Some(&w), NeighborMode::In).unwrap();
assert_eq!(d_in, d.transposed());
Source

pub fn distances_floyd_warshall<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, method: FloydWarshallAlgorithm, ) -> Result<Matrix>

All-pairs weighted shortest path lengths with the Floyd–Warshall algorithm (or one of its faster variants, see FloydWarshallAlgorithm).

Negative weights are allowed but negative cycles are not. The full all-pairs matrix is always computed internally, from and to only subset it. Useful for very dense graphs.

Binds igraph_distances_floyd_warshall. Time complexity: O(|V|³ + |E|) for the original variant, expected O(|V|² log² |V|) for the tree variant.

§Examples
use igraph::{paths::FloydWarshallAlgorithm, prelude::*};
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true).unwrap();
let d = g
    .distances_floyd_warshall(.., .., None, NeighborMode::Out, FloydWarshallAlgorithm::Original)
    .unwrap();
assert_eq!(d.row(0), vec![0.0, 1.0, 2.0]);
Source§

impl igraph_t

Source

pub fn get_shortest_paths<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<ShortestPaths>

One shortest path from from to each of the to vertices.

When several geodesics exist only one is returned (see get_all_shortest_paths). to may contain duplicates. With weights the weighted algorithms are used (Dijkstra, or Bellman–Ford for negative weights). The result also contains the shortest path tree (parents / inbound_edges).

Binds igraph_get_shortest_paths. Time complexity: O(|V| + |E|) unweighted.

§Examples
use igraph::prelude::*;
let p = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let sp = p.get_shortest_paths(0, vec![3, 1], None, NeighborMode::All).unwrap();
assert_eq!(sp.vertices, vec![vec![0, 1, 2, 3], vec![0, 1]]);
assert_eq!(sp.edges, vec![vec![0, 1, 2], vec![0]]);
assert_eq!(sp.parents, vec![-1, 0, 1, 2]);
Source

pub fn get_shortest_paths_dijkstra<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<ShortestPaths>

Weighted shortest paths from one vertex with Dijkstra’s algorithm (non-negative weights); None falls back to BFS.

The result has the same shape as for get_shortest_paths; the search stops as soon as all the targets are reached, so parents may contain -2 for vertices that were never reached.

Binds igraph_get_shortest_paths_dijkstra. Time complexity: O(|E| log |V| + |V|).

§Errors

ErrorKind::InvalidValue for negative or NaN weights, ErrorKind::InvalidVertexId for invalid vertices.

§Examples
use igraph::prelude::*;
// A square 0-1-2-3-0 whose edge (3, 0) is slow.
let c4 = Graph::cycle_graph(4, false, false).unwrap();
let w = [1.0, 1.0, 1.0, 5.0];
let sp = c4.get_shortest_paths_dijkstra(0, .., Some(&w), NeighborMode::All).unwrap();
assert_eq!(sp.vertices[3], vec![0, 1, 2, 3]);
assert_eq!(sp.parents, vec![-1, 0, 1, 2]);
assert_eq!(sp.inbound_edges, vec![-1, 0, 1, 2]);
Source

pub fn get_shortest_paths_bellman_ford<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<ShortestPaths>

Weighted shortest paths from one vertex with the Bellman–Ford algorithm, allowing negative weights (but no negative cycles).

Binds igraph_get_shortest_paths_bellman_ford. Time complexity: O(|E| |V|).

§Errors

ErrorKind::NegativeCycle when a negative cycle is found.

§Examples
use igraph::prelude::*;
// The negative edge 1 → 2 makes the detour through 1 the shortest route to 2.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
let sp = g
    .get_shortest_paths_bellman_ford(0, .., Some(&[2.0, -3.0, 1.0]), NeighborMode::Out)
    .unwrap();
assert_eq!(sp.vertices, vec![vec![0], vec![0, 1], vec![0, 1, 2]]);
Source

pub fn get_shortest_path( &self, from: VertexId, to: VertexId, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<GraphPath>

A single shortest path between two vertices (an arbitrary one if there are several). An empty GraphPath means to is unreachable (the BFS and Dijkstra searches then also emit an igraph warning).

Binds igraph_get_shortest_path. Time complexity: O(|V| + |E|) unweighted.

§Examples
use igraph::prelude::*;
let c = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
let p = c.get_shortest_path(0, 3, None, NeighborMode::All).unwrap();
assert_eq!(p.vertices, vec![0, 4, 3]);
assert_eq!(p.len(), 2);
Source

pub fn get_shortest_path_dijkstra( &self, from: VertexId, to: VertexId, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<GraphPath>

A single weighted shortest path with Dijkstra’s algorithm (non-negative weights; None falls back to BFS). An empty GraphPath means that to is unreachable.

Binds igraph_get_shortest_path_dijkstra. Time complexity: O(|E| log |V| + |V|).

§Examples
use igraph::prelude::*;
let c4 = Graph::cycle_graph(4, false, false).unwrap();
let w = [1.0, 1.0, 1.0, 5.0];
let p = c4.get_shortest_path_dijkstra(0, 3, Some(&w), NeighborMode::All).unwrap();
assert_eq!(p.vertices, vec![0, 1, 2, 3]);
assert_eq!(p.weight(&w), 3.0);
Source

pub fn get_shortest_path_bellman_ford( &self, from: VertexId, to: VertexId, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<GraphPath>

A single weighted shortest path with the Bellman–Ford algorithm (negative weights allowed, negative cycles are not).

Binds igraph_get_shortest_path_bellman_ford. Time complexity: O(|E| |V|).

§Errors

ErrorKind::NegativeCycle when a negative cycle is found.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
let w = [2.0, -3.0, 1.0];
let p = g.get_shortest_path_bellman_ford(0, 2, Some(&w), NeighborMode::Out).unwrap();
assert_eq!(p.edges, vec![0, 1]);
assert_eq!(p.weight(&w), -1.0);
// Closing a negative cycle makes shortest paths meaningless.
let mut cyclic = g.clone();
cyclic.add_edge(2, 0).unwrap();
let err = cyclic
    .get_shortest_path_bellman_ford(0, 2, Some(&[2.0, -3.0, 1.0, 0.5]), NeighborMode::Out)
    .unwrap_err();
assert_eq!(err.kind(), ErrorKind::NegativeCycle);
Source

pub fn get_shortest_path_astar<F>( &self, from: VertexId, to: VertexId, weights: Option<&[f64]>, mode: NeighborMode, heuristic: F, ) -> Result<GraphPath>
where F: FnMut(VertexId, VertexId) -> f64,

A single shortest path with the A* algorithm, guided by a heuristic.

heuristic(v, to) must estimate the distance from the candidate vertex v to the target to; smaller values make v a better candidate. The result is a true shortest path if the heuristic is admissible, i.e. never overestimates the distance. A heuristic returning always 0.0 turns A* into Dijkstra’s algorithm. Weights must be non-negative. The heuristic should return finite, non-negative values (NaN estimates break the priority queue ordering and give meaningless paths). A panic in the heuristic aborts the search and is propagated to the caller. An empty GraphPath means that to is unreachable.

§Errors

ErrorKind::InvalidVertexId for invalid from/to, ErrorKind::InvalidValue for negative or NaN weights.

Binds igraph_get_shortest_path_astar. Time complexity: worst case O(|E| log |V| + |V|); better heuristics mean faster searches.

§Examples
use igraph::prelude::*;
// A 10 x 10 grid; vertex id = x + 10 y. Manhattan distance is admissible.
let n = 10;
let grid = Graph::square_lattice(&[10, 10], 1, false, false, None).unwrap();
let manhattan = |a: i64, b: i64| ((a % n - b % n).abs() + (a / n - b / n).abs()) as f64;
let p = grid.get_shortest_path_astar(0, 99, None, NeighborMode::All, manhattan).unwrap();
assert_eq!(p.len(), 18);
Source

pub fn get_all_shortest_paths<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<AllShortestPaths>

All the shortest paths (geodesics) from from to the to vertices.

Paths are grouped by target in increasing vertex id order; unreachable targets contribute nothing. Multi-edges are considered separately, so multigraphs may yield very many paths. nrgeo[v] counts the geodesics from from to v. With weights, Dijkstra’s algorithm is used (see get_all_shortest_paths_dijkstra). Counting geodesics through each vertex is what betweenness does, for all sources at once.

Binds igraph_get_all_shortest_paths. Time complexity: O(|V| + |E|) for most graphs, O(|V|²) worst case.

§Examples
use igraph::prelude::*;
// A 4-cycle has two geodesics between opposite corners.
let c4 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false).unwrap();
let all = c4.get_all_shortest_paths(0, 2, None, NeighborMode::All).unwrap();
let mut paths = all.vertices.clone();
paths.sort();
assert_eq!(paths, vec![vec![0, 1, 2], vec![0, 3, 2]]);
assert_eq!(all.nrgeo[2], 2);
Source

pub fn get_all_shortest_paths_dijkstra<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<AllShortestPaths>

All the weighted shortest paths from one vertex, with Dijkstra’s algorithm (non-negative weights; None falls back to the unweighted get_all_shortest_paths).

Binds igraph_get_all_shortest_paths_dijkstra. Time complexity: O(|E| log |V| + |V|).

§Examples
use igraph::prelude::*;
let c4 = Graph::cycle_graph(4, false, false).unwrap();
// Equal weights: two geodesics between opposite corners ...
let all = c4.get_all_shortest_paths_dijkstra(0, 2, Some(&[1.0; 4]), NeighborMode::All).unwrap();
assert_eq!(all.nrgeo[2], 2);
// ... a slow edge (3, 0) leaves only one.
let w = [1.0, 1.0, 1.0, 5.0];
let all = c4.get_all_shortest_paths_dijkstra(0, 2, Some(&w), NeighborMode::All).unwrap();
assert_eq!(all.vertices, vec![vec![0, 1, 2]]);
assert_eq!(all.edges, vec![vec![0, 1]]);
Source

pub fn get_k_shortest_paths( &self, from: VertexId, to: VertexId, k: usize, weights: Option<&[f64]>, mode: NeighborMode, ) -> Result<Vec<GraphPath>>

The k shortest paths between two vertices, in order of increasing length (Yen’s algorithm). Fewer than k paths are returned when fewer exist. Infinite weights are treated as missing edges.

Binds igraph_get_k_shortest_paths. Time complexity: O(k |V| (|V| log |V| + |E|)).

§Examples
use igraph::prelude::*;
// In a 5-cycle there are exactly two paths between any two vertices.
let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false).unwrap();
let paths = c5.get_k_shortest_paths(0, 2, 10, None, NeighborMode::All).unwrap();
assert_eq!(paths.len(), 2);
assert_eq!(paths[0].vertices, vec![0, 1, 2]);
assert_eq!(paths[1].vertices, vec![0, 4, 3, 2]);
Source

pub fn get_all_simple_paths<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, mode: NeighborMode, options: SimplePathsOptions, ) -> Result<Vec<Vec<VertexId>>>

All the simple paths (no repeated vertex) starting at from and ending at one of the to vertices, as vertex lists.

Multi-edges are ignored. There may be exponentially many simple paths: use options to bound their length or number. Paths are listed in the order they are found.

Binds igraph_get_all_simple_paths. Time complexity: O(n!) in the worst case.

§Examples
use igraph::{paths::SimplePathsOptions, prelude::*};
let k4 = Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)], 4, false).unwrap();
// Paths from 0 to 3: the edge, two of length 2, two of length 3.
let all = k4.get_all_simple_paths(0, 3, NeighborMode::All, SimplePathsOptions::default()).unwrap();
assert_eq!(all.len(), 5);
let short = SimplePathsOptions::default().with_max_len(2);
assert_eq!(k4.get_all_simple_paths(0, 3, NeighborMode::All, short).unwrap().len(), 3);
Source§

impl igraph_t

Source

pub fn get_widest_paths<'a>( &self, from: VertexId, to: impl Into<VertexSelector<'a>>, weights: &[f64], mode: NeighborMode, ) -> Result<ShortestPaths>

Widest (maximum bottleneck) paths from one vertex to the to vertices.

The width of a path is the smallest weight among its edges, and a widest path maximizes it. weights are widths and are mandatory; they may be negative but not NaN, edges of width -INFINITY are ignored. The result has the same shape as for get_shortest_paths. A widest path tells how much can be pushed along a single route; see maxflow for the total capacity over all routes.

Binds igraph_get_widest_paths. Time complexity: O(|E| log |E| + |V|).

§Examples
use igraph::prelude::*;
// Two routes from 0 to 3: a thin direct pipe and a wide detour.
let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let widths = [1.0, 10.0, 8.0, 9.0];
let wp = g.get_widest_paths(0, .., &widths, NeighborMode::All).unwrap();
assert_eq!(wp.vertices, vec![vec![0], vec![0, 1], vec![0, 1, 2], vec![0, 1, 2, 3]]);
assert_eq!(wp.parents, vec![-1, 0, 1, 2]);
Source

pub fn get_widest_path( &self, from: VertexId, to: VertexId, weights: &[f64], mode: NeighborMode, ) -> Result<GraphPath>

A single widest (maximum bottleneck) path between two vertices, see get_widest_paths.

Binds igraph_get_widest_path. Time complexity: O(|E| log |E| + |V|).

§Examples
use igraph::prelude::*;
// Two routes from 0 to 3: a thin direct pipe and a wide detour.
let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let widths = [1.0, 10.0, 8.0, 9.0];
let p = g.get_widest_path(0, 3, &widths, NeighborMode::All).unwrap();
assert_eq!(p.vertices, vec![0, 1, 2, 3]);
Source

pub fn widest_path_widths_floyd_warshall<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: &[f64], mode: NeighborMode, ) -> Result<Matrix>

Widths of the widest paths between vertices, with a modified Floyd–Warshall algorithm (good for dense graphs).

Unreachable pairs have width -INFINITY, a vertex has width INFINITY to itself. The full matrix is always computed internally. The result equals that of widest_path_widths_dijkstra.

Binds igraph_widest_path_widths_floyd_warshall. Time complexity: O(|V|³).

§Examples
use igraph::prelude::*;
// Directed: 0 → 1 → 2 is wide, the direct edge 0 → 2 is thin, 2 reaches nothing.
let g = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true).unwrap();
let w = g
    .widest_path_widths_floyd_warshall(.., .., &[5.0, 4.0, 1.0], NeighborMode::Out)
    .unwrap();
let inf = f64::INFINITY;
assert_eq!(w.to_rows(), vec![vec![inf, 5.0, 4.0], vec![-inf, inf, 4.0], vec![-inf, -inf, inf]]);
Source

pub fn widest_path_widths_dijkstra<'a>( &self, from: impl Into<VertexSelector<'a>>, to: impl Into<VertexSelector<'a>>, weights: &[f64], mode: NeighborMode, ) -> Result<Matrix>

Widths of the widest paths between vertices, with a modified Dijkstra algorithm run from each source (good for sparse graphs). to must not contain duplicates.

Unreachable pairs have width -INFINITY, a vertex has width INFINITY to itself.

Binds igraph_widest_path_widths_dijkstra. Time complexity: O(s (|E| log |E| + |V|)) for s sources.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 3), (0, 1), (1, 2), (2, 3)], 4, false).unwrap();
let w = g.widest_path_widths_dijkstra(0, .., &[1.0, 10.0, 8.0, 9.0], NeighborMode::All).unwrap();
assert_eq!(w.row(0), vec![f64::INFINITY, 10.0, 8.0, 8.0]);
Source§

impl igraph_t

Source

pub fn random_walk( &self, start: VertexId, steps: usize, weights: Option<&[f64]>, mode: NeighborMode, stuck: RandomWalkStuck, ) -> Result<GraphPath>

A random walk of (at most) steps steps starting at start.

The walk follows edges according to mode (in directed graphs); multi-edges and loops are respected. With weights the next edge is chosen with probability proportional to its weight (non-negative, at least one positive weight among the out-edges of each vertex). When the walk reaches a vertex without outgoing edges, stuck decides whether to return the shorter walk (RandomWalkStuck::Return) or to fail (RandomWalkStuck::Error). The returned path has steps + 1 vertices and steps edges unless it got stuck. Draws from the calling thread’s default random number generator: seed it with rng::seed for reproducible walks, or run the walk with a private generator through Rng::scoped.

Binds igraph_random_walk. Time complexity: O(l + d) unweighted, O(l log k + d) weighted, where l is the walk length, d the total degree of the visited vertices and k the average degree.

§Errors
  • ErrorKind::RandomWalkStuck if the walk gets stuck and stuck is RandomWalkStuck::Error (the partial walk is discarded; use RandomWalkStuck::Return to keep it).
  • ErrorKind::InvalidValue for an invalid start vertex (igraph 1.0.0 and 1.0.1 report IGRAPH_EINVAL here, not IGRAPH_EINVVID), invalid weights (negative, NaN or wrong length), or steps >= i64::MAX. The Rust side also rejects infinite weights, and weights whose total over the edges a walk can leave some vertex through exceeds f64::MAX / 2 (loops count twice in All mode): with such weights igraph 1.0.1 never terminates. The check covers every vertex, not only the visited ones.
§Examples
use igraph::prelude::*;
let c = Graph::cycle_graph(4, false, false).unwrap();
// Seeding the (per-thread) default generator makes the walk reproducible.
rng::seed(42).unwrap();
let walk = c.random_walk(0, 20, None, NeighborMode::All, RandomWalkStuck::Error).unwrap();
rng::seed(42).unwrap();
let again = c.random_walk(0, 20, None, NeighborMode::All, RandomWalkStuck::Error).unwrap();
assert_eq!(walk, again);
assert_eq!(walk.vertices.len(), 21);
assert_eq!(walk.edges.len(), 20);
// Consecutive vertices are adjacent.
for pair in walk.vertices.windows(2) {
    assert!(c.get_eid(pair[0], pair[1], false).unwrap().is_some());
}
// A private generator leaves the default one untouched.
let mut private = Rng::new(RngType::Pcg64, 7).unwrap();
let w = private
    .scoped(|| c.random_walk(0, 5, None, NeighborMode::All, RandomWalkStuck::Error))
    .unwrap();
assert_eq!(w.len(), 5);
Source

pub fn spanner( &self, stretch: f64, weights: Option<&[f64]>, ) -> Result<Vec<EdgeId>>

The edge ids of a spanner of the graph with stretch factor stretch.

A t-spanner is a subgraph H with the same vertices, in which the distance between any two vertices is at most t times their distance in the original graph. The randomized Baswana–Sen algorithm is used (drawing from the thread’s default random number generator); edge directions are ignored. Use subgraph_from_edges (with delete_vertices = false) to extract the spanner as a graph. See also minimum_spanning_tree, the sparsest connected subgraph, which offers no stretch guarantee.

Binds igraph_spanner. Expected time complexity: O(k |E|), with k = (t + 1) / 2.

§Errors

ErrorKind::InvalidValue if stretch is below one or not finite (checked on the Rust side for NaN and infinity: igraph 1.0.1 accepts a NaN stretch and never terminates for an infinite one), or if the weights are negative, NaN or of the wrong length.

§Examples
use igraph::prelude::*;
let k8 = Graph::full(8, false, false).unwrap();
rng::seed(1).unwrap();
let kept = k8.spanner(3.0, None).unwrap();
assert!(kept.len() <= k8.ecount());
// Distances in the spanner are at most 3 times the original ones (all 1 here).
let h = k8.subgraph_from_edges(kept, false).unwrap();
assert_eq!(h.vcount(), 8);
let d = h.distances(.., .., None, NeighborMode::All).unwrap();
assert!(d.as_slice().iter().all(|&x| x <= 3.0));
Source

pub fn voronoi( &self, generators: &[VertexId], weights: Option<&[f64]>, mode: NeighborMode, tiebreaker: VoronoiTiebreaker, ) -> Result<VoronoiPartition>

Voronoi partitioning: assigns each vertex to its closest generator.

Distances are computed from the generators (mode = Out), towards them (In) or ignoring directions (All); BFS is used for unweighted graphs, Dijkstra for (non-negative) weights. tiebreaker decides which generator wins at equal distance; note that VoronoiTiebreaker::Random may produce non-contiguous cells (and draws from the thread’s default random number generator). See also community_voronoi, which picks the generators automatically to detect communities.

Binds igraph_voronoi. Time complexity: O(log |S| |E| log |V| + |V|) weighted, O(log |S| |E| + |V|) unweighted, for |S| generators.

§Examples
use igraph::prelude::*;
// Path 0-1-2-3-4-5 with generators at both ends.
let p = Graph::path_graph(6, false, false).unwrap();
let v = p.voronoi(&[0, 5], None, NeighborMode::All, VoronoiTiebreaker::First).unwrap();
assert_eq!(v.membership, vec![0, 0, 0, 1, 1, 1]);
assert_eq!(v.distances, vec![0.0, 1.0, 2.0, 2.0, 1.0, 0.0]);
Source

pub fn vertex_path_from_edge_path( &self, start: Option<VertexId>, edge_path: &[EdgeId], mode: NeighborMode, ) -> Result<Vec<VertexId>>

Converts a walk given by its edge ids into the sequence of vertices it visits (one more than the number of edges).

start is the first vertex; with None it is inferred from the walk (which then must have at least one edge). Vertices may repeat, so any walk, cycle or path is accepted, e.g. the edge sequences returned by find_cycle or eulerian_path. Edge ids are validated on the Rust side (igraph 1.0.0 and 1.0.1 do not check them).

Binds igraph_vertex_path_from_edge_path. Time complexity: O(n) for a walk of length n.

§Errors

ErrorKind::InvalidValue if the edges do not form a continuous walk from start, or if start is None and the walk is empty; ErrorKind::InvalidEdgeId for an edge id out of range; ErrorKind::InvalidVertexId for an invalid start vertex.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], 4, true).unwrap();
let v = g.vertex_path_from_edge_path(Some(0), &[0, 1, 2], NeighborMode::Out).unwrap();
assert_eq!(v, vec![0, 1, 2, 3]);
// Walking backwards along the directed edges.
let v = g.vertex_path_from_edge_path(Some(3), &[2, 1], NeighborMode::In).unwrap();
assert_eq!(v, vec![3, 2, 1]);
Source§

impl igraph_t

Source

pub fn are_adjacent(&self, v1: VertexId, v2: VertexId) -> Result<bool>

Decides whether there is an edge from v1 to v2.

In directed graphs the direction matters (v1 → v2); in undirected graphs the relation is symmetric.

Binds igraph_are_adjacent. Time complexity: O(min(log d1, log d2)) where d1 is the (out-)degree of v1 and d2 the (in-)degree of v2.

§Errors

ErrorKind::InvalidVertexId for invalid vertex ids.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1)], 3, true)?;
assert!(g.are_adjacent(0, 1)?);
assert!(!g.are_adjacent(1, 0)?);
assert_eq!(g.are_adjacent(0, 9).unwrap_err().kind(), ErrorKind::InvalidVertexId);

See also Graph::neighbors and Graph::get_adjacency for the whole neighborhood or adjacency matrix.

Source

pub fn count_multiple<'a>( &self, edges: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<i64>>

The multiplicity of the selected edges: for each edge, the number of edges between its two endpoints (itself included).

A simple graph gives all ones. In directed graphs, (a, b) and (b, a) are different edges and don’t count towards each other.

Binds igraph_count_multiple. Time complexity: O(E d), E the number of edges to check and d the average degree of their tails.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 1), (1, 2), (0, 1)], 3, false)?;
assert_eq!(g.count_multiple(..)?, vec![3, 3, 1, 3]);

See also is_multiple, and Graph::simplify to merge parallel edges.

Source

pub fn count_multiple_1(&self, edge: EdgeId) -> Result<i64>

The multiplicity of a single edge, see count_multiple.

The result always agrees with the corresponding entry of count_multiple, self-loops of undirected graphs included: the C function of igraph 1.0.0 and 1.0.1 counts each undirected self-loop twice (it scans the neighbor list of the tail, where a loop appears twice), and this wrapper halves that count.

Binds igraph_count_multiple_1. Time complexity: O(d), the out-degree of the tail of the edge.

§Errors

ErrorKind::InvalidEdgeId for an invalid edge id.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 0), (2, 2), (2, 2)], 3, false)?;
assert_eq!(g.count_multiple_1(0)?, 2);
assert_eq!(g.count_multiple_1(2)?, 2); // two self-loops on vertex 2
assert_eq!(g.count_multiple_1(4).unwrap_err().kind(), ErrorKind::InvalidEdgeId);
Source

pub fn density(&self, weights: Option<&[f64]>, loops: bool) -> Result<f64>

The density of the graph: the ratio of the number of edges to the largest possible number of edges.

The maximum number of edges is n(n-1)/2 for undirected and n(n-1) for directed graphs; when loops is true, self-loops are considered possible and these become n(n+1)/2 and n². With loops = false the result is only correct if the graph has no loops (this is not checked).

With weights, the total edge weight is used instead of the edge count, and the result may exceed 1 (the same happens with multigraphs). For the null graph the result is NaN.

Binds igraph_density. Time complexity: O(1) (O(|E|) when weighted).

§Examples
use igraph::prelude::*;
let triangle = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
assert_eq!(triangle.density(None, false)?, 1.0);
assert_eq!(triangle.density(None, true)?, 0.5); // 3 of 6 possible edges
assert_eq!(triangle.density(Some(&[0.5, 0.5, 0.5]), false)?, 0.5);

See also mean_degree and rich_club_sequence, which computes the density of a sequence of shrinking subgraphs.

Source

pub fn diversity<'a>( &self, weights: &[f64], vertices: impl Into<VertexSelector<'a>>, ) -> Result<Vec<f64>>

The structural diversity index of the selected vertices.

It is the Shannon entropy of the weights of the incident edges, normalized by the logarithm of the degree: D(i) = H(i) / log k(i) with H(i) = -Σ_j p_ij log p_ij and p_ij = w_ij / Σ_l w_il, k(i) being the degree (zero-weight edges included). Isolated vertices get NaN, vertices of degree one get 0 (NaN if the weight of their only edge is zero, as for any vertex whose incident weights are all zero). Defined by Eagle, Macy and Claxton (Science 328, 2010).

igraph 1.0.0 and 1.0.1 compute the value of degree-one vertices from the weight of edge 0 rather than of the vertex’s own edge; this wrapper recomputes those entries.

The graph must be undirected and without multi-edges; weights (required, one non-negative value per edge) are mandatory.

Binds igraph_diversity. Time complexity: O(|V| + |E|).

§Errors

ErrorKind::InvalidValue for directed graphs, multigraphs, negative weights or a wrong number of weights.

§Examples
use igraph::prelude::*;
// A star with equal weights has maximal diversity at the center.
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
let d = star.diversity(&[1.0, 1.0, 1.0], ..)?;
assert!((d[0] - 1.0).abs() < 1e-12);
assert_eq!(&d[1..], &[0.0, 0.0, 0.0]);
// A zero weight: the center's entropy is log 2 over log 3 (the edge
// still counts in its degree), and leaf 1 has no weight at all.
let d = star.diversity(&[0.0, 1.0, 1.0], ..)?;
assert!((d[0] - 2f64.ln() / 3f64.ln()).abs() < 1e-12);
assert!(d[1].is_nan());
assert_eq!(&d[2..], &[0.0, 0.0]);
Source

pub fn girth(&self) -> Result<Option<usize>>

The girth of the graph: the length of its shortest cycle, or None if the graph has no cycles.

Edge directions are ignored; self-loops and multi-edges are ignored as well, i.e. cycles of length 1 or 2 are not considered, so the girth is always at least 3. None is returned exactly for graphs without such cycles (forests, possibly with loops and multi-edges). Algorithm by Itai and Rodeh (1977).

Mutual edges u -> v, v -> u of a directed graph count as a single undirected edge. igraph 1.0.1 can get this wrong when its property cache already records “no multi-edges” (a girth of 2, or heap corruption in the chordality code), so for directed graphs this wrapper clears the cache around the C call.

Binds igraph_girth. Time complexity: O((|V| + |E|)²) in general, O(|V| + |E|) for acyclic graphs.

§Examples
use igraph::prelude::*;
// The Petersen graph has girth 5.
assert_eq!(Graph::famous("Petersen")?.girth()?, Some(5));

let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
assert_eq!(path.girth()?, None);
// Loops and multi-edges do not form cycles here.
let multi = Graph::from_edges(&[(0, 0), (0, 1), (0, 1)], 2, false)?;
assert_eq!(multi.girth()?, None);

See also Graph::find_cycle (any cycle, respecting directions) and Graph::minimum_cycle_basis (a basis of short cycles).

Source

pub fn girth_with_cycle(&self) -> Result<Option<(usize, Vec<VertexId>)>>

Like girth, but also returns the vertex ids of one shortest cycle of length at least 3, in cycle order (consecutive vertices, and the last and the first, are adjacent); None if there is no such cycle.

Binds igraph_girth.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (1, 3)], 4, false)?;
let (girth, cycle) = g.girth_with_cycle()?.unwrap();
assert_eq!(girth, 3);
assert_eq!(cycle.len(), 3);
assert!(g.is_clique(&cycle, false)?); // a triangle
Source

pub fn has_loop(&self) -> Result<bool>

Whether the graph has at least one self-loop.

The result is cached in the graph, so repeated calls are O(1).

Binds igraph_has_loop. Time complexity: O(|E|).

§Examples
use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 1)], 2, false)?;
assert!(g.has_loop()?);
g.simplify(true, true)?;
assert!(!g.has_loop()?);

See also count_loops, is_loop and Graph::simplify.

Source

pub fn has_multiple(&self) -> Result<bool>

Whether the graph has at least one multi-edge (two or more edges with the same endpoints, and the same direction in directed graphs).

The result is cached in the graph, so repeated calls are O(1).

Binds igraph_has_multiple. Time complexity: O(|E| d), d the average degree.

§Examples
use igraph::prelude::*;
let mut g = Graph::from_edges(&[(0, 1), (1, 0)], 2, true)?;
assert!(!g.has_multiple()?); // opposite directions
g.add_edge(0, 1)?;
assert!(g.has_multiple()?);
Source

pub fn count_loops(&self) -> Result<usize>

The number of self-loops in the graph.

Binds igraph_count_loops. Time complexity: O(|E|).

Source

pub fn is_loop<'a>( &self, edges: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<bool>>

For each selected edge, whether it is a self-loop.

Binds igraph_is_loop. Time complexity: O(e), the number of edges to check.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 0), (0, 1), (1, 1)], 2, false)?;
assert_eq!(g.is_loop(..)?, vec![true, false, true]);
assert_eq!(g.count_loops()?, 2);
Source

pub fn is_multiple<'a>( &self, edges: impl Into<EdgeSelector<'a>>, ) -> Result<Vec<bool>>

For each selected edge, whether it is a multi-edge.

Only the second and further occurrences of parallel edges are flagged, so that deleting all the flagged edges yields a graph without multi-edges (as Graph::simplify does). In undirected graphs (a, b) and (b, a) are parallel; in directed ones they are not.

Binds igraph_is_multiple. Time complexity: O(e d).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 1), (0, 1), (1, 0)], 3, true)?;
assert_eq!(g.is_multiple(..)?, vec![false, false, false, true, false]);
Source

pub fn is_mutual<'a>( &self, edges: impl Into<EdgeSelector<'a>>, loops: bool, ) -> Result<Vec<bool>>

For each selected edge, whether it is mutual, i.e. whether the reversed edge exists as well.

loops decides whether directed self-loops count as mutual. In undirected graphs every edge is mutual. Multiplicities are not considered: two (a, b) edges and one (b, a) edge are all mutual.

Binds igraph_is_mutual. Time complexity: O(n log d), n the number of edges to check.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 0), (1, 2), (2, 2)], 3, true)?;
assert_eq!(g.is_mutual(.., true)?, vec![true, true, false, true]);
assert_eq!(g.is_mutual(.., false)?, vec![true, true, false, false]);
Source

pub fn has_mutual(&self, loops: bool) -> Result<bool>

Whether the graph has at least one mutual edge pair (see is_mutual).

Undirected graphs have mutual edges exactly when they have edges. A directed graph without mutual edges (and loops) is an oriented graph.

Binds igraph_has_mutual. Time complexity: O(|E| log d).

§Examples
use igraph::prelude::*;
let oriented = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, true)?;
assert!(!oriented.has_mutual(true)?);
let with_loop = Graph::from_edges(&[(0, 1), (1, 1)], 2, true)?;
assert!(with_loop.has_mutual(true)?);   // the loop counts as mutual
assert!(!with_loop.has_mutual(false)?);
Source

pub fn is_simple(&self, directed: bool) -> Result<bool>

Whether the graph is simple, i.e. it has no self-loops and no multi-edges.

With directed = false, edge directions are ignored, so a directed graph with a mutual edge pair is considered non-simple. Ignored for undirected graphs.

Binds igraph_is_simple. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 0)], 2, true)?;
assert!(g.is_simple(true)?);
assert!(!g.is_simple(false)?);
Source

pub fn is_tree(&self, mode: NeighborMode) -> Result<bool>

Whether the graph is a tree.

An undirected graph is a tree if it is connected and has no cycles. For directed graphs mode selects the test: NeighborMode::Out for out-trees (arborescences, edges pointing away from the root), NeighborMode::In for in-trees, NeighborMode::All to ignore directions. The null graph is not a tree.

Binds igraph_is_tree. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 2), (2, 3)], 4, true)?;
assert!(g.is_tree(NeighborMode::Out)?);
assert!(!g.is_tree(NeighborMode::In)?);
assert_eq!(g.tree_root(NeighborMode::Out)?, Some(0));
// A complete binary tree on 15 vertices.
assert!(Graph::kary_tree(15, 2, TreeMode::Out)?.is_tree(NeighborMode::Out)?);

See also Graph::kary_tree and Graph::tree_game to build trees, and Graph::to_prufer to encode them.

Source

pub fn tree_root(&self, mode: NeighborMode) -> Result<Option<VertexId>>

The root of the graph if it is a tree (see is_tree), None otherwise.

For out-trees (in-trees) the root is the unique vertex of zero in-degree (out-degree); with NeighborMode::All or in undirected graphs any vertex can be the root, and vertex 0 is returned.

Binds igraph_is_tree.

Source

pub fn is_forest(&self, mode: NeighborMode) -> Result<bool>

Whether the graph is a forest, i.e. every connected component is a tree (equivalently, there are no undirected cycles).

mode has the same meaning as in is_tree. The null graph is a forest. The result is cached for undirected tests.

Binds igraph_is_forest. Time complexity: O(|V| + |E|).

Source

pub fn forest_roots(&self, mode: NeighborMode) -> Result<Option<Vec<VertexId>>>

The roots of the trees if the graph is a forest (see is_forest), None otherwise.

With NeighborMode::All or in undirected graphs, one vertex per component is returned (the smallest id); for out- (in-) forests the roots are the vertices of zero in- (out-) degree.

Binds igraph_is_forest.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (2, 3), (2, 4)], 6, false)?;
assert_eq!(g.forest_roots(NeighborMode::All)?, Some(vec![0, 2, 5]));
Source

pub fn is_acyclic(&self) -> Result<bool>

Whether the graph has no cycles, taking edge directions into account: for directed graphs this is the same as being a DAG.

Self-loops are cycles. The result is cached in the graph.

Binds igraph_is_acyclic. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let dag = Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], 3, true)?;
assert!(dag.is_acyclic()?);
assert!(!dag.is_forest(NeighborMode::All)?); // but it has an undirected cycle

See also Graph::is_dag (false for undirected graphs), Graph::topological_sorting and Graph::find_cycle.

Source

pub fn maxdegree<'a>( &self, vertices: impl Into<VertexSelector<'a>>, mode: NeighborMode, loops: Loops, ) -> Result<i64>

The maximum degree among the selected vertices (0 if the selection is empty).

mode selects out-, in- or total degree (ignored for undirected graphs); loops how self-loops are counted.

Binds igraph_maxdegree. Time complexity: O(v) when loops are counted, O(v d) otherwise.

§Errors

ErrorKind::InvalidVertexId for invalid vertices.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 2), (3, 0), (1, 1)], 4, true)?;
assert_eq!(g.maxdegree(.., NeighborMode::Out, Loops::Twice)?, 2);
assert_eq!(g.maxdegree(.., NeighborMode::All, Loops::Twice)?, 3);
assert_eq!(g.maxdegree(&[1], NeighborMode::All, Loops::Twice)?, 3); // loop counted twice
assert_eq!(g.maxdegree(&[1], NeighborMode::All, Loops::None)?, 1);

See also Graph::degree for the individual degrees.

Source

pub fn mean_degree(&self, loops: bool) -> Result<f64>

The mean degree of the graph.

In directed graphs the mean out-degree equals the mean in-degree, and that is what is returned (i.e. |E| / |V|); in undirected graphs it is 2|E| / |V|. With loops = false, self-loops are ignored. For the null graph the result is NaN.

Binds igraph_mean_degree. Time complexity: O(1) with loops, O(|E|) without.

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 2)], 3, false)?;
assert_eq!(g.mean_degree(true)?, 2.0);
assert!((g.mean_degree(false)? - 4.0 / 3.0).abs() < 1e-12);
assert!(Graph::new(0, false).mean_degree(true)?.is_nan());
Source

pub fn reciprocity(&self, ignore_loops: bool, mode: Reciprocity) -> Result<f64>

The reciprocity of a directed graph.

With Reciprocity::Default it is the probability that the reverse of a randomly chosen edge is also in the graph, 1 - Σ_ij |A_ij - A_ji| / (2 Σ_ij A_ij) (in multigraphs each parallel edge needs its own reverse). With Reciprocity::Ratio it is the number of mutually connected (unordered) vertex pairs divided by the number of connected pairs.

ignore_loops excludes self-loops from the count; otherwise they count as mutual. Undirected graphs always give 1; directed graphs without edges give NaN.

Binds igraph_reciprocity. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 1)], 3, true)?;
assert!((g.reciprocity(false, Reciprocity::Default)? - 2.0 / 3.0).abs() < 1e-15);
assert_eq!(g.reciprocity(false, Reciprocity::Ratio)?, 0.5);

See also is_mutual for the individual edges.

Source

pub fn strength<'a>( &self, vertices: impl Into<VertexSelector<'a>>, mode: NeighborMode, loops: Loops, weights: Option<&[f64]>, ) -> Result<Vec<f64>>

The strength (weighted degree) of the selected vertices: the sum of the weights of the incident edges.

Without weights this is the ordinary degree (as f64). mode and loops are as in degree.

Binds igraph_strength. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 2)], 3, true)?;
let w = [1.5, 2.0, 4.0];
assert_eq!(g.strength(.., NeighborMode::Out, Loops::Twice, Some(&w))?, vec![3.5, 4.0, 0.0]);
assert_eq!(g.strength(.., NeighborMode::In, Loops::Twice, Some(&w))?, vec![0.0, 1.5, 6.0]);
assert_eq!(g.strength(.., NeighborMode::All, Loops::Twice, None)?, vec![2.0, 2.0, 2.0]);
Source

pub fn sort_vertex_ids_by_degree<'a>( &self, vertices: impl Into<VertexSelector<'a>>, mode: NeighborMode, loops: Loops, order: Order, only_indices: bool, ) -> Result<Vec<i64>>

The selected vertices sorted by degree.

mode and loops define the degree, order the sort direction. With only_indices = true the result contains positions within the selection instead of vertex ids (this makes no difference when all vertices are selected).

Binds igraph_sort_vertex_ids_by_degree.

§Examples
use igraph::prelude::*;
// A star with center 2.
let g = Graph::from_edges(&[(2, 0), (2, 1), (2, 3), (3, 4)], 5, false)?;
let hubs = g.sort_vertex_ids_by_degree(.., NeighborMode::All, Loops::Twice, Order::Descending, false)?;
assert_eq!(hubs[0], 2);
assert_eq!(hubs[1], 3);

Sorting by increasing degree gives a natural vertex order for rich_club_sequence.

Source

pub fn is_perfect(&self) -> Result<bool>

Whether the graph is perfect: the chromatic number of every induced subgraph equals the size of its largest clique.

The check is based on the strong perfect graph theorem (Chudnovsky, Robertson, Seymour and Thomas). It may build the complement graph, consuming a lot of memory on large graphs. Worst-case exponential.

Binds igraph_is_perfect.

§Errors

ErrorKind::InvalidValue if the graph is directed or not simple.

§Examples
use igraph::prelude::*;
let c5 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], 5, false)?;
assert!(!c5.is_perfect()?); // an odd hole
let c6 = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 0)], 6, false)?;
assert!(c6.is_perfect()?); // bipartite
assert!(!Graph::famous("Chvatal")?.is_perfect()?);

See also is_chordal (chordal graphs are perfect) and Graph::is_bipartite (so are bipartite graphs); for perfect graphs Graph::clique_number equals the chromatic number.

Source

pub fn is_complete(&self) -> Result<bool>

Whether the graph is complete: all pairs of distinct vertices are adjacent (in both directions, for directed graphs).

Self-loops and multi-edges are ignored. The null graph and the singleton graph are complete.

Binds igraph_is_complete. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
assert!(Graph::full(5, true, false)?.is_complete()?);
let one_way = Graph::from_edges(&[(0, 1)], 2, true)?;
assert!(!one_way.is_complete()?); // the edge 1 -> 0 is missing
Source

pub fn is_clique<'a>( &self, candidate: impl Into<VertexSelector<'a>>, directed: bool, ) -> Result<bool>

Whether the candidate vertices form a clique (all pairs adjacent).

With directed = true in a directed graph, both directions are required between every pair. Empty and singleton sets are cliques.

Binds igraph_is_clique. Time complexity: O(n² log d), n the size of the candidate set.

§Examples
use igraph::prelude::*;
let karate = Graph::famous("Zachary")?;
assert!(karate.is_clique(&[0, 1, 2, 3, 7], false)?);
// A largest clique found by the cliques module is, of course, a clique.
let largest = &karate.largest_cliques()?[0];
assert!(karate.is_clique(largest, false)?);

See also Graph::maximal_cliques and Graph::largest_cliques to find cliques.

Source

pub fn is_independent_vertex_set<'a>( &self, candidate: impl Into<VertexSelector<'a>>, ) -> Result<bool>

Whether the candidate vertices form an independent set (no pair is adjacent). Empty and singleton sets are independent.

Self-loops are ignored: a vertex with a loop can still be part of an independent set.

Binds igraph_is_independent_vertex_set. Time complexity: O(n² log d).

§Examples
use igraph::prelude::*;
let c6 = Graph::ring(6, false, false, true)?;
assert!(c6.is_independent_vertex_set(&[0, 2, 4])?);
assert!(!c6.is_independent_vertex_set(&[0, 1])?);

See also Graph::independence_number and Graph::largest_independent_vertex_sets.

Source

pub fn minimum_spanning_tree( &self, weights: Option<&[f64]>, method: MstAlgorithm, ) -> Result<Vec<EdgeId>>

The edge ids of a minimum weight spanning tree (a spanning forest if the graph is disconnected).

Edge directions are ignored. Without weights (or with MstAlgorithm::Unweighted) an arbitrary spanning tree is returned. The result is deterministic. To get a maximum spanning tree, negate the weights. Weights must not be NaN.

Binds igraph_minimum_spanning_tree.

§Examples
use igraph::prelude::*;
// A square with a heavy edge 3-0 and a light diagonal.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)], 4, false)?;
let w = [1.0, 2.0, 1.0, 9.0, 0.5];
let mut tree = g.minimum_spanning_tree(Some(&w), MstAlgorithm::Kruskal)?;
tree.sort();
assert_eq!(tree, vec![0, 2, 4]);
// Keep only the tree edges (and all the vertices).
let t = g.subgraph_from_edges(&tree, false)?;
assert!(t.is_tree(NeighborMode::All)?);

See also random_spanning_tree and Graph::subgraph_from_edges to turn the edge ids into a graph.

Source

pub fn random_spanning_tree( &self, vertex: Option<VertexId>, ) -> Result<Vec<EdgeId>>

The edge ids of a spanning tree sampled uniformly at random, via a loop-erased random walk (Wilson’s algorithm).

Edge directions are ignored; edge multiplicities affect the sampling frequency. With vertex = Some(v) only the component of v is spanned; with None, a random spanning forest of all components is generated. Draws from the calling thread’s default random number generator: seed it with rng::seed for reproducible results (each thread has its own generator).

The number of distinct spanning trees of a connected graph is given by Kirchhoff’s matrix-tree theorem from the eigenvalues of get_laplacian: it is the product of the non-zero ones divided by the number of vertices.

§Errors

ErrorKind::InvalidVertexId if vertex is not a valid vertex id.

Binds igraph_random_spanning_tree.

§Examples
use igraph::prelude::*;
rng::seed(7)?;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (3, 4)], 5, false)?;
assert_eq!(g.random_spanning_tree(None)?.len(), 3);    // forest: 2 + 1 edges
assert_eq!(g.random_spanning_tree(Some(3))?.len(), 1); // only 3-4

// Same seed, same tree.
rng::seed(7)?;
let a = g.random_spanning_tree(None)?;
rng::seed(7)?;
assert_eq!(g.random_spanning_tree(None)?, a);
Source

pub fn subcomponent( &self, vertex: VertexId, mode: NeighborMode, ) -> Result<Vec<VertexId>>

The vertices reachable from vertex (itself included).

NeighborMode::Out follows edges, NeighborMode::In follows them backwards, NeighborMode::All ignores directions (which is not the union of the other two). In undirected graphs this is the connected component of vertex. The vertices are listed in BFS order, starting with vertex.

Binds igraph_subcomponent. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (3, 1)], 4, true)?;
assert_eq!(g.subcomponent(1, NeighborMode::Out)?, vec![1, 2]);
let mut up = g.subcomponent(1, NeighborMode::In)?;
up.sort();
assert_eq!(up, vec![0, 1, 3]);
§Errors

ErrorKind::InvalidVertexId for an invalid vertex.

See also Graph::connected_components (all components at once), Graph::count_reachable and Graph::bfs.

Source

pub fn unfold_tree( &self, mode: NeighborMode, roots: &[VertexId], ) -> Result<UnfoldedTree>

Unfolds the graph into a tree (or forest) by a breadth-first search from roots, replicating vertices each time they are reached again.

Every edge of the graph appears exactly once in the result: non-tree edges lead to fresh copies of their endpoints. mode controls which edges the search follows in directed graphs. Give one root per component to unfold every component.

Binds igraph_unfold_tree. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
// Unfolding a triangle from vertex 0: the closing edge copies a vertex.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0)], 3, false)?;
let unfolded = g.unfold_tree(NeighborMode::All, &[0])?;
assert_eq!((unfolded.tree.vcount(), unfolded.tree.ecount()), (4, 3));
assert!(unfolded.tree.is_tree(NeighborMode::All)?);
assert_eq!(&unfolded.vertex_index[..3], &[0, 1, 2]);
§Errors

ErrorKind::InvalidVertexId for an invalid root.

Maximum cardinality search (Tarjan and Yannakakis, 1984).

Assigns a rank to every vertex so that visiting vertices by decreasing rank always picks the one with the most already visited neighbors. A graph is chordal iff any two higher-ranked neighbors of every vertex are adjacent. Edge directions are ignored.

Mutual edges u -> v, v -> u of a directed graph count as a single undirected edge. igraph 1.0.1 can get this wrong when its property cache already records “no multi-edges” (a girth of 2, or heap corruption in the chordality code), so for directed graphs this wrapper clears the cache around the C call.

Binds igraph_maximum_cardinality_search. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (2, 3)], 4, false)?;
let mcs = g.maximum_cardinality_search()?;
for (v, &rank) in mcs.alpha.iter().enumerate() {
    assert_eq!(mcs.alpham1[rank as usize], v as i64);
}
// The ranks can be reused by the chordality test.
let c = g.is_chordal_with(Some(&mcs.alpha), Some(&mcs.alpham1))?;
assert!(c.is_chordal);
Source

pub fn is_chordal(&self) -> Result<bool>

Whether the graph is chordal: every cycle of four or more vertices has a chord (equivalently, every induced cycle is a triangle).

Edge directions are ignored. See is_chordal_with to also get the fill-in.

Mutual edges u -> v, v -> u of a directed graph count as a single undirected edge. igraph 1.0.1 can get this wrong when its property cache already records “no multi-edges” (a girth of 2, or heap corruption in the chordality code), so for directed graphs this wrapper clears the cache around the C call.

Binds igraph_is_chordal. Time complexity: linear.

§Examples
use igraph::prelude::*;
let square = Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], 4, false)?;
assert!(!square.is_chordal()?);
let c = square.is_chordal_with(None, None)?;
assert_eq!(c.fill_in.len(), 1); // one diagonal suffices
assert!(c.triangulated.is_chordal()?);
Source

pub fn is_chordal_with( &self, alpha: Option<&[i64]>, alpham1: Option<&[VertexId]>, ) -> Result<Chordality>

Chordality test that also returns the fill-in (chordal completion) and the triangulated graph.

alpha and/or alpham1 may be supplied from a previous maximum_cardinality_search on the same graph (if only one is given, the other is its inverse); with None, the search is performed internally. The fill-in is not necessarily minimal.

Binds igraph_is_chordal.

igraph 1.0.0 and 1.0.1 only check the lengths of alpha and alpham1 and then index with their values: this wrapper also checks that they are permutations, so that bad input can never cause out-of-bounds reads.

§Errors

ErrorKind::InvalidValue if alpha or alpham1 is not a permutation of 0..vcount, or if both are given and they are not inverse of each other.

§Examples
use igraph::prelude::*;
// A 5-cycle needs two chords to become chordal.
let c5 = Graph::ring(5, false, false, true)?;
let c = c5.is_chordal_with(None, None)?;
assert!(!c.is_chordal);
assert_eq!(c.fill_in.len(), 2);
assert_eq!(c.triangulated.ecount(), 7);
assert!(c.triangulated.is_chordal()?);
Source

pub fn avg_nearest_neighbor_degree<'a>( &self, vertices: impl Into<VertexSelector<'a>>, mode: NeighborMode, neighbor_degree_mode: NeighborMode, weights: Option<&[f64]>, ) -> Result<NeighborDegree>

Average degree of the neighbors of each selected vertex (knn), and its average as a function of the vertex degree (knnk).

mode chooses which neighbors are considered and neighbor_degree_mode which of their degrees is averaged (both ignored for undirected graphs). With weights, the weighted average k_nn,u = 1/s_u Σ_v w_uv k_v of Barrat et al. (PNAS 2004) is computed, s_u being the strength of u, and knnk averages knn weighted by the strengths. Isolated vertices (with weights: vertices of zero strength) get NaN, as do degrees that don’t occur in knnk.

Binds igraph_avg_nearest_neighbor_degree. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
// In a star, the center's neighbors have degree 1, the leaves' neighbor degree 3.
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
let nd = star.avg_nearest_neighbor_degree(.., NeighborMode::All, NeighborMode::All, None)?;
assert_eq!(nd.knn, vec![1.0, 3.0, 3.0, 3.0]);
assert_eq!(nd.knnk[0], 3.0); // degree-1 vertices
assert_eq!(nd.knnk[2], 1.0); // degree-3 vertices

See also degree_correlation_vector (the same k_nn(k), averaged over edges, with more mode choices) and Graph::assortativity_degree, which summarizes degree correlations in a single number: a decreasing k_nn(k) goes with a negative assortativity.

Source

pub fn degree_correlation_vector( &self, weights: Option<&[f64]>, from_mode: NeighborMode, to_mode: NeighborMode, directed_neighbors: bool, ) -> Result<Vec<f64>>

The degree correlation function k_nn(k): result[k] is the mean degree of the targets of edges whose source has degree k (NaN for degrees that don’t occur). Unlike avg_nearest_neighbor_degree, index 0 is for degree 0.

The average is over all directed edges; undirected edges count as two reciprocal directed ones. from_mode and to_mode define the degree of sources and targets (out-in, out-out, in-in, in-out correlations). With directed_neighbors = false, directed edges are also treated as reciprocal pairs. With weights, weighted averages are computed.

Binds igraph_degree_correlation_vector. Time complexity: O(|V| + |E|).

§Examples
use igraph::prelude::*;
let star = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], 4, false)?;
let knnk = star.degree_correlation_vector(None, NeighborMode::All, NeighborMode::All, true)?;
assert_eq!(knnk[1], 3.0);
assert_eq!(knnk[3], 1.0);
assert!(knnk[0].is_nan() && knnk[2].is_nan());

See also Graph::joint_degree_matrix, from which k_nn(k) can be derived, and Graph::assortativity_degree.

Source

pub fn rich_club_sequence( &self, weights: Option<&[f64]>, vertex_order: &[VertexId], normalized: bool, loops: bool, directed: bool, ) -> Result<Vec<f64>>

Density of the subgraphs left after removing vertices one by one in the given order (the rich-club sequence).

result[i] is the density of the graph remaining after the first i vertices of vertex_order have been removed (so result[0] is the density of the whole graph). With normalized = false the remaining edge count (or total weight) is returned instead. loops decides whether self-loops are possible when computing the maximum number of edges (see density); directed = false treats a directed graph as undirected. Removing vertices by increasing degree reveals whether high-degree vertices are densely interconnected.

loops is ignored when normalized is false. If loops is false but the graph has self-loops, igraph emits a warning and still divides by the loop-free maximum, so densities may exceed 1.

This function is marked experimental in igraph 1.0.0 and 1.0.1.

Binds igraph_rich_club_sequence. Time complexity: O(|V| + |E|).

§Errors

ErrorKind::InvalidValue if vertex_order is not a permutation of the vertex ids (wrong length, out-of-range or repeated entries) or the weights have the wrong length.

§Examples
use igraph::prelude::*;
// A triangle with a pendant vertex 3 attached to 0; peel off the pendant first.
let g = Graph::from_edges(&[(0, 1), (1, 2), (2, 0), (0, 3)], 4, false)?;
let seq = g.rich_club_sequence(None, &[3, 0, 1, 2], true, false, false)?;
assert!((seq[0] - 4.0 / 6.0).abs() < 1e-12);
assert_eq!(seq[1], 1.0); // the triangle is a clique

A natural order removes vertices by increasing degree, see sort_vertex_ids_by_degree.

Source

pub fn get_laplacian( &self, mode: NeighborMode, normalization: LaplacianNormalization, weights: Option<&[f64]>, ) -> Result<Matrix>

The (dense) Laplacian matrix of the graph.

L_ij = -A_ij for i ≠ j and L_ii = d_i - A_ii, where A is the (possibly weighted) adjacency matrix and d_i the degree (strength). In directed graphs mode selects out-degrees (rows sum to zero), in-degrees (columns sum to zero), or ignores directions (NeighborMode::All). In undirected graphs A_ii is twice the number (weight) of self-loops. See LaplacianNormalization for the normalized variants. Weights must be non-negative and not NaN.

For undirected graphs the unnormalized Laplacian is symmetric and positive semi-definite. Without weights (or with positive weights) the multiplicity of its zero eigenvalue is the number of connected components, and, without weights, (matrix-tree theorem) the number of spanning trees of a connected graph is the product of the non-zero eigenvalues divided by |V|.

Binds igraph_get_laplacian. Time complexity: O(|V|²).

§Errors

ErrorKind::InvalidValue for negative or NaN weights, a wrong number of weights, or a normalization that would divide by a zero degree of a non-isolated vertex (e.g. LaplacianNormalization::Symmetric with NeighborMode::Out and a vertex with in-edges only).

§Examples
use igraph::{prelude::*, structural::LaplacianNormalization};
let path = Graph::from_edges(&[(0, 1), (1, 2)], 3, false)?;
let l = path.get_laplacian(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
assert_eq!(l.to_rows(), vec![
    vec![1.0, -1.0, 0.0],
    vec![-1.0, 2.0, -1.0],
    vec![0.0, -1.0, 1.0],
]);

// Matrix-tree theorem: K4 has 4^(4-2) = 16 spanning trees.
use igraph::linalg::{lapack_dsyevr, SymmetricRange};
let k4 = Graph::full(4, false, false)?;
let l = k4.get_laplacian(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
let eigen = lapack_dsyevr(&l, &SymmetricRange::All, 1e-12)?;
let trees: f64 = eigen.values[1..].iter().product::<f64>() / 4.0;
assert!((trees - 16.0).abs() < 1e-9);

See also get_laplacian_sparse, Graph::get_adjacency, and Graph::laplacian_spectral_embedding.

Source

pub fn get_laplacian_sparse( &self, mode: NeighborMode, normalization: LaplacianNormalization, weights: Option<&[f64]>, ) -> Result<Vec<(VertexId, VertexId, f64)>>

The Laplacian matrix in sparse form, as sorted (row, column, value) triplets of its non-zero entries.

Same definition as get_laplacian; it takes O(|V| + |E|) time and memory, which makes it suitable for large sparse graphs. Duplicate entries produced by igraph (e.g. for multi-edges) are summed, and entries that sum to exactly zero are dropped. Use get_laplacian_sparsemat to get a SparseMat for the sparse linear algebra of linalg instead.

For directed graphs with NeighborMode::All, the graph is treated as undirected, exactly like the dense variant does (the sparse C implementation of igraph 1.0.0 and 1.0.1 alone would not symmetrize the matrix).

Binds igraph_get_laplacian_sparse.

§Errors

As for get_laplacian.

§Examples
use igraph::{prelude::*, structural::LaplacianNormalization};
let g = Graph::from_edges(&[(0, 1)], 3, false)?;
let l = g.get_laplacian_sparse(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
assert_eq!(l, vec![(0, 0, 1.0), (0, 1, -1.0), (1, 0, -1.0), (1, 1, 1.0)]);
Source

pub fn get_laplacian_sparsemat( &self, mode: NeighborMode, normalization: LaplacianNormalization, weights: Option<&[f64]>, ) -> Result<SparseMat>

The Laplacian matrix as a SparseMat (in triplet form, possibly with duplicate entries, which sparse operations sum up).

Same definition, and same handling of directed graphs with NeighborMode::All, as get_laplacian_sparse. The result can be fed to the sparse linear algebra of linalg, e.g. SparseMat::mul_vec or SparseMat::to_dense.

Binds igraph_get_laplacian_sparse.

§Errors

As for get_laplacian.

§Examples
use igraph::{prelude::*, structural::LaplacianNormalization};
let path = Graph::ring(4, false, false, false)?;
let l = path.get_laplacian_sparsemat(NeighborMode::All, LaplacianNormalization::Unnormalized, None)?;
// The Laplacian annihilates constant vectors...
assert_eq!(l.mul_vec(&[1.0; 4])?, vec![0.0; 4]);
// ...and x^T L x is the sum of (x_i - x_j)^2 over the edges.
let x = [0.0, 1.0, 3.0, 6.0];
let lx = l.mul_vec(&x)?;
let quad: f64 = x.iter().zip(&lx).map(|(a, b)| a * b).sum();
assert_eq!(quad, 1.0 + 4.0 + 9.0);
Source§

impl igraph_t

Source

pub fn bfs( &self, roots: &[VertexId], options: &BfsOptions<'_>, ) -> Result<BfsResult>

Breadth-first search from one or more root vertices.

The search starts from roots[0]; when its tree is exhausted, it continues from the next root in roots that was not visited yet, and so on (roots already reached from a previous one are skipped). If options.unreachable is true, the remaining vertices are then used as roots too, in increasing id order, so that the whole graph is traversed. Neighbors are enqueued in the order of the graph’s adjacency lists (increasing neighbor id). An empty roots slice is allowed: nothing is visited unless unreachable is set.

The result gathers every output of the C function: the visiting order, and, for each vertex, its rank, BFS-tree parent, predecessor and successor in the visiting order, and distance from its root. See BfsResult. Use Graph::bfs_with to run a visitor closure during the search, and Graph::bfs_simple for a lighter single-root variant that also reports distance layers.

Time complexity: O(|V| + |E|).

Binds igraph_bfs.

See also Graph::subcomponent (just the set of reachable vertices), Graph::distances and Graph::get_shortest_paths (weighted distances and paths), Graph::connected_components, and Graph::unfold_tree (turns the graph into its BFS tree).

§Errors

ErrorKind::InvalidVertexId if a root or a restricted vertex does not exist.

§Examples

Two disjoint 4-cycles 0-1-2-3 and 4-5-6-7:

use igraph::prelude::*;
use igraph::visitor::BfsOptions;

let square = Graph::ring(4, false, false, true)?;
let g = square.disjoint_union(&square)?;
let r = g.bfs(&[0], &BfsOptions::default())?;
assert_eq!(r.order, [0, 1, 3, 2]);
assert_eq!(r.dist[2], Some(2));
assert!(!r.is_visited(5));

let all = g.bfs(&[0], &BfsOptions::default().with_unreachable(true))?;
assert_eq!(all.order, [0, 1, 3, 2, 4, 5, 7, 6]);
// One search tree per connected component.
assert_eq!(all.roots(), [0, 4]);
assert_eq!(g.connected_components(Connectedness::Weak)?.count, 2);
Source

pub fn bfs_with<F>( &self, roots: &[VertexId], options: &BfsOptions<'_>, visitor: F, ) -> Result<BfsResult>
where F: FnMut(BfsVisit) -> ControlFlow<()>,

Breadth-first search calling a visitor closure on every visited vertex.

Same traversal as Graph::bfs; in addition, visitor is called each time a vertex is visited (dequeued), after all its unvisited neighbors have been enqueued, with a BfsVisit describing the vertex, its predecessor and successor in the visiting order, its rank and its distance from the root.

Return ControlFlow::Continue to go on or ControlFlow::Break to stop: the search then ends normally, the returned BfsResult has stopped set, and contains the values computed so far (the vertices not yet visited are None). Note that, when stopping, the vertex the visitor stopped at has already been visited (it is in order), but its succ entry is None. If visitor panics, the search is aborted and the panic resumes in the caller.

Binds igraph_bfs with an igraph_bfshandler_t callback.

See also Graph::dfs_with for the depth-first counterpart.

§Errors

Same as Graph::bfs.

§Examples

Find the first vertex of a path that is at least three hops away, without visiting the rest of the graph:

use igraph::prelude::*;
use igraph::visitor::BfsOptions;
use std::ops::ControlFlow;

let path = Graph::ring(6, false, false, false)?; // 0 - 1 - 2 - 3 - 4 - 5
let mut seen = vec![];
let r = path.bfs_with(&[0], &BfsOptions::default(), |v| {
    seen.push(v.vid);
    if v.dist >= 3 { ControlFlow::Break(()) } else { ControlFlow::Continue(()) }
})?;
assert_eq!(seen, [0, 1, 2, 3]);
assert!(r.stopped);
assert_eq!(r.order, [0, 1, 2, 3]);
assert_eq!(r.rank[5], None);
Source

pub fn bfs_simple( &self, root: VertexId, mode: NeighborMode, ) -> Result<BfsSimpleResult>

Simple single-source breadth-first search, reporting distance layers.

Visits the vertices reachable from root (following edges according to mode in directed graphs; mode is ignored for undirected graphs) and returns the visiting order, the layers (vertices grouped by their distance from root) and the BFS-tree parents. This is the lighter alternative to Graph::bfs when only these outputs matter.

Time complexity: O(|V| + |E|).

Binds igraph_bfs_simple.

See also Graph::neighborhood (the vertices within a number of hops, possibly from several roots) and Graph::eccentricity (which, computed with the same mode, is num_layers() - 1: igraph ignores unreachable vertices there too).

§Errors

ErrorKind::InvalidVertexId if root does not exist (checked on the Rust side: igraph 1.0.0 and 1.0.1 do not validate it).

§Examples

The layers of a complete binary tree with 7 vertices:

use igraph::prelude::*;

let t = Graph::kary_tree(7, 2, TreeMode::Undirected)?;
let r = t.bfs_simple(0, NeighborMode::All)?;
assert_eq!(r.num_layers(), 3);
assert_eq!(r.layer(0), [0]);
assert_eq!(r.layer(1), [1, 2]);
assert_eq!(r.layer(2), [3, 4, 5, 6]);
assert_eq!(r.parents[4], Some(1));
// The eccentricity of the root is the index of the last layer.
assert_eq!(t.eccentricity(0, None, NeighborMode::All)?, [2.0]);
Source

pub fn dfs(&self, root: VertexId, options: &DfsOptions) -> Result<DfsResult>

Depth-first search from a root vertex.

Explores the graph depth first from root, following edges according to options.mode in directed graphs; neighbors are tried in adjacency list order (increasing neighbor id). If options.unreachable is set, further searches are started from the unvisited vertices, in increasing id order, until all vertices are visited.

Returns the discovery order (pre-order), the completion order (post-order), the DFS-forest parents and the depth of each vertex, see DfsResult. Use Graph::dfs_with to react to discovery and completion events while the search runs.

Time complexity: O(|V| + |E|).

Binds igraph_dfs.

See also Graph::topological_sorting, Graph::is_dag and Graph::find_cycle (in crate::cycles), which answer the most common DFS questions directly, and Graph::biconnected_components.

§Errors

ErrorKind::InvalidVertexId if root does not exist (checked on the Rust side; igraph 1.0.0 and 1.0.1 would report ErrorKind::InvalidValue). In particular, the graph must have at least one vertex.

§Examples

A reverse post-order of a DAG is a topological order:

use igraph::prelude::*;
use igraph::visitor::DfsOptions;

// 0 → 1 → 3, 0 → 2 → 3
let dag = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3)], 4, true)?;
let r = dag.dfs(0, &DfsOptions::default())?;
assert_eq!(r.order, [0, 1, 3, 2]);
assert_eq!(r.order_out, [3, 1, 2, 0]);
let topo: Vec<_> = r.order_out.iter().rev().copied().collect();
assert_eq!(topo, [0, 2, 1, 3]);
assert_eq!(r.dist[3], Some(2));
// `topological_sorting` finds another valid order (Kahn's algorithm).
assert_eq!(dag.topological_sorting(NeighborMode::Out)?, [0, 1, 2, 3]);
Source

pub fn dfs_with<F>( &self, root: VertexId, options: &DfsOptions, visitor: F, ) -> Result<DfsResult>
where F: FnMut(DfsEvent) -> ControlFlow<()>,

Depth-first search calling a visitor closure on discovery and completion of every vertex.

Same traversal as Graph::dfs; in addition visitor receives a DfsEvent::Discover when a vertex is first reached and a DfsEvent::Finish when its whole subtree has been explored (the two C callbacks in_callback and out_callback, merged in a single closure so that they can share mutable state). Both events carry the depth of the vertex in its DFS tree.

Return ControlFlow::Break to stop the search: the call still returns Ok with the partial results and stopped set. If visitor panics, the search is aborted and the panic resumes in the caller.

Binds igraph_dfs with two igraph_dfshandler_t callbacks.

See also Graph::bfs_with for the breadth-first counterpart.

§Errors

Same as Graph::dfs.

§Examples

Print a tree as an indented outline, using the discovery events:

use igraph::prelude::*;
use igraph::visitor::{DfsEvent, DfsOptions};
use std::ops::ControlFlow;

let t = Graph::from_edges(&[(0, 1), (0, 2), (1, 3)], 4, false)?;
let mut outline = String::new();
t.dfs_with(0, &DfsOptions::default(), |e| {
    if let DfsEvent::Discover { vid, dist } = e {
        outline += &format!("{}{vid}\n", "  ".repeat(dist));
    }
    ControlFlow::Continue(())
})?;
assert_eq!(outline, "0\n  1\n    3\n  2\n");

Trait Implementations§

Source§

impl AsRef<igraph_t> for Graph

A graph is trivially a reference to itself: this lets functions taking several graphs, like layout_merge_dla, accept collections of graphs (Vec<Graph>, &[Graph]) as well as of references (&[&Graph]).

Source§

fn as_ref(&self) -> &Graph

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl Clone for igraph_t

Source§

fn clone(&self) -> Self

Deep copy with igraph_copy.

1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for igraph_t

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Display for igraph_t

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

A one-line summary, e.g. Undirected graph with 3 vertices and 2 edges.

The alternate form ({:#}) appends the edge list, one edge per line, as from -> to (directed) or from -- to (undirected):

use igraph::prelude::*;
let g = Graph::from_edges(&[(0, 1), (1, 2)], 3, false).unwrap();
assert_eq!(g.to_string(), "Undirected graph with 3 vertices and 2 edges");
assert_eq!(format!("{g:#}"), "Undirected graph with 3 vertices and 2 edges\n0 -- 1\n1 -- 2");
Source§

impl Drop for igraph_t

Source§

fn drop(&mut self)

Destroys the graph with igraph_destroy.

Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more
Source§

impl Extend<igraph_t> for igraph_graph_list_t

Source§

fn extend<I: IntoIterator<Item = igraph_t>>(&mut self, iter: I)

Extends a collection with the contents of an iterator. Read more
Source§

fn extend_one(&mut self, item: A)

🔬This is a nightly-only experimental API. (extend_one)
Extends a collection with exactly one element.
Source§

fn extend_reserve(&mut self, additional: usize)

🔬This is a nightly-only experimental API. (extend_one)
Reserves capacity in a collection for the given number of additional elements. Read more
Source§

impl FromIterator<igraph_t> for igraph_graph_list_t

Source§

fn from_iter<I: IntoIterator<Item = igraph_t>>(iter: I) -> Self

Creates a value from an iterator. Read more
Source§

impl PartialEq for igraph_t

Source§

fn eq(&self, other: &Self) -> bool

Two graphs are equal when they are the same labelled graph: same directedness, vertex count and multiset of edges written in terms of vertex ids (the order of the edges, and of the endpoints of undirected edges, does not matter), see is_same_graph. Use the isomorphism functions to compare structure up to relabeling.

1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl Send for igraph_t

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T> ToString for T
where T: Display + ?Sized,

Source§

fn to_string(&self) -> String

Converts the given value to a String. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.