use std::collections::VecDeque;
pub trait WeightedGraph {
fn num_nodes(&self) -> usize;
fn num_edges(&self) -> usize;
fn neighbors_with_weight<'a>(
&'a self,
node: usize,
) -> Box<dyn Iterator<Item = (usize, f32)> + 'a>;
fn degree(&self, node: usize) -> usize {
self.neighbors_with_weight(node).count()
}
fn weighted_degree(&self, node: usize) -> f32 {
self.neighbors_with_weight(node).map(|(_, w)| w).sum()
}
}
pub struct AdjListGraph {
adj: Vec<Vec<(usize, f32)>>,
num_edges: usize,
}
impl AdjListGraph {
pub fn from_edges(n_nodes: usize, edges: &[(usize, usize, f32)]) -> Self {
let mut adj = vec![Vec::new(); n_nodes];
let mut num_edges = 0usize;
for &(u, v, w) in edges {
if u == v {
continue;
}
adj[u].push((v, w));
adj[v].push((u, w));
num_edges += 1;
}
Self { adj, num_edges }
}
pub fn from_unweighted_edges(n_nodes: usize, edges: &[(usize, usize)]) -> Self {
let weighted: Vec<(usize, usize, f32)> = edges.iter().map(|&(u, v)| (u, v, 1.0)).collect();
Self::from_edges(n_nodes, &weighted)
}
}
impl WeightedGraph for AdjListGraph {
fn num_nodes(&self) -> usize {
self.adj.len()
}
fn num_edges(&self) -> usize {
self.num_edges
}
fn neighbors_with_weight<'a>(
&'a self,
node: usize,
) -> Box<dyn Iterator<Item = (usize, f32)> + 'a> {
Box::new(self.adj[node].iter().copied())
}
}
pub fn connected_components<G: WeightedGraph + ?Sized>(g: &G) -> Vec<usize> {
let n = g.num_nodes();
let mut label = vec![usize::MAX; n];
let mut queue: VecDeque<usize> = VecDeque::new();
let mut next_id = 0usize;
for root in 0..n {
if label[root] != usize::MAX {
continue;
}
label[root] = next_id;
queue.push_back(root);
while let Some(u) = queue.pop_front() {
for (v, _) in g.neighbors_with_weight(u) {
if label[v] == usize::MAX {
label[v] = next_id;
queue.push_back(v);
}
}
}
next_id += 1;
}
label
}
pub fn num_connected_components<G: WeightedGraph + ?Sized>(g: &G) -> usize {
connected_components(g)
.into_iter()
.max()
.map(|m| m + 1)
.unwrap_or(0)
}
pub fn weighted_degrees<G: WeightedGraph + ?Sized>(g: &G) -> Vec<f32> {
(0..g.num_nodes()).map(|i| g.weighted_degree(i)).collect()
}
pub fn total_edge_weight<G: WeightedGraph + ?Sized>(g: &G) -> f32 {
let mut s = 0f32;
for u in 0..g.num_nodes() {
for (v, w) in g.neighbors_with_weight(u) {
if u < v {
s += w;
}
}
}
s
}
#[cfg(test)]
mod tests {
use super::*;
struct TinyGraph {
n: usize,
adj: Vec<Vec<(usize, f32)>>,
}
impl WeightedGraph for TinyGraph {
fn num_nodes(&self) -> usize {
self.n
}
fn num_edges(&self) -> usize {
self.adj.iter().map(|v| v.len()).sum::<usize>() / 2
}
fn neighbors_with_weight<'a>(
&'a self,
node: usize,
) -> Box<dyn Iterator<Item = (usize, f32)> + 'a> {
Box::new(self.adj[node].iter().copied())
}
}
fn two_components() -> TinyGraph {
let mut adj = vec![vec![]; 6];
adj[0].push((1, 1.0));
adj[1].push((0, 1.0));
adj[2].push((3, 2.0));
adj[3].push((2, 2.0));
adj[3].push((4, 3.0));
adj[4].push((3, 3.0));
TinyGraph { n: 6, adj }
}
#[test]
fn connected_components_identifies_three_groups() {
let g = two_components();
let label = connected_components(&g);
assert_eq!(label[0], label[1]);
assert_eq!(label[2], label[3]);
assert_eq!(label[3], label[4]);
assert_ne!(label[0], label[2]);
assert_ne!(label[0], label[5]);
assert_ne!(label[2], label[5]);
assert_eq!(num_connected_components(&g), 3);
}
#[test]
fn weighted_degree_and_total_weight() {
let g = two_components();
assert_eq!(g.degree(3), 2);
assert_eq!(g.weighted_degree(3), 5.0);
assert_eq!(weighted_degrees(&g), vec![1.0, 1.0, 2.0, 5.0, 3.0, 0.0]);
assert_eq!(total_edge_weight(&g), 6.0);
}
}