use crate::core::error::{GraphinaError, Result};
use crate::core::paths::dijkstra_path_f64;
use crate::core::types::{BaseGraph, GraphConstructor, GraphinaGraph, NodeMap};
use std::fmt::Debug;
pub fn closeness_centrality<A, Ty>(graph: &BaseGraph<A, f64, Ty>) -> Result<NodeMap<f64>>
where
A: Debug,
Ty: GraphConstructor<A, f64>,
BaseGraph<A, f64, Ty>: GraphinaGraph<A, f64>,
{
if graph.node_count() == 0 {
return Err(GraphinaError::invalid_graph("Empty graph"));
}
let n = graph.node_count();
let mut centralities = NodeMap::default();
for (node, _) in graph.nodes() {
let (dist_map, _) = dijkstra_path_f64(graph, node, None)?;
let mut sum_dist = 0.0;
let mut reachable = 0usize;
for (&other_node, dist_opt) in &dist_map {
if node != other_node {
if let Some(dist_f64) = dist_opt {
if *dist_f64 > 0.0 && dist_f64.is_finite() {
sum_dist += *dist_f64;
reachable += 1;
}
}
}
}
let closeness = if sum_dist > 0.0 && n > 1 {
(reachable as f64 / sum_dist) * (reachable as f64 / (n as f64 - 1.0))
} else {
0.0
};
centralities.insert(node, closeness);
}
Ok(centralities)
}
#[cfg(test)]
mod tests {
#[test]
fn test_closeness_centrality_is_not_harmonic() {
use crate::centrality::closeness::closeness_centrality;
use crate::core::types::Graph;
let mut g = Graph::<i32, f64>::new();
let n0 = g.add_node(0);
let n1 = g.add_node(1);
let n2 = g.add_node(2);
g.add_edge(n0, n1, 1.0);
g.add_edge(n1, n2, 1.0);
let cc = closeness_centrality(&g).expect("closeness should succeed");
assert!(
(cc[&n0] - 2.0 / 3.0).abs() < 1e-9,
"expected 0.6667, got {}",
cc[&n0]
);
assert!(
(cc[&n1] - 1.0).abs() < 1e-9,
"expected 1.0, got {}",
cc[&n1]
);
assert!(
(cc[&n2] - 2.0 / 3.0).abs() < 1e-9,
"expected 0.6667, got {}",
cc[&n2]
);
}
}