use std::collections::VecDeque;
use crate::error::GraphalgResult;
use crate::repr::adjacency_list::AdjacencyList;
pub fn harmonic_centrality(g: &AdjacencyList) -> GraphalgResult<Vec<f64>> {
let n = g.n;
let mut hc = vec![0.0f64; n];
for s in 0..n {
let mut dist = vec![-1i64; n];
dist[s] = 0;
let mut q: VecDeque<usize> = VecDeque::new();
q.push_back(s);
while let Some(u) = q.pop_front() {
for &v in g.neighbors(u)? {
if dist[v] < 0 {
dist[v] = dist[u] + 1;
q.push_back(v);
}
}
}
let mut acc = 0.0f64;
for &d in dist.iter() {
if d > 0 {
acc += 1.0 / d as f64;
}
}
hc[s] = acc;
}
Ok(hc)
}
pub fn harmonic_centrality_normalized(g: &AdjacencyList) -> GraphalgResult<Vec<f64>> {
let n = g.n;
let mut hc = harmonic_centrality(g)?;
if n > 1 {
let denom = (n - 1) as f64;
for v in hc.iter_mut() {
*v /= denom;
}
}
Ok(hc)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn path_graph_centre_value() {
let mut g = AdjacencyList::new(3);
g.add_undirected_edge(0, 1).expect("ok");
g.add_undirected_edge(1, 2).expect("ok");
let h = harmonic_centrality(&g).expect("ok");
assert!((h[1] - 2.0).abs() < 1e-12, "h[1] = {}", h[1]);
assert!((h[0] - 1.5).abs() < 1e-12, "h[0] = {}", h[0]);
}
#[test]
fn complete_graph_is_n_minus_1() {
let n = 4;
let mut g = AdjacencyList::new(n);
for i in 0..n {
for j in i + 1..n {
g.add_undirected_edge(i, j).expect("ok");
}
}
let h = harmonic_centrality(&g).expect("ok");
for (v, &val) in h.iter().enumerate() {
assert!((val - 3.0).abs() < 1e-12, "vertex {v}: {val}");
}
}
#[test]
fn disconnected_graph_well_defined() {
let mut g = AdjacencyList::new(4);
g.add_undirected_edge(0, 1).expect("ok");
g.add_undirected_edge(2, 3).expect("ok");
let h = harmonic_centrality(&g).expect("ok");
for (v, &val) in h.iter().enumerate() {
assert!((val - 1.0).abs() < 1e-12, "vertex {v}: {val}");
assert!(val.is_finite());
}
}
#[test]
fn isolated_vertex_is_zero() {
let mut g = AdjacencyList::new(3);
g.add_undirected_edge(0, 1).expect("ok");
let h = harmonic_centrality(&g).expect("ok");
assert!((h[2] - 0.0).abs() < 1e-12, "isolated h[2] = {}", h[2]);
}
#[test]
fn empty_graph() {
let g = AdjacencyList::new(0);
let h = harmonic_centrality(&g).expect("ok");
assert!(h.is_empty());
}
#[test]
fn single_vertex() {
let g = AdjacencyList::new(1);
let h = harmonic_centrality(&g).expect("ok");
assert_eq!(h.len(), 1);
assert!((h[0] - 0.0).abs() < 1e-12);
}
#[test]
fn normalized_in_unit_range() {
let n = 5;
let mut g = AdjacencyList::new(n);
for i in 0..n {
for j in i + 1..n {
g.add_undirected_edge(i, j).expect("ok");
}
}
let h = harmonic_centrality_normalized(&g).expect("ok");
for &val in &h {
assert!((val - 1.0).abs() < 1e-12, "val = {val}");
assert!((0.0..=1.0).contains(&val));
}
}
#[test]
fn directed_asymmetry() {
let mut g = AdjacencyList::new(3);
g.add_edge(0, 1).expect("ok");
g.add_edge(0, 2).expect("ok");
let h = harmonic_centrality(&g).expect("ok");
assert!((h[0] - 2.0).abs() < 1e-12, "h[0] = {}", h[0]);
assert!((h[1] - 0.0).abs() < 1e-12, "h[1] = {}", h[1]);
assert!((h[2] - 0.0).abs() < 1e-12, "h[2] = {}", h[2]);
}
#[test]
fn deterministic() {
let mut g = AdjacencyList::new(6);
g.add_undirected_edge(0, 1).expect("ok");
g.add_undirected_edge(1, 2).expect("ok");
g.add_undirected_edge(2, 3).expect("ok");
g.add_undirected_edge(3, 4).expect("ok");
g.add_undirected_edge(4, 5).expect("ok");
let a = harmonic_centrality(&g).expect("ok");
let b = harmonic_centrality(&g).expect("ok");
assert_eq!(a, b);
}
#[test]
fn all_values_finite_and_nonneg() {
let mut g = AdjacencyList::new(5);
g.add_undirected_edge(0, 1).expect("ok");
g.add_undirected_edge(1, 2).expect("ok");
g.add_undirected_edge(3, 4).expect("ok");
let h = harmonic_centrality(&g).expect("ok");
assert!(h.iter().all(|&v| v.is_finite() && v >= 0.0));
}
#[test]
fn star_centre_higher_than_leaf() {
let mut g = AdjacencyList::new(4);
g.add_undirected_edge(0, 1).expect("ok");
g.add_undirected_edge(0, 2).expect("ok");
g.add_undirected_edge(0, 3).expect("ok");
let h = harmonic_centrality(&g).expect("ok");
assert!((h[0] - 3.0).abs() < 1e-12, "centre {}", h[0]);
assert!((h[1] - 2.0).abs() < 1e-12, "leaf {}", h[1]);
assert!(h[0] > h[1]);
}
}