rust_igraph/algorithms/properties/
transmission_zagreb.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 clippy::unnecessary_wraps
21)]
22
23use crate::core::{Graph, IgraphResult};
24
25fn vertex_transmissions(graph: &Graph) -> IgraphResult<Vec<u64>> {
26 let n = graph.vcount() as usize;
27 let mut sigma = 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);
34 while let Some(u) = queue.pop_front() {
35 let d_u = dist[u];
36 if let Ok(nbs) = graph.neighbors(u as u32) {
37 for nb in nbs {
38 let idx = nb as usize;
39 if dist[idx] == u32::MAX {
40 dist[idx] = d_u + 1;
41 queue.push_back(idx);
42 }
43 }
44 }
45 }
46 let mut total = 0_u64;
47 for &d in &dist {
48 if d != u32::MAX {
49 total = total.saturating_add(u64::from(d));
50 }
51 }
52 sigma[s] = total;
53 }
54
55 Ok(sigma)
56}
57
58pub fn first_transmission_zagreb(graph: &Graph) -> IgraphResult<u64> {
73 let sigma = vertex_transmissions(graph)?;
74 let mut tz1 = 0_u64;
75
76 for &sv in &sigma {
77 tz1 = tz1.saturating_add(sv.saturating_mul(sv));
78 }
79
80 Ok(tz1)
81}
82
83pub fn second_transmission_zagreb(graph: &Graph) -> IgraphResult<u64> {
100 let sigma = vertex_transmissions(graph)?;
101 let mut tz2 = 0_u64;
102
103 for (u, v) in graph.edges() {
104 if u == v {
105 continue;
106 }
107 let su = sigma[u as usize];
108 let sv = sigma[v as usize];
109 tz2 = tz2.saturating_add(su.saturating_mul(sv));
110 }
111
112 Ok(tz2)
113}
114
115pub fn reciprocal_transmission_index(graph: &Graph) -> IgraphResult<f64> {
132 let sigma = vertex_transmissions(graph)?;
133 let mut rt = 0.0_f64;
134
135 for &sv in &sigma {
136 if sv > 0 {
137 rt += 1.0 / sv as f64;
138 }
139 }
140
141 Ok(rt)
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 fn single_edge() -> Graph {
149 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
150 }
151
152 fn path3() -> Graph {
153 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
154 }
155
156 fn path4() -> Graph {
157 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
158 }
159
160 fn k3() -> Graph {
161 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
162 }
163
164 fn k4() -> Graph {
165 Graph::from_edges(
166 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
167 false,
168 Some(4),
169 )
170 .unwrap()
171 }
172
173 fn cycle4() -> Graph {
174 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
175 }
176
177 fn cycle5() -> Graph {
178 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
179 }
180
181 fn star5() -> Graph {
182 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
183 }
184
185 fn paw() -> Graph {
186 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
187 }
188
189 fn transmissions(g: &Graph) -> Vec<u64> {
191 vertex_transmissions(g).unwrap()
192 }
193
194 #[test]
197 fn sigma_empty() {
198 let g = Graph::with_vertices(0);
199 assert_eq!(transmissions(&g), Vec::<u64>::new());
200 }
201
202 #[test]
203 fn sigma_isolated() {
204 let g = Graph::with_vertices(3);
205 assert_eq!(transmissions(&g), vec![0, 0, 0]);
206 }
207
208 #[test]
209 fn sigma_single_edge() {
210 assert_eq!(transmissions(&single_edge()), vec![1, 1]);
212 }
213
214 #[test]
215 fn sigma_path3() {
216 assert_eq!(transmissions(&path3()), vec![3, 2, 3]);
218 }
219
220 #[test]
221 fn sigma_path4() {
222 assert_eq!(transmissions(&path4()), vec![6, 4, 4, 6]);
224 }
225
226 #[test]
227 fn sigma_k3() {
228 assert_eq!(transmissions(&k3()), vec![2, 2, 2]);
230 }
231
232 #[test]
233 fn sigma_k4() {
234 assert_eq!(transmissions(&k4()), vec![3, 3, 3, 3]);
236 }
237
238 #[test]
239 fn sigma_cycle4() {
240 assert_eq!(transmissions(&cycle4()), vec![4, 4, 4, 4]);
242 }
243
244 #[test]
245 fn sigma_cycle5() {
246 assert_eq!(transmissions(&cycle5()), vec![6, 6, 6, 6, 6]);
248 }
249
250 #[test]
251 fn sigma_star5() {
252 assert_eq!(transmissions(&star5()), vec![4, 7, 7, 7, 7]);
254 }
255
256 #[test]
257 fn sigma_paw() {
258 assert_eq!(transmissions(&paw()), vec![4, 4, 3, 5]);
260 }
261
262 #[test]
263 fn sigma_sum_equals_wiener() {
264 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
267 let sigma = transmissions(g);
268 let sum: u64 = sigma.iter().sum();
269 assert_eq!(sum % 2, 0);
270 }
271 }
272
273 #[test]
276 fn tz1_empty() {
277 let g = Graph::with_vertices(0);
278 assert_eq!(first_transmission_zagreb(&g).unwrap(), 0);
279 }
280
281 #[test]
282 fn tz1_isolated() {
283 let g = Graph::with_vertices(5);
284 assert_eq!(first_transmission_zagreb(&g).unwrap(), 0);
285 }
286
287 #[test]
288 fn tz1_single_edge() {
289 assert_eq!(first_transmission_zagreb(&single_edge()).unwrap(), 2);
291 }
292
293 #[test]
294 fn tz1_path3() {
295 assert_eq!(first_transmission_zagreb(&path3()).unwrap(), 22);
297 }
298
299 #[test]
300 fn tz1_path4() {
301 assert_eq!(first_transmission_zagreb(&path4()).unwrap(), 104);
303 }
304
305 #[test]
306 fn tz1_k3() {
307 assert_eq!(first_transmission_zagreb(&k3()).unwrap(), 12);
309 }
310
311 #[test]
312 fn tz1_k4() {
313 assert_eq!(first_transmission_zagreb(&k4()).unwrap(), 36);
315 }
316
317 #[test]
318 fn tz1_cycle4() {
319 assert_eq!(first_transmission_zagreb(&cycle4()).unwrap(), 64);
321 }
322
323 #[test]
324 fn tz1_cycle5() {
325 assert_eq!(first_transmission_zagreb(&cycle5()).unwrap(), 180);
327 }
328
329 #[test]
330 fn tz1_star5() {
331 assert_eq!(first_transmission_zagreb(&star5()).unwrap(), 212);
333 }
334
335 #[test]
336 fn tz1_paw() {
337 assert_eq!(first_transmission_zagreb(&paw()).unwrap(), 66);
339 }
340
341 #[test]
342 fn tz1_transmission_regular() {
343 for g in &[k3(), k4(), cycle4(), cycle5()] {
345 let n = u64::from(g.vcount());
346 let s = transmissions(g)[0];
347 assert_eq!(first_transmission_zagreb(g).unwrap(), n * s * s);
348 }
349 }
350
351 #[test]
354 fn tz2_empty() {
355 let g = Graph::with_vertices(0);
356 assert_eq!(second_transmission_zagreb(&g).unwrap(), 0);
357 }
358
359 #[test]
360 fn tz2_single_edge() {
361 assert_eq!(second_transmission_zagreb(&single_edge()).unwrap(), 1);
363 }
364
365 #[test]
366 fn tz2_path3() {
367 assert_eq!(second_transmission_zagreb(&path3()).unwrap(), 12);
369 }
370
371 #[test]
372 fn tz2_path4() {
373 assert_eq!(second_transmission_zagreb(&path4()).unwrap(), 64);
375 }
376
377 #[test]
378 fn tz2_k3() {
379 assert_eq!(second_transmission_zagreb(&k3()).unwrap(), 12);
381 }
382
383 #[test]
384 fn tz2_k4() {
385 assert_eq!(second_transmission_zagreb(&k4()).unwrap(), 54);
387 }
388
389 #[test]
390 fn tz2_cycle4() {
391 assert_eq!(second_transmission_zagreb(&cycle4()).unwrap(), 64);
393 }
394
395 #[test]
396 fn tz2_star5() {
397 assert_eq!(second_transmission_zagreb(&star5()).unwrap(), 112);
400 }
401
402 #[test]
403 fn tz2_paw() {
404 assert_eq!(second_transmission_zagreb(&paw()).unwrap(), 55);
407 }
408
409 #[test]
412 fn rt_empty() {
413 let g = Graph::with_vertices(0);
414 assert!((reciprocal_transmission_index(&g).unwrap()).abs() < 1e-10);
415 }
416
417 #[test]
418 fn rt_isolated() {
419 let g = Graph::with_vertices(5);
420 assert!((reciprocal_transmission_index(&g).unwrap()).abs() < 1e-10);
421 }
422
423 #[test]
424 fn rt_single_edge() {
425 assert!((reciprocal_transmission_index(&single_edge()).unwrap() - 2.0).abs() < 1e-10);
427 }
428
429 #[test]
430 fn rt_path3() {
431 let expected = 7.0 / 6.0;
433 assert!((reciprocal_transmission_index(&path3()).unwrap() - expected).abs() < 1e-10);
434 }
435
436 #[test]
437 fn rt_path4() {
438 let expected = 5.0 / 6.0;
440 assert!((reciprocal_transmission_index(&path4()).unwrap() - expected).abs() < 1e-10);
441 }
442
443 #[test]
444 fn rt_k3() {
445 assert!((reciprocal_transmission_index(&k3()).unwrap() - 1.5).abs() < 1e-10);
447 }
448
449 #[test]
450 fn rt_k4() {
451 let expected = 4.0 / 3.0;
453 assert!((reciprocal_transmission_index(&k4()).unwrap() - expected).abs() < 1e-10);
454 }
455
456 #[test]
457 fn rt_cycle4() {
458 assert!((reciprocal_transmission_index(&cycle4()).unwrap() - 1.0).abs() < 1e-10);
460 }
461
462 #[test]
463 fn rt_cycle5() {
464 let expected = 5.0 / 6.0;
466 assert!((reciprocal_transmission_index(&cycle5()).unwrap() - expected).abs() < 1e-10);
467 }
468
469 #[test]
470 fn rt_star5() {
471 let expected = 0.25 + 4.0 / 7.0;
473 assert!((reciprocal_transmission_index(&star5()).unwrap() - expected).abs() < 1e-10);
474 }
475
476 #[test]
477 fn rt_paw() {
478 let expected = 31.0 / 30.0;
480 assert!((reciprocal_transmission_index(&paw()).unwrap() - expected).abs() < 1e-10);
481 }
482
483 #[test]
484 fn rt_transmission_regular() {
485 for g in &[k3(), k4(), cycle4(), cycle5()] {
487 let n = f64::from(g.vcount());
488 let s = transmissions(g)[0] as f64;
489 let expected = n / s;
490 assert!((reciprocal_transmission_index(g).unwrap() - expected).abs() < 1e-8);
491 }
492 }
493
494 #[test]
497 fn all_positive_for_connected() {
498 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
499 assert!(first_transmission_zagreb(g).unwrap() > 0);
500 assert!(second_transmission_zagreb(g).unwrap() > 0);
501 assert!(reciprocal_transmission_index(g).unwrap() > 0.0);
502 }
503 }
504}