pub struct igraph_t { /* private fields */ }Implementations§
Source§impl igraph_t
impl igraph_t
Sourcepub fn setup()
pub fn setup()
Initializes igraph for the calling thread (see crate::error::ensure_init).
Calling it is optional: every safe wrapper does it automatically.
Sourcepub fn init_with(
f: impl FnOnce(*mut igraph_t) -> igraph_error_t,
) -> Result<Self>
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);Sourcepub fn new(num_vertices: usize, directed: bool) -> Self
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).
Sourcepub fn empty(num_vertices: usize, directed: bool) -> Result<Self>
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.
Sourcepub fn from_edges(
edges: &[(VertexId, VertexId)],
num_vertices: usize,
directed: bool,
) -> Result<Self>
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.
Sourcepub fn from_flat_edges(
edges: &[VertexId],
num_vertices: usize,
directed: bool,
) -> Result<Self>
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.
Sourcepub fn vcount(&self) -> usize
pub fn vcount(&self) -> usize
Number of vertices (igraph_vcount), O(1).
Sourcepub fn ecount(&self) -> usize
pub fn ecount(&self) -> usize
Number of edges (igraph_ecount), O(1).
Sourcepub fn num_vertices(&self) -> usize
pub fn num_vertices(&self) -> usize
Number of vertices; alias of vcount.
Sourcepub fn is_directed(&self) -> bool
pub fn is_directed(&self) -> bool
Whether the graph is directed (igraph_is_directed).
Sourcepub fn add_vertices(&mut self, n: usize) -> Result<()>
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.
Sourcepub fn add_edge(&mut self, from: VertexId, to: VertexId) -> Result<()>
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.
Sourcepub fn add_edges(&mut self, edges: &[(VertexId, VertexId)]) -> Result<()>
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).
Sourcepub fn add_edges_from_slice(
&mut self,
edges: &[(VertexId, VertexId)],
) -> Result<()>
pub fn add_edges_from_slice( &mut self, edges: &[(VertexId, VertexId)], ) -> Result<()>
Adds the given (from, to) edges; alias of add_edges.
Sourcepub fn add_edges_from_vector(&mut self, edges: &[VertexId]) -> Result<()>
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.
Sourcepub fn delete_edges<'a>(
&mut self,
edges: impl Into<EdgeSelector<'a>>,
) -> Result<()>
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.
Sourcepub fn delete_vertices<'a>(
&mut self,
vertices: impl Into<VertexSelector<'a>>,
) -> Result<()>
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.
Sourcepub fn delete_vertices_map<'a>(
&mut self,
vertices: impl Into<VertexSelector<'a>>,
) -> Result<(Vec<VertexId>, Vec<VertexId>)>
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.
Sourcepub fn neighbors(
&self,
vertex: VertexId,
mode: NeighborMode,
) -> Result<Vec<VertexId>>
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.
Sourcepub fn neighbors_with(
&self,
vertex: VertexId,
mode: NeighborMode,
loops: Loops,
multiple: bool,
) -> Result<Vec<VertexId>>
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.
Sourcepub fn incident(
&self,
vertex: VertexId,
mode: NeighborMode,
loops: Loops,
) -> Result<Vec<EdgeId>>
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.
Sourcepub fn degree<'a>(
&self,
vertices: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
loops: Loops,
) -> Result<Vec<igraph_int_t>>
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.
Sourcepub fn degree_of(
&self,
vertex: VertexId,
mode: NeighborMode,
loops: Loops,
) -> Result<igraph_int_t>
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.
Sourcepub fn edge(&self, edge: EdgeId) -> Result<(VertexId, VertexId)>
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.
Sourcepub fn edges<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<(VertexId, VertexId)>>
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.
Sourcepub fn edge_list(&self) -> Vec<(VertexId, VertexId)>
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.
Sourcepub fn get_eid(
&self,
from: VertexId,
to: VertexId,
directed: bool,
) -> Result<Option<EdgeId>>
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.
Sourcepub fn get_eids(
&self,
pairs: &[(VertexId, VertexId)],
directed: bool,
) -> Result<Vec<EdgeId>>
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.
Sourcepub fn get_all_eids_between(
&self,
from: VertexId,
to: VertexId,
directed: bool,
) -> Result<Vec<EdgeId>>
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.
Sourcepub fn is_same_graph(&self, other: &Self) -> Result<bool>
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());Sourcepub fn select_vertices<'a>(
&self,
vertices: impl Into<VertexSelector<'a>>,
) -> Result<Vec<VertexId>>
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.
Sourcepub fn select_edges<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<EdgeId>>
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.
Sourcepub fn invalidate_cache(&self)
pub fn invalidate_cache(&self)
Invalidates igraph’s internal cache of graph properties
(igraph_invalidate_cache); only needed after unsafe raw mutation.
Sourcepub fn try_clone(&self) -> Result<Self>
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).
Sourcepub fn add_vertex(&mut self) -> Result<VertexId>
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));Sourcepub fn add_edge_id(&mut self, from: VertexId, to: VertexId) -> Result<EdgeId>
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).
Sourcepub fn has_edge(
&self,
from: VertexId,
to: VertexId,
directed: bool,
) -> Result<bool>
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.
Sourcepub fn get_eids_opt(
&self,
pairs: &[(VertexId, VertexId)],
directed: bool,
) -> Result<Vec<Option<EdgeId>>>
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.
Sourcepub fn edges_flat<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
bycol: bool,
) -> Result<Vec<VertexId>>
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).
Sourcepub fn other_endpoint(&self, edge: EdgeId, vertex: VertexId) -> Result<VertexId>
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.
Sourcepub fn vs_size<'a>(
&self,
vertices: impl Into<VertexSelector<'a>>,
) -> Result<usize>
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.
Sourcepub fn es_size<'a>(&self, edges: impl Into<EdgeSelector<'a>>) -> Result<usize>
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.
Sourcepub fn adjacent_vertices(
&self,
vertex: VertexId,
mode: NeighborMode,
loops: Loops,
multiple: bool,
) -> Result<Vec<VertexId>>
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.
Sourcepub fn incident_edges(
&self,
vertex: VertexId,
mode: NeighborMode,
loops: Loops,
) -> Result<Vec<EdgeId>>
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.
Sourcepub fn edges_iter(
&self,
) -> impl Iterator<Item = (EdgeId, VertexId, VertexId)> + '_
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
impl igraph_t
Sourcepub fn adjlist_init(
&self,
mode: NeighborMode,
loops: Loops,
multiple: bool,
) -> Result<AdjList>
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::Nonedrops self-loops;Loops::Oncelists each loop edge once in the list of its vertex;Loops::Twicelists it twice, but only if the graph is undirected ormodeisNeighborMode::All(otherwise it behaves asOnce).multiple:truekeeps parallel edges, so a neighbor appears as many times as there are edges to it;falselists each neighbor once (with modeAllthis also merges a mutual pairu -> w,w -> uof a directed graph) and each kept self-loop once, or twice withLoops::Twicein an undirected graph or with modeAll.
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.
Sourcepub fn adjlist_init_complementer(
&self,
mode: NeighborMode,
loops: Loops,
) -> Result<AdjList>
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::Nonenever listsvamong its own neighbors;Loops::Oncelists it once if the graph has no loop onv;Loops::Twicelists it twice in that case whenmodeisNeighborMode::All, and behaves asOnceotherwise.
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]]);Sourcepub fn adjlist_init_from_inclist(&self, inclist: &IncList) -> Result<AdjList>
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
ErrorKind::InvalidValueif the incidence list does not have one entry per vertex of the graph;ErrorKind::InvalidEdgeIdif it contains an id that is not an edge of the graph (checked on the Rust side).
§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));
}
}Sourcepub fn adjlist(
adjlist: &AdjList,
mode: NeighborMode,
duplicate: bool,
) -> Result<Graph>
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::Allcreates an undirected graph;NeighborMode::Outa directed graph where each list holds the successors of its vertex;NeighborMode::Ina 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 byGraph::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::InvalidValueifduplicateis set (andmodeisNeighborMode::All) but the edges are not correctly listed twice:umust appear in the list ofwas many times aswappears in the list ofu, 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::InvalidVertexIdif 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.
Sourcepub fn inclist_init(&self, mode: NeighborMode, loops: Loops) -> Result<IncList>
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.
mode: in directed graphs, out-edges (NeighborMode::Out), in-edges (NeighborMode::In) or both (NeighborMode::All); ignored for undirected graphs. WithOutorIneach edge id appears once in the whole structure, withAlltwice (once per endpoint).loops:Loops::Nonedrops loop edges,Loops::Oncelists each loop edge once for its vertex,Loops::Twicetwice (only for undirected graphs ormode = All).
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.
Sourcepub fn lazy_adjlist_init(
&self,
mode: NeighborMode,
loops: Loops,
multiple: bool,
) -> Result<LazyAdjList<'_>>
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.
Sourcepub fn lazy_inclist_init(
&self,
mode: NeighborMode,
loops: Loops,
) -> Result<LazyIncList<'_>>
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
impl igraph_t
Sourcepub fn attribute_list(&self) -> Result<AttributeList>
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());Sourcepub fn attribute_names(&self, kind: AttributeKind) -> Result<Vec<String>>
pub fn attribute_names(&self, kind: AttributeKind) -> Result<Vec<String>>
Names of the attributes of one kind, in creation order (from
igraph_cattribute_list).
Sourcepub fn attribute_type(
&self,
kind: AttributeKind,
name: &str,
) -> Result<Option<AttributeType>>
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);Sourcepub fn has_attribute(&self, kind: AttributeKind, name: &str) -> bool
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"));Sourcepub fn graph_attr_numeric(&self, name: &str) -> Result<f64>
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);Sourcepub fn graph_attr_bool(&self, name: &str) -> Result<bool>
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.
Sourcepub fn graph_attr_str(&self, name: &str) -> Result<String>
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.
Sourcepub fn graph_attr(&self, name: &str) -> Result<AttributeValue>
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.
Sourcepub fn set_graph_attr_numeric(&mut self, name: &str, value: f64) -> Result<()>
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.
Sourcepub fn set_graph_attr_bool(&mut self, name: &str, value: bool) -> Result<()>
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.
Sourcepub fn set_graph_attr_str(&mut self, name: &str, value: &str) -> Result<()>
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.
Sourcepub fn set_graph_attr(
&mut self,
name: &str,
value: impl Into<AttributeValue>,
) -> Result<()>
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));Sourcepub fn remove_graph_attr(&mut self, name: &str) -> bool
pub fn remove_graph_attr(&mut self, name: &str) -> bool
Removes a graph attribute, returning whether it existed
(igraph_cattribute_remove_g).
Sourcepub fn remove_all_attributes(&mut self, graph: bool, vertex: bool, edge: bool)
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());Sourcepub fn vertex_attr_numeric(&self, name: &str, vid: VertexId) -> Result<f64>
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);Sourcepub fn vertex_attr_bool(&self, name: &str, vid: VertexId) -> Result<bool>
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.
Sourcepub fn vertex_attr_str(&self, name: &str, vid: VertexId) -> Result<String>
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.
Sourcepub fn vertex_attr(&self, name: &str, vid: VertexId) -> Result<AttributeValue>
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.
Sourcepub fn vertex_attr_numeric_values<'a>(
&self,
name: &str,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Vec<f64>>
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]);Sourcepub fn vertex_attr_bool_values<'a>(
&self,
name: &str,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Vec<bool>>
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.
Sourcepub fn vertex_attr_str_values<'a>(
&self,
name: &str,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Vec<String>>
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"]);Sourcepub fn vertex_attr_values<'a>(
&self,
name: &str,
vids: impl Into<VertexSelector<'a>>,
) -> Result<AttributeValues>
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.
Sourcepub fn set_vertex_attr_numeric(
&mut self,
name: &str,
vid: VertexId,
value: f64,
) -> Result<()>
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.
Sourcepub fn set_vertex_attr_bool(
&mut self,
name: &str,
vid: VertexId,
value: bool,
) -> Result<()>
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.
Sourcepub fn set_vertex_attr_str(
&mut self,
name: &str,
vid: VertexId,
value: &str,
) -> Result<()>
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.
Sourcepub fn set_vertex_attr(
&mut self,
name: &str,
vid: VertexId,
value: impl Into<AttributeValue>,
) -> Result<()>
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", ""]);Sourcepub fn set_vertex_attr_numeric_values(
&mut self,
name: &str,
values: &[f64],
) -> Result<()>
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.
Sourcepub fn set_vertex_attr_bool_values(
&mut self,
name: &str,
values: &[bool],
) -> Result<()>
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.
Sourcepub fn set_vertex_attr_str_values<S: AsRef<str>>(
&mut self,
name: &str,
values: &[S],
) -> Result<()>
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.
Sourcepub fn set_vertex_attr_values(
&mut self,
name: &str,
values: impl Into<AttributeValues>,
) -> Result<()>
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 lengthSourcepub fn remove_vertex_attr(&mut self, name: &str) -> bool
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();Sourcepub fn edge_attr_numeric(&self, name: &str, eid: EdgeId) -> Result<f64>
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.
Sourcepub fn edge_attr_bool(&self, name: &str, eid: EdgeId) -> Result<bool>
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.
Sourcepub fn edge_attr_str(&self, name: &str, eid: EdgeId) -> Result<String>
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.
Sourcepub fn edge_attr(&self, name: &str, eid: EdgeId) -> Result<AttributeValue>
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.
Sourcepub fn edge_attr_numeric_values<'a>(
&self,
name: &str,
eids: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<f64>>
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);Sourcepub fn edge_attr_bool_values<'a>(
&self,
name: &str,
eids: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<bool>>
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.
Sourcepub fn edge_attr_str_values<'a>(
&self,
name: &str,
eids: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<String>>
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.
Sourcepub fn edge_attr_values<'a>(
&self,
name: &str,
eids: impl Into<EdgeSelector<'a>>,
) -> Result<AttributeValues>
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.
Sourcepub fn set_edge_attr_numeric(
&mut self,
name: &str,
eid: EdgeId,
value: f64,
) -> Result<()>
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.
Sourcepub fn set_edge_attr_bool(
&mut self,
name: &str,
eid: EdgeId,
value: bool,
) -> Result<()>
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.
Sourcepub fn set_edge_attr_str(
&mut self,
name: &str,
eid: EdgeId,
value: &str,
) -> Result<()>
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.
Sourcepub fn set_edge_attr(
&mut self,
name: &str,
eid: EdgeId,
value: impl Into<AttributeValue>,
) -> Result<()>
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]);Sourcepub fn set_edge_attr_numeric_values(
&mut self,
name: &str,
values: &[f64],
) -> Result<()>
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.
Sourcepub fn set_edge_attr_bool_values(
&mut self,
name: &str,
values: &[bool],
) -> Result<()>
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.
Sourcepub fn set_edge_attr_str_values<S: AsRef<str>>(
&mut self,
name: &str,
values: &[S],
) -> Result<()>
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.
Sourcepub fn set_edge_attr_values(
&mut self,
name: &str,
values: impl Into<AttributeValues>,
) -> Result<()>
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.
Sourcepub fn remove_edge_attr(&mut self, name: &str) -> bool
pub fn remove_edge_attr(&mut self, name: &str) -> bool
Removes an edge attribute, returning whether it existed
(igraph_cattribute_remove_e).
Sourcepub fn add_vertices_with_attributes(
&mut self,
n: usize,
records: &[AttributeRecord],
) -> Result<()>
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"]);Sourcepub fn add_edges_with_attributes(
&mut self,
edges: &[(VertexId, VertexId)],
records: &[AttributeRecord],
) -> Result<()>
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);Sourcepub fn simplify_with_attributes(
&mut self,
remove_multiple: bool,
remove_loops: bool,
edge_comb: &AttributeCombination,
) -> Result<()>
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]);Sourcepub fn contract_vertices_with_attributes(
&mut self,
mapping: &[VertexId],
vertex_comb: &AttributeCombination,
) -> Result<()>
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 provincesSourcepub fn to_undirected_with_attributes(
&mut self,
mode: ToUndirected,
edge_comb: &AttributeCombination,
) -> Result<()>
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
impl igraph_t
Sourcepub fn is_bipartite(&self) -> Result<bool>
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());Sourcepub fn bipartite_types(&self) -> Result<Option<Vec<bool>>>
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 });
}Sourcepub fn create_bipartite(
types: &[bool],
edges: &[(VertexId, VertexId)],
directed: bool,
) -> Result<Graph>
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);Sourcepub fn full_bipartite(
n1: usize,
n2: usize,
directed: bool,
mode: NeighborMode,
) -> Result<BipartiteGraph>
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);Sourcepub fn biadjacency(
biadjmatrix: &Matrix,
directed: bool,
mode: NeighborMode,
multiple: bool,
) -> Result<BipartiteGraph>
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)]);Sourcepub fn weighted_biadjacency(
biadjmatrix: &Matrix,
directed: bool,
mode: NeighborMode,
) -> Result<WeightedBipartiteGraph>
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]);Sourcepub fn get_biadjacency(
&self,
types: &[bool],
weights: Option<&[f64]>,
) -> Result<Biadjacency>
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]));Sourcepub fn bipartite_projection_size(
&self,
types: &[bool],
) -> Result<ProjectionSize>
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));Sourcepub fn bipartite_projection(
&self,
types: &[bool],
probe1: Option<VertexId>,
) -> Result<BipartiteProjection>
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]);Sourcepub fn bipartite_projection_of(
&self,
types: &[bool],
kind: bool,
) -> Result<(Graph, Vec<i64>)>
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));Sourcepub fn bipartite_game_gnp(
n1: usize,
n2: usize,
p: f64,
options: &BipartiteGameOptions,
) -> Result<BipartiteGraph>
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);Sourcepub fn bipartite_game_gnm(
n1: usize,
n2: usize,
m: usize,
options: &BipartiteGameOptions,
) -> Result<BipartiteGraph>
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());Sourcepub fn bipartite_iea_game(
n1: usize,
n2: usize,
m: usize,
directed: bool,
mode: NeighborMode,
) -> Result<BipartiteGraph>
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));Sourcepub fn is_matching(
&self,
types: Option<&[bool]>,
matching: &[VertexId],
) -> Result<bool>
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-3Sourcepub fn is_maximal_matching(
&self,
types: Option<&[bool]>,
matching: &[VertexId],
) -> Result<bool>
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 addedSourcepub fn maximum_bipartite_matching(
&self,
types: &[bool],
weights: Option<&[f64]>,
) -> Result<BipartiteMatching>
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));Sourcepub fn maximum_bipartite_matching_eps(
&self,
types: &[bool],
weights: Option<&[f64]>,
eps: f64,
) -> Result<BipartiteMatching>
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
impl igraph_t
Sourcepub fn closeness<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
weights: Option<&[f64]>,
normalized: bool,
) -> Result<Vec<f64>>
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]);Sourcepub fn closeness_cutoff<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
weights: Option<&[f64]>,
normalized: bool,
cutoff: Option<f64>,
) -> Result<Vec<f64>>
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]);Sourcepub fn closeness_reachability<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
weights: Option<&[f64]>,
normalized: bool,
cutoff: Option<f64>,
) -> Result<Closeness>
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);Sourcepub fn harmonic_centrality<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
weights: Option<&[f64]>,
normalized: bool,
) -> Result<Vec<f64>>
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]);Sourcepub fn harmonic_centrality_cutoff<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
weights: Option<&[f64]>,
normalized: bool,
cutoff: Option<f64>,
) -> Result<Vec<f64>>
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]);Sourcepub fn betweenness<'a>(
&self,
weights: Option<&[f64]>,
vids: impl Into<VertexSelector<'a>>,
directed: bool,
normalized: bool,
) -> Result<Vec<f64>>
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]);Sourcepub fn betweenness_cutoff<'a>(
&self,
weights: Option<&[f64]>,
vids: impl Into<VertexSelector<'a>>,
directed: bool,
normalized: bool,
cutoff: Option<f64>,
) -> Result<Vec<f64>>
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]);Sourcepub 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>>
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());Sourcepub fn edge_betweenness<'a>(
&self,
weights: Option<&[f64]>,
eids: impl Into<EdgeSelector<'a>>,
directed: bool,
normalized: bool,
) -> Result<Vec<f64>>
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);Sourcepub fn edge_betweenness_cutoff<'a>(
&self,
weights: Option<&[f64]>,
eids: impl Into<EdgeSelector<'a>>,
directed: bool,
normalized: bool,
cutoff: Option<f64>,
) -> Result<Vec<f64>>
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]);Sourcepub 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>>
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]);Sourcepub fn pagerank<'a>(
&self,
weights: Option<&[f64]>,
vids: impl Into<VertexSelector<'a>>,
options: &PageRankOptions,
) -> Result<EigenScores>
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));Sourcepub fn personalized_pagerank<'a>(
&self,
weights: Option<&[f64]>,
reset: Option<&[f64]>,
vids: impl Into<VertexSelector<'a>>,
options: &PageRankOptions,
) -> Result<EigenScores>
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]);Sourcepub fn personalized_pagerank_vs<'a>(
&self,
weights: Option<&[f64]>,
reset_vids: impl Into<VertexSelector<'a>>,
vids: impl Into<VertexSelector<'a>>,
options: &PageRankOptions,
) -> Result<EigenScores>
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]);Sourcepub fn eigenvector_centrality(
&self,
mode: NeighborMode,
weights: Option<&[f64]>,
) -> Result<EigenScores>
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);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);Sourcepub fn constraint<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
) -> Result<Vec<f64>>
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]);Sourcepub fn convergence_degree(&self) -> Result<ConvergenceDegree>
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);Sourcepub fn centralization_degree(
&self,
mode: NeighborMode,
loops: Loops,
normalized: bool,
) -> Result<Centralization>
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);Sourcepub fn centralization_degree_tmax(
&self,
mode: NeighborMode,
loops: Loops,
) -> Result<f64>
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.
Sourcepub fn centralization_betweenness(
&self,
directed: bool,
normalized: bool,
) -> Result<Centralization>
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);Sourcepub fn centralization_betweenness_tmax(&self, directed: bool) -> Result<f64>
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.
Sourcepub fn centralization_closeness(
&self,
mode: NeighborMode,
normalized: bool,
) -> Result<Centralization>
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);Sourcepub fn centralization_closeness_tmax(&self, mode: NeighborMode) -> Result<f64>
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.
Sourcepub fn centralization_eigenvector_centrality(
&self,
mode: NeighborMode,
normalized: bool,
) -> Result<EigenvectorCentralization>
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);Sourcepub fn centralization_eigenvector_centrality_tmax(
&self,
mode: NeighborMode,
) -> Result<f64>
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.
Sourcepub fn local_scan_0(
&self,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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]);Sourcepub fn local_scan_0_them(
&self,
them: &Graph,
weights_them: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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]);Sourcepub fn local_scan_1_ecount(
&self,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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]);Sourcepub fn local_scan_1_ecount_them(
&self,
them: &Graph,
weights_them: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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.
Sourcepub fn local_scan_k_ecount(
&self,
k: usize,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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]);Sourcepub fn local_scan_k_ecount_them(
&self,
them: &Graph,
k: usize,
weights_them: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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.
Sourcepub fn local_scan_subset_ecount<S: AsRef<[VertexId]>>(
&self,
weights: Option<&[f64]>,
subsets: &[S],
) -> Result<Vec<f64>>
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]);Sourcepub 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
pub fn local_scan_neighborhood_ecount<S: AsRef<[VertexId]>>( &self, weights: Option<&[f64]>, neighborhoods: &[S], ) -> Result<Vec<f64>>
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
impl igraph_t
Sourcepub fn cliques(
&self,
sizes: impl RangeBounds<usize>,
max_results: Option<usize>,
) -> Result<Vec<Vec<VertexId>>>
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);Sourcepub fn cliques_callback<F>(
&self,
sizes: impl RangeBounds<usize>,
f: F,
) -> Result<()>
pub fn cliques_callback<F>( &self, sizes: impl RangeBounds<usize>, f: F, ) -> Result<()>
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));Sourcepub fn clique_size_hist(
&self,
sizes: impl RangeBounds<usize>,
) -> Result<Vec<usize>>
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]);Sourcepub fn largest_cliques(&self) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn clique_number(&self) -> Result<usize>
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);Sourcepub fn maximal_cliques(
&self,
sizes: impl RangeBounds<usize>,
max_results: Option<usize>,
) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn maximal_cliques_callback<F>(
&self,
sizes: impl RangeBounds<usize>,
f: F,
) -> Result<()>
pub fn maximal_cliques_callback<F>( &self, sizes: impl RangeBounds<usize>, f: F, ) -> Result<()>
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]);Sourcepub fn maximal_cliques_count(
&self,
sizes: impl RangeBounds<usize>,
) -> Result<usize>
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);Sourcepub fn maximal_cliques_hist(
&self,
sizes: impl RangeBounds<usize>,
) -> Result<Vec<usize>>
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]);Sourcepub fn maximal_cliques_subset(
&self,
subset: &[VertexId],
sizes: impl RangeBounds<usize>,
max_results: Option<usize>,
) -> Result<Vec<Vec<VertexId>>>
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);Sourcepub fn write_maximal_cliques<W: Write + ?Sized>(
&self,
out: &mut W,
sizes: impl RangeBounds<usize>,
max_results: Option<usize>,
) -> Result<()>
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 smallSourcepub fn weighted_cliques(
&self,
weights: Option<&[f64]>,
options: &WeightedCliqueOptions,
) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn largest_weighted_cliques(
&self,
weights: Option<&[f64]>,
) -> Result<Vec<Vec<VertexId>>>
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);Sourcepub fn weighted_clique_number(&self, weights: Option<&[f64]>) -> Result<f64>
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);Sourcepub fn independent_vertex_sets(
&self,
sizes: impl RangeBounds<usize>,
max_results: Option<usize>,
) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn largest_independent_vertex_sets(&self) -> Result<Vec<Vec<VertexId>>>
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());Sourcepub fn maximal_independent_vertex_sets(
&self,
sizes: impl RangeBounds<usize>,
max_results: Option<usize>,
) -> Result<Vec<Vec<VertexId>>>
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);Sourcepub fn independence_number(&self) -> Result<usize>
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);Sourcepub fn vertex_coloring_greedy(
&self,
heuristic: ColoringGreedy,
) -> Result<Vec<i64>>
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);Sourcepub fn is_vertex_coloring(&self, colors: &[i64]) -> Result<bool>
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());Sourcepub fn is_bipartite_coloring(
&self,
types: &[bool],
) -> Result<Option<NeighborMode>>
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);Sourcepub fn is_edge_coloring(&self, colors: &[i64]) -> Result<bool>
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
impl igraph_t
Sourcepub fn cocitation<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Matrix>
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]);Sourcepub fn bibcoupling<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Matrix>
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]);Sourcepub fn similarity_inverse_log_weighted<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
) -> Result<Matrix>
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:
NeighborMode::Out: out-neighbors (the vertices both cite); each common neighbor is weighted by its in-degree;NeighborMode::In: in-neighbors; weighted by the out-degree;NeighborMode::All: the graph is treated as undirected.
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);Sourcepub fn similarity_jaccard<'a, 'b>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'b>>,
mode: NeighborMode,
loops: bool,
) -> Result<Matrix>
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}Sourcepub fn similarity_jaccard_pairs(
&self,
pairs: &[(VertexId, VertexId)],
mode: NeighborMode,
loops: bool,
) -> Result<Vec<f64>>
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]);Sourcepub fn similarity_jaccard_es<'a>(
&self,
es: impl Into<EdgeSelector<'a>>,
mode: NeighborMode,
loops: bool,
) -> Result<Vec<f64>>
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);Sourcepub fn similarity_dice<'a, 'b>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'b>>,
mode: NeighborMode,
loops: bool,
) -> Result<Matrix>
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);Sourcepub fn similarity_dice_pairs(
&self,
pairs: &[(VertexId, VertexId)],
mode: NeighborMode,
loops: bool,
) -> Result<Vec<f64>>
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);Sourcepub fn similarity_dice_es<'a>(
&self,
es: impl Into<EdgeSelector<'a>>,
mode: NeighborMode,
loops: bool,
) -> Result<Vec<f64>>
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
impl igraph_t
Sourcepub fn coreness(&self, mode: NeighborMode) -> Result<Vec<i64>>
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]);Sourcepub fn trussness(&self) -> Result<Vec<i64>>
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]);Sourcepub fn modularity(
&self,
membership: &[i64],
weights: Option<&[f64]>,
resolution: f64,
directed: bool,
) -> Result<f64>
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);Sourcepub fn modularity_matrix(
&self,
weights: Option<&[f64]>,
resolution: f64,
directed: bool,
) -> Result<Matrix>
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]]);Sourcepub fn community_multilevel(
&self,
weights: Option<&[f64]>,
resolution: f64,
) -> Result<Multilevel>
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);Sourcepub fn community_leiden(
&self,
edge_weights: Option<&[f64]>,
vertex_out_weights: Option<&[f64]>,
vertex_in_weights: Option<&[f64]>,
options: &LeidenOptions<'_>,
) -> Result<Leiden>
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);Sourcepub fn community_leiden_simple(
&self,
weights: Option<&[f64]>,
objective: LeidenObjective,
options: &LeidenOptions<'_>,
) -> Result<Leiden>
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);Sourcepub fn community_fastgreedy(
&self,
weights: Option<&[f64]>,
) -> Result<Dendrogram>
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)]);Sourcepub fn community_walktrap(
&self,
weights: Option<&[f64]>,
steps: usize,
) -> Result<Dendrogram>
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]);Sourcepub fn community_edge_betweenness(
&self,
weights: Option<&[f64]>,
lengths: Option<&[f64]>,
directed: bool,
) -> Result<EdgeBetweennessCommunities>
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]);Sourcepub fn community_eb_get_merges(
&self,
directed: bool,
edges: &[EdgeId],
weights: Option<&[f64]>,
) -> Result<EdgeRemovalMerges>
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]);Sourcepub fn community_leading_eigenvector(
&self,
weights: Option<&[f64]>,
steps: Option<usize>,
start: Option<&[i64]>,
) -> Result<LeadingEigenvector>
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);Sourcepub fn community_leading_eigenvector_with<F>(
&self,
weights: Option<&[f64]>,
steps: Option<usize>,
start: Option<&[i64]>,
callback: F,
) -> Result<LeadingEigenvector>
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);Sourcepub fn community_spinglass(
&self,
weights: Option<&[f64]>,
options: &SpinglassOptions,
) -> Result<Spinglass>
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);Sourcepub fn community_spinglass_single(
&self,
weights: Option<&[f64]>,
vertex: VertexId,
options: &SpinglassOptions,
) -> Result<SpinglassSingle>
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));Sourcepub fn community_label_propagation(
&self,
weights: Option<&[f64]>,
options: &LabelPropagationOptions<'_>,
) -> Result<Vec<i64>>
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]));Sourcepub fn community_infomap(
&self,
edge_weights: Option<&[f64]>,
vertex_weights: Option<&[f64]>,
options: &InfomapOptions,
) -> Result<Infomap>
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);Sourcepub fn community_fluid_communities(&self, k: usize) -> Result<Vec<i64>>
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);Sourcepub fn community_voronoi(
&self,
lengths: Option<&[f64]>,
weights: Option<&[f64]>,
mode: NeighborMode,
radius: Option<f64>,
) -> Result<Voronoi>
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);Sourcepub fn community_optimal_modularity(
&self,
weights: Option<&[f64]>,
resolution: f64,
) -> Result<Clustering>
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
impl igraph_t
Sourcepub fn hrg_fit(&self, steps: usize) -> Result<Hrg>
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)));Sourcepub fn hrg_refit(&self, hrg: &mut Hrg, steps: usize) -> Result<()>
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);Sourcepub fn hrg_consensus(
&self,
start: Option<&Hrg>,
num_samples: usize,
) -> Result<HrgConsensus>
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));Sourcepub fn hrg_predict(
&self,
start: Option<&Hrg>,
num_samples: usize,
num_bins: usize,
) -> Result<HrgPrediction>
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)]);Sourcepub fn hrg_game(hrg: &Hrg) -> Result<Graph>
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());Sourcepub fn from_hrg_dendrogram(hrg: &Hrg) -> Result<(Graph, Vec<f64>)>
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
impl igraph_t
Sourcepub fn connected_components(
&self,
mode: Connectedness,
) -> Result<ConnectedComponents>
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));Sourcepub fn is_connected(&self, mode: Connectedness) -> Result<bool>
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());Sourcepub fn decompose(
&self,
mode: Connectedness,
max_components: Option<usize>,
min_vertices: usize,
) -> Result<Vec<Graph>>
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)]);Sourcepub fn articulation_points(&self) -> Result<Vec<VertexId>>
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]);Sourcepub fn bridges(&self) -> Result<Vec<EdgeId>>
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]);Sourcepub fn biconnected_components(&self) -> Result<BiconnectedComponents>
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)));Sourcepub fn is_biconnected(&self) -> Result<bool>
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());Sourcepub fn bond_percolation(
&self,
edge_order: Option<&[EdgeId]>,
) -> Result<BondPercolation>
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));Sourcepub fn site_percolation(
&self,
vertex_order: Option<&[VertexId]>,
) -> Result<SitePercolation>
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]);Sourcepub fn is_separator<'a>(
&self,
candidate: impl Into<VertexSelector<'a>>,
) -> Result<bool>
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());Sourcepub fn is_minimal_separator<'a>(
&self,
candidate: impl Into<VertexSelector<'a>>,
) -> Result<bool>
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());Sourcepub fn all_minimal_st_separators(&self) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn minimum_size_separators(&self) -> Result<Vec<Vec<VertexId>>>
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));Sourcepub fn cohesive_blocks(&self) -> Result<CohesiveBlocks>
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]);Sourcepub fn reachability(&self, mode: NeighborMode) -> Result<Reachability>
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]);Sourcepub fn count_reachable(&self, mode: NeighborMode) -> Result<Vec<usize>>
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]);Sourcepub fn transitive_closure(&self) -> Result<Graph>
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());Sourcepub fn neighborhood_size<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
order: Option<usize>,
mode: NeighborMode,
mindist: usize,
) -> Result<Vec<usize>>
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]);Sourcepub fn neighborhood<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
order: Option<usize>,
mode: NeighborMode,
mindist: usize,
) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn neighborhood_graphs<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
order: Option<usize>,
mode: NeighborMode,
mindist: usize,
) -> Result<Vec<Graph>>
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.
impl igraph_t
Deterministic generators, see the module docs.
Sourcepub fn adjacency(
matrix: &Matrix,
mode: Adjacency,
loops: Loops,
) -> Result<Graph>
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):
Adjacency::Directed: directed graph withA[i][j]edgesi -> j;Adjacency::Undirected: undirected graph, the matrix must be symmetric;Adjacency::Max/Adjacency::Min/Adjacency::Plus:max,minor sum ofA[i][j]andA[j][i]undirected edges;Adjacency::Upper/Adjacency::Lower: only the upper / lower triangle (diagonal included) is used.
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);Sourcepub fn weighted_adjacency(
matrix: &Matrix,
mode: Adjacency,
loops: Loops,
) -> Result<(Graph, Vec<f64>)>
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)]);Sourcepub fn sparse_adjacency(
n: usize,
entries: &[(VertexId, VertexId, f64)],
mode: Adjacency,
loops: Loops,
) -> Result<Graph>
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);Sourcepub fn sparse_weighted_adjacency(
n: usize,
entries: &[(VertexId, VertexId, f64)],
mode: Adjacency,
loops: Loops,
) -> Result<(Graph, Vec<f64>)>
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);Sourcepub fn small(n: usize, directed: bool, edges: &[VertexId]) -> Result<Graph>
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);Sourcepub fn star(n: usize, mode: StarMode, center: VertexId) -> Result<Graph>
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);Sourcepub fn wheel(n: usize, mode: WheelMode, center: VertexId) -> Result<Graph>
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 edgesSourcepub fn hypercube(dim: usize, directed: bool) -> Result<Graph>
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());Sourcepub fn square_lattice(
dims: &[usize],
nei: usize,
directed: bool,
mutual: bool,
periodic: Option<&[bool]>,
) -> Result<Graph>
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));Sourcepub fn ring(
n: usize,
directed: bool,
mutual: bool,
circular: bool,
) -> Result<Graph>
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)]);Sourcepub fn path_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph>
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 directionsSourcepub fn cycle_graph(n: usize, directed: bool, mutual: bool) -> Result<Graph>
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));Sourcepub fn kary_tree(n: usize, children: usize, mode: TreeMode) -> Result<Graph>
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());Sourcepub fn symmetric_tree(branches: &[usize], mode: TreeMode) -> Result<Graph>
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);Sourcepub fn regular_tree(h: usize, k: usize, mode: TreeMode) -> Result<Graph>
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);Sourcepub fn tree_from_parent_vector(
parents: &[VertexId],
mode: TreeMode,
) -> Result<Graph>
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);Sourcepub fn from_prufer(prufer: &[VertexId]) -> Result<Graph>
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]);Sourcepub fn full(n: usize, directed: bool, loops: bool) -> Result<Graph>
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);Sourcepub fn full_multipartite(
sizes: &[usize],
directed: bool,
mode: NeighborMode,
) -> Result<(Graph, Vec<i64>)>
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]);Sourcepub fn turan(n: usize, r: usize) -> Result<(Graph, Vec<i64>)>
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);Sourcepub fn full_citation(n: usize, directed: bool) -> Result<Graph>
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());Sourcepub fn atlas(number: usize) -> Result<Graph>
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));Sourcepub fn extended_chordal_ring<R: AsRef<[i64]>>(
nodes: usize,
w: &[R],
directed: bool,
) -> Result<Graph>
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);Sourcepub fn linegraph(&self) -> Result<Graph>
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));Sourcepub fn de_bruijn(m: usize, n: usize) -> Result<Graph>
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);Sourcepub fn kautz(m: usize, n: usize) -> Result<Graph>
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));Sourcepub fn circulant(n: usize, shifts: &[i64], directed: bool) -> Result<Graph>
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);Sourcepub fn generalized_petersen(n: usize, k: usize) -> Result<Graph>
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());Sourcepub fn famous(name: impl AsRef<str>) -> Result<Graph>
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);Sourcepub fn lcf(n: usize, shifts: &[i64], repeats: usize) -> Result<Graph>
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());Sourcepub fn realize_degree_sequence(
out_degrees: &[i64],
in_degrees: Option<&[i64]>,
allowed: impl Into<AllowedEdgeTypes>,
method: RealizeDegseq,
) -> Result<Graph>
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(
°rees, 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);Sourcepub fn realize_bipartite_degree_sequence(
degrees1: &[i64],
degrees2: &[i64],
allowed: impl Into<AllowedEdgeTypes>,
method: RealizeDegseq,
) -> Result<Graph>
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());Sourcepub fn triangular_lattice(
dims: &[usize],
directed: bool,
mutual: bool,
) -> Result<Graph>
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));Sourcepub fn hexagonal_lattice(
dims: &[usize],
directed: bool,
mutual: bool,
) -> Result<Graph>
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));Sourcepub fn mycielski_graph(k: usize) -> Result<Graph>
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
impl igraph_t
Sourcepub fn get_adjacency(
&self,
kind: GetAdjacency,
weights: Option<&[f64]>,
loops: Loops,
) -> Result<Matrix>
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).
kindselects 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) orGetAdjacency::Both(the full symmetric matrix). It is ignored for directed graphs.weights: optional edge weights, one per edge;Nonemeans every edge has weight 1.loopscontrols the diagonal:Loops::Noneignores self-loops (zero diagonal),Loops::Oncecounts each loop once andLoops::Twicecounts loops twice in undirected graphs (it counts edge stems, the convention that makes row sums equal to degrees). In directed graphsTwicebehaves likeOnce.
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],
]);Sourcepub fn get_adjacency_sparse(
&self,
kind: GetAdjacency,
weights: Option<&[f64]>,
loops: Loops,
) -> Result<CooMatrix>
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);Sourcepub fn get_stochastic(
&self,
column_wise: bool,
weights: Option<&[f64]>,
) -> Result<Matrix>
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());Sourcepub fn get_stochastic_sparse(
&self,
column_wise: bool,
weights: Option<&[f64]>,
) -> Result<CooMatrix>
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]);Sourcepub fn get_edgelist(&self, bycol: bool) -> Result<Vec<VertexId>>
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());Sourcepub fn to_directed(&mut self, mode: ToDirected) -> Result<()>
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 edgeebecomes two directed edges, one per direction; the first|E|edges keep the ids and endpoint order of the original ones, edge|E| + eis the reverse ofe.ToDirected::Random: each edge gets a uniformly random direction (drawn from the calling thread’s default RNG, so reproducible afterrng::seed).ToDirected::Acyclic: each edge is directed from the smaller to the larger vertex id; without self-loops the result is a DAG (seeGraph::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)]);Sourcepub fn into_directed(self, mode: ToDirected) -> Result<Graph>
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));Sourcepub fn to_undirected(&mut self, mode: ToUndirected) -> Result<()>
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 paira → 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 → bmatched withb → 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)]);Sourcepub fn to_undirected_with_comb(
&mut self,
mode: ToUndirected,
edge_comb: &AttributeCombination,
) -> Result<()>
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]);Sourcepub fn into_undirected(self, mode: ToUndirected) -> Result<Graph>
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));Sourcepub fn to_prufer(&self) -> Result<Vec<VertexId>>
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
impl igraph_t
Sourcepub fn is_dag(&self) -> Result<bool>
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 acyclicSourcepub fn topological_sorting(&self, mode: NeighborMode) -> Result<Vec<VertexId>>
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]);Sourcepub fn find_cycle(&self, mode: NeighborMode) -> Result<Option<Cycle>>
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());Sourcepub fn simple_cycles(&self, options: &SimpleCyclesOptions) -> Result<Vec<Cycle>>
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);Sourcepub fn simple_cycles_callback<F>(
&self,
options: &SimpleCyclesOptions,
f: F,
) -> Result<()>
pub fn simple_cycles_callback<F>( &self, options: &SimpleCyclesOptions, f: F, ) -> Result<()>
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));Sourcepub fn fundamental_cycles(
&self,
start: Option<VertexId>,
bfs_cutoff: Option<usize>,
) -> Result<Vec<Vec<EdgeId>>>
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:Nonereturns a complete basis;Some(v)returns only the fundamental cycles of the BFS tree rooted atv, i.e. of the (weakly) connected component ofv.bfs_cutoff:Nonereturns a complete basis;Some(k)limits the BFS depth, so that only cycles of length at most2k + 1are 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 + 1Sourcepub fn minimum_cycle_basis(
&self,
options: &MinimumCycleBasisOptions,
) -> Result<Vec<Vec<EdgeId>>>
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]);Sourcepub fn feedback_arc_set(
&self,
weights: Option<&[f64]>,
algo: FasAlgorithm,
) -> Result<Vec<EdgeId>>
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 (currentlyExactIpCg). 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]);Sourcepub fn feedback_vertex_set(
&self,
vertex_weights: Option<&[f64]>,
algo: FvsAlgorithm,
) -> Result<Vec<VertexId>>
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)?);Sourcepub fn is_eulerian(&self) -> Result<EulerianStatus>
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);Sourcepub fn eulerian_path(&self) -> Result<EulerianWalk>
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]);Sourcepub fn eulerian_cycle(&self) -> Result<EulerianWalk>
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
impl igraph_t
Sourcepub fn maxflow(
&self,
source: VertexId,
target: VertexId,
capacity: Option<&[f64]>,
) -> Result<MaxFlow>
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);Sourcepub fn maxflow_value(
&self,
source: VertexId,
target: VertexId,
capacity: Option<&[f64]>,
) -> Result<f64>
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);Sourcepub fn maxflow_value_with_stats(
&self,
source: VertexId,
target: VertexId,
capacity: Option<&[f64]>,
) -> Result<(f64, MaxflowStats)>
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 relabellingSourcepub fn st_mincut(
&self,
source: VertexId,
target: VertexId,
capacity: Option<&[f64]>,
) -> Result<Cut>
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]);Sourcepub fn st_mincut_value(
&self,
source: VertexId,
target: VertexId,
capacity: Option<&[f64]>,
) -> Result<f64>
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);Sourcepub fn mincut(&self, capacity: Option<&[f64]>) -> Result<Cut>
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-6Sourcepub fn mincut_value(&self, capacity: Option<&[f64]>) -> Result<f64>
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);Sourcepub fn st_vertex_connectivity(
&self,
source: VertexId,
target: VertexId,
neighbors: VconnNei,
) -> Result<i64>
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());Sourcepub fn vertex_connectivity(&self, checks: bool) -> Result<usize>
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);Sourcepub fn st_edge_connectivity(
&self,
source: VertexId,
target: VertexId,
) -> Result<usize>
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);Sourcepub fn edge_connectivity(&self, checks: bool) -> Result<usize>
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]);Sourcepub fn edge_disjoint_paths(
&self,
source: VertexId,
target: VertexId,
) -> Result<usize>
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);Sourcepub fn vertex_disjoint_paths(
&self,
source: VertexId,
target: VertexId,
) -> Result<usize>
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);Sourcepub fn adhesion(&self, checks: bool) -> Result<usize>
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)?);Sourcepub fn cohesion(&self, checks: bool) -> Result<usize>
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);Sourcepub fn even_tarjan_reduction(&self) -> Result<EvenTarjanReduction>
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);Sourcepub fn residual_graph(
&self,
capacity: &[f64],
flow: &[f64],
) -> Result<ResidualGraph>
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]);Sourcepub fn reverse_residual_graph(
&self,
capacity: Option<&[f64]>,
flow: &[f64],
) -> Result<Graph>
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)]);Sourcepub fn dominator_tree(
&self,
root: VertexId,
mode: NeighborMode,
) -> Result<DominatorTree>
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]);Sourcepub fn all_st_cuts(&self, source: VertexId, target: VertexId) -> Result<StCuts>
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]]);Sourcepub fn all_st_mincuts(
&self,
source: VertexId,
target: VertexId,
capacity: Option<&[f64]>,
) -> Result<StMinCuts>
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]]);Sourcepub fn gomory_hu_tree(&self, capacity: Option<&[f64]>) -> Result<GomoryHuTree>
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 edgeSource§impl igraph_t
impl igraph_t
Sourcepub fn read_graph(
path: impl AsRef<Path>,
format: GraphFormat,
directed: bool,
) -> Result<Graph>
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)]);Sourcepub fn read_graph_edgelist(
path: impl AsRef<Path>,
n: usize,
directed: bool,
) -> Result<Graph>
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…).
Sourcepub fn read_graph_edgelist_from_str(
text: &str,
n: usize,
directed: bool,
) -> Result<Graph>
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);Sourcepub fn read_graph_ncol(
path: impl AsRef<Path>,
predefnames: &[&str],
options: &NcolLglOptions,
) -> Result<Graph>
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.
Sourcepub fn read_graph_ncol_from_str(
text: &str,
predefnames: &[&str],
options: &NcolLglOptions,
) -> Result<Graph>
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)]);Sourcepub fn read_graph_lgl(
path: impl AsRef<Path>,
options: &NcolLglOptions,
) -> Result<Graph>
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.
Sourcepub fn read_graph_lgl_from_str(
text: &str,
options: &NcolLglOptions,
) -> Result<Graph>
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)]);Sourcepub fn read_graph_pajek(path: impl AsRef<Path>) -> Result<Graph>
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.
Sourcepub fn read_graph_pajek_from_str(text: &str) -> Result<Graph>
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));Sourcepub fn read_graph_graphml(path: impl AsRef<Path>, index: usize) -> Result<Graph>
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.
Sourcepub fn read_graph_graphml_from_str(text: &str, index: usize) -> Result<Graph>
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);Sourcepub fn read_graph_dimacs_flow(
path: impl AsRef<Path>,
directed: bool,
) -> Result<DimacsFlow>
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.
Sourcepub fn read_graph_dimacs_flow_from_str(
text: &str,
directed: bool,
) -> Result<DimacsFlow>
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);Sourcepub fn read_graph_graphdb(
path: impl AsRef<Path>,
directed: bool,
) -> Result<Graph>
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.
Sourcepub fn read_graph_graphdb_from_bytes(
data: &[u8],
directed: bool,
) -> Result<Graph>
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)]);Sourcepub fn read_graph_gml(path: impl AsRef<Path>) -> Result<Graph>
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.
Sourcepub fn read_graph_gml_from_str(text: &str) -> Result<Graph>
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)]);Sourcepub fn read_graph_dl(path: impl AsRef<Path>, directed: bool) -> Result<Graph>
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.
Sourcepub fn read_graph_dl_from_str(text: &str, directed: bool) -> Result<Graph>
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
impl igraph_t
Sourcepub fn write_graph(
&self,
path: impl AsRef<Path>,
format: GraphFormat,
) -> Result<()>
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;"));Sourcepub fn write_graph_edgelist(&self, path: impl AsRef<Path>) -> Result<()>
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.
Sourcepub fn write_graph_edgelist_to_string(&self) -> Result<String>
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");Sourcepub fn write_graph_ncol(
&self,
path: impl AsRef<Path>,
names: Option<&str>,
weights: Option<&str>,
) -> Result<()>
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.
Sourcepub fn write_graph_ncol_to_string(
&self,
names: Option<&str>,
weights: Option<&str>,
) -> Result<String>
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");Sourcepub fn write_graph_lgl(
&self,
path: impl AsRef<Path>,
names: Option<&str>,
weights: Option<&str>,
isolates: bool,
) -> Result<()>
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.
Sourcepub fn write_graph_lgl_to_string(
&self,
names: Option<&str>,
weights: Option<&str>,
isolates: bool,
) -> Result<String>
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");Sourcepub fn write_graph_graphml(&self, path: impl AsRef<Path>) -> Result<()>
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">"#));Sourcepub fn write_graph_graphml_with(
&self,
path: impl AsRef<Path>,
prefixattr: bool,
) -> Result<()>
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
Sourcepub fn write_graph_graphml_to_string(&self, prefixattr: bool) -> Result<String>
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);Sourcepub fn write_graph_pajek(&self, path: impl AsRef<Path>) -> Result<()>
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.
Sourcepub fn write_graph_pajek_to_string(&self) -> Result<String>
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");Sourcepub fn write_graph_dimacs_flow(
&self,
path: impl AsRef<Path>,
source: VertexId,
target: VertexId,
capacity: &[f64],
) -> Result<()>
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.
Sourcepub fn write_graph_dimacs_flow_to_string(
&self,
source: VertexId,
target: VertexId,
capacity: &[f64],
) -> Result<String>
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");Sourcepub fn write_graph_gml(
&self,
path: impl AsRef<Path>,
options: &GmlWriteOptions<'_>,
) -> Result<()>
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.
Sourcepub fn write_graph_gml_to_string(
&self,
options: &GmlWriteOptions<'_>,
) -> Result<String>
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"));Sourcepub fn write_graph_dot(&self, path: impl AsRef<Path>) -> Result<()>
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.
Sourcepub fn write_graph_dot_to_string(&self) -> Result<String>
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"));Sourcepub fn write_graph_leda(
&self,
path: impl AsRef<Path>,
vertex_attr: Option<&str>,
edge_attr: Option<&str>,
) -> Result<()>
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.
Sourcepub fn write_graph_leda_to_string(
&self,
vertex_attr: Option<&str>,
edge_attr: Option<&str>,
) -> Result<String>
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
impl igraph_t
Sourcepub fn erdos_renyi_game_gnm(
num_vertices: usize,
num_edges: usize,
directed: bool,
mode: impl Into<AllowedEdgeTypes>,
edge_labeled: bool,
) -> Result<Graph>
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);Sourcepub fn erdos_renyi_game_gnp(
num_vertices: usize,
p: f64,
directed: bool,
allowed_edge_types: impl Into<AllowedEdgeTypes>,
edge_labeled: bool,
) -> Result<Graph>
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);Sourcepub fn iea_game(
num_vertices: usize,
num_edges: usize,
directed: bool,
loops: bool,
) -> Result<Graph>
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]);Sourcepub fn barabasi_game(
num_vertices: usize,
options: &BarabasiOptions<'_>,
) -> Result<Graph>
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);Sourcepub fn barabasi_aging_game(
num_vertices: usize,
options: &BarabasiAgingOptions<'_>,
) -> Result<Graph>
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)]);Sourcepub fn recent_degree_game(
num_vertices: usize,
options: &RecentDegreeOptions<'_>,
) -> Result<Graph>
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));Sourcepub fn recent_degree_aging_game(
num_vertices: usize,
options: &RecentDegreeAgingOptions<'_>,
) -> Result<Graph>
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());Sourcepub fn growing_random_game(
num_vertices: usize,
m: usize,
directed: bool,
citation: bool,
) -> Result<Graph>
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));Sourcepub fn degree_sequence_game(
out_degrees: &[i64],
in_degrees: Option<&[i64]>,
method: DegreeSequenceMethod,
) -> Result<Graph>
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(°rees, 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());Sourcepub fn k_regular_game(
num_vertices: usize,
k: usize,
directed: bool,
multiple: bool,
) -> Result<Graph>
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());Sourcepub fn static_fitness_game(
num_edges: usize,
fitness_out: &[f64],
fitness_in: Option<&[f64]>,
allowed_edge_types: impl Into<AllowedEdgeTypes>,
) -> Result<Graph>
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);Sourcepub 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>
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());Sourcepub fn chung_lu_game(
out_weights: &[f64],
in_weights: Option<&[f64]>,
loops: bool,
variant: ChungLuVariant,
) -> Result<Graph>
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);Sourcepub fn watts_strogatz_game(
dim: usize,
size: usize,
nei: usize,
p: f64,
allowed_edge_types: impl Into<AllowedEdgeTypes>,
) -> Result<Graph>
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());Sourcepub fn rewire_edges(
&mut self,
prob: f64,
allowed_edge_types: impl Into<AllowedEdgeTypes>,
) -> Result<()>
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());Sourcepub fn rewire_directed_edges(
&mut self,
prob: f64,
loops: bool,
mode: NeighborMode,
) -> Result<()>
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);Sourcepub fn forest_fire_game(
num_vertices: usize,
fw_prob: f64,
bw_factor: f64,
ambs: usize,
directed: bool,
) -> Result<Graph>
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());Sourcepub fn sbm_game(
pref_matrix: &Matrix,
block_sizes: &[i64],
directed: bool,
allowed_edge_types: impl Into<AllowedEdgeTypes>,
) -> Result<Graph>
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)));Sourcepub fn hsbm_game(
num_vertices: usize,
block_size: usize,
rho: &[f64],
c: &Matrix,
p: f64,
) -> Result<Graph>
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);Sourcepub fn hsbm_list_game(
num_vertices: usize,
block_sizes: &[i64],
rhos: &[Vec<f64>],
cs: &[Matrix],
p: f64,
) -> Result<Graph>
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);Sourcepub fn preference_game(
num_vertices: usize,
type_dist: Option<&[f64]>,
fixed_sizes: bool,
pref_matrix: &Matrix,
directed: bool,
loops: bool,
) -> Result<TypedGraph>
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());Sourcepub fn asymmetric_preference_game(
num_vertices: usize,
type_dist_matrix: Option<&Matrix>,
pref_matrix: &Matrix,
loops: bool,
) -> Result<AsymmetricTypedGraph>
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));
}Sourcepub fn callaway_traits_game(
num_vertices: usize,
edges_per_step: usize,
type_dist: Option<&[f64]>,
pref_matrix: &Matrix,
directed: bool,
) -> Result<TypedGraph>
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]));Sourcepub fn establishment_game(
num_vertices: usize,
k: usize,
type_dist: Option<&[f64]>,
pref_matrix: &Matrix,
directed: bool,
) -> Result<TypedGraph>
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());Sourcepub fn grg_game(
num_vertices: usize,
radius: f64,
torus: bool,
) -> Result<GeometricGraph>
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);
}Sourcepub fn lastcit_game(
num_vertices: usize,
edges_per_node: usize,
agebins: usize,
preference: &[f64],
directed: bool,
) -> Result<Graph>
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);Sourcepub fn cited_type_game(
types: &[i64],
pref: &[f64],
edges_per_step: usize,
directed: bool,
) -> Result<Graph>
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)]);Sourcepub fn citing_cited_type_game(
types: &[i64],
pref: &Matrix,
edges_per_step: usize,
directed: bool,
) -> Result<Graph>
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)]);Sourcepub fn simple_interconnected_islands_game(
islands_n: usize,
islands_size: usize,
islands_pin: f64,
n_inter: usize,
) -> Result<Graph>
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);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());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());Sourcepub fn tree_game(
num_vertices: usize,
directed: bool,
method: RandomTreeMethod,
) -> Result<Graph>
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-treeSourcepub fn dot_product_game(vecs: &Matrix, directed: bool) -> Result<Graph>
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
impl igraph_t
Sourcepub fn isomorphic(&self, other: &Graph) -> Result<bool>
pub fn isomorphic(&self, other: &Graph) -> Result<bool>
Whether self and other are isomorphic (igraph_isomorphic).
The algorithm is chosen automatically:
- a directed and an undirected graph are an error;
- if either graph has multi-edges, both are simplified and colorized and compared with VF2;
- graphs with different vertex or edge counts are not isomorphic;
- small loop-free graphs supported by
isoclass(directed with 3–4 vertices, undirected with 3–6) use precomputed O(1) data; - 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());Sourcepub fn subisomorphic(&self, pattern: &Graph) -> Result<bool>
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());Sourcepub fn simplify_and_colorize(&self) -> Result<ColorizedGraph>
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]);Sourcepub fn isomorphic_vf2(
&self,
other: &Graph,
opts: &mut Vf2Options<'_>,
) -> Result<Option<IsoMapping>>
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]);Sourcepub fn count_isomorphisms_vf2(
&self,
other: &Graph,
opts: &mut Vf2Options<'_>,
) -> Result<usize>
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);Sourcepub fn get_isomorphisms_vf2(
&self,
other: &Graph,
opts: &mut Vf2Options<'_>,
) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn get_isomorphisms_vf2_callback(
&self,
other: &Graph,
opts: &mut Vf2Options<'_>,
handler: impl FnMut(&[VertexId], &[VertexId]) -> bool,
) -> Result<()>
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);Sourcepub fn subisomorphic_vf2(
&self,
pattern: &Graph,
opts: &mut Vf2Options<'_>,
) -> Result<Option<IsoMapping>>
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 matchSourcepub fn count_subisomorphisms_vf2(
&self,
pattern: &Graph,
opts: &mut Vf2Options<'_>,
) -> Result<usize>
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);Sourcepub fn get_subisomorphisms_vf2(
&self,
pattern: &Graph,
opts: &mut Vf2Options<'_>,
) -> Result<Vec<Vec<VertexId>>>
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 orientationsSourcepub fn get_subisomorphisms_vf2_callback(
&self,
pattern: &Graph,
opts: &mut Vf2Options<'_>,
handler: impl FnMut(&[VertexId], &[VertexId]) -> bool,
) -> Result<()>
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);Sourcepub fn subisomorphic_lad(
&self,
target: &Graph,
domains: Option<&[Vec<VertexId>]>,
induced: bool,
) -> Result<Option<Vec<VertexId>>>
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);Sourcepub fn get_subisomorphisms_lad(
&self,
target: &Graph,
domains: Option<&[Vec<VertexId>]>,
induced: bool,
) -> Result<Vec<Vec<VertexId>>>
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);Sourcepub fn isomorphic_bliss(
&self,
other: &Graph,
colors1: Option<&[i64]>,
colors2: Option<&[i64]>,
sh: BlissSh,
) -> Result<BlissIsomorphism>
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");Sourcepub fn canonical_permutation(
&self,
colors: Option<&[i64]>,
) -> Result<Vec<VertexId>>
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());Sourcepub fn canonical_permutation_bliss(
&self,
colors: Option<&[i64]>,
sh: BlissSh,
) -> Result<(Vec<VertexId>, BlissInfo)>
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).
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");Sourcepub fn canonical_form(&self, colors: Option<&[i64]>) -> Result<Graph>
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());Sourcepub fn count_automorphisms(&self, colors: Option<&[i64]>) -> Result<f64>
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);Sourcepub fn count_automorphisms_bliss(
&self,
colors: Option<&[i64]>,
sh: BlissSh,
) -> Result<BlissInfo>
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!Sourcepub fn automorphism_group(
&self,
colors: Option<&[i64]>,
) -> Result<Vec<Vec<VertexId>>>
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]]);Sourcepub fn automorphism_group_bliss(
&self,
colors: Option<&[i64]>,
sh: BlissSh,
) -> Result<(Vec<Vec<VertexId>>, BlissInfo)>
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");Sourcepub fn isoclass(&self) -> Result<usize>
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);Sourcepub fn isoclass_subgraph<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
) -> Result<usize>
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); // pathSourcepub fn isoclass_create(
size: usize,
number: usize,
directed: bool,
) -> Result<Graph>
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);Sourcepub fn motifs_randesu(
&self,
size: usize,
cut_prob: Option<&[f64]>,
) -> Result<Vec<f64>>
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]);Sourcepub fn motifs_randesu_callback(
&self,
size: usize,
cut_prob: Option<&[f64]>,
f: impl FnMut(&[VertexId], usize) -> bool,
) -> Result<()>
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]]);Sourcepub fn motifs_randesu_no(
&self,
size: usize,
cut_prob: Option<&[f64]>,
) -> Result<f64>
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)Sourcepub fn motifs_randesu_estimate(
&self,
size: usize,
cut_prob: Option<&[f64]>,
sample: MotifSample<'_>,
) -> Result<f64>
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);Sourcepub fn dyad_census(&self) -> Result<DyadCensus>
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));Sourcepub fn triad_census(&self) -> Result<TriadCensus>
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);Sourcepub fn count_adjacent_triangles<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Vec<f64>>
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]);Sourcepub fn list_triangles(&self) -> Result<Vec<[VertexId; 3]>>
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]]);Sourcepub fn count_triangles(&self) -> Result<f64>
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);Sourcepub fn graphlets_candidate_basis(
&self,
weights: &[f64],
) -> Result<GraphletBasis>
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));Sourcepub fn graphlets_project(
&self,
weights: &[f64],
cliques: &[Vec<VertexId>],
start: Option<&[f64]>,
niter: usize,
) -> Result<Vec<f64>>
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);Sourcepub fn graphlets(&self, weights: &[f64], niter: usize) -> Result<Graphlets>
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
impl igraph_t
Sourcepub fn layout_random(&self) -> Result<Matrix>
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);Sourcepub fn layout_random_3d(&self) -> Result<Matrix>
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.
Sourcepub fn layout_circle<'a>(
&self,
order: impl Into<VertexSelector<'a>>,
) -> Result<Matrix>
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]);Sourcepub fn layout_star(
&self,
center: VertexId,
order: Option<&[VertexId]>,
) -> Result<Matrix>
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]);Sourcepub fn layout_grid(&self, width: Option<usize>) -> Result<Matrix>
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],
]);Sourcepub fn layout_grid_3d(
&self,
width: Option<usize>,
height: Option<usize>,
) -> Result<Matrix>
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]);Sourcepub fn layout_sphere(&self) -> Result<Matrix>
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);
}Sourcepub fn layout_fruchterman_reingold(
&self,
options: &FruchtermanReingoldOptions<'_>,
) -> Result<Matrix>
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));Sourcepub fn layout_fruchterman_reingold_3d(
&self,
options: &FruchtermanReingoldOptions<'_>,
) -> Result<Matrix>
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.
Sourcepub fn layout_kamada_kawai(
&self,
options: &KamadaKawaiOptions<'_>,
) -> Result<Matrix>
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);Sourcepub fn layout_kamada_kawai_3d(
&self,
options: &KamadaKawaiOptions<'_>,
) -> Result<Matrix>
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.
Sourcepub fn layout_lgl(&self, options: &LglOptions) -> Result<Matrix>
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()));Sourcepub fn layout_reingold_tilford(
&self,
mode: NeighborMode,
roots: Option<&[VertexId]>,
rootlevel: Option<&[i64]>,
) -> Result<Matrix>
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 centeredSourcepub fn layout_reingold_tilford_circular(
&self,
mode: NeighborMode,
roots: Option<&[VertexId]>,
rootlevel: Option<&[i64]>,
) -> Result<Matrix>
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);Sourcepub fn roots_for_tree_layout(
&self,
mode: NeighborMode,
heuristic: RootChoice,
) -> Result<Vec<VertexId>>
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]);Sourcepub fn layout_sugiyama(
&self,
options: &SugiyamaOptions<'_>,
) -> Result<SugiyamaLayout>
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 shortcutSourcepub fn layout_mds(&self, dist: Option<&Matrix>, dim: usize) -> Result<Matrix>
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);Sourcepub fn layout_bipartite(
&self,
types: &[bool],
hgap: f64,
vgap: f64,
maxiter: usize,
) -> Result<Matrix>
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]);Sourcepub fn layout_umap(&self, options: &UmapOptions<'_>) -> Result<Matrix>
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.
Sourcepub fn layout_umap_3d(&self, options: &UmapOptions<'_>) -> Result<Matrix>
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.
Sourcepub fn layout_umap_compute_weights(
&self,
distances: Option<&[f64]>,
) -> Result<Vec<f64>>
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)));Sourcepub fn layout_drl(
&self,
options: &DrlOptions,
weights: Option<&[f64]>,
initial: Option<&Matrix>,
) -> Result<Matrix>
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));Sourcepub fn layout_drl_3d(
&self,
options: &DrlOptions,
weights: Option<&[f64]>,
initial: Option<&Matrix>,
) -> Result<Matrix>
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.
Sourcepub fn layout_graphopt(&self, options: &GraphoptOptions<'_>) -> Result<Matrix>
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);Sourcepub fn layout_gem(&self, options: &GemOptions<'_>) -> Result<Matrix>
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);Sourcepub fn layout_davidson_harel(
&self,
options: &DavidsonHarelOptions<'_>,
) -> Result<Matrix>
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));Sourcepub fn layout_align(&self, layout: &mut Matrix) -> Result<()>
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
impl igraph_t
Sourcepub fn eigen_adjacency(
&self,
which: &EigenWhich,
algorithm: EigenAlgorithm,
options: &ArpackOptions,
) -> Result<SymmetricEigen>
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
impl igraph_t
Sourcepub fn adjacency_spectral_embedding(
&self,
no: usize,
weights: Option<&[f64]>,
which: EmbeddingWhich,
scaled: bool,
cvec: Option<&[f64]>,
options: &ArpackOptions,
) -> Result<SpectralEmbedding>
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: returnX,Yif true,U,Votherwise.cvec: added to the diagonal of the adjacency matrix before the decomposition; either one value per vertex, a single value for all, orNonefor zero. A common choice is half the degrees.options: ARPACK options; onlytolandmxiterare used (igraph setswhich,nevandncv, 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: orthogonalSourcepub fn laplacian_spectral_embedding(
&self,
no: usize,
weights: Option<&[f64]>,
which: EmbeddingWhich,
kind: LaplacianEmbeddingType,
scaled: bool,
options: &ArpackOptions,
) -> Result<SpectralEmbedding>
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
impl igraph_t
Sourcepub fn sir(
&self,
beta: f64,
gamma: f64,
num_simulations: usize,
) -> Result<Vec<SirRun>>
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
impl igraph_t
Sourcepub fn delaunay_graph(points: &Matrix) -> Result<Graph>
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);Sourcepub fn lune_beta_skeleton(points: &Matrix, beta: f64) -> Result<Graph>
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());Sourcepub fn circle_beta_skeleton(points: &Matrix, beta: f64) -> Result<Graph>
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.
Sourcepub fn beta_weighted_gabriel_graph(
points: &Matrix,
max_beta: f64,
) -> Result<(Graph, Vec<f64>)>
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]));Sourcepub fn gabriel_graph(points: &Matrix) -> Result<Graph>
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);Sourcepub fn relative_neighborhood_graph(points: &Matrix) -> Result<Graph>
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)]);Sourcepub fn nearest_neighbor_graph(
points: &Matrix,
metric: Metric,
k: Option<usize>,
cutoff: Option<f64>,
directed: bool,
) -> Result<Graph>
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)]);Sourcepub fn spatial_edge_lengths(
&self,
points: &Matrix,
metric: Metric,
) -> Result<Vec<f64>>
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
impl igraph_t
Sourcepub fn transitivity_undirected(&self, mode: TransitivityMode) -> Result<f64>
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);Sourcepub fn transitivity_local_undirected<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
mode: TransitivityMode,
) -> Result<Vec<f64>>
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 leafSourcepub fn transitivity_avglocal_undirected(
&self,
mode: TransitivityMode,
) -> Result<f64>
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);Sourcepub fn transitivity_barrat<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: TransitivityMode,
) -> Result<Vec<f64>>
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);
}Sourcepub fn ecc<'a>(
&self,
eids: impl Into<EdgeSelector<'a>>,
k: usize,
offset: bool,
normalize: bool,
) -> Result<Vec<f64>>
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 oneSourcepub fn assortativity(
&self,
weights: Option<&[f64]>,
values: &[f64],
values_in: Option<&[f64]>,
directed: bool,
normalized: bool,
) -> Result<f64>
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_jFor 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);Sourcepub fn assortativity_nominal(
&self,
types: &[igraph_int_t],
directed: bool,
normalized: bool,
) -> Result<f64>
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);Sourcepub fn assortativity_degree(&self, directed: bool) -> Result<f64>
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());Sourcepub fn joint_degree_matrix(
&self,
weights: Option<&[f64]>,
max_out_degree: Option<usize>,
max_in_degree: Option<usize>,
) -> Result<Matrix>
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],
]);Sourcepub fn joint_degree_distribution(
&self,
weights: Option<&[f64]>,
options: &JointDegreeDistributionOptions,
) -> Result<Matrix>
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);Sourcepub 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>
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
impl igraph_t
Sourcepub fn disjoint_union(&self, other: &Graph) -> Result<Graph>
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)]);Sourcepub fn disjoint_union_many<'a>(
graphs: impl IntoIterator<Item = &'a Graph>,
) -> Result<Graph>
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));Sourcepub fn union(&self, other: &Graph) -> Result<Graph>
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)]);Sourcepub fn union_map(&self, other: &Graph) -> Result<EdgeMapped>
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 1Sourcepub fn union_many<'a>(
graphs: impl IntoIterator<Item = &'a Graph>,
) -> Result<Graph>
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)]);Sourcepub fn union_many_map<'a>(
graphs: impl IntoIterator<Item = &'a Graph>,
) -> Result<EdgeMappedMany>
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.
Sourcepub fn intersection(&self, other: &Graph) -> Result<Graph>
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)]);Sourcepub fn intersection_map(&self, other: &Graph) -> Result<EdgeMapped>
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]));Sourcepub fn intersection_many<'a>(
graphs: impl IntoIterator<Item = &'a Graph>,
) -> Result<Graph>
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)]);Sourcepub fn intersection_many_map<'a>(
graphs: impl IntoIterator<Item = &'a Graph>,
) -> Result<EdgeMappedMany>
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.
Sourcepub fn difference(&self, sub: &Graph) -> Result<Graph>
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)]);Sourcepub fn join(&self, other: &Graph) -> Result<Graph>
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);Sourcepub fn complementer(&self, loops: bool) -> Result<Graph>
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)]);Sourcepub fn compose(&self, other: &Graph) -> Result<Graph>
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)]);Sourcepub fn compose_map(&self, other: &Graph) -> Result<EdgeMapped>
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.
Sourcepub fn contract_vertices(&mut self, mapping: &[VertexId]) -> Result<()>
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)]);Sourcepub fn permute_vertices(&self, permutation: &[VertexId]) -> Result<Graph>
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));Sourcepub fn connect_neighborhood(
&mut self,
order: usize,
mode: NeighborMode,
) -> Result<()>
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]);Sourcepub fn graph_power(&self, order: usize, directed: bool) -> Result<Graph>
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);Sourcepub fn rewire(
&mut self,
trials: usize,
allowed: EdgeTypeSw,
) -> Result<RewiringStats>
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]);Sourcepub fn simplify(
&mut self,
remove_multiple: bool,
remove_loops: bool,
) -> Result<()>
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)]);Sourcepub fn induced_subgraph<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
implementation: SubgraphImplementation,
) -> Result<Graph>
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)]);Sourcepub fn induced_subgraph_map<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
implementation: SubgraphImplementation,
) -> Result<InducedSubgraph>
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)]);Sourcepub fn induced_subgraph_edges<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
) -> Result<Vec<EdgeId>>
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]);Sourcepub fn subgraph_from_edges<'a>(
&self,
eids: impl Into<EdgeSelector<'a>>,
delete_vertices: bool,
) -> Result<Graph>
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)]));Sourcepub fn reverse_edges<'a>(
&mut self,
eids: impl Into<EdgeSelector<'a>>,
) -> Result<()>
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)]);Sourcepub fn product(&self, other: &Graph, kind: Product) -> Result<Graph>
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:
Product | condition | edges (undirected) |
|---|---|---|
Cartesian | u = u' and v ~ v', or u ~ u' and v = v' | |V1||E2| + |V2||E1| |
Lexicographic | u = u' and v ~ v', or u ~ u' | |V1||E2| + |V2|²|E1| |
Strong | Cartesian or tensor condition | |V1||E2| + |V2||E1| + 2|E1||E2| |
Tensor | u ~ u' and v ~ v' | 2|E1||E2| |
Modular | both 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());Sourcepub fn rooted_product(&self, other: &Graph, root: VertexId) -> Result<Graph>
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]);Sourcepub fn mycielskian(&self, k: usize) -> Result<Graph>
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-freeSource§impl igraph_t
impl igraph_t
Sourcepub fn diameter(&self) -> Result<f64>
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());Sourcepub fn diameter_with_path(
&self,
weights: Option<&[f64]>,
directed: bool,
unconn: bool,
) -> Result<Diameter>
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,truereturns the longest geodesic within a component,falsereturnsINFINITY.
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<_>>());Sourcepub fn pseudo_diameter(
&self,
weights: Option<&[f64]>,
start: Option<VertexId>,
directed: bool,
unconn: bool,
) -> Result<PseudoDiameter>
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);Sourcepub fn eccentricity<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<f64>>
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]);Sourcepub fn radius(&self, weights: Option<&[f64]>, mode: NeighborMode) -> Result<f64>
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);Sourcepub fn graph_center(
&self,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<VertexId>>
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]);Sourcepub fn average_path_length(
&self,
weights: Option<&[f64]>,
directed: bool,
unconn: bool,
) -> Result<f64>
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: iftrue, only pairs connected by a path are averaged; iffalse, disconnected graphs giveINFINITY.
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);Sourcepub fn average_path_length_details(
&self,
weights: Option<&[f64]>,
directed: bool,
unconn: bool,
) -> Result<AveragePathLength>
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);Sourcepub fn path_length_hist(&self, directed: bool) -> Result<PathLengthHistogram>
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);Sourcepub fn global_efficiency(
&self,
weights: Option<&[f64]>,
directed: bool,
) -> Result<f64>
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);Sourcepub fn local_efficiency<'a>(
&self,
vids: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
directed: bool,
mode: NeighborMode,
) -> Result<Vec<f64>>
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]);Sourcepub fn average_local_efficiency(
&self,
weights: Option<&[f64]>,
directed: bool,
mode: NeighborMode,
) -> Result<f64>
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
impl igraph_t
Sourcepub fn distances<'a>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Matrix>
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]]);Sourcepub 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>
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]);Sourcepub fn distances_dijkstra<'a>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Matrix>
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);Sourcepub 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>
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]);Sourcepub fn distances_bellman_ford<'a>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Matrix>
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]);Sourcepub fn distances_johnson<'a>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Matrix>
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());Sourcepub 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>
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
impl igraph_t
Sourcepub fn get_shortest_paths<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<ShortestPaths>
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]);Sourcepub fn get_shortest_paths_dijkstra<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<ShortestPaths>
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]);Sourcepub fn get_shortest_paths_bellman_ford<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<ShortestPaths>
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]]);Sourcepub fn get_shortest_path(
&self,
from: VertexId,
to: VertexId,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<GraphPath>
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);Sourcepub fn get_shortest_path_dijkstra(
&self,
from: VertexId,
to: VertexId,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<GraphPath>
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);Sourcepub fn get_shortest_path_bellman_ford(
&self,
from: VertexId,
to: VertexId,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<GraphPath>
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);Sourcepub fn get_shortest_path_astar<F>(
&self,
from: VertexId,
to: VertexId,
weights: Option<&[f64]>,
mode: NeighborMode,
heuristic: F,
) -> Result<GraphPath>
pub fn get_shortest_path_astar<F>( &self, from: VertexId, to: VertexId, weights: Option<&[f64]>, mode: NeighborMode, heuristic: F, ) -> Result<GraphPath>
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);Sourcepub fn get_all_shortest_paths<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<AllShortestPaths>
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);Sourcepub fn get_all_shortest_paths_dijkstra<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<AllShortestPaths>
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]]);Sourcepub fn get_k_shortest_paths(
&self,
from: VertexId,
to: VertexId,
k: usize,
weights: Option<&[f64]>,
mode: NeighborMode,
) -> Result<Vec<GraphPath>>
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]);Sourcepub fn get_all_simple_paths<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
options: SimplePathsOptions,
) -> Result<Vec<Vec<VertexId>>>
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
impl igraph_t
Sourcepub fn get_widest_paths<'a>(
&self,
from: VertexId,
to: impl Into<VertexSelector<'a>>,
weights: &[f64],
mode: NeighborMode,
) -> Result<ShortestPaths>
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]);Sourcepub fn get_widest_path(
&self,
from: VertexId,
to: VertexId,
weights: &[f64],
mode: NeighborMode,
) -> Result<GraphPath>
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]);Sourcepub fn widest_path_widths_floyd_warshall<'a>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'a>>,
weights: &[f64],
mode: NeighborMode,
) -> Result<Matrix>
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]]);Sourcepub fn widest_path_widths_dijkstra<'a>(
&self,
from: impl Into<VertexSelector<'a>>,
to: impl Into<VertexSelector<'a>>,
weights: &[f64],
mode: NeighborMode,
) -> Result<Matrix>
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
impl igraph_t
Sourcepub fn random_walk(
&self,
start: VertexId,
steps: usize,
weights: Option<&[f64]>,
mode: NeighborMode,
stuck: RandomWalkStuck,
) -> Result<GraphPath>
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::RandomWalkStuckif the walk gets stuck andstuckisRandomWalkStuck::Error(the partial walk is discarded; useRandomWalkStuck::Returnto keep it).ErrorKind::InvalidValuefor an invalidstartvertex (igraph 1.0.0 and 1.0.1 reportIGRAPH_EINVALhere, notIGRAPH_EINVVID), invalid weights (negative, NaN or wrong length), orsteps >= i64::MAX. The Rust side also rejects infinite weights, and weights whose total over the edges a walk can leave some vertex through exceedsf64::MAX / 2(loops count twice inAllmode): 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);Sourcepub fn spanner(
&self,
stretch: f64,
weights: Option<&[f64]>,
) -> Result<Vec<EdgeId>>
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));Sourcepub fn voronoi(
&self,
generators: &[VertexId],
weights: Option<&[f64]>,
mode: NeighborMode,
tiebreaker: VoronoiTiebreaker,
) -> Result<VoronoiPartition>
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]);Sourcepub fn vertex_path_from_edge_path(
&self,
start: Option<VertexId>,
edge_path: &[EdgeId],
mode: NeighborMode,
) -> Result<Vec<VertexId>>
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
impl igraph_t
Sourcepub fn are_adjacent(&self, v1: VertexId, v2: VertexId) -> Result<bool>
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.
Sourcepub fn count_multiple<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<i64>>
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.
Sourcepub fn count_multiple_1(&self, edge: EdgeId) -> Result<i64>
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);Sourcepub fn density(&self, weights: Option<&[f64]>, loops: bool) -> Result<f64>
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.
Sourcepub fn diversity<'a>(
&self,
weights: &[f64],
vertices: impl Into<VertexSelector<'a>>,
) -> Result<Vec<f64>>
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]);Sourcepub fn girth(&self) -> Result<Option<usize>>
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).
Sourcepub fn girth_with_cycle(&self) -> Result<Option<(usize, Vec<VertexId>)>>
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 triangleSourcepub fn has_loop(&self) -> Result<bool>
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.
Sourcepub fn has_multiple(&self) -> Result<bool>
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()?);Sourcepub fn count_loops(&self) -> Result<usize>
pub fn count_loops(&self) -> Result<usize>
The number of self-loops in the graph.
Binds igraph_count_loops.
Time complexity: O(|E|).
Sourcepub fn is_loop<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<bool>>
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);Sourcepub fn is_multiple<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
) -> Result<Vec<bool>>
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]);Sourcepub fn is_mutual<'a>(
&self,
edges: impl Into<EdgeSelector<'a>>,
loops: bool,
) -> Result<Vec<bool>>
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]);Sourcepub fn has_mutual(&self, loops: bool) -> Result<bool>
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)?);Sourcepub fn is_simple(&self, directed: bool) -> Result<bool>
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)?);Sourcepub fn is_tree(&self, mode: NeighborMode) -> Result<bool>
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.
Sourcepub fn tree_root(&self, mode: NeighborMode) -> Result<Option<VertexId>>
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.
Sourcepub fn is_forest(&self, mode: NeighborMode) -> Result<bool>
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|).
Sourcepub fn forest_roots(&self, mode: NeighborMode) -> Result<Option<Vec<VertexId>>>
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]));Sourcepub fn is_acyclic(&self) -> Result<bool>
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 cycleSee also Graph::is_dag (false for undirected graphs),
Graph::topological_sorting and Graph::find_cycle.
Sourcepub fn maxdegree<'a>(
&self,
vertices: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
loops: Loops,
) -> Result<i64>
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.
Sourcepub fn mean_degree(&self, loops: bool) -> Result<f64>
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());Sourcepub fn reciprocity(&self, ignore_loops: bool, mode: Reciprocity) -> Result<f64>
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.
Sourcepub fn strength<'a>(
&self,
vertices: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
loops: Loops,
weights: Option<&[f64]>,
) -> Result<Vec<f64>>
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]);Sourcepub 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>>
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.
Sourcepub fn is_perfect(&self) -> Result<bool>
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.
Sourcepub fn is_complete(&self) -> Result<bool>
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 missingSourcepub fn is_clique<'a>(
&self,
candidate: impl Into<VertexSelector<'a>>,
directed: bool,
) -> Result<bool>
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.
Sourcepub fn is_independent_vertex_set<'a>(
&self,
candidate: impl Into<VertexSelector<'a>>,
) -> Result<bool>
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.
Sourcepub fn minimum_spanning_tree(
&self,
weights: Option<&[f64]>,
method: MstAlgorithm,
) -> Result<Vec<EdgeId>>
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.
Sourcepub fn random_spanning_tree(
&self,
vertex: Option<VertexId>,
) -> Result<Vec<EdgeId>>
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);Sourcepub fn subcomponent(
&self,
vertex: VertexId,
mode: NeighborMode,
) -> Result<Vec<VertexId>>
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.
Sourcepub fn unfold_tree(
&self,
mode: NeighborMode,
roots: &[VertexId],
) -> Result<UnfoldedTree>
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.
Sourcepub fn maximum_cardinality_search(&self) -> Result<CardinalitySearch>
pub fn maximum_cardinality_search(&self) -> Result<CardinalitySearch>
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);Sourcepub fn is_chordal(&self) -> Result<bool>
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()?);Sourcepub fn is_chordal_with(
&self,
alpha: Option<&[i64]>,
alpham1: Option<&[VertexId]>,
) -> Result<Chordality>
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()?);Sourcepub fn avg_nearest_neighbor_degree<'a>(
&self,
vertices: impl Into<VertexSelector<'a>>,
mode: NeighborMode,
neighbor_degree_mode: NeighborMode,
weights: Option<&[f64]>,
) -> Result<NeighborDegree>
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 verticesSee 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.
Sourcepub fn degree_correlation_vector(
&self,
weights: Option<&[f64]>,
from_mode: NeighborMode,
to_mode: NeighborMode,
directed_neighbors: bool,
) -> Result<Vec<f64>>
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.
Sourcepub fn rich_club_sequence(
&self,
weights: Option<&[f64]>,
vertex_order: &[VertexId],
normalized: bool,
loops: bool,
directed: bool,
) -> Result<Vec<f64>>
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 cliqueA natural order removes vertices by increasing degree, see
sort_vertex_ids_by_degree.
Sourcepub fn get_laplacian(
&self,
mode: NeighborMode,
normalization: LaplacianNormalization,
weights: Option<&[f64]>,
) -> Result<Matrix>
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.
Sourcepub fn get_laplacian_sparse(
&self,
mode: NeighborMode,
normalization: LaplacianNormalization,
weights: Option<&[f64]>,
) -> Result<Vec<(VertexId, VertexId, f64)>>
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)]);Sourcepub fn get_laplacian_sparsemat(
&self,
mode: NeighborMode,
normalization: LaplacianNormalization,
weights: Option<&[f64]>,
) -> Result<SparseMat>
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
impl igraph_t
Sourcepub fn bfs(
&self,
roots: &[VertexId],
options: &BfsOptions<'_>,
) -> Result<BfsResult>
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);Sourcepub fn bfs_with<F>(
&self,
roots: &[VertexId],
options: &BfsOptions<'_>,
visitor: F,
) -> Result<BfsResult>
pub fn bfs_with<F>( &self, roots: &[VertexId], options: &BfsOptions<'_>, visitor: F, ) -> Result<BfsResult>
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);Sourcepub fn bfs_simple(
&self,
root: VertexId,
mode: NeighborMode,
) -> Result<BfsSimpleResult>
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]);Sourcepub fn dfs(&self, root: VertexId, options: &DfsOptions) -> Result<DfsResult>
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]);Sourcepub fn dfs_with<F>(
&self,
root: VertexId,
options: &DfsOptions,
visitor: F,
) -> Result<DfsResult>
pub fn dfs_with<F>( &self, root: VertexId, options: &DfsOptions, visitor: F, ) -> Result<DfsResult>
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]).
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§impl Display for igraph_t
impl Display for igraph_t
Source§fn fmt(&self, f: &mut Formatter<'_>) -> Result
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 Extend<igraph_t> for igraph_graph_list_t
impl Extend<igraph_t> for igraph_graph_list_t
Source§fn extend<I: IntoIterator<Item = igraph_t>>(&mut self, iter: I)
fn extend<I: IntoIterator<Item = igraph_t>>(&mut self, iter: I)
Source§fn extend_one(&mut self, item: A)
fn extend_one(&mut self, item: A)
extend_one)Source§fn extend_reserve(&mut self, additional: usize)
fn extend_reserve(&mut self, additional: usize)
extend_one)Source§impl FromIterator<igraph_t> for igraph_graph_list_t
impl FromIterator<igraph_t> for igraph_graph_list_t
Source§impl PartialEq for igraph_t
impl PartialEq for igraph_t
Source§fn eq(&self, other: &Self) -> bool
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.