uni_algo/algo/algorithms/
eigenvector_centrality.rs1use crate::algo::GraphProjection;
10use crate::algo::algorithms::Algorithm;
11use uni_common::core::id::Vid;
12
13pub struct EigenvectorCentrality;
14
15#[derive(Debug, Clone)]
16pub struct EigenvectorCentralityConfig {
17 pub max_iterations: usize,
18 pub tolerance: f64,
19}
20
21impl Default for EigenvectorCentralityConfig {
22 fn default() -> Self {
23 Self {
24 max_iterations: 100,
25 tolerance: 1e-6,
26 }
27 }
28}
29
30pub struct EigenvectorCentralityResult {
31 pub scores: Vec<(Vid, f64)>,
32 pub iterations: usize,
33}
34
35impl Algorithm for EigenvectorCentrality {
36 type Config = EigenvectorCentralityConfig;
37 type Result = EigenvectorCentralityResult;
38
39 fn name() -> &'static str {
40 "eigenvector_centrality"
41 }
42
43 fn run(graph: &GraphProjection, config: Self::Config) -> Self::Result {
44 let n = graph.vertex_count();
45 if n == 0 {
46 return EigenvectorCentralityResult {
47 scores: Vec::new(),
48 iterations: 0,
49 };
50 }
51
52 let mut x = vec![1.0 / (n as f64).sqrt(); n]; let mut next_x = vec![0.0; n];
54 let mut iterations = 0;
55
56 for iter in 0..config.max_iterations {
57 iterations = iter + 1;
58 next_x.fill(0.0);
59
60 for (u, &x_u) in x.iter().enumerate().take(n) {
69 if x_u == 0.0 {
70 continue;
71 }
72 for (i, &v_u32) in graph.out_neighbors(u as u32).iter().enumerate() {
73 let weight = if graph.has_weights() {
74 graph.out_weight(u as u32, i)
75 } else {
76 1.0
77 };
78 next_x[v_u32 as usize] += x_u * weight;
79 }
80 }
81
82 let mut norm_sq = 0.0;
84 for val in &next_x {
85 norm_sq += val * val;
86 }
87 let norm = norm_sq.sqrt();
88
89 if norm == 0.0 {
90 break;
92 }
93
94 for val in &mut next_x {
95 *val /= norm;
96 }
97
98 let mut diff = 0.0;
100 for i in 0..n {
101 diff += (next_x[i] - x[i]).abs();
102 }
103
104 x.copy_from_slice(&next_x);
105
106 if diff < config.tolerance {
107 break;
108 }
109 }
110
111 let scores = x
112 .into_iter()
113 .enumerate()
114 .map(|(i, s)| (graph.to_vid(i as u32), s))
115 .collect();
116
117 EigenvectorCentralityResult { scores, iterations }
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::*;
124 use crate::algo::test_utils::build_test_graph;
125
126 #[test]
127 fn test_eigenvector_centrality_star() {
128 let vids2 = vec![Vid::from(0), Vid::from(1), Vid::from(2), Vid::from(3)];
133 let edges2 = vec![
134 (Vid::from(0), Vid::from(1)),
135 (Vid::from(1), Vid::from(0)),
136 (Vid::from(1), Vid::from(2)),
137 (Vid::from(2), Vid::from(1)),
138 (Vid::from(2), Vid::from(0)),
139 (Vid::from(0), Vid::from(2)),
140 (Vid::from(0), Vid::from(3)),
141 (Vid::from(3), Vid::from(0)),
142 ];
143 let graph2 = build_test_graph(vids2, edges2);
144 let result2 = EigenvectorCentrality::run(&graph2, EigenvectorCentralityConfig::default());
145 let map2: std::collections::HashMap<_, _> = result2.scores.into_iter().collect();
146
147 assert!(map2[&Vid::from(0)] > map2[&Vid::from(3)]);
149 assert!(map2[&Vid::from(0)] > map2[&Vid::from(1)]);
150 }
151}