use crate::graph::Graph;
use crate::routing::dijkstra;
use std::cmp::Ordering;
use std::collections::BinaryHeap;
const EPS: f64 = 1e-9;
struct Entry {
dist: f64,
node: usize,
}
impl PartialEq for Entry {
fn eq(&self, other: &Self) -> bool {
self.dist == other.dist
}
}
impl Eq for Entry {}
impl PartialOrd for Entry {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Entry {
fn cmp(&self, other: &Self) -> Ordering {
other
.dist
.partial_cmp(&self.dist)
.unwrap_or(Ordering::Equal)
}
}
pub fn closeness_centrality(graph: &Graph, normalized: bool) -> Vec<f64> {
let n = graph.n_nodes;
let mut out = vec![0.0; n];
for (v, slot) in out.iter_mut().enumerate() {
let dist = dijkstra(graph, v);
let mut sum = 0.0;
let mut reach = 0usize;
for &d in dist.iter() {
if d.is_finite() && d > 0.0 {
sum += d;
reach += 1;
}
}
if sum > 0.0 && reach > 0 {
let mut c = reach as f64 / sum;
if normalized && n > 1 {
c *= reach as f64 / (n as f64 - 1.0);
}
*slot = c;
}
}
out
}
pub fn betweenness_centrality(graph: &Graph, normalized: bool) -> Vec<f64> {
let n = graph.n_nodes;
let mut bc = vec![0.0f64; n];
for s in 0..n {
let mut dist = vec![f64::INFINITY; n];
let mut sigma = vec![0.0f64; n];
let mut preds: Vec<Vec<usize>> = vec![Vec::new(); n];
let mut done = vec![false; n];
let mut stack: Vec<usize> = Vec::new();
dist[s] = 0.0;
sigma[s] = 1.0;
let mut heap = BinaryHeap::new();
heap.push(Entry { dist: 0.0, node: s });
while let Some(Entry { dist: d, node: v }) = heap.pop() {
if done[v] {
continue;
}
done[v] = true;
stack.push(v);
for (w, c) in graph.neighbors(v) {
let nd = d + c;
if nd < dist[w] - EPS {
dist[w] = nd;
sigma[w] = sigma[v];
preds[w].clear();
preds[w].push(v);
heap.push(Entry { dist: nd, node: w });
} else if (nd - dist[w]).abs() <= EPS {
sigma[w] += sigma[v];
preds[w].push(v);
}
}
}
let mut delta = vec![0.0f64; n];
while let Some(w) = stack.pop() {
let coeff = (1.0 + delta[w]) / sigma[w];
for &v in &preds[w] {
delta[v] += sigma[v] * coeff;
}
if w != s {
bc[w] += delta[w];
}
}
}
if normalized && n > 2 {
let scale = 1.0 / ((n as f64 - 1.0) * (n as f64 - 2.0));
for x in bc.iter_mut() {
*x *= scale;
}
}
bc
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn betweenness_of_a_path_peaks_in_the_middle() {
let from = vec![0, 1, 2, 3, 1, 2, 3, 4];
let to = vec![1, 2, 3, 4, 0, 1, 2, 3];
let w = vec![1.0; 8];
let g = Graph::from_edges(&from, &to, &w, 5);
let bc = betweenness_centrality(&g, false);
assert!(bc[2] > bc[1]);
assert!(bc[1] > bc[0]);
assert_eq!(bc[0], 0.0); }
#[test]
fn closeness_center_beats_leaf() {
let from = vec![0, 1, 0, 2, 0, 3];
let to = vec![1, 0, 2, 0, 3, 0];
let w = vec![1.0; 6];
let g = Graph::from_edges(&from, &to, &w, 4);
let cc = closeness_centrality(&g, false);
assert!(cc[0] > cc[1]);
}
}