rust_igraph/algorithms/properties/
reciprocal_distance_degree.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
24pub fn reciprocal_degree_distance(graph: &Graph) -> IgraphResult<f64> {
42 let n = graph.vcount() as usize;
43 let mut rdd = 0.0_f64;
44
45 for s in 0..n {
46 let ds = graph.degree(s as u32)? as f64;
47 let mut dist = vec![u32::MAX; n];
48 dist[s] = 0;
49 let mut queue = std::collections::VecDeque::new();
50 queue.push_back(s);
51 while let Some(u) = queue.pop_front() {
52 let d_u = dist[u];
53 if let Ok(nbs) = graph.neighbors(u as u32) {
54 for nb in nbs {
55 let idx = nb as usize;
56 if dist[idx] == u32::MAX {
57 dist[idx] = d_u + 1;
58 queue.push_back(idx);
59 }
60 }
61 }
62 }
63 for t in (s + 1)..n {
64 if dist[t] != u32::MAX && dist[t] > 0 {
65 let dt = graph.degree(t as u32)? as f64;
66 rdd += (ds + dt) / f64::from(dist[t]);
67 }
68 }
69 }
70
71 Ok(rdd)
72}
73
74pub fn multiplicatively_weighted_harary(graph: &Graph) -> IgraphResult<f64> {
92 let n = graph.vcount() as usize;
93 let mut hm = 0.0_f64;
94
95 for s in 0..n {
96 let ds = graph.degree(s as u32)? as f64;
97 if ds == 0.0 {
98 continue;
99 }
100 let mut dist = vec![u32::MAX; n];
101 dist[s] = 0;
102 let mut queue = std::collections::VecDeque::new();
103 queue.push_back(s);
104 while let Some(u) = queue.pop_front() {
105 let d_u = dist[u];
106 if let Ok(nbs) = graph.neighbors(u as u32) {
107 for nb in nbs {
108 let idx = nb as usize;
109 if dist[idx] == u32::MAX {
110 dist[idx] = d_u + 1;
111 queue.push_back(idx);
112 }
113 }
114 }
115 }
116 for t in (s + 1)..n {
117 if dist[t] != u32::MAX && dist[t] > 0 {
118 let dt = graph.degree(t as u32)? as f64;
119 if dt > 0.0 {
120 hm += (ds * dt) / f64::from(dist[t]);
121 }
122 }
123 }
124 }
125
126 Ok(hm)
127}
128
129pub fn terminal_wiener_index(graph: &Graph) -> IgraphResult<u64> {
147 let n = graph.vcount() as usize;
148 let mut pendants = Vec::new();
149 for v in 0..n {
150 if graph.degree(v as u32)? == 1 {
151 pendants.push(v);
152 }
153 }
154
155 if pendants.len() < 2 {
156 return Ok(0);
157 }
158
159 let mut tw = 0_u64;
160
161 for (i, &s) in pendants.iter().enumerate() {
162 let mut dist = vec![u32::MAX; n];
163 dist[s] = 0;
164 let mut queue = std::collections::VecDeque::new();
165 queue.push_back(s);
166 while let Some(u) = queue.pop_front() {
167 let d_u = dist[u];
168 if let Ok(nbs) = graph.neighbors(u as u32) {
169 for nb in nbs {
170 let idx = nb as usize;
171 if dist[idx] == u32::MAX {
172 dist[idx] = d_u + 1;
173 queue.push_back(idx);
174 }
175 }
176 }
177 }
178 for &t in &pendants[(i + 1)..] {
179 if dist[t] != u32::MAX {
180 tw = tw.saturating_add(u64::from(dist[t]));
181 }
182 }
183 }
184
185 Ok(tw)
186}
187
188#[cfg(test)]
189mod tests {
190 use super::*;
191
192 fn single_edge() -> Graph {
193 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
194 }
195
196 fn path3() -> Graph {
197 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
198 }
199
200 fn path4() -> Graph {
201 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
202 }
203
204 fn k3() -> Graph {
205 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
206 }
207
208 fn k4() -> Graph {
209 Graph::from_edges(
210 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
211 false,
212 Some(4),
213 )
214 .unwrap()
215 }
216
217 fn cycle4() -> Graph {
218 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
219 }
220
221 fn cycle5() -> Graph {
222 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
223 }
224
225 fn star5() -> Graph {
226 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
227 }
228
229 fn paw() -> Graph {
230 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
231 }
232
233 #[test]
236 fn rdd_empty() {
237 let g = Graph::with_vertices(0);
238 assert!((reciprocal_degree_distance(&g).unwrap()).abs() < 1e-10);
239 }
240
241 #[test]
242 fn rdd_isolated() {
243 let g = Graph::with_vertices(5);
244 assert!((reciprocal_degree_distance(&g).unwrap()).abs() < 1e-10);
245 }
246
247 #[test]
248 fn rdd_single_edge() {
249 assert!((reciprocal_degree_distance(&single_edge()).unwrap() - 2.0).abs() < 1e-10);
251 }
252
253 #[test]
254 fn rdd_path3() {
255 assert!((reciprocal_degree_distance(&path3()).unwrap() - 7.0).abs() < 1e-10);
257 }
258
259 #[test]
260 fn rdd_path4() {
261 let expected = 3.0 + 1.5 + 2.0 / 3.0 + 4.0 + 1.5 + 3.0;
265 assert!((reciprocal_degree_distance(&path4()).unwrap() - expected).abs() < 1e-10);
266 }
267
268 #[test]
269 fn rdd_k3() {
270 assert!((reciprocal_degree_distance(&k3()).unwrap() - 12.0).abs() < 1e-10);
273 }
274
275 #[test]
276 fn rdd_k4() {
277 assert!((reciprocal_degree_distance(&k4()).unwrap() - 36.0).abs() < 1e-10);
279 }
280
281 #[test]
282 fn rdd_cycle4() {
283 assert!((reciprocal_degree_distance(&cycle4()).unwrap() - 20.0).abs() < 1e-10);
286 }
287
288 #[test]
289 fn rdd_cycle5() {
290 assert!((reciprocal_degree_distance(&cycle5()).unwrap() - 30.0).abs() < 1e-10);
293 }
294
295 #[test]
296 fn rdd_star5() {
297 assert!((reciprocal_degree_distance(&star5()).unwrap() - 26.0).abs() < 1e-10);
301 }
302
303 #[test]
304 fn rdd_paw() {
305 let expected = 4.0 + 5.0 + 1.5 + 5.0 + 1.5 + 4.0;
309 assert!((reciprocal_degree_distance(&paw()).unwrap() - expected).abs() < 1e-10);
310 }
311
312 #[test]
313 fn rdd_regular_formula() {
314 let g = k4();
319 let n = 4.0_f64;
320 let r = 3.0;
321 let harary = n * (n - 1.0) / 2.0; let expected = 2.0 * r * harary;
323 assert!((reciprocal_degree_distance(&g).unwrap() - expected).abs() < 1e-8);
324 }
325
326 #[test]
329 fn hm_empty() {
330 let g = Graph::with_vertices(0);
331 assert!((multiplicatively_weighted_harary(&g).unwrap()).abs() < 1e-10);
332 }
333
334 #[test]
335 fn hm_isolated() {
336 let g = Graph::with_vertices(5);
337 assert!((multiplicatively_weighted_harary(&g).unwrap()).abs() < 1e-10);
338 }
339
340 #[test]
341 fn hm_single_edge() {
342 assert!((multiplicatively_weighted_harary(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
344 }
345
346 #[test]
347 fn hm_path3() {
348 assert!((multiplicatively_weighted_harary(&path3()).unwrap() - 4.5).abs() < 1e-10);
350 }
351
352 #[test]
353 fn hm_k3() {
354 assert!((multiplicatively_weighted_harary(&k3()).unwrap() - 12.0).abs() < 1e-10);
356 }
357
358 #[test]
359 fn hm_k4() {
360 assert!((multiplicatively_weighted_harary(&k4()).unwrap() - 54.0).abs() < 1e-10);
362 }
363
364 #[test]
365 fn hm_cycle4() {
366 assert!((multiplicatively_weighted_harary(&cycle4()).unwrap() - 20.0).abs() < 1e-10);
369 }
370
371 #[test]
372 fn hm_star5() {
373 assert!((multiplicatively_weighted_harary(&star5()).unwrap() - 19.0).abs() < 1e-10);
377 }
378
379 #[test]
380 fn hm_paw() {
381 let expected = 4.0 + 6.0 + 1.0 + 6.0 + 1.0 + 3.0;
385 assert!((multiplicatively_weighted_harary(&paw()).unwrap() - expected).abs() < 1e-10);
386 }
387
388 #[test]
389 fn hm_regular_formula() {
390 let g = k4();
393 let n = 4.0_f64;
394 let r = 3.0;
395 let harary = n * (n - 1.0) / 2.0;
396 let expected = r * r * harary;
397 assert!((multiplicatively_weighted_harary(&g).unwrap() - expected).abs() < 1e-8);
398 }
399
400 #[test]
401 fn hm_equals_rdd_for_regular() {
402 for g in &[k3(), k4(), cycle4(), cycle5()] {
405 let hm = multiplicatively_weighted_harary(g).unwrap();
406 let rdd = reciprocal_degree_distance(g).unwrap();
407 let r = g.degree(0).unwrap() as f64;
408 assert!((hm / rdd - r / 2.0).abs() < 1e-8);
409 }
410 }
411
412 #[test]
415 fn tw_empty() {
416 let g = Graph::with_vertices(0);
417 assert_eq!(terminal_wiener_index(&g).unwrap(), 0);
418 }
419
420 #[test]
421 fn tw_isolated() {
422 let g = Graph::with_vertices(5);
423 assert_eq!(terminal_wiener_index(&g).unwrap(), 0);
424 }
425
426 #[test]
427 fn tw_single_edge() {
428 assert_eq!(terminal_wiener_index(&single_edge()).unwrap(), 1);
430 }
431
432 #[test]
433 fn tw_path3() {
434 assert_eq!(terminal_wiener_index(&path3()).unwrap(), 2);
436 }
437
438 #[test]
439 fn tw_path4() {
440 assert_eq!(terminal_wiener_index(&path4()).unwrap(), 3);
442 }
443
444 #[test]
445 fn tw_k3() {
446 assert_eq!(terminal_wiener_index(&k3()).unwrap(), 0);
448 }
449
450 #[test]
451 fn tw_k4() {
452 assert_eq!(terminal_wiener_index(&k4()).unwrap(), 0);
453 }
454
455 #[test]
456 fn tw_cycle4() {
457 assert_eq!(terminal_wiener_index(&cycle4()).unwrap(), 0);
458 }
459
460 #[test]
461 fn tw_cycle5() {
462 assert_eq!(terminal_wiener_index(&cycle5()).unwrap(), 0);
463 }
464
465 #[test]
466 fn tw_star5() {
467 assert_eq!(terminal_wiener_index(&star5()).unwrap(), 12);
470 }
471
472 #[test]
473 fn tw_paw() {
474 assert_eq!(terminal_wiener_index(&paw()).unwrap(), 0);
476 }
477
478 #[test]
479 fn tw_caterpillar() {
480 let g =
485 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (1, 4), (2, 5)], false, Some(6)).unwrap();
486 assert_eq!(terminal_wiener_index(&g).unwrap(), 16);
487 }
488
489 #[test]
490 fn tw_double_star() {
491 let g =
497 Graph::from_edges(&[(0, 2), (1, 2), (2, 3), (3, 4), (3, 5)], false, Some(6)).unwrap();
498 assert_eq!(terminal_wiener_index(&g).unwrap(), 16);
499 }
500
501 #[test]
504 fn rdd_geq_harary_for_nonregular() {
505 for g in &[single_edge(), path3(), k3(), k4(), star5(), paw()] {
509 let rdd = reciprocal_degree_distance(g).unwrap();
510 assert!(rdd >= 0.0);
511 }
512 }
513
514 #[test]
515 fn all_positive_for_connected() {
516 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
517 assert!(reciprocal_degree_distance(g).unwrap() > 0.0);
518 assert!(multiplicatively_weighted_harary(g).unwrap() > 0.0);
519 }
520 }
521}