1use std::collections::VecDeque;
14
15use crate::id::{GraphNodeId, index_of, position};
16
17use super::error::GraphAnalysisError;
18use super::index::{AnalysisContext, SelectedIndexes};
19use super::limits::GraphAnalysisLimits;
20
21#[derive(Clone, Copy, Debug)]
28pub struct BetweennessCentrality {
29 node: GraphNodeId,
30 raw: f64,
31 normalized: f64,
32}
33
34impl BetweennessCentrality {
35 pub fn node(self) -> GraphNodeId {
37 self.node
38 }
39
40 pub fn raw(self) -> f64 {
42 self.raw
43 }
44
45 pub fn normalized(self) -> f64 {
48 self.normalized
49 }
50}
51
52struct Brandes {
60 totals: Box<[f64]>,
61 distance: Box<[Option<u32>]>,
62 routes: Box<[f64]>,
63 predecessors: Box<[Vec<GraphNodeId>]>,
64 share: Box<[f64]>,
65 order: Vec<GraphNodeId>,
66 queue: VecDeque<GraphNodeId>,
67}
68
69impl Brandes {
70 fn empty(width: usize) -> Self {
72 Self {
73 totals: vec![0.0; width].into_boxed_slice(),
74 distance: vec![None; width].into_boxed_slice(),
75 routes: vec![0.0; width].into_boxed_slice(),
76 predecessors: vec![Vec::new(); width].into_boxed_slice(),
77 share: vec![0.0; width].into_boxed_slice(),
78 order: Vec::with_capacity(width),
79 queue: VecDeque::new(),
80 }
81 }
82
83 fn walk(&mut self, indexes: &SelectedIndexes) {
85 for at in 0..self.totals.len() {
86 self.round(indexes, GraphNodeId::new(position(at)));
87 }
88 }
89
90 fn round(&mut self, indexes: &SelectedIndexes, source: GraphNodeId) {
93 self.restart(source);
94 self.measure(indexes);
95 self.credit(source);
96 }
97
98 fn restart(&mut self, source: GraphNodeId) {
103 self.distance.iter_mut().for_each(|steps| *steps = None);
104 self.routes.iter_mut().for_each(|routes| *routes = 0.0);
105 self.share.iter_mut().for_each(|share| *share = 0.0);
106 self.predecessors.iter_mut().for_each(Vec::clear);
107 self.order.clear();
108 self.queue.clear();
109 self.settle(source, 0);
110 add(&mut self.routes, source, 1.0);
111 }
112
113 fn measure(&mut self, indexes: &SelectedIndexes) {
115 while let Some(node) = self.queue.pop_front() {
116 self.relax(indexes, node);
117 }
118 }
119
120 fn relax(&mut self, indexes: &SelectedIndexes, node: GraphNodeId) {
122 let next = match self.steps(node) {
123 Some(steps) => steps + 1,
124 None => return,
125 };
126 for entry in indexes.outgoing(node) {
127 self.follow((node, entry.node()), next);
128 }
129 }
130
131 fn follow(&mut self, step: (GraphNodeId, GraphNodeId), next: u32) {
137 let (from, reached) = step;
138 match self.steps(reached) {
139 None => self.settle(reached, next),
140 Some(steps) if steps == next => (),
141 Some(_) => return,
142 }
143 self.join(reached, from);
144 }
145
146 fn settle(&mut self, node: GraphNodeId, steps: u32) {
152 match self.distance.get_mut(index_of(node.index())) {
153 Some(slot) => *slot = Some(steps),
154 None => return,
155 }
156 self.order.push(node);
157 self.queue.push_back(node);
158 }
159
160 fn join(&mut self, node: GraphNodeId, from: GraphNodeId) {
162 let arriving = held(&self.routes, from);
163 add(&mut self.routes, node, arriving);
164 if let Some(list) = self.predecessors.get_mut(index_of(node.index())) {
165 list.push(from);
166 }
167 }
168
169 fn steps(&self, node: GraphNodeId) -> Option<u32> {
171 self.distance.get(index_of(node.index())).copied().flatten()
172 }
173
174 fn credit(&mut self, source: GraphNodeId) {
179 let Self {
180 totals,
181 routes,
182 predecessors,
183 share,
184 order,
185 ..
186 } = self;
187 for node in order.iter().rev() {
188 pass_back((&routes[..], &predecessors[..]), share, *node);
189 award((totals, &share[..]), source, *node);
190 }
191 }
192
193 fn scored(self) -> Box<[BetweennessCentrality]> {
195 let pairs = ordered_pairs(self.totals.len());
196 self.totals
197 .iter()
198 .enumerate()
199 .map(|(at, score)| BetweennessCentrality {
200 node: GraphNodeId::new(position(at)),
201 raw: *score,
202 normalized: against(*score, pairs),
203 })
204 .collect()
205 }
206}
207
208pub(crate) fn betweenness(
213 analysis: &AnalysisContext<'_>,
214) -> Result<Box<[BetweennessCentrality]>, GraphAnalysisError> {
215 let indexes = analysis.indexes();
216 admitted_work(indexes, analysis.limits())?;
217 let mut pass = Brandes::empty(indexes.node_count());
218 pass.walk(indexes);
219 Ok(pass.scored())
220}
221
222pub(crate) fn admitted_work(
229 indexes: &SelectedIndexes,
230 limits: GraphAnalysisLimits,
231) -> Result<(), GraphAnalysisError> {
232 let nodes = u64::from(position(indexes.node_count()));
233 let required = nodes
234 .checked_add(u64::from(indexes.selected_edges()))
235 .and_then(|round| nodes.checked_mul(round))
236 .ok_or(GraphAnalysisError::BetweennessWorkOverflow)?;
237 match required <= limits.max_betweenness_work() {
238 true => Ok(()),
239 false => Err(GraphAnalysisError::BetweennessWorkLimitExceeded {
240 required,
241 limit: limits.max_betweenness_work(),
242 }),
243 }
244}
245
246fn pass_back(reached: (&[f64], &[Vec<GraphNodeId>]), share: &mut [f64], node: GraphNodeId) {
248 let (routes, predecessors) = reached;
249 let carried = (1.0 + held(share, node)) / held(routes, node);
250 for from in entering(predecessors, node) {
251 add(share, *from, held(routes, *from) * carried);
252 }
253}
254
255fn award(state: (&mut [f64], &[f64]), source: GraphNodeId, node: GraphNodeId) {
257 let (totals, share) = state;
258 match node == source {
259 true => (),
260 false => add(totals, node, held(share, node)),
261 }
262}
263
264fn entering(predecessors: &[Vec<GraphNodeId>], node: GraphNodeId) -> &[GraphNodeId] {
266 match predecessors.get(index_of(node.index())) {
267 Some(list) => list,
268 None => &[],
269 }
270}
271
272fn held(values: &[f64], node: GraphNodeId) -> f64 {
274 values
275 .get(index_of(node.index()))
276 .copied()
277 .unwrap_or_default()
278}
279
280fn add(values: &mut [f64], node: GraphNodeId, amount: f64) {
282 if let Some(slot) = values.get_mut(index_of(node.index())) {
283 *slot += amount;
284 }
285}
286
287fn ordered_pairs(nodes: usize) -> Option<f64> {
290 match nodes > 2 {
291 true => Some((nodes - 1) as f64 * (nodes - 2) as f64),
292 false => None,
293 }
294}
295
296fn against(raw: f64, pairs: Option<f64>) -> f64 {
298 match pairs {
299 Some(count) => raw / count,
300 None => 0.0,
301 }
302}