reddb_server/storage/engine/algorithms/
centrality.rs1use std::collections::{HashMap, HashSet, VecDeque};
10
11use super::super::graph_store::GraphStore;
12
13pub struct BetweennessCentrality;
22
23#[derive(Debug, Clone)]
25pub struct BetweennessResult {
26 pub scores: HashMap<String, f64>,
28 pub normalized: bool,
30}
31
32impl BetweennessResult {
33 pub fn top(&self, n: usize) -> Vec<(String, f64)> {
35 let mut sorted: Vec<_> = self.scores.iter().map(|(k, v)| (k.clone(), *v)).collect();
36 sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
37 sorted.truncate(n);
38 sorted
39 }
40
41 pub fn score(&self, node_id: &str) -> Option<f64> {
43 self.scores.get(node_id).copied()
44 }
45}
46
47impl BetweennessCentrality {
48 pub fn compute(graph: &GraphStore, normalize: bool) -> BetweennessResult {
52 let nodes: Vec<String> = graph.iter_nodes().map(|n| n.id.clone()).collect();
53 let n = nodes.len();
54
55 if n < 2 {
56 return BetweennessResult {
57 scores: nodes.into_iter().map(|id| (id, 0.0)).collect(),
58 normalized: normalize,
59 };
60 }
61
62 let mut centrality: HashMap<String, f64> =
63 nodes.iter().map(|id| (id.clone(), 0.0)).collect();
64
65 for source in &nodes {
67 let mut stack: Vec<String> = Vec::new();
69 let mut predecessors: HashMap<String, Vec<String>> = HashMap::new();
70 let mut sigma: HashMap<String, f64> =
71 nodes.iter().map(|id| (id.clone(), 0.0)).collect();
72 let mut dist: HashMap<String, i64> = nodes.iter().map(|id| (id.clone(), -1)).collect();
73
74 sigma.insert(source.clone(), 1.0);
75 dist.insert(source.clone(), 0);
76
77 let mut queue: VecDeque<String> = VecDeque::new();
78 queue.push_back(source.clone());
79
80 while let Some(v) = queue.pop_front() {
82 stack.push(v.clone());
83 let v_dist = *dist.get(&v).unwrap_or(&0);
84
85 for (_, w, _) in graph.outgoing_edges(&v) {
86 if *dist.get(&w).unwrap_or(&-1) < 0 {
88 queue.push_back(w.clone());
89 dist.insert(w.clone(), v_dist + 1);
90 }
91
92 if *dist.get(&w).unwrap_or(&0) == v_dist + 1 {
94 let sigma_v = *sigma.get(&v).unwrap_or(&0.0);
95 let sigma_w = sigma.entry(w.clone()).or_insert(0.0);
96 *sigma_w += sigma_v;
97 predecessors.entry(w.clone()).or_default().push(v.clone());
98 }
99 }
100 }
101
102 let mut delta: HashMap<String, f64> =
104 nodes.iter().map(|id| (id.clone(), 0.0)).collect();
105
106 while let Some(w) = stack.pop() {
107 if let Some(preds) = predecessors.get(&w) {
108 let sigma_w = *sigma.get(&w).unwrap_or(&1.0);
109 let delta_w = *delta.get(&w).unwrap_or(&0.0);
110
111 for v in preds {
112 let sigma_v = *sigma.get(v).unwrap_or(&1.0);
113 let d = (sigma_v / sigma_w) * (1.0 + delta_w);
114 *delta.entry(v.clone()).or_insert(0.0) += d;
115 }
116 }
117
118 if w != *source {
119 let c = centrality.entry(w.clone()).or_insert(0.0);
120 *c += *delta.get(&w).unwrap_or(&0.0);
121 }
122 }
123 }
124
125 if normalize && n > 2 {
127 let norm_factor = 1.0 / ((n - 1) * (n - 2)) as f64;
128 for score in centrality.values_mut() {
129 *score *= norm_factor;
130 }
131 }
132
133 BetweennessResult {
134 scores: centrality,
135 normalized: normalize,
136 }
137 }
138}
139
140pub struct ClosenessCentrality;
149
150#[derive(Debug, Clone)]
152pub struct ClosenessResult {
153 pub scores: HashMap<String, f64>,
155}
156
157impl ClosenessResult {
158 pub fn top(&self, n: usize) -> Vec<(String, f64)> {
160 let mut sorted: Vec<_> = self.scores.iter().map(|(k, v)| (k.clone(), *v)).collect();
161 sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
162 sorted.truncate(n);
163 sorted
164 }
165}
166
167impl ClosenessCentrality {
168 pub fn compute(graph: &GraphStore) -> ClosenessResult {
173 let nodes: Vec<String> = graph.iter_nodes().map(|n| n.id.clone()).collect();
174 let n = nodes.len();
175
176 if n <= 1 {
177 return ClosenessResult {
178 scores: nodes.into_iter().map(|id| (id, 1.0)).collect(),
179 };
180 }
181
182 let mut scores: HashMap<String, f64> = HashMap::new();
183
184 for source in &nodes {
185 let mut distances: HashMap<String, usize> = HashMap::new();
187 let mut queue: VecDeque<(String, usize)> = VecDeque::new();
188
189 queue.push_back((source.clone(), 0));
190 distances.insert(source.clone(), 0);
191
192 while let Some((current, dist)) = queue.pop_front() {
193 for (_, neighbor, _) in graph.outgoing_edges(¤t) {
194 if let std::collections::hash_map::Entry::Vacant(slot) =
196 distances.entry(neighbor)
197 {
198 let next_dist = dist + 1;
199 queue.push_back((slot.key().clone(), next_dist));
200 slot.insert(next_dist);
201 }
202 }
203 }
204
205 let sum_reciprocal: f64 = distances
207 .iter()
208 .filter(|(k, _)| *k != source)
209 .map(|(_, d)| 1.0 / (*d as f64))
210 .sum();
211
212 let closeness = sum_reciprocal / (n - 1) as f64;
213 scores.insert(source.clone(), closeness);
214 }
215
216 ClosenessResult { scores }
217 }
218}
219
220pub struct DegreeCentrality;
230
231#[derive(Debug, Clone)]
233pub struct DegreeCentralityResult {
234 pub in_degree: HashMap<String, usize>,
236 pub out_degree: HashMap<String, usize>,
238 pub total_degree: HashMap<String, usize>,
240}
241
242impl DegreeCentralityResult {
243 pub fn top_by_total(&self, n: usize) -> Vec<(String, usize)> {
245 let mut sorted: Vec<_> = self
246 .total_degree
247 .iter()
248 .map(|(k, v)| (k.clone(), *v))
249 .collect();
250 sorted.sort_by_key(|b| std::cmp::Reverse(b.1));
251 sorted.truncate(n);
252 sorted
253 }
254
255 pub fn top_by_in_degree(&self, n: usize) -> Vec<(String, usize)> {
257 let mut sorted: Vec<_> = self
258 .in_degree
259 .iter()
260 .map(|(k, v)| (k.clone(), *v))
261 .collect();
262 sorted.sort_by_key(|b| std::cmp::Reverse(b.1));
263 sorted.truncate(n);
264 sorted
265 }
266
267 pub fn top_by_out_degree(&self, n: usize) -> Vec<(String, usize)> {
269 let mut sorted: Vec<_> = self
270 .out_degree
271 .iter()
272 .map(|(k, v)| (k.clone(), *v))
273 .collect();
274 sorted.sort_by_key(|b| std::cmp::Reverse(b.1));
275 sorted.truncate(n);
276 sorted
277 }
278}
279
280impl DegreeCentrality {
281 pub fn compute(graph: &GraphStore) -> DegreeCentralityResult {
283 let mut in_degree: HashMap<String, usize> = HashMap::new();
284 let mut out_degree: HashMap<String, usize> = HashMap::new();
285
286 for node in graph.iter_nodes() {
288 in_degree.insert(node.id.clone(), 0);
289 out_degree.insert(node.id.clone(), 0);
290 }
291
292 for node in graph.iter_nodes() {
294 let out_count = graph.outgoing_edges(&node.id).len();
295 out_degree.insert(node.id.clone(), out_count);
296
297 for (_, target, _) in graph.outgoing_edges(&node.id) {
299 *in_degree.entry(target).or_insert(0) += 1;
300 }
301 }
302
303 let total_degree: HashMap<String, usize> = in_degree
305 .keys()
306 .map(|k| {
307 let total = in_degree.get(k).unwrap_or(&0) + out_degree.get(k).unwrap_or(&0);
308 (k.clone(), total)
309 })
310 .collect();
311
312 DegreeCentralityResult {
313 in_degree,
314 out_degree,
315 total_degree,
316 }
317 }
318}
319
320pub struct EigenvectorCentrality {
329 pub epsilon: f64,
331 pub max_iterations: usize,
333}
334
335impl Default for EigenvectorCentrality {
336 fn default() -> Self {
337 Self {
338 epsilon: 1e-6,
339 max_iterations: 100,
340 }
341 }
342}
343
344#[derive(Debug, Clone)]
346pub struct EigenvectorResult {
347 pub scores: HashMap<String, f64>,
349 pub iterations: usize,
351 pub converged: bool,
353}
354
355impl EigenvectorResult {
356 pub fn top(&self, n: usize) -> Vec<(String, f64)> {
358 let mut sorted: Vec<_> = self.scores.iter().map(|(k, v)| (k.clone(), *v)).collect();
359 sorted.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal));
360 sorted.truncate(n);
361 sorted
362 }
363}
364
365impl EigenvectorCentrality {
366 pub fn new() -> Self {
367 Self::default()
368 }
369
370 pub fn compute(&self, graph: &GraphStore) -> EigenvectorResult {
372 let nodes: Vec<String> = graph.iter_nodes().map(|n| n.id.clone()).collect();
373 let n = nodes.len();
374
375 if n == 0 {
376 return EigenvectorResult {
377 scores: HashMap::new(),
378 iterations: 0,
379 converged: true,
380 };
381 }
382
383 let mut neighbors: HashMap<String, Vec<String>> = HashMap::new();
385 for node in &nodes {
386 let mut node_neighbors: HashSet<String> = HashSet::new();
387 for (_, target, _) in graph.outgoing_edges(node) {
388 node_neighbors.insert(target);
389 }
390 for (_, source, _) in graph.incoming_edges(node) {
391 node_neighbors.insert(source);
392 }
393 neighbors.insert(node.clone(), node_neighbors.into_iter().collect());
394 }
395
396 let init_score = 1.0 / (n as f64).sqrt();
398 let mut scores: HashMap<String, f64> =
399 nodes.iter().map(|id| (id.clone(), init_score)).collect();
400
401 let mut converged = false;
402 let mut iterations = 0;
403
404 for iter in 0..self.max_iterations {
405 iterations = iter + 1;
406 let mut new_scores: HashMap<String, f64> = HashMap::new();
407
408 for node in &nodes {
410 let sum: f64 = neighbors
411 .get(node)
412 .map(|nbrs| {
413 nbrs.iter()
414 .map(|n| scores.get(n).copied().unwrap_or(0.0))
415 .sum()
416 })
417 .unwrap_or(0.0);
418 new_scores.insert(node.clone(), sum);
419 }
420
421 let norm: f64 = new_scores.values().map(|v| v * v).sum::<f64>().sqrt();
423 if norm > 0.0 {
424 for score in new_scores.values_mut() {
425 *score /= norm;
426 }
427 }
428
429 let diff: f64 = nodes
431 .iter()
432 .map(|id| {
433 let old = scores.get(id).copied().unwrap_or(0.0);
434 let new = new_scores.get(id).copied().unwrap_or(0.0);
435 (old - new).abs()
436 })
437 .sum();
438
439 scores = new_scores;
440
441 if diff < self.epsilon {
442 converged = true;
443 break;
444 }
445 }
446
447 EigenvectorResult {
448 scores,
449 iterations,
450 converged,
451 }
452 }
453}