rust_igraph/algorithms/properties/
edge_betweenness_cutoff.rs1use std::collections::VecDeque;
10
11use crate::core::graph::EdgeId;
12use crate::core::{Graph, IgraphResult, VertexId};
13
14pub fn edge_betweenness_cutoff(graph: &Graph, cutoff: u32) -> IgraphResult<Vec<f64>> {
42 let n = graph.vcount();
43 let n_us = n as usize;
44 let m = graph.ecount();
45 let mut betweenness_e = vec![0.0_f64; m];
46
47 if n == 0 || m == 0 {
48 return Ok(betweenness_e);
49 }
50
51 let mut sigma = vec![0.0_f64; n_us];
52 let mut dist = vec![-1_i64; n_us];
53 let mut pred: Vec<Vec<(VertexId, EdgeId)>> = vec![Vec::new(); n_us];
54 let mut stack: Vec<VertexId> = Vec::with_capacity(n_us);
55 let mut delta_v = vec![0.0_f64; n_us];
56 let cutoff_i64 = i64::from(cutoff);
57
58 for s in 0..n {
59 sigma.fill(0.0);
60 dist.fill(-1);
61 for slot in &mut pred {
62 slot.clear();
63 }
64 stack.clear();
65 delta_v.fill(0.0);
66
67 sigma[s as usize] = 1.0;
68 dist[s as usize] = 0;
69 let mut queue: VecDeque<VertexId> = VecDeque::new();
70 queue.push_back(s);
71
72 while let Some(v) = queue.pop_front() {
73 stack.push(v);
74 let v_dist = dist[v as usize];
75
76 if v_dist >= cutoff_i64 {
77 continue;
78 }
79
80 for e in graph.incident(v)? {
81 let w = graph.edge_other(e, v)?;
82 if dist[w as usize] < 0 {
83 queue.push_back(w);
84 dist[w as usize] = v_dist + 1;
85 }
86 if dist[w as usize] == v_dist + 1 {
87 sigma[w as usize] += sigma[v as usize];
88 pred[w as usize].push((v, e));
89 }
90 }
91 }
92
93 while let Some(w) = stack.pop() {
94 for &(v, e) in &pred[w as usize] {
95 let c = (sigma[v as usize] / sigma[w as usize]) * (1.0 + delta_v[w as usize]);
96 delta_v[v as usize] += c;
97 betweenness_e[e as usize] += c;
98 }
99 }
100 }
101
102 if !graph.is_directed() {
103 for slot in &mut betweenness_e {
104 *slot /= 2.0;
105 }
106 }
107
108 Ok(betweenness_e)
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 fn close(actual: &[f64], expected: &[f64], tol: f64) {
116 assert_eq!(actual.len(), expected.len(), "length mismatch");
117 for (i, (a, e)) in actual.iter().zip(expected.iter()).enumerate() {
118 assert!((a - e).abs() < tol, "edge {i}: actual={a} expected={e}");
119 }
120 }
121
122 #[test]
123 fn empty_graph() {
124 let g = Graph::new(0, false).unwrap();
125 let eb = edge_betweenness_cutoff(&g, 1).unwrap();
126 assert!(eb.is_empty());
127 }
128
129 #[test]
130 fn no_edges() {
131 let g = Graph::new(3, false).unwrap();
132 let eb = edge_betweenness_cutoff(&g, 5).unwrap();
133 assert!(eb.is_empty());
134 }
135
136 #[test]
137 fn path_full_cutoff() {
138 let mut g = Graph::new(4, false).unwrap();
140 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
141 let eb = edge_betweenness_cutoff(&g, 100).unwrap();
142 close(&eb, &[3.0, 4.0, 3.0], 1e-12);
143 }
144
145 #[test]
146 fn path_cutoff_1() {
147 let mut g = Graph::new(4, false).unwrap();
149 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
150 let eb = edge_betweenness_cutoff(&g, 1).unwrap();
151 close(&eb, &[1.0, 1.0, 1.0], 1e-12);
153 }
154
155 #[test]
156 fn path_cutoff_2() {
157 let mut g = Graph::new(4, false).unwrap();
162 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
163 let eb = edge_betweenness_cutoff(&g, 2).unwrap();
164 close(&eb, &[2.0, 3.0, 2.0], 1e-12);
165 }
166
167 #[test]
168 fn triangle() {
169 let mut g = Graph::new(3, false).unwrap();
171 g.add_edges(vec![(0, 1), (1, 2), (2, 0)]).unwrap();
172 let eb = edge_betweenness_cutoff(&g, 10).unwrap();
173 close(&eb, &[1.0, 1.0, 1.0], 1e-12);
174 }
175
176 #[test]
177 fn star_cutoff_2() {
178 let mut g = Graph::new(5, false).unwrap();
182 g.add_edges(vec![(0, 1), (0, 2), (0, 3), (0, 4)]).unwrap();
183 let eb = edge_betweenness_cutoff(&g, 2).unwrap();
184 close(&eb, &[4.0, 4.0, 4.0, 4.0], 1e-12);
185 }
186
187 #[test]
188 fn directed_path() {
189 let mut g = Graph::new(4, true).unwrap();
191 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
192 let eb = edge_betweenness_cutoff(&g, 100).unwrap();
193 close(&eb, &[3.0, 4.0, 3.0], 1e-12);
197 }
198
199 #[test]
200 fn directed_cutoff_2() {
201 let mut g = Graph::new(4, true).unwrap();
206 g.add_edges(vec![(0, 1), (1, 2), (2, 3)]).unwrap();
207 let eb = edge_betweenness_cutoff(&g, 2).unwrap();
208 close(&eb, &[2.0, 3.0, 2.0], 1e-12);
209 }
210
211 #[test]
212 fn cutoff_zero() {
213 let mut g = Graph::new(3, false).unwrap();
214 g.add_edges(vec![(0, 1), (1, 2)]).unwrap();
215 let eb = edge_betweenness_cutoff(&g, 0).unwrap();
216 close(&eb, &[0.0, 0.0], 1e-12);
217 }
218}