use std::collections::VecDeque;
use std::hash::Hash;
use std::sync::RwLock;
use hashbrown::HashMap;
use petgraph::algo::dijkstra;
use petgraph::visit::{
EdgeCount,
EdgeIndexable,
EdgeRef,
GraphBase,
GraphProp, IntoEdges,
IntoEdgesDirected,
IntoNeighbors,
IntoNeighborsDirected,
IntoNodeIdentifiers,
NodeCount,
NodeIndexable,
Reversed,
Visitable,
};
use rayon_cond::CondIterator;
pub fn betweenness_centrality<G>(
graph: G,
include_endpoints: bool,
normalized: bool,
parallel_threshold: usize,
) -> Vec<Option<f64>>
where
G: NodeIndexable
+ IntoNodeIdentifiers
+ IntoNeighborsDirected
+ NodeCount
+ GraphProp
+ GraphBase
+ std::marker::Sync,
<G as GraphBase>::NodeId: std::cmp::Eq + Hash + Send,
{
let max_index = graph.node_bound();
let mut betweenness: Vec<Option<f64>> = vec![None; max_index];
for node_s in graph.node_identifiers() {
let is: usize = graph.to_index(node_s);
betweenness[is] = Some(0.0);
}
let locked_betweenness = RwLock::new(&mut betweenness);
let node_indices: Vec<G::NodeId> = graph.node_identifiers().collect();
CondIterator::new(node_indices, graph.node_count() >= parallel_threshold)
.map(|node_s| (shortest_path_for_centrality(&graph, &node_s), node_s))
.for_each(|(mut shortest_path_calc, node_s)| {
_accumulate_vertices(
&locked_betweenness,
max_index,
&mut shortest_path_calc,
node_s,
&graph,
include_endpoints,
);
});
_rescale(
&mut betweenness,
graph.node_count(),
normalized,
graph.is_directed(),
include_endpoints,
);
betweenness
}
pub fn edge_betweenness_centrality<G>(
graph: G,
normalized: bool,
parallel_threshold: usize,
) -> Vec<Option<f64>>
where
G: NodeIndexable
+ EdgeIndexable
+ IntoEdges
+ IntoNodeIdentifiers
+ IntoNeighborsDirected
+ NodeCount
+ EdgeCount
+ GraphProp
+ Sync,
G::NodeId: Eq + Hash + Send,
G::EdgeId: Eq + Hash + Send,
{
let max_index = graph.node_bound();
let mut betweenness = vec![None; graph.edge_bound()];
for edge in graph.edge_references() {
let is: usize = EdgeIndexable::to_index(&graph, edge.id());
betweenness[is] = Some(0.0);
}
let locked_betweenness = RwLock::new(&mut betweenness);
let node_indices: Vec<G::NodeId> = graph.node_identifiers().collect();
CondIterator::new(node_indices, graph.node_count() >= parallel_threshold)
.map(|node_s| shortest_path_for_edge_centrality(&graph, &node_s))
.for_each(|mut shortest_path_calc| {
accumulate_edges(
&locked_betweenness,
max_index,
&mut shortest_path_calc,
&graph,
);
});
_rescale(
&mut betweenness,
graph.node_count(),
normalized,
graph.is_directed(),
true,
);
betweenness
}
fn _rescale(
betweenness: &mut [Option<f64>],
node_count: usize,
normalized: bool,
directed: bool,
include_endpoints: bool,
) {
let mut do_scale = true;
let mut scale = 1.0;
if normalized {
if include_endpoints {
if node_count < 2 {
do_scale = false;
} else {
scale = 1.0 / (node_count * (node_count - 1)) as f64;
}
} else if node_count <= 2 {
do_scale = false;
} else {
scale = 1.0 / ((node_count - 1) * (node_count - 2)) as f64;
}
} else if !directed {
scale = 0.5;
} else {
do_scale = false;
}
if do_scale {
for x in betweenness.iter_mut() {
*x = x.map(|y| y * scale);
}
}
}
fn _accumulate_vertices<G>(
locked_betweenness: &RwLock<&mut Vec<Option<f64>>>,
max_index: usize,
path_calc: &mut ShortestPathData<G>,
node_s: <G as GraphBase>::NodeId,
graph: G,
include_endpoints: bool,
) where
G: NodeIndexable
+ IntoNodeIdentifiers
+ IntoNeighborsDirected
+ NodeCount
+ GraphProp
+ GraphBase
+ std::marker::Sync,
<G as GraphBase>::NodeId: std::cmp::Eq + Hash,
{
let mut delta = vec![0.0; max_index];
for w in &path_calc.verts_sorted_by_distance {
let iw = graph.to_index(*w);
let coeff = (1.0 + delta[iw]) / path_calc.sigma[w];
let p_w = path_calc.predecessors.get(w).unwrap();
for v in p_w {
let iv = graph.to_index(*v);
delta[iv] += path_calc.sigma[v] * coeff;
}
}
let mut betweenness = locked_betweenness.write().unwrap();
if include_endpoints {
let i_node_s = graph.to_index(node_s);
betweenness[i_node_s] = betweenness[i_node_s]
.map(|x| x + ((path_calc.verts_sorted_by_distance.len() - 1) as f64));
for w in &path_calc.verts_sorted_by_distance {
if *w != node_s {
let iw = graph.to_index(*w);
betweenness[iw] = betweenness[iw].map(|x| x + delta[iw] + 1.0);
}
}
} else {
for w in &path_calc.verts_sorted_by_distance {
if *w != node_s {
let iw = graph.to_index(*w);
betweenness[iw] = betweenness[iw].map(|x| x + delta[iw]);
}
}
}
}
fn accumulate_edges<G>(
locked_betweenness: &RwLock<&mut Vec<Option<f64>>>,
max_index: usize,
path_calc: &mut ShortestPathDataWithEdges<G>,
graph: G,
) where
G: NodeIndexable + EdgeIndexable + Sync,
G::NodeId: Eq + Hash,
G::EdgeId: Eq + Hash,
{
let mut delta = vec![0.0; max_index];
for w in &path_calc.verts_sorted_by_distance {
let iw = NodeIndexable::to_index(&graph, *w);
let coeff = (1.0 + delta[iw]) / path_calc.sigma[w];
let p_w = path_calc.predecessors.get(w).unwrap();
let e_w = path_calc.predecessor_edges.get(w).unwrap();
let mut betweenness = locked_betweenness.write().unwrap();
for i in 0..p_w.len() {
let v = p_w[i];
let iv = NodeIndexable::to_index(&graph, v);
let ie = EdgeIndexable::to_index(&graph, e_w[i]);
let c = path_calc.sigma[&v] * coeff;
betweenness[ie] = betweenness[ie].map(|x| x + c);
delta[iv] += c;
}
}
}
struct ShortestPathData<G>
where
G: GraphBase,
<G as GraphBase>::NodeId: std::cmp::Eq + Hash,
{
verts_sorted_by_distance: Vec<G::NodeId>,
predecessors: HashMap<G::NodeId, Vec<G::NodeId>>,
sigma: HashMap<G::NodeId, f64>,
}
fn shortest_path_for_centrality<G>(graph: G, node_s: &G::NodeId) -> ShortestPathData<G>
where
G: NodeIndexable + IntoNodeIdentifiers + IntoNeighborsDirected + NodeCount + GraphBase,
<G as GraphBase>::NodeId: std::cmp::Eq + Hash,
{
let mut verts_sorted_by_distance: Vec<G::NodeId> = Vec::new(); let c = graph.node_count();
let mut predecessors = HashMap::<G::NodeId, Vec<G::NodeId>>::with_capacity(c);
let mut sigma = HashMap::<G::NodeId, f64>::with_capacity(c);
let mut distance = HashMap::<G::NodeId, i64>::with_capacity(c);
#[allow(non_snake_case)]
let mut Q: VecDeque<G::NodeId> = VecDeque::with_capacity(c);
for node in graph.node_identifiers() {
predecessors.insert(node, Vec::new());
sigma.insert(node, 0.0);
distance.insert(node, -1);
}
sigma.insert(*node_s, 1.0);
distance.insert(*node_s, 0);
Q.push_back(*node_s);
while let Some(v) = Q.pop_front() {
verts_sorted_by_distance.push(v);
let distance_v = distance[&v];
for w in graph.neighbors(v) {
if distance[&w] < 0 {
Q.push_back(w);
distance.insert(w, distance_v + 1);
}
if distance[&w] == distance_v + 1 {
sigma.insert(w, sigma[&w] + sigma[&v]);
let e_p = predecessors.get_mut(&w).unwrap();
e_p.push(v);
}
}
}
verts_sorted_by_distance.reverse(); ShortestPathData {
verts_sorted_by_distance,
predecessors,
sigma,
}
}
struct ShortestPathDataWithEdges<G>
where
G: GraphBase,
G::NodeId: Eq + Hash,
G::EdgeId: Eq + Hash,
{
verts_sorted_by_distance: Vec<G::NodeId>,
predecessors: HashMap<G::NodeId, Vec<G::NodeId>>,
predecessor_edges: HashMap<G::NodeId, Vec<G::EdgeId>>,
sigma: HashMap<G::NodeId, f64>,
}
fn shortest_path_for_edge_centrality<G>(
graph: G,
node_s: &G::NodeId,
) -> ShortestPathDataWithEdges<G>
where
G: NodeIndexable
+ IntoNodeIdentifiers
+ IntoNeighborsDirected
+ NodeCount
+ GraphBase
+ IntoEdges,
G::NodeId: Eq + Hash,
G::EdgeId: Eq + Hash,
{
let mut verts_sorted_by_distance: Vec<G::NodeId> = Vec::new(); let c = graph.node_count();
let mut predecessors = HashMap::<G::NodeId, Vec<G::NodeId>>::with_capacity(c);
let mut predecessor_edges = HashMap::<G::NodeId, Vec<G::EdgeId>>::with_capacity(c);
let mut sigma = HashMap::<G::NodeId, f64>::with_capacity(c);
let mut distance = HashMap::<G::NodeId, i64>::with_capacity(c);
#[allow(non_snake_case)]
let mut Q: VecDeque<G::NodeId> = VecDeque::with_capacity(c);
for node in graph.node_identifiers() {
predecessors.insert(node, Vec::new());
predecessor_edges.insert(node, Vec::new());
sigma.insert(node, 0.0);
distance.insert(node, -1);
}
sigma.insert(*node_s, 1.0);
distance.insert(*node_s, 0);
Q.push_back(*node_s);
while let Some(v) = Q.pop_front() {
verts_sorted_by_distance.push(v);
let distance_v = distance[&v];
for edge in graph.edges(v) {
let w = edge.target();
if distance[&w] < 0 {
Q.push_back(w);
distance.insert(w, distance_v + 1);
}
if distance[&w] == distance_v + 1 {
sigma.insert(w, sigma[&w] + sigma[&v]);
let e_p = predecessors.get_mut(&w).unwrap();
e_p.push(v);
predecessor_edges.get_mut(&w).unwrap().push(edge.id());
}
}
}
verts_sorted_by_distance.reverse(); ShortestPathDataWithEdges {
verts_sorted_by_distance,
predecessors,
predecessor_edges,
sigma,
}
}
#[cfg(test)]
mod test_edge_betweenness_centrality {
use crate::centrality::edge_betweenness_centrality;
use petgraph::graph::edge_index;
use petgraph::prelude::StableGraph;
use petgraph::Undirected;
macro_rules! assert_almost_equal {
($x:expr, $y:expr, $d:expr) => {
if ($x - $y).abs() >= $d {
panic!("{} != {} within delta of {}", $x, $y, $d);
}
};
}
#[test]
fn test_undirected_graph_normalized() {
let graph = petgraph::graph::UnGraph::<(), ()>::from_edges(&[
(0, 6),
(0, 4),
(0, 1),
(0, 5),
(1, 6),
(1, 7),
(1, 3),
(1, 4),
(2, 6),
(2, 3),
(3, 5),
(3, 7),
(3, 6),
(4, 5),
(5, 6),
]);
let output = edge_betweenness_centrality(&graph, true, 50);
let result = output.iter().map(|x| x.unwrap()).collect::<Vec<f64>>();
let expected_values = vec![
0.1023809, 0.0547619, 0.0922619, 0.05654762, 0.09940476, 0.125, 0.09940476, 0.12440476,
0.12857143, 0.12142857, 0.13511905, 0.125, 0.06547619, 0.08869048, 0.08154762,
];
for i in 0..15 {
assert_almost_equal!(result[i], expected_values[i], 1e-4);
}
}
#[test]
fn test_undirected_graph_unnormalized() {
let graph = petgraph::graph::UnGraph::<(), ()>::from_edges(&[
(0, 2),
(0, 4),
(0, 1),
(1, 3),
(1, 5),
(1, 7),
(2, 7),
(2, 3),
(3, 5),
(3, 6),
(4, 6),
(5, 7),
]);
let output = edge_betweenness_centrality(&graph, false, 50);
let result = output.iter().map(|x| x.unwrap()).collect::<Vec<f64>>();
let expected_values = vec![
3.83333, 5.5, 5.33333, 3.5, 2.5, 3.0, 3.5, 4.0, 3.66667, 6.5, 3.5, 2.16667,
];
for i in 0..12 {
assert_almost_equal!(result[i], expected_values[i], 1e-4);
}
}
#[test]
fn test_directed_graph_normalized() {
let graph = petgraph::graph::DiGraph::<(), ()>::from_edges(&[
(0, 1),
(1, 0),
(1, 3),
(1, 2),
(1, 4),
(2, 3),
(2, 4),
(2, 1),
(3, 2),
(4, 3),
]);
let output = edge_betweenness_centrality(&graph, true, 50);
let result = output.iter().map(|x| x.unwrap()).collect::<Vec<f64>>();
let expected_values = vec![0.2, 0.2, 0.1, 0.1, 0.1, 0.05, 0.1, 0.3, 0.35, 0.2];
for i in 0..10 {
assert_almost_equal!(result[i], expected_values[i], 1e-4);
}
}
#[test]
fn test_directed_graph_unnormalized() {
let graph = petgraph::graph::DiGraph::<(), ()>::from_edges(&[
(0, 4),
(1, 0),
(1, 3),
(2, 3),
(2, 4),
(2, 0),
(3, 4),
(3, 2),
(3, 1),
(4, 1),
]);
let output = edge_betweenness_centrality(&graph, false, 50);
let result = output.iter().map(|x| x.unwrap()).collect::<Vec<f64>>();
let expected_values = vec![4.5, 3.0, 6.5, 1.5, 1.5, 1.5, 1.5, 4.5, 2.0, 7.5];
for i in 0..10 {
assert_almost_equal!(result[i], expected_values[i], 1e-4);
}
}
#[test]
fn test_stable_graph_with_removed_edges() {
let mut graph: StableGraph<(), (), Undirected> =
StableGraph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)]);
graph.remove_edge(edge_index(1));
let result = edge_betweenness_centrality(&graph, false, 50);
let expected_values = vec![Some(3.0), None, Some(3.0), Some(4.0)];
assert_eq!(result, expected_values);
}
}
pub fn eigenvector_centrality<G, F, E>(
graph: G,
mut weight_fn: F,
max_iter: Option<usize>,
tol: Option<f64>,
) -> Result<Option<Vec<f64>>, E>
where
G: NodeIndexable + IntoNodeIdentifiers + IntoNeighbors + IntoEdges + NodeCount,
G::NodeId: Eq + std::hash::Hash,
F: FnMut(G::EdgeRef) -> Result<f64, E>,
{
let tol: f64 = tol.unwrap_or(1e-6);
let max_iter = max_iter.unwrap_or(100);
let mut x: Vec<f64> = vec![1.; graph.node_bound()];
let node_count = graph.node_count();
for _ in 0..max_iter {
let x_last = x.clone();
for node_index in graph.node_identifiers() {
let node = graph.to_index(node_index);
for edge in graph.edges(node_index) {
let w = weight_fn(edge)?;
let neighbor = edge.target();
x[graph.to_index(neighbor)] += x_last[node] * w;
}
}
let norm: f64 = x.iter().map(|val| val.powi(2)).sum::<f64>().sqrt();
if norm == 0. {
return Ok(None);
}
for v in x.iter_mut() {
*v /= norm;
}
if (0..x.len())
.map(|node| (x[node] - x_last[node]).abs())
.sum::<f64>()
< node_count as f64 * tol
{
return Ok(Some(x));
}
}
Ok(None)
}
pub fn katz_centrality<G, F, E>(
graph: G,
mut weight_fn: F,
alpha: Option<f64>,
beta_map: Option<HashMap<usize, f64>>,
beta_scalar: Option<f64>,
max_iter: Option<usize>,
tol: Option<f64>,
) -> Result<Option<Vec<f64>>, E>
where
G: NodeIndexable + IntoNodeIdentifiers + IntoNeighbors + IntoEdges + NodeCount,
G::NodeId: Eq + std::hash::Hash,
F: FnMut(G::EdgeRef) -> Result<f64, E>,
{
let alpha: f64 = alpha.unwrap_or(0.1);
let mut beta: HashMap<usize, f64> = beta_map.unwrap_or_else(HashMap::new);
if beta.is_empty() {
let beta_scalar = beta_scalar.unwrap_or(1.0);
for node_index in graph.node_identifiers() {
let node = graph.to_index(node_index);
beta.insert(node, beta_scalar);
}
} else {
for node_index in graph.node_identifiers() {
let node = graph.to_index(node_index);
if !beta.contains_key(&node) {
return Ok(None); }
}
}
let tol: f64 = tol.unwrap_or(1e-6);
let max_iter = max_iter.unwrap_or(1000);
let mut x: Vec<f64> = vec![0.; graph.node_bound()];
let node_count = graph.node_count();
for _ in 0..max_iter {
let x_last = x.clone();
x = vec![0.; graph.node_bound()];
for node_index in graph.node_identifiers() {
let node = graph.to_index(node_index);
for edge in graph.edges(node_index) {
let w = weight_fn(edge)?;
let neighbor = edge.target();
x[graph.to_index(neighbor)] += x_last[node] * w;
}
}
for node_index in graph.node_identifiers() {
let node = graph.to_index(node_index);
x[node] = alpha * x[node] + beta.get(&node).unwrap_or(&0.0);
}
if (0..x.len())
.map(|node| (x[node] - x_last[node]).abs())
.sum::<f64>()
< node_count as f64 * tol
{
let norm: f64 = x.iter().map(|val| val.powi(2)).sum::<f64>().sqrt();
if norm == 0. {
return Ok(None);
}
for v in x.iter_mut() {
*v /= norm;
}
return Ok(Some(x));
}
}
Ok(None)
}
#[cfg(test)]
mod test_eigenvector_centrality {
use crate::centrality::eigenvector_centrality;
use crate::petgraph;
use crate::Result;
macro_rules! assert_almost_equal {
($x:expr, $y:expr, $d:expr) => {
if ($x - $y).abs() >= $d {
panic!("{} != {} within delta of {}", $x, $y, $d);
}
};
}
#[test]
fn test_no_convergence() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges(&[(0, 1), (1, 2)]);
let output: Result<Option<Vec<f64>>> =
eigenvector_centrality(&g, |_| Ok(1.), Some(0), None);
let result = output.unwrap();
assert_eq!(None, result);
}
#[test]
fn test_undirected_complete_graph() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges([
(0, 1),
(0, 2),
(0, 3),
(0, 4),
(1, 2),
(1, 3),
(1, 4),
(2, 3),
(2, 4),
(3, 4),
]);
let output: Result<Option<Vec<f64>>> = eigenvector_centrality(&g, |_| Ok(1.), None, None);
let result = output.unwrap().unwrap();
let expected_value: f64 = (1_f64 / 5_f64).sqrt();
let expected_values: Vec<f64> = vec![expected_value; 5];
for i in 0..5 {
assert_almost_equal!(expected_values[i], result[i], 1e-4);
}
}
#[test]
fn test_undirected_path_graph() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges(&[(0, 1), (1, 2)]);
let output: Result<Option<Vec<f64>>> = eigenvector_centrality(&g, |_| Ok(1.), None, None);
let result = output.unwrap().unwrap();
let expected_values: Vec<f64> = vec![0.5, 0.7071, 0.5];
for i in 0..3 {
assert_almost_equal!(expected_values[i], result[i], 1e-4);
}
}
#[test]
fn test_directed_graph() {
let g = petgraph::graph::DiGraph::<i32, ()>::from_edges([
(0, 1),
(0, 2),
(1, 3),
(2, 1),
(2, 4),
(3, 1),
(3, 4),
(3, 5),
(4, 5),
(4, 6),
(4, 7),
(5, 7),
(6, 0),
(6, 4),
(6, 7),
(7, 5),
(7, 6),
]);
let output: Result<Option<Vec<f64>>> = eigenvector_centrality(&g, |_| Ok(2.), None, None);
let result = output.unwrap().unwrap();
let expected_values: Vec<f64> = vec![
0.2140437, 0.2009269, 0.1036383, 0.0972886, 0.3113323, 0.4891686, 0.4420605, 0.6016448,
];
for i in 0..8 {
assert_almost_equal!(expected_values[i], result[i], 1e-4);
}
}
}
#[cfg(test)]
mod test_katz_centrality {
use crate::centrality::katz_centrality;
use crate::petgraph;
use crate::Result;
use hashbrown::HashMap;
macro_rules! assert_almost_equal {
($x:expr, $y:expr, $d:expr) => {
if ($x - $y).abs() >= $d {
panic!("{} != {} within delta of {}", $x, $y, $d);
}
};
}
#[test]
fn test_no_convergence() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges(&[(0, 1), (1, 2)]);
let output: Result<Option<Vec<f64>>> =
katz_centrality(&g, |_| Ok(1.), None, None, None, Some(0), None);
let result = output.unwrap();
assert_eq!(None, result);
}
#[test]
fn test_incomplete_beta() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges(&[(0, 1), (1, 2)]);
let beta_map: HashMap<usize, f64> = [(0, 1.0)].iter().cloned().collect();
let output: Result<Option<Vec<f64>>> =
katz_centrality(&g, |_| Ok(1.), None, Some(beta_map), None, None, None);
let result = output.unwrap();
assert_eq!(None, result);
}
#[test]
fn test_complete_beta() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges(&[(0, 1), (1, 2)]);
let beta_map: HashMap<usize, f64> =
[(0, 0.5), (1, 1.0), (2, 0.5)].iter().cloned().collect();
let output: Result<Option<Vec<f64>>> =
katz_centrality(&g, |_| Ok(1.), None, Some(beta_map), None, None, None);
let result = output.unwrap().unwrap();
let expected_values: Vec<f64> =
vec![0.4318894504492167, 0.791797325823564, 0.4318894504492167];
for i in 0..3 {
assert_almost_equal!(expected_values[i], result[i], 1e-4);
}
}
#[test]
fn test_undirected_complete_graph() {
let g = petgraph::graph::UnGraph::<i32, ()>::from_edges([
(0, 1),
(0, 2),
(0, 3),
(0, 4),
(1, 2),
(1, 3),
(1, 4),
(2, 3),
(2, 4),
(3, 4),
]);
let output: Result<Option<Vec<f64>>> =
katz_centrality(&g, |_| Ok(1.), Some(0.2), None, Some(1.1), None, None);
let result = output.unwrap().unwrap();
let expected_value: f64 = (1_f64 / 5_f64).sqrt();
let expected_values: Vec<f64> = vec![expected_value; 5];
for i in 0..5 {
assert_almost_equal!(expected_values[i], result[i], 1e-4);
}
}
#[test]
fn test_directed_graph() {
let g = petgraph::graph::DiGraph::<i32, ()>::from_edges([
(0, 1),
(0, 2),
(1, 3),
(2, 1),
(2, 4),
(3, 1),
(3, 4),
(3, 5),
(4, 5),
(4, 6),
(4, 7),
(5, 7),
(6, 0),
(6, 4),
(6, 7),
(7, 5),
(7, 6),
]);
let output: Result<Option<Vec<f64>>> =
katz_centrality(&g, |_| Ok(1.), None, None, None, None, None);
let result = output.unwrap().unwrap();
let expected_values: Vec<f64> = vec![
0.3135463087489011,
0.3719056758615039,
0.3094350787809586,
0.31527101632646026,
0.3760169058294464,
0.38618584417917906,
0.35465874858087904,
0.38976653416801743,
];
for i in 0..8 {
assert_almost_equal!(expected_values[i], result[i], 1e-4);
}
}
}
pub fn closeness_centrality<G>(graph: G, wf_improved: bool) -> Vec<Option<f64>>
where
G: NodeIndexable
+ IntoNodeIdentifiers
+ GraphBase
+ IntoEdges
+ Visitable
+ NodeCount
+ IntoEdgesDirected,
G::NodeId: std::hash::Hash + Eq,
{
let max_index = graph.node_bound();
let mut closeness: Vec<Option<f64>> = vec![None; max_index];
for node_s in graph.node_identifiers() {
let is = graph.to_index(node_s);
let map = dijkstra(Reversed(&graph), node_s, None, |_| 1);
let reachable_nodes_count = map.len();
let dists_sum: usize = map.into_values().sum();
if reachable_nodes_count == 1 {
closeness[is] = Some(0.0);
continue;
}
closeness[is] = Some((reachable_nodes_count - 1) as f64 / dists_sum as f64);
if wf_improved {
let node_count = graph.node_count();
closeness[is] = closeness[is]
.map(|c| c * (reachable_nodes_count - 1) as f64 / (node_count - 1) as f64);
}
}
closeness
}