rust_igraph/algorithms/properties/
degree_eccentricity.rs1#![allow(
15 clippy::cast_possible_truncation,
16 clippy::cast_precision_loss,
17 clippy::many_single_char_names,
18 clippy::needless_range_loop,
19 clippy::too_many_lines
20)]
21
22use crate::core::{Graph, IgraphResult};
23
24fn bfs_ecc_and_dist_sum(graph: &Graph) -> IgraphResult<(Vec<u32>, Vec<u64>)> {
25 let n = graph.vcount() as usize;
26 let mut ecc = vec![0_u32; n];
27 let mut dist_sum = vec![0_u64; n];
28
29 for s in 0..n {
30 let mut dist = vec![u32::MAX; n];
31 dist[s] = 0;
32 let mut queue = std::collections::VecDeque::new();
33 queue.push_back(s as u32);
34 while let Some(u) = queue.pop_front() {
35 let d_u = dist[u as usize];
36 for nb in graph.neighbors(u)? {
37 let idx = nb as usize;
38 if dist[idx] == u32::MAX {
39 dist[idx] = d_u + 1;
40 queue.push_back(nb);
41 }
42 }
43 }
44 let mut max_d = 0_u32;
45 let mut sum = 0_u64;
46 for &d in &dist {
47 if d != u32::MAX {
48 if d > max_d {
49 max_d = d;
50 }
51 sum += u64::from(d);
52 }
53 }
54 ecc[s] = max_d;
55 dist_sum[s] = sum;
56 }
57
58 Ok((ecc, dist_sum))
59}
60
61pub fn lanzhou_index(graph: &Graph) -> IgraphResult<u64> {
78 let n = graph.vcount();
79 if n == 0 {
80 return Ok(0);
81 }
82
83 let (ecc, _) = bfs_ecc_and_dist_sum(graph)?;
84 let mut lz = 0_u64;
85
86 for v in 0..n {
87 let d = graph.degree(v)? as u64;
88 let e = u64::from(ecc[v as usize]);
89 lz = lz.saturating_add(d.saturating_mul(d).saturating_mul(e));
90 }
91
92 Ok(lz)
93}
94
95pub fn degree_eccentricity_index(graph: &Graph) -> IgraphResult<u64> {
112 let n = graph.vcount();
113 if n == 0 {
114 return Ok(0);
115 }
116
117 let (ecc, _) = bfs_ecc_and_dist_sum(graph)?;
118 let mut de = 0_u64;
119
120 for v in 0..n {
121 let d = graph.degree(v)? as u64;
122 let e = u64::from(ecc[v as usize]);
123 de = de.saturating_add(d.saturating_mul(e));
124 }
125
126 Ok(de)
127}
128
129pub fn eccentric_distance_sum(graph: &Graph) -> IgraphResult<u64> {
148 let n = graph.vcount();
149 if n == 0 {
150 return Ok(0);
151 }
152
153 let (ecc, dist_sum) = bfs_ecc_and_dist_sum(graph)?;
154 let mut eds = 0_u64;
155
156 for v in 0..n as usize {
157 let e = u64::from(ecc[v]);
158 eds = eds.saturating_add(e.saturating_mul(dist_sum[v]));
159 }
160
161 Ok(eds)
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167
168 fn single_edge() -> Graph {
169 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
170 }
171
172 fn path3() -> Graph {
173 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
174 }
175
176 fn path4() -> Graph {
177 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
178 }
179
180 fn k3() -> Graph {
181 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
182 }
183
184 fn k4() -> Graph {
185 Graph::from_edges(
186 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
187 false,
188 Some(4),
189 )
190 .unwrap()
191 }
192
193 fn cycle4() -> Graph {
194 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
195 }
196
197 fn cycle5() -> Graph {
198 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
199 }
200
201 fn star5() -> Graph {
202 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
203 }
204
205 fn paw() -> Graph {
206 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
207 }
208
209 #[test]
212 fn bfs_path3() {
213 let (ecc, ds) = bfs_ecc_and_dist_sum(&path3()).unwrap();
214 assert_eq!(ecc, vec![2, 1, 2]);
215 assert_eq!(ds, vec![3, 2, 3]);
216 }
217
218 #[test]
219 fn bfs_k3() {
220 let (ecc, ds) = bfs_ecc_and_dist_sum(&k3()).unwrap();
221 assert_eq!(ecc, vec![1, 1, 1]);
222 assert_eq!(ds, vec![2, 2, 2]);
223 }
224
225 #[test]
226 fn bfs_star5() {
227 let (ecc, ds) = bfs_ecc_and_dist_sum(&star5()).unwrap();
228 assert_eq!(ecc[0], 1);
229 for i in 1..5 {
230 assert_eq!(ecc[i], 2);
231 }
232 assert_eq!(ds[0], 4);
233 for i in 1..5 {
234 assert_eq!(ds[i], 7);
235 }
236 }
237
238 #[test]
241 fn lz_empty() {
242 let g = Graph::with_vertices(0);
243 assert_eq!(lanzhou_index(&g).unwrap(), 0);
244 }
245
246 #[test]
247 fn lz_isolated() {
248 let g = Graph::with_vertices(5);
249 assert_eq!(lanzhou_index(&g).unwrap(), 0);
250 }
251
252 #[test]
253 fn lz_single_edge() {
254 assert_eq!(lanzhou_index(&single_edge()).unwrap(), 2);
256 }
257
258 #[test]
259 fn lz_path3() {
260 assert_eq!(lanzhou_index(&path3()).unwrap(), 8);
262 }
263
264 #[test]
265 fn lz_path4() {
266 assert_eq!(lanzhou_index(&path4()).unwrap(), 22);
269 }
270
271 #[test]
272 fn lz_k3() {
273 assert_eq!(lanzhou_index(&k3()).unwrap(), 12);
275 }
276
277 #[test]
278 fn lz_k4() {
279 assert_eq!(lanzhou_index(&k4()).unwrap(), 36);
281 }
282
283 #[test]
284 fn lz_cycle4() {
285 assert_eq!(lanzhou_index(&cycle4()).unwrap(), 32);
287 }
288
289 #[test]
290 fn lz_cycle5() {
291 assert_eq!(lanzhou_index(&cycle5()).unwrap(), 40);
293 }
294
295 #[test]
296 fn lz_star5() {
297 assert_eq!(lanzhou_index(&star5()).unwrap(), 24);
300 }
301
302 #[test]
303 fn lz_paw() {
304 assert_eq!(lanzhou_index(&paw()).unwrap(), 27);
307 }
308
309 #[test]
312 fn de_empty() {
313 let g = Graph::with_vertices(0);
314 assert_eq!(degree_eccentricity_index(&g).unwrap(), 0);
315 }
316
317 #[test]
318 fn de_isolated() {
319 let g = Graph::with_vertices(5);
320 assert_eq!(degree_eccentricity_index(&g).unwrap(), 0);
321 }
322
323 #[test]
324 fn de_single_edge() {
325 assert_eq!(degree_eccentricity_index(&single_edge()).unwrap(), 2);
327 }
328
329 #[test]
330 fn de_path3() {
331 assert_eq!(degree_eccentricity_index(&path3()).unwrap(), 6);
333 }
334
335 #[test]
336 fn de_path4() {
337 assert_eq!(degree_eccentricity_index(&path4()).unwrap(), 14);
340 }
341
342 #[test]
343 fn de_k3() {
344 assert_eq!(degree_eccentricity_index(&k3()).unwrap(), 6);
346 }
347
348 #[test]
349 fn de_k4() {
350 assert_eq!(degree_eccentricity_index(&k4()).unwrap(), 12);
352 }
353
354 #[test]
355 fn de_cycle4() {
356 assert_eq!(degree_eccentricity_index(&cycle4()).unwrap(), 16);
358 }
359
360 #[test]
361 fn de_cycle5() {
362 assert_eq!(degree_eccentricity_index(&cycle5()).unwrap(), 20);
364 }
365
366 #[test]
367 fn de_star5() {
368 assert_eq!(degree_eccentricity_index(&star5()).unwrap(), 12);
370 }
371
372 #[test]
373 fn de_paw() {
374 assert_eq!(degree_eccentricity_index(&paw()).unwrap(), 13);
377 }
378
379 #[test]
380 fn de_leq_lz() {
381 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
383 assert!(degree_eccentricity_index(g).unwrap() <= lanzhou_index(g).unwrap());
384 }
385 }
386
387 #[test]
390 fn eds_empty() {
391 let g = Graph::with_vertices(0);
392 assert_eq!(eccentric_distance_sum(&g).unwrap(), 0);
393 }
394
395 #[test]
396 fn eds_isolated() {
397 let g = Graph::with_vertices(5);
398 assert_eq!(eccentric_distance_sum(&g).unwrap(), 0);
399 }
400
401 #[test]
402 fn eds_single_edge() {
403 assert_eq!(eccentric_distance_sum(&single_edge()).unwrap(), 2);
405 }
406
407 #[test]
408 fn eds_path3() {
409 assert_eq!(eccentric_distance_sum(&path3()).unwrap(), 14);
411 }
412
413 #[test]
414 fn eds_path4() {
415 assert_eq!(eccentric_distance_sum(&path4()).unwrap(), 52);
417 }
418
419 #[test]
420 fn eds_k3() {
421 assert_eq!(eccentric_distance_sum(&k3()).unwrap(), 6);
423 }
424
425 #[test]
426 fn eds_k4() {
427 assert_eq!(eccentric_distance_sum(&k4()).unwrap(), 12);
429 }
430
431 #[test]
432 fn eds_cycle4() {
433 assert_eq!(eccentric_distance_sum(&cycle4()).unwrap(), 32);
435 }
436
437 #[test]
438 fn eds_cycle5() {
439 assert_eq!(eccentric_distance_sum(&cycle5()).unwrap(), 60);
441 }
442
443 #[test]
444 fn eds_star5() {
445 assert_eq!(eccentric_distance_sum(&star5()).unwrap(), 60);
448 }
449
450 #[test]
451 fn eds_paw() {
452 assert_eq!(eccentric_distance_sum(&paw()).unwrap(), 29);
456 }
457
458 #[test]
461 fn all_nonneg() {
462 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
463 assert!(lanzhou_index(g).unwrap() > 0);
464 assert!(degree_eccentricity_index(g).unwrap() > 0);
465 assert!(eccentric_distance_sum(g).unwrap() > 0);
466 }
467 }
468
469 #[test]
470 fn lz_regular_formula() {
471 for g in &[k3(), k4()] {
473 let n = u64::from(g.vcount());
474 let r = g.degree(0).unwrap() as u64;
475 let (ecc, _) = bfs_ecc_and_dist_sum(g).unwrap();
476 let e = u64::from(ecc[0]);
477 assert_eq!(lanzhou_index(g).unwrap(), n * r * r * e);
478 }
479 }
480
481 #[test]
482 fn de_regular_formula() {
483 for g in &[k3(), k4()] {
485 let n = u64::from(g.vcount());
486 let r = g.degree(0).unwrap() as u64;
487 let (ecc, _) = bfs_ecc_and_dist_sum(g).unwrap();
488 let e = u64::from(ecc[0]);
489 assert_eq!(degree_eccentricity_index(g).unwrap(), n * r * e);
490 }
491 }
492}