use std::collections::HashMap;
use super::attributes::AttributeValue;
use super::cache::{
CachedProperty, PropertyCache, invalidate_after_add_edges, invalidate_after_add_vertices,
};
use super::error::{IgraphError, IgraphResult};
pub type VertexId = u32;
pub type EdgeId = u32;
pub type EdgeIter<'a> = std::iter::Map<
std::iter::Zip<std::slice::Iter<'a, VertexId>, std::slice::Iter<'a, VertexId>>,
fn((&'a VertexId, &'a VertexId)) -> (VertexId, VertexId),
>;
pub struct NeighborsIter<'a> {
graph: &'a Graph,
out_pos: usize,
out_end: usize,
in_pos: usize,
in_end: usize,
directed: bool,
}
impl Iterator for NeighborsIter<'_> {
type Item = VertexId;
fn next(&mut self) -> Option<Self::Item> {
if self.directed {
if self.out_pos < self.out_end {
let eid = self.graph.oi[self.out_pos] as usize;
self.out_pos += 1;
Some(self.graph.to[eid])
} else {
None
}
} else {
let have_out = self.out_pos < self.out_end;
let have_in = self.in_pos < self.in_end;
match (have_out, have_in) {
(false, false) => None,
(true, false) => {
let eid = self.graph.oi[self.out_pos] as usize;
self.out_pos += 1;
Some(self.graph.to[eid])
}
(false, true) => {
let eid = self.graph.ii[self.in_pos] as usize;
self.in_pos += 1;
Some(self.graph.from[eid])
}
(true, true) => {
let a = self.graph.to[self.graph.oi[self.out_pos] as usize];
let b = self.graph.from[self.graph.ii[self.in_pos] as usize];
if a <= b {
self.out_pos += 1;
Some(a)
} else {
self.in_pos += 1;
Some(b)
}
}
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
let remaining = (self.out_end - self.out_pos) + (self.in_end - self.in_pos);
(remaining, Some(remaining))
}
}
impl ExactSizeIterator for NeighborsIter<'_> {}
#[derive(Debug, Clone, Default)]
pub struct Graph {
n: u32,
directed: bool,
from: Vec<VertexId>,
to: Vec<VertexId>,
oi: Vec<EdgeId>,
ii: Vec<EdgeId>,
os: Vec<u32>,
is: Vec<u32>,
cache: PropertyCache,
gattrs: HashMap<String, AttributeValue>,
vertex_attrs: HashMap<String, Vec<AttributeValue>>,
edge_attrs: HashMap<String, Vec<AttributeValue>>,
}
impl Graph {
pub fn new(n: u32, directed: bool) -> IgraphResult<Self> {
let mut g = Self {
n: 0,
directed,
from: Vec::new(),
to: Vec::new(),
oi: Vec::new(),
ii: Vec::new(),
os: vec![0],
is: vec![0],
cache: PropertyCache::new(),
gattrs: HashMap::new(),
vertex_attrs: HashMap::new(),
edge_attrs: HashMap::new(),
};
g.add_vertices(n)?;
Ok(g)
}
pub fn from_edges(
edges: &[(u32, u32)],
directed: bool,
n_override: Option<u32>,
) -> IgraphResult<Self> {
let max_id = edges
.iter()
.flat_map(|&(u, v)| [u, v])
.max()
.map_or(Some(0), |m| m.checked_add(1));
let auto_n = max_id.ok_or(IgraphError::InvalidArgument(
"vertex id overflow in from_edges".to_owned(),
))?;
let n = n_override.map_or(auto_n, |ov| ov.max(auto_n));
let mut g = Self::new(n, directed)?;
g.add_edges(edges.to_vec())?;
Ok(g)
}
pub fn from_weighted_edges(
edges: &[(u32, u32, f64)],
directed: bool,
n_override: Option<u32>,
) -> IgraphResult<(Self, Vec<f64>)> {
let plain: Vec<(u32, u32)> = edges.iter().map(|&(u, v, _)| (u, v)).collect();
let weights: Vec<f64> = edges.iter().map(|&(_, _, w)| w).collect();
let g = Self::from_edges(&plain, directed, n_override)?;
Ok((g, weights))
}
pub fn from_edge_list_str(s: &str) -> IgraphResult<Self> {
use std::io::Cursor;
crate::algorithms::io::edgelist::read_edgelist(Cursor::new(s))
}
pub fn from_adjacency_matrix(matrix: &[Vec<f64>], directed: bool) -> IgraphResult<Self> {
let n = matrix.len();
for row in matrix {
if row.len() != n {
return Err(IgraphError::InvalidArgument(format!(
"adjacency matrix is not square: got row of length {} for {}×{} matrix",
row.len(),
n,
n
)));
}
}
let n_u32 = u32::try_from(n)
.map_err(|_| IgraphError::InvalidArgument("matrix too large for u32".to_owned()))?;
let mut graph = Self::new(n_u32, directed)?;
#[allow(clippy::cast_possible_truncation)]
if directed {
for (i, row) in matrix.iter().enumerate() {
for (j, &val) in row.iter().enumerate() {
let count = val.round() as i64;
for _ in 0..count.max(0) {
graph.add_edge(i as u32, j as u32)?;
}
}
}
} else {
for (i, row) in matrix.iter().enumerate() {
for (j, &val) in row.iter().enumerate().skip(i) {
let count = val.round() as i64;
for _ in 0..count.max(0) {
graph.add_edge(i as u32, j as u32)?;
}
}
}
}
Ok(graph)
}
pub fn from_adjacency_matrix_weighted(
matrix: &[Vec<f64>],
directed: bool,
) -> IgraphResult<(Self, Vec<f64>)> {
let n = matrix.len();
for row in matrix {
if row.len() != n {
return Err(IgraphError::InvalidArgument(format!(
"adjacency matrix is not square: got row of length {} for {}×{} matrix",
row.len(),
n,
n
)));
}
}
let n_u32 = u32::try_from(n)
.map_err(|_| IgraphError::InvalidArgument("matrix too large for u32".to_owned()))?;
let mut graph = Self::new(n_u32, directed)?;
let mut weights = Vec::new();
#[allow(clippy::cast_possible_truncation)]
if directed {
for (i, row) in matrix.iter().enumerate() {
for (j, &w) in row.iter().enumerate() {
if w != 0.0 {
graph.add_edge(i as u32, j as u32)?;
weights.push(w);
}
}
}
} else {
for (i, row) in matrix.iter().enumerate() {
for (j, &w) in row.iter().enumerate().skip(i) {
if w != 0.0 {
graph.add_edge(i as u32, j as u32)?;
weights.push(w);
}
}
}
}
Ok((graph, weights))
}
pub fn from_adjacency_list(adj_list: &[Vec<u32>], directed: bool) -> IgraphResult<Self> {
let n = u32::try_from(adj_list.len()).map_err(|_| {
IgraphError::InvalidArgument("adjacency list too large for u32".to_owned())
})?;
let mut graph = Self::new(n, directed)?;
if directed {
for (src, neighbors) in adj_list.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
let src_u32 = src as u32;
for &tgt in neighbors {
if tgt >= n {
return Err(IgraphError::VertexOutOfRange { id: tgt, n });
}
graph.add_edge(src_u32, tgt)?;
}
}
} else {
for (src, neighbors) in adj_list.iter().enumerate() {
#[allow(clippy::cast_possible_truncation)]
let src_u32 = src as u32;
for &tgt in neighbors {
if tgt >= n {
return Err(IgraphError::VertexOutOfRange { id: tgt, n });
}
if src_u32 <= tgt {
graph.add_edge(src_u32, tgt)?;
}
}
}
}
Ok(graph)
}
pub fn with_vertices(n: u32) -> Self {
let len = n as usize + 1;
Self {
n,
directed: false,
from: Vec::new(),
to: Vec::new(),
oi: Vec::new(),
ii: Vec::new(),
os: vec![0; len],
is: vec![0; len],
cache: PropertyCache::new(),
gattrs: HashMap::new(),
vertex_attrs: HashMap::new(),
edge_attrs: HashMap::new(),
}
}
#[must_use]
pub fn vcount(&self) -> u32 {
self.n
}
#[must_use]
pub fn ecount(&self) -> usize {
self.from.len()
}
#[must_use]
pub fn is_directed(&self) -> bool {
self.directed
}
pub fn vertex_ids(&self) -> impl Iterator<Item = VertexId> {
0..self.n
}
pub fn edge_ids(&self) -> impl Iterator<Item = u32> {
let m = u32::try_from(self.from.len()).unwrap_or(u32::MAX);
0..m
}
pub fn edges(&self) -> impl Iterator<Item = (VertexId, VertexId)> + '_ {
self.from.iter().zip(self.to.iter()).map(|(&u, &v)| (u, v))
}
pub fn iter(&self) -> EdgeIter<'_> {
self.from.iter().zip(self.to.iter()).map(|(&a, &b)| (a, b))
}
pub fn has_edge(&self, from: VertexId, to: VertexId) -> bool {
self.find_eid(from, to).ok().flatten().is_some()
}
pub fn add_vertices(&mut self, nv: u32) -> IgraphResult<(VertexId, VertexId)> {
let new_n = self
.n
.checked_add(nv)
.ok_or(IgraphError::Internal("vertex count overflow"))?;
let first = self.n;
let ec = u32::try_from(self.ecount())
.map_err(|_| IgraphError::Internal("edge count exceeds u32::MAX"))?;
for _ in 0..nv {
self.os.push(ec);
self.is.push(ec);
}
for vals in self.vertex_attrs.values_mut() {
if let Some(first_val) = vals.first() {
let default = first_val.default_for_same_type();
vals.resize(new_n as usize, default);
}
}
self.n = new_n;
if nv > 0 {
invalidate_after_add_vertices(&self.cache);
}
Ok((first, new_n.saturating_sub(1)))
}
pub fn add_edge(&mut self, u: VertexId, v: VertexId) -> IgraphResult<()> {
self.add_edges(std::iter::once((u, v)))
}
pub fn add_edges<I>(&mut self, edges: I) -> IgraphResult<()>
where
I: IntoIterator<Item = (VertexId, VertexId)>,
{
let m_before = self.ecount();
for (u, v) in edges {
self.check_vertex(u)?;
self.check_vertex(v)?;
if !self.directed && u > v {
self.from.push(v);
self.to.push(u);
} else {
self.from.push(u);
self.to.push(v);
}
}
self.rebuild_indexes()?;
let m_after = self.ecount();
if m_after > m_before {
for vals in self.edge_attrs.values_mut() {
if let Some(first_val) = vals.first() {
let default = first_val.default_for_same_type();
vals.resize(m_after, default);
}
}
invalidate_after_add_edges(&self.cache);
}
Ok(())
}
pub fn neighbors(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
self.check_vertex(v)?;
let v_idx = v as usize;
if self.directed {
let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
let out: Vec<VertexId> = self.oi[out_range]
.iter()
.map(|&e| self.to[e as usize])
.collect();
Ok(out)
} else {
let out_start = self.os[v_idx] as usize;
let out_end = self.os[v_idx + 1] as usize;
let in_start = self.is[v_idx] as usize;
let in_end = self.is[v_idx + 1] as usize;
let mut out = Vec::with_capacity((out_end - out_start) + (in_end - in_start));
let mut out_idx = out_start;
let mut in_idx = in_start;
while out_idx < out_end && in_idx < in_end {
let a = self.to[self.oi[out_idx] as usize];
let b = self.from[self.ii[in_idx] as usize];
if a <= b {
out.push(a);
out_idx += 1;
} else {
out.push(b);
in_idx += 1;
}
}
while out_idx < out_end {
out.push(self.to[self.oi[out_idx] as usize]);
out_idx += 1;
}
while in_idx < in_end {
out.push(self.from[self.ii[in_idx] as usize]);
in_idx += 1;
}
Ok(out)
}
}
pub fn neighbors_iter(&self, v: VertexId) -> IgraphResult<NeighborsIter<'_>> {
self.check_vertex(v)?;
let v_idx = v as usize;
let out_pos = self.os[v_idx] as usize;
let out_end = self.os[v_idx + 1] as usize;
let (in_pos, in_end) = if self.directed {
(0, 0)
} else {
(self.is[v_idx] as usize, self.is[v_idx + 1] as usize)
};
Ok(NeighborsIter {
graph: self,
out_pos,
out_end,
in_pos,
in_end,
directed: self.directed,
})
}
pub fn to_adjacency_list(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
let n = self.vcount();
let mut adj = vec![Vec::new(); n as usize];
for v in 0..n {
adj[v as usize] = self.neighbors(v)?;
}
Ok(adj)
}
pub fn to_adjacency_matrix(&self) -> Vec<Vec<f64>> {
let n = self.n as usize;
let mut mat = vec![vec![0.0f64; n]; n];
for eid in 0..self.ecount() {
let u = self.from[eid] as usize;
let v = self.to[eid] as usize;
mat[u][v] += 1.0;
if !self.directed && u != v {
mat[v][u] += 1.0;
}
}
mat
}
pub fn degree(&self, v: VertexId) -> IgraphResult<usize> {
self.check_vertex(v)?;
let v_idx = v as usize;
let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
Ok(out + in_count)
}
pub fn out_degree(&self, v: VertexId) -> IgraphResult<usize> {
self.check_vertex(v)?;
let v_idx = v as usize;
if self.directed {
Ok((self.os[v_idx + 1] - self.os[v_idx]) as usize)
} else {
let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
Ok(out + in_count)
}
}
pub fn in_degree(&self, v: VertexId) -> IgraphResult<usize> {
self.check_vertex(v)?;
let v_idx = v as usize;
if self.directed {
Ok((self.is[v_idx + 1] - self.is[v_idx]) as usize)
} else {
let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
let in_count = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
Ok(out + in_count)
}
}
pub fn max_degree(&self) -> usize {
let n = self.vcount();
if n == 0 {
return 0;
}
(0..n)
.map(|v| {
let v_idx = v as usize;
let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
let inc = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
if self.directed { out } else { out + inc }
})
.max()
.unwrap_or(0)
}
pub fn min_degree(&self) -> usize {
let n = self.vcount();
if n == 0 {
return 0;
}
(0..n)
.map(|v| {
let v_idx = v as usize;
let out = (self.os[v_idx + 1] - self.os[v_idx]) as usize;
let inc = (self.is[v_idx + 1] - self.is[v_idx]) as usize;
if self.directed { out } else { out + inc }
})
.min()
.unwrap_or(0)
}
pub fn edge_source(&self, eid: EdgeId) -> IgraphResult<VertexId> {
self.check_edge(eid)?;
Ok(self.from[eid as usize])
}
pub fn edge_target(&self, eid: EdgeId) -> IgraphResult<VertexId> {
self.check_edge(eid)?;
Ok(self.to[eid as usize])
}
pub fn edge(&self, eid: EdgeId) -> IgraphResult<(VertexId, VertexId)> {
self.check_edge(eid)?;
let i = eid as usize;
Ok((self.from[i], self.to[i]))
}
pub fn edge_other(&self, eid: EdgeId, vid: VertexId) -> IgraphResult<VertexId> {
let (u, v) = self.edge(eid)?;
if vid == u {
Ok(v)
} else if vid == v {
Ok(u)
} else {
Err(IgraphError::InvalidArgument(format!(
"vertex {vid} is not an endpoint of edge {eid} ({u}, {v})"
)))
}
}
pub fn incident(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
self.check_vertex(v)?;
let v_idx = v as usize;
let out_range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
if self.directed {
Ok(self.oi[out_range].to_vec())
} else {
let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
let mut out = Vec::with_capacity(out_range.len() + in_range.len());
out.extend_from_slice(&self.oi[out_range]);
out.extend_from_slice(&self.ii[in_range]);
Ok(out)
}
}
pub(crate) fn incident_in(&self, v: VertexId) -> IgraphResult<Vec<EdgeId>> {
self.check_vertex(v)?;
let v_idx = v as usize;
if self.directed {
let in_range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
Ok(self.ii[in_range].to_vec())
} else {
self.incident(v)
}
}
pub fn get_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<EdgeId> {
self.find_eid(from, to)?
.ok_or_else(|| IgraphError::InvalidArgument(format!("no edge between {from} and {to}")))
}
pub fn find_eid(&self, from: VertexId, to: VertexId) -> IgraphResult<Option<EdgeId>> {
self.check_vertex(from)?;
self.check_vertex(to)?;
if self.directed {
let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
for &e in &self.oi[range] {
if self.to[e as usize] == to {
return Ok(Some(e));
}
}
Ok(None)
} else {
let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
for &e in &self.oi[range] {
if self.to[e as usize] == hi {
return Ok(Some(e));
}
}
Ok(None)
}
}
pub fn get_all_eids_between(&self, from: VertexId, to: VertexId) -> IgraphResult<Vec<EdgeId>> {
self.check_vertex(from)?;
self.check_vertex(to)?;
let mut out = Vec::new();
if self.directed {
let range = self.os[from as usize] as usize..self.os[from as usize + 1] as usize;
for &e in &self.oi[range] {
if self.to[e as usize] == to {
out.push(e);
}
}
} else {
let (lo, hi) = if from <= to { (from, to) } else { (to, from) };
let range = self.os[lo as usize] as usize..self.os[lo as usize + 1] as usize;
for &e in &self.oi[range] {
if self.to[e as usize] == hi {
out.push(e);
}
}
}
out.sort_unstable();
Ok(out)
}
pub(crate) fn out_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
self.check_vertex(v)?;
let v_idx = v as usize;
let range = self.os[v_idx] as usize..self.os[v_idx + 1] as usize;
Ok(self.oi[range]
.iter()
.map(|&e| self.to[e as usize])
.collect())
}
pub(crate) fn in_neighbors_vec(&self, v: VertexId) -> IgraphResult<Vec<VertexId>> {
self.check_vertex(v)?;
let v_idx = v as usize;
let range = self.is[v_idx] as usize..self.is[v_idx + 1] as usize;
Ok(self.ii[range]
.iter()
.map(|&e| self.from[e as usize])
.collect())
}
pub fn delete_edges(&mut self, edges: &[EdgeId]) -> IgraphResult<()> {
let m = self.ecount();
let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
for &eid in edges {
if (eid as usize) >= m {
return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
}
}
if edges.is_empty() {
return Ok(());
}
let mut remove = vec![false; m];
for &eid in edges {
remove[eid as usize] = true;
}
let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
for (e, &is_removed) in remove.iter().enumerate() {
if !is_removed {
new_from.push(self.from[e]);
new_to.push(self.to[e]);
}
}
for vals in self.edge_attrs.values_mut() {
let mut new_vals = Vec::with_capacity(new_from.len());
for (e, &is_removed) in remove.iter().enumerate() {
if !is_removed {
new_vals.push(vals[e].clone());
}
}
*vals = new_vals;
}
self.from = new_from;
self.to = new_to;
self.rebuild_indexes()?;
self.cache.invalidate_all();
Ok(())
}
pub fn delete_vertices(&mut self, vertices: &[VertexId]) -> IgraphResult<()> {
self.delete_vertices_map(vertices).map(|_| ())
}
pub fn delete_vertices_map(
&mut self,
vertices: &[VertexId],
) -> IgraphResult<(Vec<Option<VertexId>>, Vec<VertexId>)> {
let n_u32 = self.n;
let n = n_u32 as usize;
for &vid in vertices {
if vid >= n_u32 {
return Err(IgraphError::VertexOutOfRange { id: vid, n: n_u32 });
}
}
let mut remove = vec![false; n];
for &vid in vertices {
remove[vid as usize] = true;
}
let mut map: Vec<Option<VertexId>> = vec![None; n];
let mut invmap: Vec<VertexId> = Vec::new();
let mut next_new: u32 = 0;
for (i, &is_removed) in remove.iter().enumerate() {
if !is_removed {
let i_u32 = u32::try_from(i)
.map_err(|_| IgraphError::Internal("vertex index exceeds u32::MAX"))?;
map[i] = Some(next_new);
invmap.push(i_u32);
next_new = next_new
.checked_add(1)
.ok_or(IgraphError::Internal("new vertex count overflow"))?;
}
}
let m = self.ecount();
let mut new_from: Vec<VertexId> = Vec::with_capacity(m);
let mut new_to: Vec<VertexId> = Vec::with_capacity(m);
let mut edge_keep = Vec::with_capacity(m);
for (u, v) in self.from.iter().zip(self.to.iter()) {
if let (Some(nu), Some(nv)) = (map[*u as usize], map[*v as usize]) {
new_from.push(nu);
new_to.push(nv);
edge_keep.push(true);
} else {
edge_keep.push(false);
}
}
for vals in self.vertex_attrs.values_mut() {
let new_vals: Vec<AttributeValue> = remove
.iter()
.enumerate()
.filter(|&(_, is_removed)| !is_removed)
.map(|(i, _)| vals[i].clone())
.collect();
*vals = new_vals;
}
for vals in self.edge_attrs.values_mut() {
let new_vals: Vec<AttributeValue> = edge_keep
.iter()
.enumerate()
.filter(|&(_, keep)| *keep)
.map(|(i, _)| vals[i].clone())
.collect();
*vals = new_vals;
}
self.n = next_new;
self.from = new_from;
self.to = new_to;
self.rebuild_indexes()?;
self.cache.invalidate_all();
Ok((map, invmap))
}
#[must_use]
pub fn cache_get(&self, prop: CachedProperty) -> Option<bool> {
self.cache.get(prop)
}
pub fn cache_set(&self, prop: CachedProperty, value: bool) {
self.cache.set(prop, value);
}
pub fn cache_invalidate(&self, prop: CachedProperty) {
self.cache.invalidate(prop);
}
pub fn cache_invalidate_all(&self) {
self.cache.invalidate_all();
}
fn check_vertex(&self, v: VertexId) -> IgraphResult<()> {
if v >= self.n {
return Err(IgraphError::VertexOutOfRange { id: v, n: self.n });
}
Ok(())
}
fn check_edge(&self, eid: EdgeId) -> IgraphResult<()> {
let m = self.ecount();
let m_u32 = u32::try_from(m).unwrap_or(u32::MAX);
if (eid as usize) >= m {
return Err(IgraphError::EdgeOutOfRange { id: eid, m: m_u32 });
}
Ok(())
}
fn rebuild_indexes(&mut self) -> IgraphResult<()> {
let m = self.ecount();
let n = self.n as usize;
let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
.map(|e| {
Ok::<_, IgraphError>((
self.from[e],
self.to[e],
u32::try_from(e)
.map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
))
})
.collect::<Result<_, _>>()?;
tuples.sort_unstable_by_key(|a| (a.0, a.1));
self.oi = tuples.iter().map(|t| t.2).collect();
self.os = vec![0u32; n + 1];
for &(u, _, _) in &tuples {
self.os[u as usize + 1] = self.os[u as usize + 1]
.checked_add(1)
.ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
}
for i in 1..=n {
self.os[i] = self.os[i]
.checked_add(self.os[i - 1])
.ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
}
let mut tuples: Vec<(VertexId, VertexId, u32)> = (0..m)
.map(|e| {
Ok::<_, IgraphError>((
self.to[e],
self.from[e],
u32::try_from(e)
.map_err(|_| IgraphError::Internal("edge id exceeds u32::MAX"))?,
))
})
.collect::<Result<_, _>>()?;
tuples.sort_unstable_by_key(|a| (a.0, a.1));
self.ii = tuples.iter().map(|t| t.2).collect();
self.is = vec![0u32; n + 1];
for &(v, _, _) in &tuples {
self.is[v as usize + 1] = self.is[v as usize + 1]
.checked_add(1)
.ok_or(IgraphError::Internal("degree overflow in rebuild_indexes"))?;
}
for i in 1..=n {
self.is[i] = self.is[i]
.checked_add(self.is[i - 1])
.ok_or(IgraphError::Internal("offset overflow in rebuild_indexes"))?;
}
Ok(())
}
}
impl Graph {
pub fn density(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::basic::density(self)
}
pub fn is_connected(&self) -> IgraphResult<bool> {
crate::algorithms::connectivity::is_connected::is_connected(
self,
crate::algorithms::connectivity::is_connected::ConnectednessMode::Weak,
)
}
pub fn is_simple(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_simple::is_simple(self)
}
pub fn connected_components(
&self,
) -> IgraphResult<crate::algorithms::connectivity::components::ConnectedComponents> {
crate::algorithms::connectivity::components::connected_components(self)
}
pub fn pagerank(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::pagerank::pagerank(self)
}
pub fn betweenness(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::betweenness::betweenness(self)
}
pub fn closeness(&self) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::closeness::closeness(self)
}
pub fn eigenvector_centrality(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::eigenvector::eigenvector_centrality(self)
}
pub fn clustering_coefficients(&self) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::triangles::transitivity_local_undirected(self)
}
pub fn complement(&self) -> IgraphResult<Graph> {
crate::algorithms::operators::complementer::complementer(self, false)
}
pub fn line_graph(&self) -> IgraphResult<Graph> {
crate::algorithms::operators::line_graph::line_graph(self)
}
pub fn louvain(&self) -> IgraphResult<crate::algorithms::community::louvain::LouvainResult> {
crate::algorithms::community::louvain::louvain(self)
}
pub fn leiden(&self) -> IgraphResult<crate::algorithms::community::leiden::LeidenResult> {
crate::algorithms::community::leiden::leiden(self)
}
pub fn bridges(&self) -> IgraphResult<Vec<EdgeId>> {
crate::algorithms::connectivity::bridges::bridges(self)
}
pub fn coreness(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::coreness::coreness(self)
}
pub fn induced_subgraph(
&self,
vertices: &[VertexId],
) -> IgraphResult<crate::algorithms::operators::induced_subgraph::InducedSubgraphResult> {
crate::algorithms::operators::induced_subgraph::induced_subgraph(self, vertices)
}
pub fn to_dot(&self, labels: Option<&[String]>) -> IgraphResult<String> {
let mut buf = Vec::new();
crate::algorithms::io::dot::write_dot(self, labels, &mut buf)?;
String::from_utf8(buf).map_err(|e| {
IgraphError::InvalidArgument(format!("DOT output is not valid UTF-8: {e}"))
})
}
pub fn bfs(&self, root: VertexId) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::traversal::bfs::bfs(self, root)
}
pub fn dfs(&self, root: VertexId) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::traversal::dfs::dfs(self, root)
}
pub fn shortest_paths(&self, source: VertexId) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::paths::shortest_paths::get_shortest_paths(self, source)
}
pub fn dijkstra(&self, source: VertexId, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::paths::dijkstra::dijkstra_distances(self, source, weights)
}
pub fn degree_sequence(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::degree::degree_sequence(
self,
crate::algorithms::properties::degree::DegreeMode::All,
)
}
pub fn diameter(&self) -> IgraphResult<Option<u32>> {
crate::algorithms::paths::radii::diameter(self)
}
pub fn transitivity(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::triangles::transitivity_undirected(self)
}
pub fn clique_number(&self) -> IgraphResult<u32> {
crate::algorithms::cliques::clique_number(self)
}
pub fn largest_cliques(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::cliques::largest_cliques(self)
}
pub fn maximal_cliques_count(&self) -> IgraphResult<u64> {
crate::algorithms::cliques::maximal_cliques_count(self)
}
pub fn clique_size_hist(&self) -> IgraphResult<Vec<u64>> {
crate::algorithms::cliques::clique_size_hist(self)
}
pub fn average_local_efficiency(&self) -> IgraphResult<f64> {
crate::algorithms::properties::efficiency::average_local_efficiency(self)
}
pub fn count_mutual(&self) -> IgraphResult<usize> {
crate::algorithms::properties::mutual::count_mutual(self, true)
}
pub fn maximal_independent_vertex_sets(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::cliques::maximal_independent_vertex_sets(self)
}
pub fn largest_independent_vertex_sets(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::cliques::largest_independent_vertex_sets(self)
}
pub fn edge_coloring_greedy(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::coloring::edge_coloring_greedy(self)
}
pub fn chromatic_number_upper_bound(&self) -> IgraphResult<u32> {
crate::algorithms::coloring::chromatic_number_upper_bound(self)
}
pub fn is_perfect(&self) -> IgraphResult<bool> {
crate::algorithms::properties::perfect::is_perfect(self)
}
pub fn transitivity_avglocal(&self) -> IgraphResult<f64> {
crate::algorithms::properties::triangles::transitivity_avglocal_undirected(
self,
crate::algorithms::properties::triangles::TransitivityMode::Zero,
)
}
pub fn mean_degree(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::basic::mean_degree(self, true)
}
pub fn degeneracy(&self) -> IgraphResult<u32> {
crate::algorithms::properties::is_k_degenerate::degeneracy(self)
}
pub fn convergence_degree(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::convergence_degree::convergence_degree(self)
}
pub fn count_loops(&self) -> IgraphResult<usize> {
crate::algorithms::properties::multiplicity::count_loops(self)
}
pub fn avg_nearest_neighbor_degree(&self) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::knn::avg_nearest_neighbor_degree(self)
}
pub fn bibcoupling(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::similarity::bibcoupling(self)
}
pub fn biconnected_components(
&self,
) -> IgraphResult<crate::algorithms::connectivity::biconnected::BiconnectedComponents> {
crate::algorithms::connectivity::biconnected::biconnected_components(self)
}
pub fn minimum_size_separators(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::connectivity::separators::minimum_size_separators(self)
}
pub fn all_minimal_st_separators(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::connectivity::separators::all_minimal_st_separators(self)
}
pub fn adhesion(&self) -> IgraphResult<i64> {
crate::algorithms::flow::edge_connectivity::adhesion(self, true)
}
pub fn cohesion(&self) -> IgraphResult<i64> {
crate::algorithms::flow::vertex_connectivity::cohesion(self, true)
}
pub fn bfs_tree(
&self,
root: VertexId,
) -> IgraphResult<crate::algorithms::traversal::bfs::BfsTree> {
crate::algorithms::traversal::bfs::bfs_tree(self, root)
}
pub fn dfs_tree(
&self,
root: VertexId,
) -> IgraphResult<crate::algorithms::traversal::dfs::DfsTree> {
crate::algorithms::traversal::dfs::dfs_tree(self, root)
}
pub fn articulation_points(&self) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::connectivity::articulation::articulation_points(self)
}
pub fn topological_sort(&self) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::properties::topological_sorting::topological_sorting(
self,
crate::algorithms::paths::dijkstra::DijkstraMode::Out,
)
}
pub fn minimum_spanning_tree(&self) -> IgraphResult<Vec<EdgeId>> {
crate::algorithms::spanning::mst::minimum_spanning_tree(
self,
None,
crate::algorithms::spanning::mst::MstAlgorithm::Automatic,
)
}
pub fn summary(&self) -> IgraphResult<crate::algorithms::properties::summary::GraphSummary> {
crate::algorithms::properties::summary::graph_summary(self)
}
pub fn max_flow(&self, source: VertexId, target: VertexId) -> IgraphResult<f64> {
crate::algorithms::flow::max_flow::max_flow_value(self, source, target, None)
}
pub fn decompose(&self) -> IgraphResult<Vec<Graph>> {
crate::algorithms::connectivity::decompose::decompose(self)
}
pub fn is_biconnected(&self) -> IgraphResult<bool> {
crate::algorithms::connectivity::is_biconnected::is_biconnected(self)
}
pub fn label_propagation(
&self,
) -> IgraphResult<crate::algorithms::community::label_propagation::LpaResult> {
crate::algorithms::community::label_propagation::label_propagation(self)
}
pub fn walktrap(&self) -> IgraphResult<crate::algorithms::community::walktrap::WalktrapResult> {
crate::algorithms::community::walktrap::walktrap(self)
}
pub fn fast_greedy(
&self,
) -> IgraphResult<crate::algorithms::community::fast_greedy_modularity::FastGreedyResult> {
crate::algorithms::community::fast_greedy_modularity::fast_greedy_modularity(self)
}
pub fn hits(&self) -> IgraphResult<crate::algorithms::properties::hits::HitsScores> {
crate::algorithms::properties::hits::hub_and_authority_scores(self)
}
pub fn katz_centrality(&self, alpha: f64, beta: f64) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::katz_centrality::katz_centrality(
self, alpha, beta, None, None,
)
}
pub fn assortativity(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::assortativity::assortativity_degree(self)
}
pub fn from_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let p = path.as_ref();
let ext = p
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
.to_ascii_lowercase();
match ext.as_str() {
"gml" => Self::from_gml_file(p),
"graphml" | "xml" => Self::from_graphml_file(p),
"dot" | "gv" => Self::from_dot_file(p),
"net" | "pajek" => Self::from_pajek_file(p),
"ncol" => Self::from_ncol_file(p),
"lgl" => Self::from_lgl_file(p),
"leda" | "lgr" => Self::from_leda_file(p),
"dl" => Self::from_dl_file(p),
"edges" | "edgelist" | "txt" | "csv" => Self::from_edgelist_file(p),
_ => Err(IgraphError::InvalidArgument(format!(
"cannot detect graph format from extension \".{ext}\"; \
use a format-specific method like from_gml_file()"
))),
}
}
pub fn to_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let p = path.as_ref();
let ext = p
.extension()
.and_then(|e| e.to_str())
.unwrap_or("")
.to_ascii_lowercase();
match ext.as_str() {
"gml" => self.to_gml_file(p),
"graphml" | "xml" => self.to_graphml_file(p),
"dot" | "gv" => self.to_dot_file(p),
"net" | "pajek" => self.to_pajek_file(p),
"ncol" => self.to_ncol_file(p),
"lgl" => self.to_lgl_file(p),
"leda" | "lgr" => self.to_leda_file(p),
"dl" => self.to_dl_file(p),
"edges" | "edgelist" | "txt" | "csv" => self.to_edgelist_file(p),
_ => Err(IgraphError::InvalidArgument(format!(
"cannot detect graph format from extension \".{ext}\"; \
use a format-specific method like to_gml_file()"
))),
}
}
pub fn from_edgelist_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
crate::algorithms::io::edgelist::read_edgelist(std::io::BufReader::new(file))
}
pub fn to_edgelist_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::edgelist::write_edgelist(self, &mut file)
}
pub fn from_gml_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
crate::algorithms::io::gml::read_gml(std::io::BufReader::new(file))
}
pub fn to_gml_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::gml::write_gml(self, &mut file)
}
pub fn from_graphml_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::graphml::read_graphml(std::io::BufReader::new(file))?;
Ok(result.graph)
}
pub fn to_graphml_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::graphml::write_graphml(self, None, &mut file)
}
pub fn from_dot_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::dot::read_dot(std::io::BufReader::new(file))?;
Ok(result.graph)
}
pub fn to_dot_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::dot::write_dot(self, None, &mut file)
}
pub fn from_pajek_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::pajek::read_pajek(std::io::BufReader::new(file))?;
Ok(result.graph)
}
pub fn to_pajek_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::pajek::write_pajek(self, None, None, &mut file)
}
pub fn from_ncol_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::ncol::read_ncol(std::io::BufReader::new(file))?;
Ok(result.graph)
}
pub fn to_ncol_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::ncol::write_ncol(self, None, None, &mut file)
}
pub fn from_lgl_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::lgl::read_lgl(std::io::BufReader::new(file))?;
Ok(result.graph)
}
pub fn to_lgl_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::lgl::write_lgl(self, None, None, &mut file)
}
pub fn from_leda_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::leda::read_leda(std::io::BufReader::new(file))?;
Ok(result.graph)
}
pub fn to_leda_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::leda::write_leda(self, None, None, &mut file)
}
pub fn from_dl_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result = crate::algorithms::io::dl::read_dl(std::io::BufReader::new(file), false)?;
Ok(result.graph)
}
pub fn to_dl_file<P: AsRef<std::path::Path>>(&self, path: P) -> IgraphResult<()> {
let mut file = std::fs::File::create(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot create file: {e}")))?;
crate::algorithms::io::dl::write_dl(self, None, None, &mut file)
}
pub fn from_dimacs_file<P: AsRef<std::path::Path>>(path: P) -> IgraphResult<Self> {
let file = std::fs::File::open(path.as_ref())
.map_err(|e| IgraphError::InvalidArgument(format!("cannot open file: {e}")))?;
let result =
crate::algorithms::io::dimacs::read_dimacs(std::io::BufReader::new(file), true)?;
Ok(result.graph)
}
pub fn erdos_renyi(n: u32, p: f64, seed: u64) -> IgraphResult<Self> {
crate::algorithms::games::erdos_renyi::erdos_renyi_gnp(n, p, false, false, seed)
}
pub fn barabasi_albert(n: u32, m: u32, seed: u64) -> IgraphResult<Self> {
crate::algorithms::games::barabasi::barabasi_game_bag(n, m, true, false, seed)
}
pub fn watts_strogatz(n: u32, k: u32, p: f64, seed: u64) -> IgraphResult<Self> {
crate::algorithms::games::watts::watts_strogatz_game(n, k / 2, p, false, false, seed)
}
pub fn strongly_connected_components(
&self,
) -> IgraphResult<crate::algorithms::connectivity::components::ConnectedComponents> {
crate::algorithms::connectivity::strong::strongly_connected_components(self)
}
pub fn shortest_path_to(
&self,
source: VertexId,
target: VertexId,
weights: Option<&[f64]>,
) -> IgraphResult<crate::algorithms::paths::get_shortest_path::ShortestPath> {
use crate::algorithms::paths::dijkstra::DijkstraMode;
let mode = if self.directed {
DijkstraMode::Out
} else {
DijkstraMode::All
};
crate::algorithms::paths::get_shortest_path::get_shortest_path(
self, source, target, weights, mode,
)
}
pub fn average_path_length(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::basic::mean_distance(self)
}
pub fn is_bipartite(
&self,
) -> IgraphResult<crate::algorithms::properties::is_bipartite::BipartiteResult> {
crate::algorithms::properties::is_bipartite::is_bipartite(self)
}
pub fn simplify(&self, remove_multiple: bool, remove_loops: bool) -> IgraphResult<Graph> {
crate::algorithms::operators::simplify::simplify(self, remove_multiple, remove_loops)
}
pub fn reverse(&self) -> IgraphResult<Graph> {
crate::algorithms::operators::reverse::reverse(self)
}
pub fn to_directed(
&self,
mode: crate::algorithms::operators::to_directed::ToDirectedMode,
) -> IgraphResult<Graph> {
crate::algorithms::operators::to_directed::to_directed(self, mode)
}
pub fn to_undirected(
&self,
mode: crate::algorithms::operators::to_undirected::ToUndirectedMode,
) -> IgraphResult<Graph> {
crate::algorithms::operators::to_undirected::to_undirected(self, mode)
}
pub fn contract_vertices(&self, mapping: &[VertexId]) -> IgraphResult<Graph> {
crate::algorithms::operators::contract_vertices::contract_vertices(self, mapping)
}
pub fn random_walk(
&self,
start: VertexId,
steps: u32,
seed: u64,
) -> IgraphResult<(Vec<VertexId>, Vec<EdgeId>)> {
use crate::algorithms::paths::dijkstra::DijkstraMode;
let mode = if self.directed {
DijkstraMode::Out
} else {
DijkstraMode::All
};
crate::algorithms::paths::random_walk::random_walk(self, None, start, mode, steps, seed)
}
pub fn radius(&self) -> IgraphResult<Option<u32>> {
crate::algorithms::paths::radii::radius(self)
}
pub fn eccentricity(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::paths::radii::eccentricity(self)
}
pub fn girth(&self) -> IgraphResult<Option<u32>> {
crate::algorithms::properties::girth::girth(self)
}
pub fn is_tree(
&self,
mode: crate::algorithms::paths::dijkstra::DijkstraMode,
) -> IgraphResult<Option<VertexId>> {
crate::algorithms::properties::is_tree::is_tree(self, mode)
}
pub fn is_dag(&self) -> bool {
crate::algorithms::properties::is_dag::is_dag(self)
}
pub fn count_triangles(&self) -> IgraphResult<u64> {
crate::algorithms::properties::triangles::count_triangles(self)
}
pub fn harmonic_centrality(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::harmonic::harmonic_centrality(self)
}
pub fn neighborhood_size(&self, order: i32) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::neighborhood::neighborhood_size(self, order)
}
pub fn vertex_connectivity(&self) -> IgraphResult<i64> {
crate::algorithms::flow::vertex_connectivity::vertex_connectivity(self, true)
}
pub fn edge_connectivity(&self) -> IgraphResult<i64> {
crate::algorithms::flow::edge_connectivity::edge_connectivity(self, true)
}
pub fn subcomponent(
&self,
source: VertexId,
mode: crate::algorithms::connectivity::subcomponent::SubcomponentMode,
) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::connectivity::subcomponent::subcomponent(self, source, mode)
}
pub fn cliques(
&self,
min_size: u32,
max_size: u32,
max_results: Option<usize>,
) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::cliques::cliques(self, min_size, max_size, max_results)
}
pub fn maximal_cliques(&self) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::cliques::maximal_cliques(self)
}
pub fn independence_number(&self) -> IgraphResult<u32> {
crate::algorithms::cliques::independence_number(self)
}
pub fn permute_vertices(&self, permutation: &[VertexId]) -> IgraphResult<Graph> {
crate::algorithms::operators::permute_vertices::permute_vertices(self, permutation)
}
pub fn layout_fruchterman_reingold(&self) -> IgraphResult<Vec<(f64, f64)>> {
use crate::algorithms::layout::fruchterman_reingold::FrParams;
crate::algorithms::layout::fruchterman_reingold::layout_fruchterman_reingold(
self,
&FrParams::default(),
)
}
pub fn layout_kamada_kawai(&self) -> IgraphResult<Vec<[f64; 2]>> {
use crate::algorithms::layout::kamada_kawai::KkParams;
let params = KkParams::default_for(self.vcount() as usize);
crate::algorithms::layout::kamada_kawai::layout_kamada_kawai(self, None, ¶ms, None)
}
pub fn layout_drl(&self) -> IgraphResult<Vec<[f64; 2]>> {
use crate::algorithms::layout::drl::DrlOptions;
crate::algorithms::layout::drl::layout_drl(self, None, &DrlOptions::default(), None)
}
pub fn layout_circle(&self) -> Vec<(f64, f64)> {
crate::algorithms::layout::simple::layout_circle(self, None)
}
pub fn layout_random(&self, seed: u64) -> Vec<(f64, f64)> {
crate::algorithms::layout::simple::layout_random(self, seed)
}
pub fn layout_grid(&self, width: i32) -> Vec<(f64, f64)> {
crate::algorithms::layout::simple::layout_grid(self, width)
}
pub fn count_adjacent_triangles(&self) -> IgraphResult<Vec<u64>> {
crate::algorithms::properties::triangles::count_adjacent_triangles(self)
}
pub fn transitivity_local_undirected(&self) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::triangles::transitivity_local_undirected(self)
}
pub fn reciprocity(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::reciprocity::reciprocity(self)
}
pub fn constraint(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::constraint::constraint(self, weights)
}
pub fn has_multiple(&self) -> IgraphResult<bool> {
crate::algorithms::properties::multiplicity::has_multiple(self)
}
pub fn count_multiple(&self) -> IgraphResult<Vec<usize>> {
crate::algorithms::properties::multiplicity::count_multiple(self)
}
pub fn are_adjacent(&self, v1: VertexId, v2: VertexId) -> IgraphResult<bool> {
crate::algorithms::properties::are_adjacent::are_adjacent(self, v1, v2)
}
pub fn triad_census(
&self,
) -> IgraphResult<crate::algorithms::motifs::triad_census::TriadCensus> {
crate::algorithms::motifs::triad_census::triad_census(self)
}
pub fn dyad_census(&self) -> IgraphResult<crate::algorithms::motifs::DyadCensus> {
crate::algorithms::motifs::dyad_census(self)
}
pub fn similarity_jaccard(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::similarity::similarity_jaccard(self)
}
pub fn cocitation(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::similarity::cocitation(self)
}
pub fn is_cograph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_cograph::is_cograph(self)
}
pub fn is_series_parallel(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_series_parallel::is_series_parallel(self)
}
pub fn is_outerplanar(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_outerplanar::is_outerplanar(self)
}
pub fn is_chordal(&self) -> IgraphResult<bool> {
let result = crate::algorithms::chordality::is_chordal(self, None)?;
Ok(result.chordal)
}
pub fn is_forest(&self) -> IgraphResult<bool> {
Ok(crate::algorithms::properties::is_forest::is_forest(
self,
crate::algorithms::paths::dijkstra::DijkstraMode::Out,
)?
.is_some())
}
pub fn fundamental_cycles(&self) -> IgraphResult<Vec<Vec<u32>>> {
crate::algorithms::fundamental_cycles::fundamental_cycles(self, None, None)
}
pub fn minimum_cycle_basis(&self) -> IgraphResult<Vec<Vec<u32>>> {
crate::algorithms::minimum_cycle_basis::minimum_cycle_basis(self, None, false)
}
pub fn feedback_arc_set(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::feedback_arc_set::feedback_arc_set(
self,
None,
crate::algorithms::feedback_arc_set::FasAlgorithm::EadesLinSmyth,
)
}
pub fn maximum_cut(&self) -> IgraphResult<crate::algorithms::max_cut::MaxCutResult> {
crate::algorithms::max_cut::maximum_cut(self)
}
pub fn minimum_vertex_cover(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::vertex_cover::minimum_vertex_cover(self)
}
pub fn minimum_edge_cover(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::edge_cover::minimum_edge_cover(self)
}
pub fn maximum_independent_set(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::independent_set::maximum_independent_set(self)
}
pub fn vertex_coloring(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::coloring::vertex_coloring_greedy(
self,
crate::algorithms::coloring::GreedyColoringHeuristic::ColoredNeighbors,
)
}
pub fn random_spanning_tree(&self, seed: u64) -> IgraphResult<Vec<u32>> {
crate::algorithms::spanning::random_spanning_tree::random_spanning_tree(self, None, seed)
}
pub fn edge_betweenness_community(
&self,
) -> IgraphResult<crate::algorithms::community::edge_betweenness_community::EdgeBetweennessResult>
{
crate::algorithms::community::edge_betweenness_community::edge_betweenness_community(self)
}
pub fn canonical_permutation(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::isomorphism::canonical::canonical_permutation::canonical_permutation(
self, None,
)
}
pub fn count_automorphisms(&self) -> IgraphResult<f64> {
crate::algorithms::isomorphism::canonical::count_automorphisms::count_automorphisms(
self, None,
)
}
pub fn automorphism_group(&self) -> IgraphResult<Vec<Vec<u32>>> {
crate::algorithms::isomorphism::canonical::automorphism_group::automorphism_group(
self, None,
)
}
pub fn sir(
&self,
beta: f64,
gamma: f64,
no_sim: usize,
seed: u64,
) -> IgraphResult<Vec<crate::algorithms::epidemics::Sir>> {
crate::algorithms::epidemics::sir(self, beta, gamma, no_sim, seed)
}
pub fn spanner(&self, stretch: f64) -> IgraphResult<Vec<u32>> {
crate::algorithms::paths::spanner::spanner(self, stretch, None)
}
pub fn is_acyclic(&self) -> bool {
crate::algorithms::properties::is_acyclic::is_acyclic(self)
}
pub fn is_apex_forest(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_apex_forest::is_apex_forest(self)
}
pub fn is_apex_tree(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_apex_tree::is_apex_tree(self)
}
pub fn is_banner_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_banner_free::is_banner_free(self)
}
pub fn is_biclique(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_biclique::is_biclique(self)
}
pub fn is_biregular(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_biregular::is_biregular(self)
}
pub fn is_block_graph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_block::is_block_graph(self)
}
pub fn is_bowtie_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_bowtie_free::is_bowtie_free(self)
}
pub fn is_bull_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_bull_free::is_bull_free(self)
}
pub fn is_c4_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_c4_free::is_c4_free(self)
}
pub fn is_c5_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_c5_free::is_c5_free(self)
}
pub fn is_cactus_graph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_cactus::is_cactus_graph(self)
}
pub fn is_caterpillar(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_caterpillar::is_caterpillar(self)
}
pub fn is_chain_graph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_chain_graph::is_chain_graph(self)
}
pub fn is_chordal_bipartite(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_chordal_bipartite::is_chordal_bipartite(self)
}
pub fn is_claw_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_claw_free::is_claw_free(self)
}
pub fn is_cluster_graph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_cluster::is_cluster_graph(self)
}
pub fn is_co_bipartite(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_co_bipartite::is_co_bipartite(self)
}
pub fn is_co_chordal(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_co_chordal::is_co_chordal(self)
}
pub fn is_complete(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_complete::is_complete(self)
}
pub fn is_complete_bipartite(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_complete_bipartite::is_complete_bipartite(self)
}
pub fn is_cricket_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_cricket_free::is_cricket_free(self)
}
pub fn is_cubic(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_cubic::is_cubic(self)
}
pub fn is_cycle(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_cycle::is_cycle(self)
}
pub fn is_dart_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_dart_free::is_dart_free(self)
}
pub fn is_diamond_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_diamond_free::is_diamond_free(self)
}
pub fn is_distance_hereditary(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_distance_hereditary::is_distance_hereditary(self)
}
pub fn is_eulerian(
&self,
) -> IgraphResult<crate::algorithms::paths::eulerian::EulerianClassification> {
crate::algorithms::paths::eulerian::is_eulerian(self)
}
pub fn is_fork_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_fork_free::is_fork_free(self)
}
pub fn is_gem_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_gem_free::is_gem_free(self)
}
pub fn is_geodetic(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_geodetic::is_geodetic(self)
}
pub fn is_house_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_house_free::is_house_free(self)
}
pub fn is_k_degenerate(&self, k: u32) -> IgraphResult<bool> {
crate::algorithms::properties::is_k_degenerate::is_k_degenerate(self, k)
}
pub fn is_lobster(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_lobster::is_lobster(self)
}
pub fn is_net_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_net_free::is_net_free(self)
}
pub fn is_p5_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_p5_free::is_p5_free(self)
}
pub fn is_path(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_path::is_path(self)
}
pub fn is_paw_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_paw_free::is_paw_free(self)
}
pub fn is_proper_interval(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_proper_interval::is_proper_interval(self)
}
pub fn is_pseudo_forest(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_pseudo_forest::is_pseudo_forest(self)
}
pub fn is_ptolemaic(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_ptolemaic::is_ptolemaic(self)
}
pub fn is_self_complementary(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_self_complementary::is_self_complementary(self)
}
pub fn is_semicomplete(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_semicomplete::is_semicomplete(self)
}
pub fn is_spider(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_spider::is_spider(self)
}
pub fn is_split_graph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_split::is_split_graph(self)
}
pub fn is_star(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_star::is_star(self)
}
pub fn is_strongly_chordal(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_strongly_chordal::is_strongly_chordal(self)
}
pub fn is_threshold_graph(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_threshold::is_threshold_graph(self)
}
pub fn is_tournament(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_tournament::is_tournament(self)
}
pub fn is_triangle_free(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_triangle_free::is_triangle_free(self)
}
pub fn is_trivially_perfect(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_trivially_perfect::is_trivially_perfect(self)
}
pub fn is_unicyclic(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_unicyclic::is_unicyclic(self)
}
pub fn is_wheel(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_wheel::is_wheel(self)
}
pub fn is_regular(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_regular::is_regular(self)
}
pub fn is_strongly_regular(
&self,
) -> IgraphResult<
Option<crate::algorithms::properties::is_strongly_regular::StronglyRegularParams>,
> {
crate::algorithms::properties::is_strongly_regular::is_strongly_regular(self)
}
pub fn is_weakly_chordal(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_weakly_chordal::is_weakly_chordal(self)
}
pub fn is_well_covered(&self) -> IgraphResult<bool> {
crate::algorithms::properties::is_well_covered::is_well_covered(self)
}
pub fn is_windmill(&self) -> IgraphResult<Option<(u32, u32)>> {
crate::algorithms::properties::is_windmill::is_windmill(self)
}
pub fn is_complete_multipartite(&self) -> IgraphResult<Option<Vec<u32>>> {
crate::algorithms::properties::is_complete_multipartite::is_complete_multipartite(self)
}
pub fn satisfies_dirac(&self) -> IgraphResult<bool> {
crate::algorithms::properties::satisfies_dirac::satisfies_dirac(self)
}
pub fn satisfies_ore(&self) -> IgraphResult<bool> {
crate::algorithms::properties::satisfies_ore::satisfies_ore(self)
}
pub fn edge_betweenness(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::edge_betweenness::edge_betweenness(self)
}
pub fn betweenness_cutoff(&self, cutoff: u32) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::betweenness_cutoff::betweenness_cutoff(self, cutoff)
}
pub fn betweenness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::betweenness_weighted::betweenness_weighted(self, weights)
}
pub fn closeness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::closeness_weighted::closeness_weighted(self, weights)
}
pub fn edge_betweenness_cutoff(&self, cutoff: u32) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::edge_betweenness_cutoff::edge_betweenness_cutoff(
self, cutoff,
)
}
pub fn edge_betweenness_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::edge_betweenness_weighted::edge_betweenness_weighted(
self, weights,
)
}
pub fn harmonic_centrality_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::harmonic_weighted::harmonic_centrality_weighted(
self, weights,
)
}
pub fn pagerank_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::pagerank_weighted::pagerank_weighted(self, weights)
}
pub fn cohesive_blocks(
&self,
) -> IgraphResult<crate::algorithms::connectivity::cohesive_blocks::CohesiveBlocks> {
crate::algorithms::connectivity::cohesive_blocks::cohesive_blocks(self)
}
pub fn count_reachable(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::connectivity::reachability::count_reachable(self)
}
pub fn reachability_matrix(&self) -> IgraphResult<Vec<Vec<bool>>> {
crate::algorithms::connectivity::reachability_matrix::reachability_matrix(self)
}
pub fn transitive_closure(&self) -> IgraphResult<Graph> {
crate::algorithms::connectivity::transitive_closure::transitive_closure(self)
}
pub fn mincut(
&self,
capacity: Option<&[f64]>,
) -> IgraphResult<crate::algorithms::flow::mincut::Mincut> {
crate::algorithms::flow::mincut::mincut(self, capacity)
}
pub fn mincut_value(&self, capacity: Option<&[f64]>) -> IgraphResult<f64> {
crate::algorithms::flow::mincut_value::mincut_value(self, capacity)
}
pub fn gomory_hu_tree(
&self,
capacity: Option<&[f64]>,
) -> IgraphResult<crate::algorithms::flow::gomory_hu_tree::GomoryHuTree> {
crate::algorithms::flow::gomory_hu_tree::gomory_hu_tree(self, capacity)
}
pub fn all_st_cuts(
&self,
source: VertexId,
target: VertexId,
) -> IgraphResult<crate::algorithms::flow::all_st_cuts::StCuts> {
crate::algorithms::flow::all_st_cuts::all_st_cuts(self, source, target)
}
pub fn edge_disjoint_paths(&self, source: VertexId, target: VertexId) -> IgraphResult<i64> {
crate::algorithms::flow::edge_disjoint_paths::edge_disjoint_paths(self, source, target)
}
pub fn distances(&self, source: VertexId) -> IgraphResult<Vec<Option<u32>>> {
crate::algorithms::paths::distances::distances(self, source)
}
pub fn eulerian_path(&self) -> IgraphResult<Option<Vec<u32>>> {
crate::algorithms::paths::eulerian_construct::eulerian_path(self)
}
pub fn mean_distance(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::basic::mean_distance(self)
}
pub fn topological_sorting(&self) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::properties::topological_sorting::topological_sorting(
self,
crate::algorithms::paths::dijkstra::DijkstraMode::Out,
)
}
pub fn list_triangles(&self) -> IgraphResult<Vec<(u32, u32, u32)>> {
crate::algorithms::properties::list_triangles::list_triangles(self)
}
pub fn degree_distribution(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::degree_distribution::degree_distribution(
self,
crate::algorithms::properties::degree::DegreeMode::All,
)
}
pub fn get_edgelist(&self) -> IgraphResult<Vec<(VertexId, VertexId)>> {
crate::algorithms::properties::edgelist::get_edgelist(self)
}
pub fn regularity(&self) -> IgraphResult<Option<u32>> {
crate::algorithms::properties::is_regular::regularity(self)
}
pub fn find_cycle(&self) -> IgraphResult<crate::algorithms::cycles::CycleResult> {
crate::algorithms::cycles::find_cycle(self, crate::algorithms::cycles::CycleMode::All)
}
pub fn joint_degree_matrix(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<Vec<f64>>> {
crate::algorithms::properties::joint_degree_matrix::joint_degree_matrix(self, weights)
}
pub fn minimum_dominating_set(&self) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::dominating_set::minimum_dominating_set(self)
}
pub fn graph_power(&self, order: u32) -> IgraphResult<Graph> {
crate::algorithms::operators::connect_neighborhood::graph_power(self, order)
}
pub fn trussness(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::properties::trussness::trussness(self)
}
pub fn union(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::union::union(self, other)
}
pub fn intersection(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::intersection::intersection(self, other)
}
pub fn difference(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::difference::difference(self, other)
}
pub fn disjoint_union(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::disjoint_union::disjoint_union(self, other)
}
pub fn join(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::join::join(self, other)
}
pub fn compose(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::compose::compose(self, other)
}
pub fn cartesian_product(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::products::cartesian_product(self, other)
}
pub fn tensor_product(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::products::tensor_product(self, other)
}
pub fn strong_product(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::products::strong_product(self, other)
}
pub fn lexicographic_product(&self, other: &Graph) -> IgraphResult<Graph> {
crate::algorithms::operators::products::lexicographic_product(self, other)
}
pub fn connect_neighborhood(&self, order: u32) -> IgraphResult<Graph> {
crate::algorithms::operators::connect_neighborhood::connect_neighborhood(self, order)
}
pub fn rewire(&self, num_trials: usize, loops: bool, seed: u64) -> IgraphResult<Graph> {
crate::algorithms::operators::rewire::rewire(self, num_trials, loops, seed)
}
pub fn rewire_edges(&self, prob: f64, loops: bool, seed: u64) -> IgraphResult<Graph> {
crate::algorithms::operators::rewire_edges::rewire_edges(self, prob, loops, seed)
}
pub fn subgraph_from_edges(
&self,
eids: &[u32],
) -> IgraphResult<crate::algorithms::operators::subgraph_from_edges::SubgraphFromEdgesResult>
{
crate::algorithms::operators::subgraph_from_edges::subgraph_from_edges(self, eids, true)
}
pub fn even_tarjan_reduction(
&self,
) -> IgraphResult<crate::algorithms::operators::even_tarjan::EvenTarjanResult> {
crate::algorithms::operators::even_tarjan::even_tarjan_reduction(self)
}
pub fn bipartite_projection(
&self,
types: &[bool],
project_type: bool,
) -> IgraphResult<crate::algorithms::operators::bipartite_projection::BipartiteProjection> {
crate::algorithms::operators::bipartite_projection::bipartite_projection(
self,
types,
project_type,
)
}
pub fn bellman_ford_distances(
&self,
source: VertexId,
weights: &[f64],
) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::paths::bellman_ford::bellman_ford_distances(self, source, weights)
}
pub fn floyd_warshall_distances(
&self,
weights: Option<&[f64]>,
) -> IgraphResult<Vec<Vec<Option<f64>>>> {
crate::algorithms::paths::floyd_warshall::floyd_warshall_distances(self, weights)
}
pub fn k_shortest_paths(
&self,
source: VertexId,
target: VertexId,
weights: &[f64],
k: usize,
) -> IgraphResult<Vec<crate::algorithms::paths::k_shortest_paths::KShortestPath>> {
use crate::algorithms::paths::dijkstra::DijkstraMode;
crate::algorithms::paths::k_shortest_paths::k_shortest_paths(
self,
source,
target,
weights,
k,
DijkstraMode::Out,
)
}
pub fn all_simple_paths(
&self,
from: u32,
to: Option<&[u32]>,
min_length: i32,
max_length: i32,
) -> IgraphResult<Vec<Vec<u32>>> {
crate::algorithms::paths::simple_paths::all_simple_paths(
self,
from,
to,
crate::algorithms::paths::simple_paths::SimplePathMode::Out,
min_length,
max_length,
-1,
)
}
pub fn maximum_bipartite_matching(
&self,
types: &[bool],
) -> IgraphResult<crate::algorithms::matching::MatchingResult> {
crate::algorithms::matching::maximum_bipartite_matching(self, types)
}
pub fn vertex_coloring_greedy(
&self,
heuristic: crate::algorithms::coloring::GreedyColoringHeuristic,
) -> IgraphResult<Vec<u32>> {
crate::algorithms::coloring::vertex_coloring_greedy(self, heuristic)
}
pub fn simple_cycles(
&self,
mode: crate::algorithms::simple_cycles::SimpleCycleMode,
min_length: u32,
max_length: Option<u32>,
) -> IgraphResult<Vec<crate::algorithms::simple_cycles::SimpleCycle>> {
crate::algorithms::simple_cycles::simple_cycles(self, mode, min_length, max_length, None)
}
pub fn leading_eigenvector(
&self,
weights: Option<&[f64]>,
steps: Option<u32>,
) -> IgraphResult<crate::algorithms::community::leading_eigenvector::LeadingEigenvectorResult>
{
crate::algorithms::community::leading_eigenvector::leading_eigenvector(self, weights, steps)
}
pub fn fluid_communities(
&self,
k: u32,
) -> IgraphResult<crate::algorithms::community::fluid_communities::FluidResult> {
crate::algorithms::community::fluid_communities::fluid_communities(self, k)
}
pub fn motifs_randesu(&self, size: u32) -> IgraphResult<Vec<f64>> {
crate::algorithms::motifs::motifs_randesu::motifs_randesu(self, size)
}
pub fn personalized_pagerank(&self, reset: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::personalized_pagerank::personalized_pagerank_default(
self, reset,
)
}
pub fn isomorphic_vf2(
&self,
other: &Graph,
) -> IgraphResult<crate::algorithms::isomorphism::vf2::Vf2Isomorphism> {
crate::algorithms::isomorphism::vf2::isomorphic_vf2(self, other, None, None, None, None)
}
pub fn isomorphic(&self, other: &Graph) -> IgraphResult<bool> {
crate::algorithms::isomorphism::queries::isomorphic(self, other)
}
pub fn count_isomorphisms_vf2(&self, other: &Graph) -> IgraphResult<u64> {
crate::algorithms::isomorphism::vf2::count_isomorphisms_vf2(
self, other, None, None, None, None,
)
}
pub fn independent_vertex_sets(
&self,
min_size: u32,
max_size: u32,
) -> IgraphResult<Vec<Vec<u32>>> {
crate::algorithms::cliques::independent_vertex_sets(self, min_size, max_size, None)
}
pub fn global_efficiency(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::efficiency::global_efficiency(self)
}
pub fn local_efficiency(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::efficiency::local_efficiency(self)
}
pub fn assortativity_degree(&self) -> IgraphResult<Option<f64>> {
crate::algorithms::properties::assortativity::assortativity_degree(self)
}
pub fn diversity(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::strength::diversity(self, weights)
}
pub fn distances_all(&self) -> IgraphResult<Vec<Option<u32>>> {
crate::algorithms::paths::distances_all::distances_all(self)
}
pub fn distances_from(&self, sources: &[VertexId]) -> IgraphResult<Vec<Option<u32>>> {
crate::algorithms::paths::distances_from::distances_from(self, sources)
}
pub fn get_shortest_paths(&self, source: VertexId) -> IgraphResult<Vec<Vec<VertexId>>> {
crate::algorithms::paths::shortest_paths::get_shortest_paths(self, source)
}
pub fn get_all_shortest_paths(
&self,
source: VertexId,
) -> IgraphResult<crate::AllShortestPaths> {
crate::algorithms::paths::all_shortest_paths::get_all_shortest_paths(self, source)
}
pub fn johnson_distances(&self, weights: &[f64]) -> IgraphResult<Vec<Vec<Option<f64>>>> {
crate::algorithms::paths::johnson::johnson_distances(self, weights)
}
pub fn widest_paths(
&self,
from: VertexId,
weights: &[f64],
) -> IgraphResult<crate::WidestPaths> {
crate::algorithms::paths::widest_path::widest_paths(self, from, weights)
}
pub fn graph_center(&self) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::paths::graph_center::graph_center(
self,
crate::algorithms::paths::radii::EccMode::Out,
)
}
pub fn path_length_hist(&self, directed: bool) -> IgraphResult<crate::PathLengthHistResult> {
crate::algorithms::paths::histogram::path_length_hist(self, directed)
}
pub fn eulerian_cycle(&self) -> IgraphResult<Vec<EdgeId>> {
crate::algorithms::paths::eulerian_construct::eulerian_cycle(self)
}
pub fn hub_and_authority_scores(&self) -> IgraphResult<crate::HitsScores> {
crate::algorithms::properties::hits::hub_and_authority_scores(self)
}
pub fn strength(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::strength::strength(self, weights)
}
pub fn knnk(&self) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::knn::knnk(self)
}
pub fn transitivity_barrat(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::triangles::transitivity_barrat(self, weights)
}
pub fn local_scan_1(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::local_scan::local_scan_1(self, weights)
}
pub fn maximum_cardinality_search(&self) -> IgraphResult<crate::McsResult> {
crate::algorithms::chordality::maximum_cardinality_search(self)
}
pub fn max_degree_vertex(&self) -> IgraphResult<Option<VertexId>> {
crate::algorithms::properties::degree::max_degree_vertex(
self,
crate::algorithms::properties::degree::DegreeMode::All,
)
}
pub fn has_loop(&self) -> IgraphResult<bool> {
crate::algorithms::properties::multiplicity::has_loop(self)
}
pub fn has_mutual(&self, loops: bool) -> IgraphResult<bool> {
crate::algorithms::properties::mutual::has_mutual(self, loops)
}
pub fn is_multiple(&self) -> IgraphResult<Vec<bool>> {
crate::algorithms::properties::multiplicity::is_multiple(self)
}
pub fn is_mutual(&self, loops: bool) -> IgraphResult<Vec<bool>> {
crate::algorithms::properties::mutual::is_mutual(self, loops)
}
pub fn modularity(&self, membership: &[u32], resolution: f64) -> IgraphResult<Option<f64>> {
crate::algorithms::community::modularity::modularity(self, membership, resolution)
}
pub fn mycielskian(&self, k: u32) -> IgraphResult<Graph> {
crate::algorithms::constructors::mycielskian::mycielskian(self, k)
}
pub fn to_prufer(&self) -> IgraphResult<Vec<u32>> {
crate::algorithms::constructors::prufer::to_prufer(self)
}
pub fn bond_percolation(
&self,
edge_order: &[EdgeId],
) -> IgraphResult<crate::EdgelistPercolation> {
crate::algorithms::connectivity::percolation::bond_percolation(self, edge_order)
}
pub fn site_percolation(
&self,
vertex_order: &[VertexId],
) -> IgraphResult<crate::SitePercolation> {
crate::algorithms::connectivity::percolation::site_percolation(self, vertex_order)
}
pub fn reachability(
&self,
mode: crate::ReachabilityMode,
) -> IgraphResult<crate::ReachabilityResult> {
crate::algorithms::connectivity::reachability_scc::reachability(self, mode)
}
pub fn neighborhood_graphs(&self, order: i32) -> IgraphResult<Vec<Graph>> {
crate::algorithms::properties::neighborhood::neighborhood_graphs(self, order)
}
pub fn weighted_clique_number(&self, vertex_weights: &[f64]) -> IgraphResult<f64> {
crate::algorithms::cliques::weighted_clique_number(self, vertex_weights)
}
pub fn isoclass(&self) -> IgraphResult<u32> {
crate::algorithms::motifs::isoclass::isoclass(self)
}
pub fn layout_mds(&self, dist: Option<&[Vec<f64>]>) -> IgraphResult<Vec<[f64; 2]>> {
crate::algorithms::layout::mds::layout_mds(self, dist)
}
pub fn layout_sphere(&self) -> Vec<(f64, f64, f64)> {
crate::algorithms::layout::simple::layout_sphere(self)
}
pub fn louvain_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LouvainResult> {
crate::algorithms::community::louvain::louvain_weighted(self, weights)
}
pub fn leiden_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LeidenResult> {
crate::algorithms::community::leiden::leiden_weighted(self, weights)
}
pub fn label_propagation_weighted(&self, weights: &[f64]) -> IgraphResult<crate::LpaResult> {
crate::algorithms::community::label_propagation::label_propagation_weighted(self, weights)
}
pub fn walktrap_weighted(&self, weights: &[f64]) -> IgraphResult<crate::WalktrapResult> {
crate::algorithms::community::walktrap::walktrap_weighted(self, weights)
}
pub fn diameter_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
crate::algorithms::paths::radii::diameter_weighted(self, weights)
}
pub fn eccentricity_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<f64>> {
crate::algorithms::paths::radii::eccentricity_weighted(self, weights)
}
pub fn radius_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
crate::algorithms::paths::radii::radius_weighted(self, weights)
}
pub fn knnk_weighted(&self, weights: &[f64]) -> IgraphResult<Vec<Option<f64>>> {
crate::algorithms::properties::knn::knnk_weighted(self, weights)
}
pub fn pagerank_linsys(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::pagerank_linsys::pagerank_linsys(self)
}
pub fn local_scan_k(&self, k: u32, weights: Option<&[f64]>) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::local_scan_k::local_scan_k(self, k, weights)
}
pub fn is_loop(&self) -> IgraphResult<Vec<bool>> {
crate::algorithms::properties::multiplicity::is_loop(self)
}
pub fn is_clique(&self, vertices: &[VertexId], directed: bool) -> IgraphResult<bool> {
crate::algorithms::properties::is_clique::is_clique(self, vertices, directed)
}
pub fn is_independent_vertex_set(&self, vertices: &[VertexId]) -> IgraphResult<bool> {
crate::algorithms::properties::is_clique::is_independent_vertex_set(self, vertices)
}
pub fn is_separator(&self, candidates: &[VertexId]) -> IgraphResult<bool> {
crate::algorithms::connectivity::separators::is_separator(self, candidates)
}
pub fn is_minimal_separator(&self, candidates: &[VertexId]) -> IgraphResult<bool> {
crate::algorithms::connectivity::separators::is_minimal_separator(self, candidates)
}
pub fn is_vertex_coloring(&self, colors: &[u32]) -> IgraphResult<bool> {
crate::algorithms::coloring::is_vertex_coloring(self, colors)
}
pub fn is_edge_coloring(&self, colors: &[u32]) -> IgraphResult<bool> {
crate::algorithms::coloring::is_edge_coloring(self, colors)
}
pub fn is_vertex_cover(&self, cover: &[VertexId]) -> bool {
crate::algorithms::vertex_cover::is_vertex_cover(self, cover)
}
pub fn is_edge_cover(&self, cover: &[EdgeId]) -> bool {
crate::algorithms::edge_cover::is_edge_cover(self, cover)
}
pub fn is_dominating_set(&self, dom_set: &[VertexId]) -> bool {
crate::algorithms::dominating_set::is_dominating_set(self, dom_set)
}
pub fn reverse_edges(&self, eids: &[u32]) -> IgraphResult<Graph> {
crate::algorithms::operators::reverse::reverse_edges(self, eids)
}
pub fn induced_subgraph_edges(&self, vids: &[u32]) -> IgraphResult<Vec<u32>> {
crate::algorithms::operators::induced_subgraph_edges::induced_subgraph_edges(self, vids)
}
pub fn similarity_dice(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::similarity::similarity_dice(self)
}
pub fn similarity_inverse_log_weighted(&self) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::similarity::similarity_inverse_log_weighted(self)
}
pub fn similarity_jaccard_es(&self, eids: &[u32]) -> IgraphResult<Vec<f64>> {
crate::algorithms::properties::similarity::similarity_jaccard_es(self, eids)
}
pub fn layout_lgl(&self, params: &crate::LglParams) -> IgraphResult<Vec<[f64; 2]>> {
crate::algorithms::layout::lgl::layout_lgl(self, params)
}
pub fn layout_random_3d(&self, seed: u64) -> Vec<(f64, f64, f64)> {
crate::algorithms::layout::simple::layout_random_3d(self, seed)
}
pub fn layout_grid_3d(&self, width: i32, height: i32) -> Vec<(f64, f64, f64)> {
crate::algorithms::layout::simple::layout_grid_3d(self, width, height)
}
pub fn motifs_randesu_no(&self, size: u32) -> IgraphResult<f64> {
crate::algorithms::motifs::motifs_randesu::motifs_randesu_no(self, size)
}
pub fn graph_summary(&self) -> IgraphResult<crate::GraphSummary> {
crate::algorithms::properties::summary::graph_summary(self)
}
pub fn graph_summary_string(&self) -> IgraphResult<String> {
crate::graph_summary_string(self)
}
pub fn get_adjacency(
&self,
adj_type: crate::AdjacencyType,
loops: crate::LoopHandling,
) -> IgraphResult<Vec<Vec<f64>>> {
crate::algorithms::properties::adjacency::get_adjacency(self, adj_type, None, loops)
}
pub fn get_adjacency_weighted(
&self,
adj_type: crate::AdjacencyType,
weights: &[f64],
loops: crate::LoopHandling,
) -> IgraphResult<Vec<Vec<f64>>> {
crate::algorithms::properties::adjacency::get_adjacency(
self,
adj_type,
Some(weights),
loops,
)
}
pub fn get_laplacian(&self) -> IgraphResult<Vec<Vec<f64>>> {
crate::algorithms::properties::laplacian::get_laplacian(
self,
crate::DegreeMode::All,
crate::LaplacianNormalization::Unnormalized,
None,
)
}
pub fn get_laplacian_full(
&self,
mode: crate::DegreeMode,
normalization: crate::LaplacianNormalization,
weights: Option<&[f64]>,
) -> IgraphResult<Vec<Vec<f64>>> {
crate::algorithms::properties::laplacian::get_laplacian(self, mode, normalization, weights)
}
pub fn get_stochastic(&self, column_wise: bool) -> IgraphResult<Vec<Vec<f64>>> {
crate::algorithms::properties::stochastic::get_stochastic(self, column_wise, None)
}
pub fn adjacency_spectral_embedding(
&self,
no: usize,
which: crate::SpectralWhich,
) -> IgraphResult<crate::AdjacencySpectralEmbeddingResult> {
crate::algorithms::embedding::adjacency_spectral_embedding::adjacency_spectral_embedding(
self, no, None, which, true, None,
)
}
pub fn laplacian_spectral_embedding(
&self,
no: usize,
which: crate::SpectralWhich,
lap_type: crate::LaplacianType,
) -> IgraphResult<crate::LaplacianSpectralEmbeddingResult> {
crate::algorithms::embedding::laplacian_spectral_embedding::laplacian_spectral_embedding(
self, no, None, which, lap_type, true,
)
}
pub fn eigen_adjacency(
&self,
nev: usize,
which: crate::EigenWhich,
) -> IgraphResult<crate::EigenDecomposition> {
crate::algorithms::eigen::adjacency::eigen_adjacency(self, nev, which)
}
pub fn feedback_vertex_set(&self, algo: crate::FvsAlgorithm) -> IgraphResult<Vec<VertexId>> {
crate::algorithms::feedback_vertex_set::feedback_vertex_set(self, None, algo)
}
pub fn complementer(&self) -> IgraphResult<Graph> {
crate::algorithms::operators::complementer::complementer(self, false)
}
pub fn bipartite_projection_size(
&self,
types: &[bool],
) -> IgraphResult<crate::BipartiteProjectionSize> {
crate::algorithms::operators::bipartite_projection_size::bipartite_projection_size(
self, types,
)
}
pub fn unfold_tree(
&self,
roots: &[VertexId],
mode: crate::DegreeMode,
) -> IgraphResult<crate::UnfoldTreeResult> {
crate::algorithms::properties::unfold_tree::unfold_tree(self, roots, mode)
}
pub fn st_edge_connectivity(&self, source: u32, target: u32) -> IgraphResult<i64> {
crate::st_edge_connectivity(self, source, target)
}
pub fn vertex_disjoint_paths(&self, source: u32, target: u32) -> IgraphResult<i64> {
crate::vertex_disjoint_paths(self, source, target)
}
pub fn isomorphic_bliss(
&self,
other: &Graph,
colors1: Option<&[u32]>,
colors2: Option<&[u32]>,
) -> IgraphResult<crate::Vf2Isomorphism> {
crate::isomorphic_bliss(self, other, colors1, colors2)
}
pub fn subisomorphic_lad(
&self,
pattern: &Graph,
induced: bool,
domains: Option<&[Vec<u32>]>,
) -> IgraphResult<crate::LadSubisomorphism> {
crate::subisomorphic_lad(pattern, self, domains, induced)
}
pub fn betweenness_subset(&self, sources: &[u32], targets: &[u32]) -> IgraphResult<Vec<f64>> {
crate::betweenness_subset(self, sources, targets, self.is_directed())
}
pub fn edge_betweenness_subset(
&self,
sources: &[u32],
targets: &[u32],
) -> IgraphResult<Vec<f64>> {
crate::edge_betweenness_subset(self, sources, targets, self.is_directed())
}
pub fn edge_betweenness_community_weighted(
&self,
weights: &[f64],
) -> IgraphResult<crate::EdgeBetweennessResult> {
crate::edge_betweenness_community_weighted(self, weights)
}
pub fn modularity_matrix(&self, weights: Option<&[f64]>) -> IgraphResult<Vec<Vec<f64>>> {
crate::modularity_matrix(self, weights, 1.0, self.is_directed())
}
pub fn is_same_graph(&self, other: &Graph) -> bool {
crate::is_same_graph(self, other)
}
pub fn mean_distance_weighted(&self, weights: &[f64]) -> IgraphResult<Option<f64>> {
crate::mean_distance_weighted(self, weights, self.is_directed(), true)
}
pub fn set_graph_attribute(&mut self, name: impl Into<String>, value: AttributeValue) {
self.gattrs.insert(name.into(), value);
}
#[must_use]
pub fn graph_attribute(&self, name: &str) -> Option<&AttributeValue> {
self.gattrs.get(name)
}
pub fn delete_graph_attribute(&mut self, name: &str) -> bool {
self.gattrs.remove(name).is_some()
}
#[must_use]
pub fn has_graph_attribute(&self, name: &str) -> bool {
self.gattrs.contains_key(name)
}
#[must_use]
pub fn graph_attribute_names(&self) -> Vec<&str> {
self.gattrs.keys().map(String::as_str).collect()
}
pub fn set_vertex_attribute(
&mut self,
name: impl Into<String>,
vertex: VertexId,
value: AttributeValue,
) -> IgraphResult<()> {
self.check_vertex(vertex)?;
let n = self.n as usize;
let key = name.into();
let vals = self.vertex_attrs.entry(key).or_insert_with(|| {
let default = value.default_for_same_type();
vec![default; n]
});
vals[vertex as usize] = value;
Ok(())
}
pub fn set_vertex_attribute_all(
&mut self,
name: impl Into<String>,
values: Vec<AttributeValue>,
) -> IgraphResult<()> {
if values.len() != self.n as usize {
return Err(IgraphError::InvalidArgument(format!(
"attribute vector length {} does not match vcount {}",
values.len(),
self.n,
)));
}
self.vertex_attrs.insert(name.into(), values);
Ok(())
}
#[must_use]
pub fn vertex_attribute(&self, name: &str, vertex: VertexId) -> Option<&AttributeValue> {
self.vertex_attrs
.get(name)
.and_then(|vals| vals.get(vertex as usize))
}
#[must_use]
pub fn vertex_attributes(&self, name: &str) -> Option<&[AttributeValue]> {
self.vertex_attrs.get(name).map(Vec::as_slice)
}
pub fn delete_vertex_attribute(&mut self, name: &str) -> bool {
self.vertex_attrs.remove(name).is_some()
}
#[must_use]
pub fn has_vertex_attribute(&self, name: &str) -> bool {
self.vertex_attrs.contains_key(name)
}
#[must_use]
pub fn vertex_attribute_names(&self) -> Vec<&str> {
self.vertex_attrs.keys().map(String::as_str).collect()
}
pub fn set_edge_attribute(
&mut self,
name: impl Into<String>,
edge: EdgeId,
value: AttributeValue,
) -> IgraphResult<()> {
let m = self.ecount();
if (edge as usize) >= m {
return Err(IgraphError::EdgeOutOfRange {
id: edge,
m: u32::try_from(m).unwrap_or(u32::MAX),
});
}
let key = name.into();
let vals = self.edge_attrs.entry(key).or_insert_with(|| {
let default = value.default_for_same_type();
vec![default; m]
});
vals[edge as usize] = value;
Ok(())
}
pub fn set_edge_attribute_all(
&mut self,
name: impl Into<String>,
values: Vec<AttributeValue>,
) -> IgraphResult<()> {
let m = self.ecount();
if values.len() != m {
return Err(IgraphError::InvalidArgument(format!(
"attribute vector length {} does not match ecount {}",
values.len(),
m,
)));
}
self.edge_attrs.insert(name.into(), values);
Ok(())
}
#[must_use]
pub fn edge_attribute(&self, name: &str, edge: EdgeId) -> Option<&AttributeValue> {
self.edge_attrs
.get(name)
.and_then(|vals| vals.get(edge as usize))
}
#[must_use]
pub fn edge_attributes(&self, name: &str) -> Option<&[AttributeValue]> {
self.edge_attrs.get(name).map(Vec::as_slice)
}
pub fn delete_edge_attribute(&mut self, name: &str) -> bool {
self.edge_attrs.remove(name).is_some()
}
#[must_use]
pub fn has_edge_attribute(&self, name: &str) -> bool {
self.edge_attrs.contains_key(name)
}
#[must_use]
pub fn edge_attribute_names(&self) -> Vec<&str> {
self.edge_attrs.keys().map(String::as_str).collect()
}
}
impl std::fmt::Display for Graph {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let kind = if self.directed {
"Directed"
} else {
"Undirected"
};
write!(
f,
"{kind} graph with {} vertices and {} edges",
self.n,
self.ecount()
)
}
}
impl<'a> IntoIterator for &'a Graph {
type Item = (VertexId, VertexId);
type IntoIter = EdgeIter<'a>;
fn into_iter(self) -> Self::IntoIter {
self.iter()
}
}
impl TryFrom<&[(VertexId, VertexId)]> for Graph {
type Error = IgraphError;
fn try_from(edges: &[(VertexId, VertexId)]) -> IgraphResult<Self> {
let n = match edges.iter().flat_map(|&(u, v)| [u, v]).max() {
Some(m) => m
.checked_add(1)
.ok_or_else(|| IgraphError::InvalidArgument("vertex id overflow".to_owned()))?,
None => 0,
};
let mut g = Self::new(n, false)?;
g.add_edges(edges.to_vec())?;
Ok(g)
}
}
impl TryFrom<Vec<(VertexId, VertexId)>> for Graph {
type Error = IgraphError;
fn try_from(edges: Vec<(VertexId, VertexId)>) -> IgraphResult<Self> {
let n = match edges.iter().flat_map(|&(u, v)| [u, v]).max() {
Some(m) => m
.checked_add(1)
.ok_or_else(|| IgraphError::InvalidArgument("vertex id overflow".to_owned()))?,
None => 0,
};
let mut g = Self::new(n, false)?;
g.add_edges(edges)?;
Ok(g)
}
}
impl std::iter::FromIterator<(VertexId, VertexId)> for Graph {
fn from_iter<I: IntoIterator<Item = (VertexId, VertexId)>>(iter: I) -> Self {
let edges: Vec<(VertexId, VertexId)> = iter.into_iter().collect();
Self::try_from(edges).expect("FromIterator: vertex id overflow or invalid edge")
}
}
impl Extend<(VertexId, VertexId)> for Graph {
fn extend<I: IntoIterator<Item = (VertexId, VertexId)>>(&mut self, iter: I) {
let edges: Vec<(VertexId, VertexId)> = iter.into_iter().collect();
if edges.is_empty() {
return;
}
let max_id = edges
.iter()
.flat_map(|&(u, v)| [u, v])
.max()
.expect("non-empty edges");
if max_id >= self.n {
self.add_vertices(max_id - self.n + 1)
.expect("Extend: failed to add vertices");
}
self.add_edges(edges).expect("Extend: failed to add edges");
}
}
impl PartialEq for Graph {
fn eq(&self, other: &Self) -> bool {
if self.directed != other.directed || self.n != other.n || self.ecount() != other.ecount() {
return false;
}
let mut self_edges: Vec<(VertexId, VertexId)> = self.iter().collect();
let mut other_edges: Vec<(VertexId, VertexId)> = other.iter().collect();
self_edges.sort_unstable();
other_edges.sort_unstable();
self_edges == other_edges
}
}
impl Eq for Graph {}
impl std::hash::Hash for Graph {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.directed.hash(state);
self.n.hash(state);
let mut edges: Vec<(VertexId, VertexId)> = self.iter().collect();
edges.sort_unstable();
edges.hash(state);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_graph_counts() {
let g = Graph::with_vertices(0);
assert_eq!(g.vcount(), 0);
assert_eq!(g.ecount(), 0);
assert!(!g.is_directed());
}
#[test]
fn new_directed_flag() {
let g = Graph::new(3, true).unwrap();
assert!(g.is_directed());
let g = Graph::new(3, false).unwrap();
assert!(!g.is_directed());
}
#[test]
fn add_vertices_then_edges() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 2);
assert_eq!(g.degree(1).unwrap(), 2);
let mut nbrs = g.neighbors(1).unwrap();
nbrs.sort_unstable();
assert_eq!(nbrs, vec![0, 2]);
}
#[test]
fn out_of_range_vertex_errors() {
let mut g = Graph::with_vertices(2);
let err = g.add_edge(0, 5).unwrap_err();
assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 2 }));
}
#[test]
fn self_loop_counted_correctly() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
assert_eq!(g.ecount(), 1);
assert_eq!(g.degree(0).unwrap(), 2);
let mut nbrs = g.neighbors(0).unwrap();
nbrs.sort_unstable();
assert_eq!(nbrs, vec![0, 0]);
}
#[test]
fn parallel_edges() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
g.add_edge(0, 1).unwrap();
assert_eq!(g.ecount(), 2);
assert_eq!(g.degree(0).unwrap(), 2);
assert_eq!(g.degree(1).unwrap(), 2);
}
#[test]
fn undirected_canonicalisation() {
let mut g = Graph::with_vertices(2);
g.add_edge(1, 0).unwrap();
g.add_edge(0, 1).unwrap();
assert_eq!(g.ecount(), 2);
let mut n0 = g.neighbors(0).unwrap();
let mut n1 = g.neighbors(1).unwrap();
n0.sort_unstable();
n1.sort_unstable();
assert_eq!(n0, vec![1, 1]);
assert_eq!(n1, vec![0, 0]);
}
#[test]
fn directed_neighbors_are_outgoing_only() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(2, 0).unwrap();
assert_eq!(g.neighbors(0).unwrap(), vec![1]);
assert_eq!(g.neighbors(2).unwrap(), vec![0]);
assert!(g.neighbors(1).unwrap().is_empty());
assert_eq!(g.degree(0).unwrap(), 2); assert_eq!(g.degree(1).unwrap(), 1); assert_eq!(g.degree(2).unwrap(), 1); }
#[test]
fn add_edges_batch_then_rebuild() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0, 1), (0, 2), (1, 2), (2, 3)]).unwrap();
assert_eq!(g.ecount(), 4);
assert_eq!(g.degree(0).unwrap(), 2);
assert_eq!(g.degree(1).unwrap(), 2);
assert_eq!(g.degree(2).unwrap(), 3);
assert_eq!(g.degree(3).unwrap(), 1);
}
#[test]
fn clone_is_deep() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
let g2 = g.clone();
g.add_edge(0, 2).unwrap();
assert_eq!(g.ecount(), 3);
assert_eq!(g2.ecount(), 2);
}
#[test]
fn os_invariant_is_monotone() {
let mut g = Graph::with_vertices(5);
g.add_edges(vec![(0, 1), (0, 2), (3, 4), (1, 2)]).unwrap();
for w in g.os.windows(2) {
assert!(w[0] <= w[1]);
}
assert_eq!(g.os[0], 0);
assert_eq!(*g.os.last().unwrap() as usize, g.ecount());
}
#[test]
fn vertex_out_of_range_when_adding_edge() {
let mut g = Graph::with_vertices(2);
let e = g.add_edge(2, 0).unwrap_err();
assert!(matches!(e, IgraphError::VertexOutOfRange { id: 2, n: 2 }));
assert_eq!(g.ecount(), 0);
}
#[test]
fn edge_endpoints_round_trip() {
let mut g = Graph::new(3, true).unwrap();
g.add_edges(vec![(0, 1), (2, 0), (1, 2)]).unwrap();
assert_eq!(g.edge(0).unwrap(), (0, 1));
assert_eq!(g.edge(1).unwrap(), (2, 0));
assert_eq!(g.edge(2).unwrap(), (1, 2));
assert_eq!(g.edge_source(1).unwrap(), 2);
assert_eq!(g.edge_target(1).unwrap(), 0);
}
#[test]
fn edge_other_endpoint() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 2).unwrap();
assert_eq!(g.edge_other(0, 0).unwrap(), 2);
assert_eq!(g.edge_other(0, 2).unwrap(), 0);
let err = g.edge_other(0, 1).unwrap_err();
assert!(matches!(err, IgraphError::InvalidArgument(_)));
}
#[test]
fn edge_out_of_range() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap();
let err = g.edge(5).unwrap_err();
assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 1 }));
}
#[test]
fn incident_returns_edge_ids_matching_neighbors_order() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0, 1), (0, 2), (3, 0)]).unwrap();
let eids = g.incident(0).unwrap();
let resolved: Vec<u32> = eids.iter().map(|&e| g.edge_other(e, 0).unwrap()).collect();
assert_eq!(resolved, g.neighbors(0).unwrap());
}
#[test]
fn incident_self_loop_appears_twice_undirected() {
let mut g = Graph::with_vertices(1);
g.add_edge(0, 0).unwrap();
let eids = g.incident(0).unwrap();
assert_eq!(eids, vec![0, 0]);
assert_eq!(g.degree(0).unwrap(), 2);
}
#[test]
fn incident_directed_returns_outgoing_only() {
let mut g = Graph::new(3, true).unwrap();
g.add_edges(vec![(0, 1), (2, 0)]).unwrap();
assert_eq!(g.incident(0).unwrap(), vec![0]);
assert_eq!(g.incident(2).unwrap(), vec![1]);
assert!(g.incident(1).unwrap().is_empty());
}
#[test]
fn get_eid_undirected_finds_edge_either_way() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
assert_eq!(g.get_eid(1, 0).unwrap(), 0);
assert_eq!(g.get_eid(1, 2).unwrap(), 1);
assert_eq!(g.get_eid(2, 1).unwrap(), 1);
}
#[test]
fn get_eid_directed_respects_direction() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
assert!(g.get_eid(1, 0).is_err()); }
#[test]
fn find_eid_returns_none_for_missing() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
assert_eq!(g.find_eid(0, 2).unwrap(), None);
assert!(g.find_eid(0, 99).is_err()); }
#[test]
fn get_eid_self_loop() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 0).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 0).unwrap(), 0);
assert_eq!(g.get_eid(0, 1).unwrap(), 1);
}
#[test]
fn get_all_eids_between_returns_all_parallel() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); let eids = g.get_all_eids_between(0, 1).unwrap();
assert_eq!(eids, vec![0, 1, 2]);
let eids = g.get_all_eids_between(1, 0).unwrap();
assert_eq!(eids, vec![0, 1, 2]);
}
#[test]
fn get_all_eids_between_directed_one_way_only() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_all_eids_between(0, 1).unwrap(), vec![0, 1]);
assert_eq!(g.get_all_eids_between(1, 0).unwrap(), Vec::<EdgeId>::new());
}
#[test]
fn get_eid_returns_lowest_id_for_parallel() {
let mut g = Graph::with_vertices(2);
g.add_edge(0, 1).unwrap(); g.add_edge(0, 1).unwrap(); assert_eq!(g.get_eid(0, 1).unwrap(), 0);
}
#[test]
fn delete_edges_empty_input_is_noop() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
g.delete_edges(&[]).unwrap();
assert_eq!(g.ecount(), 2);
assert_eq!(g.degree(1).unwrap(), 2);
}
#[test]
fn delete_edges_single_edge_undirected() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2), (0, 2)]).unwrap();
g.delete_edges(&[1]).unwrap();
assert_eq!(g.ecount(), 2);
assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
assert_eq!(g.find_eid(0, 2).unwrap(), Some(1));
assert_eq!(g.find_eid(1, 2).unwrap(), None);
assert_eq!(g.degree(1).unwrap(), 1);
assert_eq!(g.degree(2).unwrap(), 1);
}
#[test]
fn delete_edges_duplicate_ids_tolerated() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
g.delete_edges(&[0, 0, 0]).unwrap();
assert_eq!(g.ecount(), 1);
assert_eq!(g.find_eid(1, 2).unwrap(), Some(0));
}
#[test]
fn delete_edges_all_edges_leaves_isolated_vertices() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
g.delete_edges(&[0, 1]).unwrap();
assert_eq!(g.ecount(), 0);
assert_eq!(g.vcount(), 3);
for v in 0..3 {
assert_eq!(g.degree(v).unwrap(), 0);
}
}
#[test]
fn delete_edges_out_of_range_errors_and_preserves_state() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
let err = g.delete_edges(&[5]).unwrap_err();
assert!(matches!(err, IgraphError::EdgeOutOfRange { id: 5, m: 2 }));
assert_eq!(g.ecount(), 2);
}
#[test]
fn delete_edges_self_loop_directed() {
let mut g = Graph::new(2, true).unwrap();
g.add_edges(vec![(0, 0), (0, 1)]).unwrap();
g.delete_edges(&[0]).unwrap(); assert_eq!(g.ecount(), 1);
assert_eq!(g.degree(0).unwrap(), 1);
assert_eq!(g.find_eid(0, 1).unwrap(), Some(0));
}
#[test]
fn delete_vertices_empty_input_is_noop() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
g.delete_vertices(&[]).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 2);
}
#[test]
fn delete_vertices_single_renumbers() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0, 1), (1, 2), (2, 3), (0, 3)]).unwrap();
g.delete_vertices(&[1]).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 2);
assert!(g.find_eid(1, 2).unwrap().is_some());
assert!(g.find_eid(0, 2).unwrap().is_some());
assert_eq!(g.find_eid(0, 1).unwrap(), None);
}
#[test]
fn delete_vertices_duplicate_ids_tolerated() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
g.delete_vertices(&[1, 1, 1]).unwrap();
assert_eq!(g.vcount(), 2);
assert_eq!(g.ecount(), 0);
}
#[test]
fn delete_vertices_all_yields_empty_graph() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
g.delete_vertices(&[0, 1, 2]).unwrap();
assert_eq!(g.vcount(), 0);
assert_eq!(g.ecount(), 0);
}
#[test]
fn delete_vertices_out_of_range_errors_and_preserves_state() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
let err = g.delete_vertices(&[5]).unwrap_err();
assert!(matches!(err, IgraphError::VertexOutOfRange { id: 5, n: 3 }));
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 2);
}
#[test]
fn delete_vertices_map_returns_correct_mappings() {
let mut g = Graph::with_vertices(5);
g.add_edges(vec![(0, 1), (1, 2), (2, 3), (3, 4)]).unwrap();
let (map, invmap) = g.delete_vertices_map(&[1, 3]).unwrap();
assert_eq!(map, vec![Some(0), None, Some(1), None, Some(2)]);
assert_eq!(invmap, vec![0, 2, 4]);
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 0);
}
#[test]
fn delete_vertices_directed_preserves_direction() {
let mut g = Graph::new(4, true).unwrap();
g.add_edges(vec![(0, 1), (1, 2), (2, 0), (3, 0)]).unwrap();
g.delete_vertices(&[3]).unwrap();
assert_eq!(g.vcount(), 3);
assert!(g.is_directed());
assert!(g.get_eid(0, 1).is_ok());
assert!(g.get_eid(1, 0).is_err()); }
#[test]
fn delete_vertices_self_loop_on_removed_vertex() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 0), (0, 1), (1, 2)]).unwrap();
g.delete_vertices(&[0]).unwrap();
assert_eq!(g.vcount(), 2);
assert_eq!(g.ecount(), 1);
assert!(g.find_eid(0, 1).unwrap().is_some());
}
#[test]
fn delete_vertices_preserves_parallel_edges() {
let mut g = Graph::with_vertices(3);
g.add_edges(vec![(0, 1), (0, 1), (1, 2)]).unwrap();
g.delete_vertices(&[2]).unwrap();
assert_eq!(g.vcount(), 2);
assert_eq!(g.ecount(), 2); assert_eq!(g.degree(0).unwrap(), 2);
assert_eq!(g.degree(1).unwrap(), 2);
}
#[test]
fn add_edges_after_delete_works() {
let mut g = Graph::with_vertices(4);
g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
g.delete_vertices(&[0]).unwrap(); g.add_edge(0, 2).unwrap();
assert_eq!(g.ecount(), 3);
assert_eq!(g.degree(0).unwrap(), 2); assert!(g.find_eid(0, 2).unwrap().is_some());
}
#[test]
fn from_adjacency_matrix_undirected_triangle() {
let adj = vec![
vec![0.0, 1.0, 1.0],
vec![1.0, 0.0, 1.0],
vec![1.0, 1.0, 0.0],
];
let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 3);
assert!(!g.is_directed());
}
#[test]
fn from_adjacency_matrix_directed() {
let adj = vec![
vec![0.0, 1.0, 0.0],
vec![0.0, 0.0, 1.0],
vec![1.0, 0.0, 0.0],
];
let g = Graph::from_adjacency_matrix(&adj, true).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 3);
assert!(g.is_directed());
}
#[test]
fn from_adjacency_matrix_with_self_loop() {
let adj = vec![vec![1.0, 1.0], vec![1.0, 0.0]];
let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
assert_eq!(g.vcount(), 2);
assert_eq!(g.ecount(), 2); }
#[test]
fn from_adjacency_matrix_empty() {
let adj: Vec<Vec<f64>> = Vec::new();
let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
assert_eq!(g.vcount(), 0);
assert_eq!(g.ecount(), 0);
}
#[test]
fn from_adjacency_matrix_non_square_error() {
let adj = vec![vec![0.0, 1.0], vec![1.0, 0.0, 1.0]];
assert!(Graph::from_adjacency_matrix(&adj, false).is_err());
}
#[test]
fn from_adjacency_matrix_weighted_basic() {
let adj = vec![
vec![0.0, 2.5, 0.0],
vec![2.5, 0.0, 1.0],
vec![0.0, 1.0, 0.0],
];
let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, false).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 2);
assert_eq!(weights.len(), 2);
assert!((weights[0] - 2.5).abs() < 1e-10);
assert!((weights[1] - 1.0).abs() < 1e-10);
}
#[test]
fn from_adjacency_matrix_weighted_directed() {
let adj = vec![
vec![0.0, 3.0, 0.0],
vec![0.0, 0.0, 2.0],
vec![1.5, 0.0, 0.0],
];
let (g, weights) = Graph::from_adjacency_matrix_weighted(&adj, true).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 3);
assert_eq!(weights.len(), 3);
assert!((weights[0] - 3.0).abs() < 1e-10);
assert!((weights[1] - 2.0).abs() < 1e-10);
assert!((weights[2] - 1.5).abs() < 1e-10);
}
#[test]
fn from_adjacency_matrix_multi_edges() {
let adj = vec![vec![0.0, 3.0], vec![3.0, 0.0]];
let g = Graph::from_adjacency_matrix(&adj, false).unwrap();
assert_eq!(g.vcount(), 2);
assert_eq!(g.ecount(), 3); }
#[test]
fn from_adjacency_list_undirected_triangle() {
let adj = vec![vec![1, 2], vec![0, 2], vec![0, 1]];
let g = Graph::from_adjacency_list(&adj, false).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 3);
assert!(!g.is_directed());
}
#[test]
fn from_adjacency_list_directed() {
let adj = vec![vec![1, 2], vec![2], vec![]];
let g = Graph::from_adjacency_list(&adj, true).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 3);
assert!(g.is_directed());
}
#[test]
fn from_adjacency_list_empty() {
let adj: Vec<Vec<u32>> = Vec::new();
let g = Graph::from_adjacency_list(&adj, false).unwrap();
assert_eq!(g.vcount(), 0);
assert_eq!(g.ecount(), 0);
}
#[test]
fn from_adjacency_list_isolated_vertices() {
let adj = vec![vec![], vec![], vec![]];
let g = Graph::from_adjacency_list(&adj, false).unwrap();
assert_eq!(g.vcount(), 3);
assert_eq!(g.ecount(), 0);
}
#[test]
fn from_adjacency_list_self_loop() {
let adj = vec![vec![0, 1], vec![0]];
let g = Graph::from_adjacency_list(&adj, false).unwrap();
assert_eq!(g.vcount(), 2);
assert_eq!(g.ecount(), 2); }
#[test]
fn from_adjacency_list_out_of_range_error() {
let adj = vec![vec![5]]; assert!(Graph::from_adjacency_list(&adj, false).is_err());
}
#[test]
fn neighbors_iter_matches_neighbors_undirected() {
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)], false, None).unwrap();
for v in 0..g.vcount() {
let from_vec = g.neighbors(v).unwrap();
let from_iter: Vec<VertexId> = g.neighbors_iter(v).unwrap().collect();
assert_eq!(from_vec, from_iter, "mismatch at vertex {v}");
}
}
#[test]
fn neighbors_iter_matches_neighbors_directed() {
let g = Graph::from_edges(&[(0, 1), (0, 2), (1, 3), (2, 3), (3, 4)], true, None).unwrap();
for v in 0..g.vcount() {
let from_vec = g.neighbors(v).unwrap();
let from_iter: Vec<VertexId> = g.neighbors_iter(v).unwrap().collect();
assert_eq!(from_vec, from_iter, "mismatch at vertex {v}");
}
}
#[test]
fn neighbors_iter_exact_size() {
let g = Graph::from_edges(&[(0, 1), (0, 2), (0, 3)], false, None).unwrap();
let iter = g.neighbors_iter(0).unwrap();
assert_eq!(iter.len(), 3);
}
#[test]
fn neighbors_iter_invalid_vertex() {
let g = Graph::with_vertices(3);
assert!(g.neighbors_iter(5).is_err());
}
}