rust_igraph/algorithms/properties/
multiplicative_connectivity.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
22pub fn multiplicative_sum_connectivity(graph: &Graph) -> IgraphResult<f64> {
41 let mut log_sum = 0.0_f64;
42 let mut count = 0_usize;
43
44 for (u, v) in graph.edges() {
45 if u == v {
46 continue;
47 }
48 let du = graph.degree(u)? as f64;
49 let dv = graph.degree(v)? as f64;
50 let s = du + dv;
51 if s > 0.0 {
52 log_sum -= 0.5 * s.ln();
53 count += 1;
54 }
55 }
56
57 if count == 0 {
58 return Ok(1.0);
59 }
60
61 Ok(log_sum.exp())
62}
63
64pub fn multiplicative_randic(graph: &Graph) -> IgraphResult<f64> {
81 let mut log_sum = 0.0_f64;
82 let mut count = 0_usize;
83
84 for (u, v) in graph.edges() {
85 if u == v {
86 continue;
87 }
88 let du = graph.degree(u)? as f64;
89 let dv = graph.degree(v)? as f64;
90 let product = du * dv;
91 if product > 0.0 {
92 log_sum -= 0.5 * product.ln();
93 count += 1;
94 }
95 }
96
97 if count == 0 {
98 return Ok(1.0);
99 }
100
101 Ok(log_sum.exp())
102}
103
104pub fn multiplicative_abc(graph: &Graph) -> IgraphResult<f64> {
122 let mut log_sum = 0.0_f64;
123 let mut count = 0_usize;
124
125 for (u, v) in graph.edges() {
126 if u == v {
127 continue;
128 }
129 let du = graph.degree(u)? as f64;
130 let dv = graph.degree(v)? as f64;
131 let product = du * dv;
132 let numer = du + dv - 2.0;
133 if product > 0.0 && numer > 0.0 {
134 log_sum += 0.5 * (numer / product).ln();
135 count += 1;
136 }
137 }
138
139 if count == 0 {
140 return Ok(1.0);
141 }
142
143 Ok(log_sum.exp())
144}
145
146pub fn multiplicative_ga(graph: &Graph) -> IgraphResult<f64> {
165 let mut log_sum = 0.0_f64;
166 let mut count = 0_usize;
167
168 for (u, v) in graph.edges() {
169 if u == v {
170 continue;
171 }
172 let du = graph.degree(u)? as f64;
173 let dv = graph.degree(v)? as f64;
174 let s = du + dv;
175 if s > 0.0 {
176 let factor = 2.0 * (du * dv).sqrt() / s;
177 if factor > 0.0 {
178 log_sum += factor.ln();
179 count += 1;
180 }
181 }
182 }
183
184 if count == 0 {
185 return Ok(1.0);
186 }
187
188 Ok(log_sum.exp())
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 fn single_edge() -> Graph {
196 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
197 }
198
199 fn path3() -> Graph {
200 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
201 }
202
203 fn k3() -> Graph {
204 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
205 }
206
207 fn k4() -> Graph {
208 Graph::from_edges(
209 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
210 false,
211 Some(4),
212 )
213 .unwrap()
214 }
215
216 fn cycle4() -> Graph {
217 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
218 }
219
220 fn cycle5() -> Graph {
221 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
222 }
223
224 fn star5() -> Graph {
225 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
226 }
227
228 fn paw() -> Graph {
229 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
230 }
231
232 #[test]
235 fn msc_empty() {
236 let g = Graph::with_vertices(0);
237 assert!((multiplicative_sum_connectivity(&g).unwrap() - 1.0).abs() < 1e-10);
238 }
239
240 #[test]
241 fn msc_isolated() {
242 let g = Graph::with_vertices(5);
243 assert!((multiplicative_sum_connectivity(&g).unwrap() - 1.0).abs() < 1e-10);
244 }
245
246 #[test]
247 fn msc_single_edge() {
248 let expected = 1.0 / 2.0_f64.sqrt();
250 assert!(
251 (multiplicative_sum_connectivity(&single_edge()).unwrap() - expected).abs() < 1e-10
252 );
253 }
254
255 #[test]
256 fn msc_k3() {
257 assert!((multiplicative_sum_connectivity(&k3()).unwrap() - 0.125).abs() < 1e-10);
259 }
260
261 #[test]
262 fn msc_k4() {
263 let expected = (1.0 / 6.0_f64.sqrt()).powi(6);
265 assert!((multiplicative_sum_connectivity(&k4()).unwrap() - expected).abs() < 1e-10);
266 }
267
268 #[test]
269 fn msc_path3() {
270 let expected = 1.0 / 3.0;
272 assert!((multiplicative_sum_connectivity(&path3()).unwrap() - expected).abs() < 1e-10);
273 }
274
275 #[test]
276 fn msc_cycle4() {
277 let expected = 1.0 / 16.0;
279 assert!((multiplicative_sum_connectivity(&cycle4()).unwrap() - expected).abs() < 1e-10);
280 }
281
282 #[test]
283 fn msc_star5() {
284 let expected = 1.0 / 25.0;
286 assert!((multiplicative_sum_connectivity(&star5()).unwrap() - expected).abs() < 1e-10);
287 }
288
289 #[test]
290 fn msc_paw() {
291 let expected = 0.5 * (1.0 / 5.0_f64.sqrt()).powi(2) * 0.5;
296 assert!((multiplicative_sum_connectivity(&paw()).unwrap() - expected).abs() < 1e-10);
297 }
298
299 #[test]
302 fn mr_empty() {
303 let g = Graph::with_vertices(0);
304 assert!((multiplicative_randic(&g).unwrap() - 1.0).abs() < 1e-10);
305 }
306
307 #[test]
308 fn mr_isolated() {
309 let g = Graph::with_vertices(5);
310 assert!((multiplicative_randic(&g).unwrap() - 1.0).abs() < 1e-10);
311 }
312
313 #[test]
314 fn mr_single_edge() {
315 assert!((multiplicative_randic(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
317 }
318
319 #[test]
320 fn mr_k3() {
321 assert!((multiplicative_randic(&k3()).unwrap() - 0.125).abs() < 1e-10);
323 }
324
325 #[test]
326 fn mr_k4() {
327 let expected = (1.0_f64 / 3.0).powi(6);
329 assert!((multiplicative_randic(&k4()).unwrap() - expected).abs() < 1e-10);
330 }
331
332 #[test]
333 fn mr_path3() {
334 assert!((multiplicative_randic(&path3()).unwrap() - 0.5).abs() < 1e-10);
336 }
337
338 #[test]
339 fn mr_cycle4() {
340 let expected = 1.0 / 16.0;
342 assert!((multiplicative_randic(&cycle4()).unwrap() - expected).abs() < 1e-10);
343 }
344
345 #[test]
346 fn mr_star5() {
347 let expected = 1.0 / 16.0;
349 assert!((multiplicative_randic(&star5()).unwrap() - expected).abs() < 1e-10);
350 }
351
352 #[test]
353 fn mr_paw() {
354 let expected = 0.5 * (1.0 / 6.0_f64.sqrt()).powi(2) * (1.0 / 3.0_f64.sqrt());
359 assert!((multiplicative_randic(&paw()).unwrap() - expected).abs() < 1e-10);
360 }
361
362 #[test]
365 fn mabc_empty() {
366 let g = Graph::with_vertices(0);
367 assert!((multiplicative_abc(&g).unwrap() - 1.0).abs() < 1e-10);
368 }
369
370 #[test]
371 fn mabc_isolated() {
372 let g = Graph::with_vertices(5);
373 assert!((multiplicative_abc(&g).unwrap() - 1.0).abs() < 1e-10);
374 }
375
376 #[test]
377 fn mabc_single_edge() {
378 assert!((multiplicative_abc(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
380 }
381
382 #[test]
383 fn mabc_k3() {
384 let expected = (1.0 / 2.0_f64.sqrt()).powi(3);
386 assert!((multiplicative_abc(&k3()).unwrap() - expected).abs() < 1e-10);
387 }
388
389 #[test]
390 fn mabc_k4() {
391 let expected = (2.0_f64 / 3.0).powi(6);
393 assert!((multiplicative_abc(&k4()).unwrap() - expected).abs() < 1e-10);
394 }
395
396 #[test]
397 fn mabc_path3() {
398 assert!((multiplicative_abc(&path3()).unwrap() - 0.5).abs() < 1e-10);
400 }
401
402 #[test]
403 fn mabc_cycle4() {
404 assert!((multiplicative_abc(&cycle4()).unwrap() - 0.25).abs() < 1e-10);
406 }
407
408 #[test]
409 fn mabc_star5() {
410 let expected = (3.0_f64.sqrt() / 2.0).powi(4);
412 assert!((multiplicative_abc(&star5()).unwrap() - expected).abs() < 1e-10);
413 }
414
415 #[test]
416 fn mabc_paw() {
417 let expected = (1.0 / 2.0_f64.sqrt()).powi(3) * (2.0_f64 / 3.0).sqrt();
422 assert!((multiplicative_abc(&paw()).unwrap() - expected).abs() < 1e-10);
423 }
424
425 #[test]
428 fn mga_empty() {
429 let g = Graph::with_vertices(0);
430 assert!((multiplicative_ga(&g).unwrap() - 1.0).abs() < 1e-10);
431 }
432
433 #[test]
434 fn mga_isolated() {
435 let g = Graph::with_vertices(5);
436 assert!((multiplicative_ga(&g).unwrap() - 1.0).abs() < 1e-10);
437 }
438
439 #[test]
440 fn mga_single_edge() {
441 assert!((multiplicative_ga(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
443 }
444
445 #[test]
446 fn mga_k3() {
447 assert!((multiplicative_ga(&k3()).unwrap() - 1.0).abs() < 1e-10);
449 }
450
451 #[test]
452 fn mga_k4() {
453 assert!((multiplicative_ga(&k4()).unwrap() - 1.0).abs() < 1e-10);
454 }
455
456 #[test]
457 fn mga_cycle4() {
458 assert!((multiplicative_ga(&cycle4()).unwrap() - 1.0).abs() < 1e-10);
459 }
460
461 #[test]
462 fn mga_cycle5() {
463 assert!((multiplicative_ga(&cycle5()).unwrap() - 1.0).abs() < 1e-10);
464 }
465
466 #[test]
467 fn mga_path3() {
468 let factor = 2.0 * 2.0_f64.sqrt() / 3.0;
470 let expected = factor * factor;
471 assert!((multiplicative_ga(&path3()).unwrap() - expected).abs() < 1e-10);
472 }
473
474 #[test]
475 fn mga_star5() {
476 let expected = (4.0_f64 / 5.0).powi(4);
478 assert!((multiplicative_ga(&star5()).unwrap() - expected).abs() < 1e-10);
479 }
480
481 #[test]
482 fn mga_paw() {
483 let expected = (2.0 * 6.0_f64.sqrt() / 5.0).powi(2) * (3.0_f64.sqrt() / 2.0);
488 assert!((multiplicative_ga(&paw()).unwrap() - expected).abs() < 1e-10);
489 }
490
491 #[test]
494 fn regular_ga_is_one() {
495 for g in &[k3(), k4(), cycle4(), cycle5()] {
496 assert!((multiplicative_ga(g).unwrap() - 1.0).abs() < 1e-10);
497 }
498 }
499
500 #[test]
501 fn ga_le_one_for_simple() {
502 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
504 assert!(multiplicative_ga(g).unwrap() <= 1.0 + 1e-10);
505 }
506 }
507
508 #[test]
509 fn all_positive() {
510 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
511 assert!(multiplicative_sum_connectivity(g).unwrap() > 0.0);
512 assert!(multiplicative_randic(g).unwrap() > 0.0);
513 assert!(multiplicative_abc(g).unwrap() > 0.0);
514 assert!(multiplicative_ga(g).unwrap() > 0.0);
515 }
516 }
517}