Skip to content

Commit 5634bfd

Browse files
Remove The Hashmap from Shorted Path for Centrality Computation (#1307)
* Remove the Hashmap of the katz centrality computation to avoid better performance * cargo fmt * Remove the Hashmap from the ShortestPath_for_centrality computation * clippy advice * Fix * fmt * Fix python test * Remove Hashmap from accumulate vertice too * Remove useless comment * Remove hashmap for betwenness with edge too and clean the code * fmt * Fix clippy * Address PR comment (part 1) * Address PR comment (part 2) * Address PR comment (part 3) * Add another test case covering case with node removals --------- Co-authored-by: Ivan Carvalho <ivancarvalho@gatech.edu>
1 parent d1379aa commit 5634bfd

1 file changed

Lines changed: 120 additions & 69 deletions

File tree

rustworkx-core/src/centrality.rs

Lines changed: 120 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ where
7979
+ GraphProp
8080
+ GraphBase
8181
+ std::marker::Sync,
82-
<G as GraphBase>::NodeId: std::cmp::Eq + Hash + Send,
82+
<G as GraphBase>::NodeId: std::cmp::Eq + Send,
8383
// rustfmt deletes the following comments if placed inline above
8484
// + IntoNodeIdentifiers // for node_identifiers()
8585
// + IntoNeighborsDirected // for neighbors()
@@ -184,8 +184,8 @@ where
184184
+ EdgeCount
185185
+ GraphProp
186186
+ Sync,
187-
G::NodeId: Eq + Hash + Send,
188-
G::EdgeId: Eq + Hash + Send,
187+
G::NodeId: Eq + Send,
188+
G::EdgeId: Eq + Send,
189189
{
190190
let max_index = graph.node_bound();
191191
let mut betweenness = vec![None; graph.edge_bound()];
@@ -264,16 +264,15 @@ fn _accumulate_vertices<G>(
264264
+ GraphProp
265265
+ GraphBase
266266
+ std::marker::Sync,
267-
<G as GraphBase>::NodeId: std::cmp::Eq + Hash,
267+
<G as GraphBase>::NodeId: std::cmp::Eq,
268268
{
269269
let mut delta = vec![0.0; max_index];
270270
for w in &path_calc.verts_sorted_by_distance {
271271
let iw = graph.to_index(*w);
272-
let coeff = (1.0 + delta[iw]) / path_calc.sigma[w];
273-
let p_w = path_calc.predecessors.get(w).unwrap();
274-
for v in p_w {
275-
let iv = graph.to_index(*v);
276-
delta[iv] += path_calc.sigma[v] * coeff;
272+
let coeff = (1.0 + delta[iw]) / path_calc.sigma[iw];
273+
let p_w = path_calc.predecessors.get(iw).unwrap();
274+
for iv in p_w {
275+
delta[*iv] += path_calc.sigma[*iv] * coeff;
277276
}
278277
}
279278
let mut betweenness = locked_betweenness.write().unwrap();
@@ -304,21 +303,21 @@ fn accumulate_edges<G>(
304303
graph: G,
305304
) where
306305
G: NodeIndexable + EdgeIndexable + Sync,
307-
G::NodeId: Eq + Hash,
308-
G::EdgeId: Eq + Hash,
306+
G::NodeId: Eq,
307+
G::EdgeId: Eq,
309308
{
310309
let mut delta = vec![0.0; max_index];
311310
for w in &path_calc.verts_sorted_by_distance {
312311
let iw = NodeIndexable::to_index(&graph, *w);
313-
let coeff = (1.0 + delta[iw]) / path_calc.sigma[w];
314-
let p_w = path_calc.predecessors.get(w).unwrap();
315-
let e_w = path_calc.predecessor_edges.get(w).unwrap();
312+
let coeff = (1.0 + delta[iw]) / path_calc.sigma[iw];
313+
let p_w = path_calc.predecessors.get(iw).unwrap();
314+
let e_w = path_calc.predecessor_edges.get(iw).unwrap();
316315
let mut betweenness = locked_betweenness.write().unwrap();
317316
for i in 0..p_w.len() {
318317
let v = p_w[i];
319318
let iv = NodeIndexable::to_index(&graph, v);
320319
let ie = EdgeIndexable::to_index(&graph, e_w[i]);
321-
let c = path_calc.sigma[&v] * coeff;
320+
let c = path_calc.sigma[iv] * coeff;
322321
betweenness[ie] = betweenness[ie].map(|x| x + c);
323322
delta[iv] += c;
324323
}
@@ -358,7 +357,7 @@ where
358357
+ IntoNeighborsDirected
359358
+ NodeCount
360359
+ GraphProp,
361-
G::NodeId: Eq + Hash,
360+
G::NodeId: Eq,
362361
{
363362
let node_count = graph.node_count() as f64;
364363
let mut centrality = vec![0.0; graph.node_bound()];
@@ -396,46 +395,42 @@ where
396395
struct ShortestPathData<G>
397396
where
398397
G: GraphBase,
399-
<G as GraphBase>::NodeId: std::cmp::Eq + Hash,
398+
<G as GraphBase>::NodeId: std::cmp::Eq,
400399
{
401400
verts_sorted_by_distance: Vec<G::NodeId>,
402-
predecessors: HashMap<G::NodeId, Vec<G::NodeId>>,
403-
sigma: HashMap<G::NodeId, f64>,
401+
predecessors: Vec<Vec<usize>>,
402+
sigma: Vec<f64>,
404403
}
405-
406404
fn shortest_path_for_centrality<G>(graph: G, node_s: &G::NodeId) -> ShortestPathData<G>
407405
where
408406
G: NodeIndexable + IntoNodeIdentifiers + IntoNeighborsDirected + NodeCount + GraphBase,
409-
<G as GraphBase>::NodeId: std::cmp::Eq + Hash,
407+
<G as GraphBase>::NodeId: std::cmp::Eq,
410408
{
411-
let mut verts_sorted_by_distance: Vec<G::NodeId> = Vec::new(); // a stack
412409
let c = graph.node_count();
413-
let mut predecessors = HashMap::<G::NodeId, Vec<G::NodeId>>::with_capacity(c);
414-
let mut sigma = HashMap::<G::NodeId, f64>::with_capacity(c);
415-
let mut distance = HashMap::<G::NodeId, i64>::with_capacity(c);
410+
let max_index = graph.node_bound();
411+
let mut verts_sorted_by_distance: Vec<G::NodeId> = Vec::with_capacity(c); // a stack
412+
let mut predecessors: Vec<Vec<usize>> = vec![Vec::new(); max_index];
413+
let mut sigma: Vec<f64> = vec![0.; max_index];
414+
let mut distance: Vec<Option<usize>> = vec![None; max_index];
416415
#[allow(non_snake_case)]
417416
let mut Q: VecDeque<G::NodeId> = VecDeque::with_capacity(c);
418-
419-
for node in graph.node_identifiers() {
420-
predecessors.insert(node, Vec::new());
421-
sigma.insert(node, 0.0);
422-
distance.insert(node, -1);
423-
}
424-
sigma.insert(*node_s, 1.0);
425-
distance.insert(*node_s, 0);
417+
let node_s_index = graph.to_index(*node_s);
418+
sigma[node_s_index] = 1.0;
419+
distance[node_s_index] = Some(0);
426420
Q.push_back(*node_s);
427421
while let Some(v) = Q.pop_front() {
428422
verts_sorted_by_distance.push(v);
429-
let distance_v = distance[&v];
423+
let v_idx = graph.to_index(v);
424+
let distance_v = distance[v_idx].unwrap();
430425
for w in graph.neighbors(v) {
431-
if distance[&w] < 0 {
426+
let w_idx = graph.to_index(w);
427+
if distance[w_idx].is_none() {
432428
Q.push_back(w);
433-
distance.insert(w, distance_v + 1);
429+
distance[w_idx] = Some(distance_v + 1);
434430
}
435-
if distance[&w] == distance_v + 1 {
436-
sigma.insert(w, sigma[&w] + sigma[&v]);
437-
let e_p = predecessors.get_mut(&w).unwrap();
438-
e_p.push(v);
431+
if distance[w_idx] == Some(distance_v + 1) {
432+
sigma[w_idx] += sigma[v_idx];
433+
predecessors[w_idx].push(v_idx);
439434
}
440435
}
441436
}
@@ -450,13 +445,13 @@ where
450445
struct ShortestPathDataWithEdges<G>
451446
where
452447
G: GraphBase,
453-
G::NodeId: Eq + Hash,
454-
G::EdgeId: Eq + Hash,
448+
G::NodeId: Eq,
449+
G::EdgeId: Eq,
455450
{
456451
verts_sorted_by_distance: Vec<G::NodeId>,
457-
predecessors: HashMap<G::NodeId, Vec<G::NodeId>>,
458-
predecessor_edges: HashMap<G::NodeId, Vec<G::EdgeId>>,
459-
sigma: HashMap<G::NodeId, f64>,
452+
predecessors: Vec<Vec<G::NodeId>>,
453+
predecessor_edges: Vec<Vec<G::EdgeId>>,
454+
sigma: Vec<f64>,
460455
}
461456

462457
fn shortest_path_for_edge_centrality<G>(
@@ -470,41 +465,37 @@ where
470465
+ NodeCount
471466
+ GraphBase
472467
+ IntoEdges,
473-
G::NodeId: Eq + Hash,
474-
G::EdgeId: Eq + Hash,
468+
G::NodeId: Eq,
469+
G::EdgeId: Eq,
475470
{
476471
let mut verts_sorted_by_distance: Vec<G::NodeId> = Vec::new(); // a stack
477-
let c = graph.node_count();
478-
let mut predecessors = HashMap::<G::NodeId, Vec<G::NodeId>>::with_capacity(c);
479-
let mut predecessor_edges = HashMap::<G::NodeId, Vec<G::EdgeId>>::with_capacity(c);
480-
let mut sigma = HashMap::<G::NodeId, f64>::with_capacity(c);
481-
let mut distance = HashMap::<G::NodeId, i64>::with_capacity(c);
472+
let c = graph.node_bound();
473+
let mut predecessors = vec![Vec::new(); c];
474+
let mut predecessor_edges = vec![Vec::new(); c];
475+
let mut sigma = vec![0.0; c];
476+
let mut distance: Vec<Option<usize>> = vec![None; c];
482477
#[allow(non_snake_case)]
483478
let mut Q: VecDeque<G::NodeId> = VecDeque::with_capacity(c);
484479

485-
for node in graph.node_identifiers() {
486-
predecessors.insert(node, Vec::new());
487-
predecessor_edges.insert(node, Vec::new());
488-
sigma.insert(node, 0.0);
489-
distance.insert(node, -1);
490-
}
491-
sigma.insert(*node_s, 1.0);
492-
distance.insert(*node_s, 0);
480+
sigma[graph.to_index(*node_s)] = 1.;
481+
distance[graph.to_index(*node_s)] = Some(0);
493482
Q.push_back(*node_s);
494483
while let Some(v) = Q.pop_front() {
495484
verts_sorted_by_distance.push(v);
496-
let distance_v = distance[&v];
485+
let v_index = graph.to_index(v);
486+
let distance_v = distance[v_index].unwrap();
497487
for edge in graph.edges(v) {
498488
let w = edge.target();
499-
if distance[&w] < 0 {
489+
let w_index = graph.to_index(w);
490+
if distance[w_index].is_none() {
500491
Q.push_back(w);
501-
distance.insert(w, distance_v + 1);
492+
distance[w_index] = Some(distance_v + 1);
502493
}
503-
if distance[&w] == distance_v + 1 {
504-
sigma.insert(w, sigma[&w] + sigma[&v]);
505-
let e_p = predecessors.get_mut(&w).unwrap();
494+
if distance[w_index] == Some(distance_v + 1) {
495+
sigma[w_index] += sigma[v_index];
496+
let e_p = predecessors.get_mut(w_index).unwrap();
506497
e_p.push(v);
507-
predecessor_edges.get_mut(&w).unwrap().push(edge.id());
498+
predecessor_edges.get_mut(w_index).unwrap().push(edge.id());
508499
}
509500
}
510501
}
@@ -641,6 +632,66 @@ mod test_edge_betweenness_centrality {
641632
let expected_values = vec![Some(3.0), None, Some(3.0), Some(4.0)];
642633
assert_eq!(result, expected_values);
643634
}
635+
636+
#[test]
637+
fn test_stable_graph_with_removed_nodes_and_edges() {
638+
let mut graph: StableGraph<(), (), Undirected> = StableGraph::default();
639+
let n0 = graph.add_node(());
640+
let d0 = graph.add_node(());
641+
let n1 = graph.add_node(());
642+
let d1 = graph.add_node(());
643+
let n2 = graph.add_node(());
644+
let d2 = graph.add_node(());
645+
let n3 = graph.add_node(());
646+
647+
graph.remove_node(d0);
648+
graph.remove_node(d1);
649+
graph.remove_node(d2);
650+
651+
graph.add_edge(n0, n1, ());
652+
graph.add_edge(n1, n2, ());
653+
graph.add_edge(n2, n3, ());
654+
graph.add_edge(n3, n0, ());
655+
656+
graph.remove_edge(edge_index(1));
657+
let result = edge_betweenness_centrality(&graph, false, 50);
658+
let expected_values = vec![Some(3.0), None, Some(3.0), Some(4.0)];
659+
assert_eq!(result, expected_values);
660+
}
661+
}
662+
663+
#[cfg(test)]
664+
mod test_betweenness_centrality {
665+
use crate::centrality::betweenness_centrality;
666+
use petgraph::Undirected;
667+
use petgraph::graph::edge_index;
668+
use petgraph::prelude::StableGraph;
669+
670+
#[test]
671+
fn test_stable_graph_with_removed_nodes_and_edges() {
672+
let mut graph: StableGraph<(), (), Undirected> = StableGraph::default();
673+
let n0 = graph.add_node(());
674+
let d0 = graph.add_node(());
675+
let n1 = graph.add_node(());
676+
let d1 = graph.add_node(());
677+
let n2 = graph.add_node(());
678+
let d2 = graph.add_node(());
679+
let n3 = graph.add_node(());
680+
681+
graph.remove_node(d0);
682+
graph.remove_node(d1);
683+
graph.remove_node(d2);
684+
685+
graph.add_edge(n0, n1, ());
686+
graph.add_edge(n1, n2, ());
687+
graph.add_edge(n2, n3, ());
688+
graph.add_edge(n3, n0, ());
689+
graph.remove_edge(edge_index(1));
690+
691+
let result = betweenness_centrality(&graph, false, false, 50);
692+
let expected_values = vec![Some(2.0), None, Some(0.0), None, Some(0.0), None, Some(2.0)];
693+
assert_eq!(result, expected_values);
694+
}
644695
}
645696

646697
/// Compute the eigenvector centrality of a graph
@@ -694,7 +745,7 @@ pub fn eigenvector_centrality<G, F, E>(
694745
) -> Result<Option<Vec<f64>>, E>
695746
where
696747
G: NodeIndexable + IntoNodeIdentifiers + IntoNeighbors + IntoEdges + NodeCount,
697-
G::NodeId: Eq + std::hash::Hash,
748+
G::NodeId: Eq,
698749
F: FnMut(G::EdgeRef) -> Result<f64, E>,
699750
{
700751
let tol: f64 = tol.unwrap_or(1e-6);
@@ -790,7 +841,7 @@ pub fn katz_centrality<G, F, E>(
790841
) -> Result<Option<Vec<f64>>, E>
791842
where
792843
G: NodeIndexable + IntoNodeIdentifiers + IntoNeighbors + IntoEdges + NodeCount,
793-
G::NodeId: Eq + std::hash::Hash,
844+
G::NodeId: Eq,
794845
F: FnMut(G::EdgeRef) -> Result<f64, E>,
795846
{
796847
let alpha: f64 = alpha.unwrap_or(0.1);

0 commit comments

Comments
 (0)