rust_igraph/algorithms/properties/
abc_variants.rs1#![allow(
13 clippy::cast_possible_truncation,
14 clippy::cast_precision_loss,
15 clippy::many_single_char_names,
16 clippy::needless_range_loop,
17 clippy::too_many_lines
18)]
19
20use crate::core::{Graph, IgraphResult};
21
22fn eccentricities(graph: &Graph) -> IgraphResult<Vec<u32>> {
23 let n = graph.vcount() as usize;
24 let mut ecc = vec![0_u32; n];
25
26 for s in 0..n {
27 let mut dist = vec![u32::MAX; n];
28 dist[s] = 0;
29 let mut queue = std::collections::VecDeque::new();
30 queue.push_back(s as u32);
31 while let Some(u) = queue.pop_front() {
32 let d_u = dist[u as usize];
33 for nb in graph.neighbors(u)? {
34 let idx = nb as usize;
35 if dist[idx] == u32::MAX {
36 dist[idx] = d_u + 1;
37 queue.push_back(nb);
38 }
39 }
40 }
41 let mut max_d = 0_u32;
42 for &d in &dist {
43 if d != u32::MAX && d > max_d {
44 max_d = d;
45 }
46 }
47 ecc[s] = max_d;
48 }
49
50 Ok(ecc)
51}
52
53pub fn fourth_abc_index(graph: &Graph) -> IgraphResult<f64> {
73 let n = graph.vcount();
74 if n == 0 {
75 return Ok(0.0);
76 }
77
78 let ecc = eccentricities(graph)?;
79 let mut abc4 = 0.0_f64;
80
81 for (u, v) in graph.edges() {
82 if u == v {
83 continue;
84 }
85 let eu = f64::from(ecc[u as usize]);
86 let ev = f64::from(ecc[v as usize]);
87 let prod = eu * ev;
88 if prod <= 0.0 {
89 continue;
90 }
91 let numer = eu + ev - 2.0;
92 if numer < 0.0 {
93 continue;
94 }
95 abc4 += (numer / prod).sqrt();
96 }
97
98 Ok(abc4)
99}
100
101pub fn fifth_ga_index(graph: &Graph) -> IgraphResult<f64> {
119 let n = graph.vcount();
120 if n == 0 {
121 return Ok(0.0);
122 }
123
124 let ecc = eccentricities(graph)?;
125 let mut ga5 = 0.0_f64;
126
127 for (u, v) in graph.edges() {
128 if u == v {
129 continue;
130 }
131 let eu = f64::from(ecc[u as usize]);
132 let ev = f64::from(ecc[v as usize]);
133 let denom = eu + ev;
134 if denom <= 0.0 {
135 continue;
136 }
137 ga5 += 2.0 * (eu * ev).sqrt() / denom;
138 }
139
140 Ok(ga5)
141}
142
143pub fn degree_sum_index(graph: &Graph) -> IgraphResult<f64> {
159 let mut ds = 0.0_f64;
160
161 for (u, v) in graph.edges() {
162 if u == v {
163 continue;
164 }
165 let du = graph.degree(u)? as f64;
166 let dv = graph.degree(v)? as f64;
167 ds += (du + dv).sqrt();
168 }
169
170 Ok(ds)
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 fn single_edge() -> Graph {
178 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
179 }
180
181 fn path3() -> Graph {
182 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
183 }
184
185 fn k3() -> Graph {
186 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
187 }
188
189 fn k4() -> Graph {
190 Graph::from_edges(
191 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
192 false,
193 Some(4),
194 )
195 .unwrap()
196 }
197
198 fn cycle4() -> Graph {
199 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
200 }
201
202 fn cycle5() -> Graph {
203 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
204 }
205
206 fn star5() -> Graph {
207 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
208 }
209
210 fn paw() -> Graph {
211 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
212 }
213
214 #[test]
217 fn ecc_path3() {
218 let ecc = eccentricities(&path3()).unwrap();
219 assert_eq!(ecc, vec![2, 1, 2]);
220 }
221
222 #[test]
223 fn ecc_k3() {
224 let ecc = eccentricities(&k3()).unwrap();
225 assert_eq!(ecc, vec![1, 1, 1]);
226 }
227
228 #[test]
229 fn ecc_k4() {
230 let ecc = eccentricities(&k4()).unwrap();
231 assert_eq!(ecc, vec![1, 1, 1, 1]);
232 }
233
234 #[test]
235 fn ecc_cycle4() {
236 let ecc = eccentricities(&cycle4()).unwrap();
237 assert_eq!(ecc, vec![2, 2, 2, 2]);
238 }
239
240 #[test]
241 fn ecc_star5() {
242 let ecc = eccentricities(&star5()).unwrap();
243 assert_eq!(ecc[0], 1);
244 for i in 1..5 {
245 assert_eq!(ecc[i], 2);
246 }
247 }
248
249 #[test]
252 fn abc4_empty() {
253 let g = Graph::with_vertices(0);
254 assert!((fourth_abc_index(&g).unwrap()).abs() < 1e-10);
255 }
256
257 #[test]
258 fn abc4_single_edge() {
259 assert!((fourth_abc_index(&single_edge()).unwrap()).abs() < 1e-10);
261 }
262
263 #[test]
264 fn abc4_path3() {
265 let expected = std::f64::consts::SQRT_2;
270 assert!((fourth_abc_index(&path3()).unwrap() - expected).abs() < 1e-10);
271 }
272
273 #[test]
274 fn abc4_k3() {
275 assert!((fourth_abc_index(&k3()).unwrap()).abs() < 1e-10);
278 }
279
280 #[test]
281 fn abc4_k4() {
282 assert!((fourth_abc_index(&k4()).unwrap()).abs() < 1e-10);
284 }
285
286 #[test]
287 fn abc4_cycle4() {
288 let expected = 2.0 * std::f64::consts::SQRT_2;
292 assert!((fourth_abc_index(&cycle4()).unwrap() - expected).abs() < 1e-10);
293 }
294
295 #[test]
296 fn abc4_cycle5() {
297 let expected = 5.0 / std::f64::consts::SQRT_2;
301 assert!((fourth_abc_index(&cycle5()).unwrap() - expected).abs() < 1e-10);
302 }
303
304 #[test]
305 fn abc4_star5() {
306 let expected = 2.0 * std::f64::consts::SQRT_2;
310 assert!((fourth_abc_index(&star5()).unwrap() - expected).abs() < 1e-10);
311 }
312
313 #[test]
314 fn abc4_paw() {
315 let ecc = eccentricities(&paw()).unwrap();
317 assert_eq!(ecc, vec![2, 2, 1, 2]);
318 let expected = 2.0 * std::f64::consts::SQRT_2;
324 assert!((fourth_abc_index(&paw()).unwrap() - expected).abs() < 1e-10);
325 }
326
327 #[test]
330 fn ga5_empty() {
331 let g = Graph::with_vertices(0);
332 assert!((fifth_ga_index(&g).unwrap()).abs() < 1e-10);
333 }
334
335 #[test]
336 fn ga5_single_edge() {
337 assert!((fifth_ga_index(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
339 }
340
341 #[test]
342 fn ga5_path3() {
343 let expected = 4.0 * std::f64::consts::SQRT_2 / 3.0;
348 assert!((fifth_ga_index(&path3()).unwrap() - expected).abs() < 1e-10);
349 }
350
351 #[test]
352 fn ga5_k3() {
353 assert!((fifth_ga_index(&k3()).unwrap() - 3.0).abs() < 1e-10);
355 }
356
357 #[test]
358 fn ga5_k4() {
359 assert!((fifth_ga_index(&k4()).unwrap() - 6.0).abs() < 1e-10);
361 }
362
363 #[test]
364 fn ga5_cycle4() {
365 assert!((fifth_ga_index(&cycle4()).unwrap() - 4.0).abs() < 1e-10);
367 }
368
369 #[test]
370 fn ga5_cycle5() {
371 assert!((fifth_ga_index(&cycle5()).unwrap() - 5.0).abs() < 1e-10);
373 }
374
375 #[test]
376 fn ga5_star5() {
377 let expected = 8.0 * std::f64::consts::SQRT_2 / 3.0;
381 assert!((fifth_ga_index(&star5()).unwrap() - expected).abs() < 1e-10);
382 }
383
384 #[test]
385 fn ga5_equal_ecc_gives_m() {
386 for g in &[k3(), k4(), cycle4(), cycle5()] {
388 let m = g.ecount() as f64;
389 assert!((fifth_ga_index(g).unwrap() - m).abs() < 1e-10);
390 }
391 }
392
393 #[test]
396 fn ds_empty() {
397 let g = Graph::with_vertices(0);
398 assert!((degree_sum_index(&g).unwrap()).abs() < 1e-10);
399 }
400
401 #[test]
402 fn ds_single_edge() {
403 let expected = std::f64::consts::SQRT_2;
405 assert!((degree_sum_index(&single_edge()).unwrap() - expected).abs() < 1e-10);
406 }
407
408 #[test]
409 fn ds_path3() {
410 let expected = 2.0 * 3.0_f64.sqrt();
413 assert!((degree_sum_index(&path3()).unwrap() - expected).abs() < 1e-10);
414 }
415
416 #[test]
417 fn ds_k3() {
418 assert!((degree_sum_index(&k3()).unwrap() - 6.0).abs() < 1e-10);
420 }
421
422 #[test]
423 fn ds_k4() {
424 let expected = 6.0 * 6.0_f64.sqrt();
426 assert!((degree_sum_index(&k4()).unwrap() - expected).abs() < 1e-10);
427 }
428
429 #[test]
430 fn ds_cycle4() {
431 assert!((degree_sum_index(&cycle4()).unwrap() - 8.0).abs() < 1e-10);
433 }
434
435 #[test]
436 fn ds_star5() {
437 let expected = 4.0 * 5.0_f64.sqrt();
439 assert!((degree_sum_index(&star5()).unwrap() - expected).abs() < 1e-10);
440 }
441
442 #[test]
443 fn ds_paw() {
444 let expected = 4.0 + 2.0 * 5.0_f64.sqrt();
448 assert!((degree_sum_index(&paw()).unwrap() - expected).abs() < 1e-10);
449 }
450
451 #[test]
452 fn ds_regular_formula() {
453 for g in &[k3(), k4(), cycle4(), cycle5()] {
455 let m = g.ecount() as f64;
456 let r = g.degree(0).unwrap() as f64;
457 let expected = m * (2.0 * r).sqrt();
458 assert!((degree_sum_index(g).unwrap() - expected).abs() < 1e-8);
459 }
460 }
461
462 #[test]
465 fn all_nonneg() {
466 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
467 assert!(fourth_abc_index(g).unwrap() >= -1e-10);
468 assert!(fifth_ga_index(g).unwrap() >= -1e-10);
469 assert!(degree_sum_index(g).unwrap() >= -1e-10);
470 }
471 }
472
473 #[test]
474 fn ga5_leq_m() {
475 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
477 let m = g.ecount() as f64;
478 assert!(fifth_ga_index(g).unwrap() <= m + 1e-10);
479 }
480 }
481}