#[derive(Debug, Clone)]
pub struct GraphData {
pub(crate) n: usize,
pub(crate) out_offsets: Vec<usize>,
pub(crate) out_targets: Vec<usize>,
pub(crate) out_weights: Vec<f64>,
pub(crate) total_weight: f64,
pub(crate) total_node_weight: f64,
pub(crate) out_degree: Vec<f64>,
pub(crate) node_weight: Vec<f64>,
pub(crate) directed: bool,
pub(crate) in_offsets: Vec<usize>,
pub(crate) in_targets: Vec<usize>,
pub(crate) in_weights: Vec<f64>,
pub(crate) in_degree: Vec<f64>,
}
impl GraphData {
#[inline]
pub fn node_count(&self) -> usize {
self.n
}
#[inline]
pub fn total_weight(&self) -> f64 {
self.total_weight
}
#[inline]
pub fn total_node_weight(&self) -> f64 {
self.total_node_weight
}
#[inline]
pub fn is_directed(&self) -> bool {
self.directed
}
#[inline]
pub fn neighbors(&self, node: usize) -> impl Iterator<Item = (usize, f64)> + '_ {
let (targets, weights) = self.neighbor_slices(node);
targets
.iter()
.zip(weights.iter())
.map(|(&t, &w)| (t, w))
}
#[inline]
pub fn neighbor_slices(&self, node: usize) -> (&[usize], &[f64]) {
if node >= self.n {
return (&[], &[]);
}
let start = self.out_offsets[node];
let end = self.out_offsets[node + 1];
(&self.out_targets[start..end], &self.out_weights[start..end])
}
#[inline]
pub fn degree_of(&self, node: usize) -> f64 {
if node >= self.n {
return 0.0;
}
if self.directed {
self.out_degree[node] + self.in_degree[node]
} else {
self.out_degree[node]
}
}
#[inline]
pub fn node_weight(&self, node: usize) -> f64 {
if node >= self.n {
return 0.0;
}
self.node_weight[node]
}
#[inline]
pub fn out_neighbors(&self, node: usize) -> impl Iterator<Item = (usize, f64)> + '_ {
let (targets, weights) = self.out_neighbor_slices(node);
targets
.iter()
.zip(weights.iter())
.map(|(&t, &w)| (t, w))
}
#[inline]
pub fn out_neighbor_slices(&self, node: usize) -> (&[usize], &[f64]) {
if node >= self.n {
return (&[], &[]);
}
let start = self.out_offsets[node];
let end = self.out_offsets[node + 1];
(&self.out_targets[start..end], &self.out_weights[start..end])
}
#[inline]
pub fn out_degree_of(&self, node: usize) -> f64 {
if node >= self.n {
return 0.0;
}
self.out_degree[node]
}
#[inline]
pub fn in_neighbors(&self, node: usize) -> impl Iterator<Item = (usize, f64)> + '_ {
let (targets, weights) = self.in_neighbor_slices(node);
targets.iter().zip(weights.iter()).map(|(&t, &w)| (t, w))
}
#[inline]
pub fn in_neighbor_slices(&self, node: usize) -> (&[usize], &[f64]) {
if self.directed && node < self.in_offsets.len() - 1 {
let start = self.in_offsets[node];
let end = self.in_offsets[node + 1];
(&self.in_targets[start..end], &self.in_weights[start..end])
} else {
(&[], &[])
}
}
#[inline]
pub fn in_degree_of(&self, node: usize) -> f64 {
if self.directed && node < self.in_degree.len() {
self.in_degree[node]
} else {
0.0
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::GraphDataBuilder;
fn make_graph() -> GraphData {
let mut b = GraphDataBuilder::new(3);
b.add_edge(0, 1, 1.0).unwrap();
b.add_edge(1, 2, 2.0).unwrap();
b.build().unwrap()
}
#[test]
fn in_neighbors_ok_on_oob_node() {
let g = make_graph();
let v: Vec<_> = g.in_neighbors(3).collect();
assert!(v.is_empty());
}
#[test]
fn in_neighbor_slices_ok_on_oob_node() {
let g = make_graph();
let (t, w) = g.in_neighbor_slices(3);
assert!(t.is_empty());
assert!(w.is_empty());
}
#[test]
fn in_degree_of_ok_on_oob_node() {
let g = make_graph();
assert_eq!(g.in_degree_of(3), 0.0);
}
#[test]
fn neighbors_returns_empty_for_oob() {
let g = make_graph();
let v: Vec<_> = g.neighbors(3).collect();
assert!(v.is_empty());
let v2: Vec<_> = g.neighbors(usize::MAX).collect();
assert!(v2.is_empty());
}
#[test]
fn neighbor_slices_returns_empty_for_oob() {
let g = make_graph();
let (t, w) = g.neighbor_slices(3);
assert!(t.is_empty());
assert!(w.is_empty());
}
#[test]
fn degree_of_returns_zero_for_oob() {
let g = make_graph();
assert_eq!(g.degree_of(3), 0.0);
}
#[test]
fn node_weight_returns_zero_for_oob() {
let g = make_graph();
assert_eq!(g.node_weight(3), 0.0);
}
#[test]
fn out_neighbors_returns_empty_for_oob() {
let g = make_graph();
let v: Vec<_> = g.out_neighbors(3).collect();
assert!(v.is_empty());
}
#[test]
fn out_neighbor_slices_returns_empty_for_oob() {
let g = make_graph();
let (t, w) = g.out_neighbor_slices(3);
assert!(t.is_empty());
assert!(w.is_empty());
}
#[test]
fn out_degree_of_returns_zero_for_oob() {
let g = make_graph();
assert_eq!(g.out_degree_of(3), 0.0);
}
#[test]
fn valid_neighbors_still_work() {
let g = make_graph();
let v: Vec<_> = g.neighbors(0).collect();
assert_eq!(v, vec![(1, 1.0)]);
let v2: Vec<_> = g.neighbors(1).collect();
assert_eq!(v2.len(), 2);
}
#[test]
fn valid_neighbor_slices_still_work() {
let g = make_graph();
let (t, w) = g.neighbor_slices(0);
assert_eq!(t.len(), 1);
assert_eq!(w.len(), 1);
}
#[test]
fn valid_degree_of_still_works() {
let g = make_graph();
assert_eq!(g.degree_of(0), 1.0);
}
#[test]
fn valid_node_weight_still_works() {
let g = make_graph();
assert_eq!(g.node_weight(0), 1.0);
}
}