Run Betweenness Centrality on the CSR index. `params.sample_size`: if set, only sample that many source nodes (approximate). If `None`, compute exact centrality (all sources). Returns `(node_id, centrality)` sorted by centrality descending.
(csr: &CsrIndex, params: &AlgoParams)
| 23 | /// |
| 24 | /// Returns `(node_id, centrality)` sorted by centrality descending. |
| 25 | pub fn run(csr: &CsrIndex, params: &AlgoParams) -> AlgoResultBatch { |
| 26 | let n = csr.node_count(); |
| 27 | if n == 0 { |
| 28 | return AlgoResultBatch::new(GraphAlgorithm::Betweenness); |
| 29 | } |
| 30 | |
| 31 | let mut cb = vec![0.0f64; n]; |
| 32 | |
| 33 | // Determine source nodes: all or sampled subset. |
| 34 | let sources: Vec<usize> = match params.sample_size { |
| 35 | Some(sample) if sample < n => { |
| 36 | // Deterministic sampling via LCG. |
| 37 | let mut state: u64 = (n as u64).wrapping_mul(0x517cc1b727220a95).wrapping_add(1); |
| 38 | let mut selected = Vec::with_capacity(sample); |
| 39 | let mut used = vec![false; n]; |
| 40 | while selected.len() < sample { |
| 41 | state = state |
| 42 | .wrapping_mul(6_364_136_223_846_793_005) |
| 43 | .wrapping_add(1); |
| 44 | let idx = (state >> 33) as usize % n; |
| 45 | if !used[idx] { |
| 46 | used[idx] = true; |
| 47 | selected.push(idx); |
| 48 | } |
| 49 | } |
| 50 | selected |
| 51 | } |
| 52 | _ => (0..n).collect(), |
| 53 | }; |
| 54 | |
| 55 | let scale = if params.sample_size.is_some() && sources.len() < n { |
| 56 | // Scale approximate centrality to estimate full centrality. |
| 57 | n as f64 / sources.len() as f64 |
| 58 | } else { |
| 59 | 1.0 |
| 60 | }; |
| 61 | |
| 62 | // Brandes' algorithm: BFS from each source, then reverse accumulation. |
| 63 | for &s in &sources { |
| 64 | brandes_from_source(csr, s, n, &mut cb); |
| 65 | } |
| 66 | |
| 67 | // Scale and normalize. |
| 68 | if scale != 1.0 { |
| 69 | for c in cb.iter_mut() { |
| 70 | *c *= scale; |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | // Build result sorted by centrality descending. |
| 75 | let mut scored: Vec<(usize, f64)> = cb.into_iter().enumerate().collect(); |
| 76 | scored.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap_or(std::cmp::Ordering::Equal)); |
| 77 | |
| 78 | let mut batch = AlgoResultBatch::new(GraphAlgorithm::Betweenness); |
| 79 | for (node, centrality) in scored { |
| 80 | batch.push_node_f64(csr.node_name_raw(node as u32).to_string(), centrality); |
| 81 | } |
| 82 | batch |