use alloc::{collections::VecDeque, vec::Vec};
use num_traits::{AsPrimitive, Float};
use super::{CLOSENESS_SCORE_SCALE, NodeScorer, usize_to_f64};
use crate::traits::UndirectedMonopartiteMonoplexGraph;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ClosenessCentralityScorer {
wf_improved: bool,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct ClosenessCentralityScorerBuilder {
wf_improved: bool,
}
impl ClosenessCentralityScorer {
#[inline]
#[must_use]
pub const fn new(wf_improved: bool) -> Self {
Self { wf_improved }
}
#[inline]
#[must_use]
pub const fn builder() -> ClosenessCentralityScorerBuilder {
ClosenessCentralityScorerBuilder::new()
}
}
impl Default for ClosenessCentralityScorer {
#[inline]
fn default() -> Self {
Self::new(true)
}
}
impl ClosenessCentralityScorerBuilder {
#[inline]
#[must_use]
pub const fn new() -> Self {
Self { wf_improved: true }
}
#[inline]
#[must_use]
pub const fn wf_improved(mut self, wf_improved: bool) -> Self {
self.wf_improved = wf_improved;
self
}
#[inline]
#[must_use]
pub const fn build(self) -> ClosenessCentralityScorer {
ClosenessCentralityScorer::new(self.wf_improved)
}
}
impl Default for ClosenessCentralityScorerBuilder {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<G> NodeScorer<G> for ClosenessCentralityScorer
where
G: UndirectedMonopartiteMonoplexGraph,
{
type Score = f64;
fn score_nodes(&self, graph: &G) -> Vec<Self::Score> {
let n = graph.number_of_nodes().as_();
if n == 0 {
return Vec::new();
}
let nodes: Vec<G::NodeId> = graph.node_ids().collect();
debug_assert_eq!(nodes.len(), n);
debug_assert!(nodes.iter().enumerate().all(|(i, node)| (*node).as_() == i));
let mut scores = vec![0.0; n];
let mut distance = vec![usize::MAX; n];
let mut queue = VecDeque::with_capacity(n);
for source_index in 0..n {
distance.fill(usize::MAX);
queue.clear();
distance[source_index] = 0;
queue.push_back(source_index);
let mut total_shortest_path_distance = 0usize;
let mut reachable_nodes = 1usize;
while let Some(node_index) = queue.pop_front() {
let node = nodes[node_index];
let node_distance = distance[node_index];
for neighbor in graph.neighbors(node) {
let neighbor_index = neighbor.as_();
if distance[neighbor_index] != usize::MAX {
continue;
}
let neighbor_distance = node_distance + 1;
distance[neighbor_index] = neighbor_distance;
total_shortest_path_distance += neighbor_distance;
reachable_nodes += 1;
queue.push_back(neighbor_index);
}
}
if total_shortest_path_distance > 0 && n > 1 {
let reachable_minus_source = reachable_nodes - 1;
let mut score = usize_to_f64(reachable_minus_source)
/ usize_to_f64(total_shortest_path_distance);
if self.wf_improved {
score *= usize_to_f64(reachable_minus_source) / usize_to_f64(n - 1);
}
scores[source_index] =
Float::round(score * CLOSENESS_SCORE_SCALE) / CLOSENESS_SCORE_SCALE;
}
}
scores
}
}