use std::collections::VecDeque;
use crate::core::graph::EdgeId;
use crate::core::{Graph, IgraphResult, VertexId};
pub fn edge_betweenness(graph: &Graph) -> IgraphResult<Vec<f64>> {
let n = graph.vcount();
let n_us = n as usize;
let m = graph.ecount();
let mut betweenness_e = vec![0.0_f64; m];
if n == 0 || m == 0 {
return Ok(betweenness_e);
}
let mut sigma = vec![0.0_f64; n_us];
let mut dist = vec![-1i64; n_us];
let mut pred: Vec<Vec<(VertexId, EdgeId)>> = vec![Vec::new(); n_us];
let mut stack: Vec<VertexId> = Vec::with_capacity(n_us);
let mut delta_v = vec![0.0_f64; n_us];
for s in 0..n {
sigma.fill(0.0);
dist.fill(-1);
for slot in &mut pred {
slot.clear();
}
stack.clear();
delta_v.fill(0.0);
sigma[s as usize] = 1.0;
dist[s as usize] = 0;
let mut queue: VecDeque<VertexId> = VecDeque::new();
queue.push_back(s);
while let Some(v) = queue.pop_front() {
stack.push(v);
for e in graph.incident(v)? {
let w = graph.edge_other(e, v)?;
if dist[w as usize] < 0 {
queue.push_back(w);
dist[w as usize] = dist[v as usize] + 1;
}
if dist[w as usize] == dist[v as usize] + 1 {
sigma[w as usize] += sigma[v as usize];
pred[w as usize].push((v, e));
}
}
}
while let Some(w) = stack.pop() {
for &(v, e) in &pred[w as usize] {
let c = (sigma[v as usize] / sigma[w as usize]) * (1.0 + delta_v[w as usize]);
delta_v[v as usize] += c;
betweenness_e[e as usize] += c;
}
}
}
if !graph.is_directed() {
for slot in &mut betweenness_e {
*slot /= 2.0;
}
}
Ok(betweenness_e)
}
#[cfg(test)]
mod tests {
use super::*;
fn close(actual: &[f64], expected: &[f64], tol: f64) {
assert_eq!(actual.len(), expected.len(), "length mismatch");
for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
assert!((a - e).abs() < tol, "edge {i}: actual={a} expected={e}");
}
}
#[test]
fn empty_graph_yields_empty() {
let g = Graph::with_vertices(0);
assert!(edge_betweenness(&g).unwrap().is_empty());
}
#[test]
fn isolated_vertices_no_edges_empty() {
let g = Graph::with_vertices(5);
assert!(edge_betweenness(&g).unwrap().is_empty());
}
#[test]
fn path_3_edge_betweenness() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let eb = edge_betweenness(&g).unwrap();
close(&eb, &[2.0, 2.0], 1e-12);
}
#[test]
fn path_4_edge_betweenness() {
let mut g = Graph::with_vertices(4);
for i in 0..3u32 {
g.add_edge(i, i + 1).unwrap();
}
let eb = edge_betweenness(&g).unwrap();
close(&eb, &[3.0, 4.0, 3.0], 1e-12);
}
#[test]
fn k3_triangle_uniform_one() {
let mut g = Graph::with_vertices(3);
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(2, 0).unwrap();
let eb = edge_betweenness(&g).unwrap();
close(&eb, &[1.0, 1.0, 1.0], 1e-12);
}
#[test]
fn star_4_each_edge_three() {
let mut g = Graph::with_vertices(4);
for v in 1..4 {
g.add_edge(0, v).unwrap();
}
let eb = edge_betweenness(&g).unwrap();
close(&eb, &[3.0, 3.0, 3.0], 1e-12);
}
#[test]
fn cycle_4_uniform_two() {
let mut g = Graph::with_vertices(4);
for i in 0..4u32 {
g.add_edge(i, (i + 1) % 4).unwrap();
}
let eb = edge_betweenness(&g).unwrap();
close(&eb, &[2.0, 2.0, 2.0, 2.0], 1e-12);
}
#[test]
fn directed_path_3_edge_betweenness() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let eb = edge_betweenness(&g).unwrap();
close(&eb, &[2.0, 2.0], 1e-12);
}
}