rust_igraph/algorithms/properties/
exponential_indices.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 exponential_augmented_zagreb(graph: &Graph) -> IgraphResult<f64> {
41 let mut result = 0.0_f64;
42
43 for (u, v) in graph.edges() {
44 if u == v {
45 continue;
46 }
47 let du = graph.degree(u)? as f64;
48 let dv = graph.degree(v)? as f64;
49 let denom = du + dv - 2.0;
50 if denom > 0.0 {
51 result += (du * dv / denom).exp();
52 }
53 }
54
55 Ok(result)
56}
57
58pub fn exponential_randic(graph: &Graph) -> IgraphResult<f64> {
75 let mut result = 0.0_f64;
76
77 for (u, v) in graph.edges() {
78 if u == v {
79 continue;
80 }
81 let du = graph.degree(u)? as f64;
82 let dv = graph.degree(v)? as f64;
83 let product = du * dv;
84 if product > 0.0 {
85 result += (1.0 / product.sqrt()).exp();
86 }
87 }
88
89 Ok(result)
90}
91
92pub fn exponential_abc(graph: &Graph) -> IgraphResult<f64> {
110 let mut result = 0.0_f64;
111
112 for (u, v) in graph.edges() {
113 if u == v {
114 continue;
115 }
116 let du = graph.degree(u)? as f64;
117 let dv = graph.degree(v)? as f64;
118 let product = du * dv;
119 let numer = du + dv - 2.0;
120 if product > 0.0 && numer >= 0.0 {
121 result += (numer / product).sqrt().exp();
122 }
123 }
124
125 Ok(result)
126}
127
128pub fn exponential_ga(graph: &Graph) -> IgraphResult<f64> {
145 let mut result = 0.0_f64;
146
147 for (u, v) in graph.edges() {
148 if u == v {
149 continue;
150 }
151 let du = graph.degree(u)? as f64;
152 let dv = graph.degree(v)? as f64;
153 let sum = du + dv;
154 if sum > 0.0 {
155 result += (2.0 * (du * dv).sqrt() / sum).exp();
156 }
157 }
158
159 Ok(result)
160}
161
162#[cfg(test)]
163mod tests {
164 use super::*;
165
166 fn single_edge() -> Graph {
167 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
168 }
169
170 fn path3() -> Graph {
171 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
172 }
173
174 fn k3() -> Graph {
175 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
176 }
177
178 fn k4() -> Graph {
179 Graph::from_edges(
180 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
181 false,
182 Some(4),
183 )
184 .unwrap()
185 }
186
187 fn cycle4() -> Graph {
188 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
189 }
190
191 fn cycle5() -> Graph {
192 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
193 }
194
195 fn star5() -> Graph {
196 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
197 }
198
199 fn paw() -> Graph {
200 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
201 }
202
203 #[test]
206 fn eaz_empty() {
207 let g = Graph::with_vertices(0);
208 assert!(exponential_augmented_zagreb(&g).unwrap().abs() < 1e-10);
209 }
210
211 #[test]
212 fn eaz_isolated() {
213 let g = Graph::with_vertices(5);
214 assert!(exponential_augmented_zagreb(&g).unwrap().abs() < 1e-10);
215 }
216
217 #[test]
218 fn eaz_single_edge() {
219 assert!(exponential_augmented_zagreb(&single_edge()).unwrap().abs() < 1e-10);
221 }
222
223 #[test]
224 fn eaz_k3() {
225 let expected = 3.0 * 2.0_f64.exp();
227 assert!((exponential_augmented_zagreb(&k3()).unwrap() - expected).abs() < 1e-10);
228 }
229
230 #[test]
231 fn eaz_k4() {
232 let expected = 6.0 * 2.25_f64.exp();
234 assert!((exponential_augmented_zagreb(&k4()).unwrap() - expected).abs() < 1e-10);
235 }
236
237 #[test]
238 fn eaz_path3() {
239 let expected = 2.0 * 2.0_f64.exp();
241 assert!((exponential_augmented_zagreb(&path3()).unwrap() - expected).abs() < 1e-10);
242 }
243
244 #[test]
245 fn eaz_cycle4() {
246 let expected = 4.0 * 2.0_f64.exp();
248 assert!((exponential_augmented_zagreb(&cycle4()).unwrap() - expected).abs() < 1e-10);
249 }
250
251 #[test]
252 fn eaz_cycle5() {
253 let expected = 5.0 * 2.0_f64.exp();
254 assert!((exponential_augmented_zagreb(&cycle5()).unwrap() - expected).abs() < 1e-10);
255 }
256
257 #[test]
258 fn eaz_star5() {
259 let expected = 4.0 * (4.0_f64 / 3.0).exp();
261 assert!((exponential_augmented_zagreb(&star5()).unwrap() - expected).abs() < 1e-10);
262 }
263
264 #[test]
265 fn eaz_paw() {
266 let expected = 3.0 * 2.0_f64.exp() + 1.5_f64.exp();
271 assert!((exponential_augmented_zagreb(&paw()).unwrap() - expected).abs() < 1e-10);
272 }
273
274 #[test]
277 fn er_empty() {
278 let g = Graph::with_vertices(0);
279 assert!(exponential_randic(&g).unwrap().abs() < 1e-10);
280 }
281
282 #[test]
283 fn er_isolated() {
284 let g = Graph::with_vertices(5);
285 assert!(exponential_randic(&g).unwrap().abs() < 1e-10);
286 }
287
288 #[test]
289 fn er_single_edge() {
290 let expected = 1.0_f64.exp();
292 assert!((exponential_randic(&single_edge()).unwrap() - expected).abs() < 1e-10);
293 }
294
295 #[test]
296 fn er_k3() {
297 let expected = 3.0 * 0.5_f64.exp();
299 assert!((exponential_randic(&k3()).unwrap() - expected).abs() < 1e-10);
300 }
301
302 #[test]
303 fn er_k4() {
304 let expected = 6.0 * (1.0_f64 / 3.0).exp();
306 assert!((exponential_randic(&k4()).unwrap() - expected).abs() < 1e-10);
307 }
308
309 #[test]
310 fn er_path3() {
311 let expected = 2.0 * (1.0 / 2.0_f64.sqrt()).exp();
313 assert!((exponential_randic(&path3()).unwrap() - expected).abs() < 1e-10);
314 }
315
316 #[test]
317 fn er_cycle4() {
318 let expected = 4.0 * 0.5_f64.exp();
320 assert!((exponential_randic(&cycle4()).unwrap() - expected).abs() < 1e-10);
321 }
322
323 #[test]
324 fn er_cycle5() {
325 let expected = 5.0 * 0.5_f64.exp();
326 assert!((exponential_randic(&cycle5()).unwrap() - expected).abs() < 1e-10);
327 }
328
329 #[test]
330 fn er_star5() {
331 let expected = 4.0 * 0.5_f64.exp();
333 assert!((exponential_randic(&star5()).unwrap() - expected).abs() < 1e-10);
334 }
335
336 #[test]
337 fn er_paw() {
338 let expected =
343 0.5_f64.exp() + 2.0 * (1.0 / 6.0_f64.sqrt()).exp() + (1.0 / 3.0_f64.sqrt()).exp();
344 assert!((exponential_randic(&paw()).unwrap() - expected).abs() < 1e-10);
345 }
346
347 #[test]
350 fn eabc_empty() {
351 let g = Graph::with_vertices(0);
352 assert!(exponential_abc(&g).unwrap().abs() < 1e-10);
353 }
354
355 #[test]
356 fn eabc_isolated() {
357 let g = Graph::with_vertices(5);
358 assert!(exponential_abc(&g).unwrap().abs() < 1e-10);
359 }
360
361 #[test]
362 fn eabc_single_edge() {
363 let expected = 1.0;
365 assert!((exponential_abc(&single_edge()).unwrap() - expected).abs() < 1e-10);
366 }
367
368 #[test]
369 fn eabc_k3() {
370 let expected = 3.0 * (1.0 / 2.0_f64.sqrt()).exp();
372 assert!((exponential_abc(&k3()).unwrap() - expected).abs() < 1e-10);
373 }
374
375 #[test]
376 fn eabc_k4() {
377 let expected = 6.0 * (2.0 / 3.0_f64).exp();
379 assert!((exponential_abc(&k4()).unwrap() - expected).abs() < 1e-10);
380 }
381
382 #[test]
383 fn eabc_path3() {
384 let expected = 2.0 * (1.0 / 2.0_f64.sqrt()).exp();
386 assert!((exponential_abc(&path3()).unwrap() - expected).abs() < 1e-10);
387 }
388
389 #[test]
390 fn eabc_cycle4() {
391 let expected = 4.0 * (1.0 / 2.0_f64.sqrt()).exp();
393 assert!((exponential_abc(&cycle4()).unwrap() - expected).abs() < 1e-10);
394 }
395
396 #[test]
397 fn eabc_star5() {
398 let expected = 4.0 * (3.0_f64.sqrt() / 2.0).exp();
400 assert!((exponential_abc(&star5()).unwrap() - expected).abs() < 1e-10);
401 }
402
403 #[test]
404 fn eabc_paw() {
405 let inv_sqrt2 = 1.0 / 2.0_f64.sqrt();
410 let expected = 3.0 * inv_sqrt2.exp() + (2.0_f64 / 3.0).sqrt().exp();
411 assert!((exponential_abc(&paw()).unwrap() - expected).abs() < 1e-10);
412 }
413
414 #[test]
417 fn ega_empty() {
418 let g = Graph::with_vertices(0);
419 assert!(exponential_ga(&g).unwrap().abs() < 1e-10);
420 }
421
422 #[test]
423 fn ega_isolated() {
424 let g = Graph::with_vertices(5);
425 assert!(exponential_ga(&g).unwrap().abs() < 1e-10);
426 }
427
428 #[test]
429 fn ega_single_edge() {
430 let expected = 1.0_f64.exp();
432 assert!((exponential_ga(&single_edge()).unwrap() - expected).abs() < 1e-10);
433 }
434
435 #[test]
436 fn ega_k3() {
437 let expected = 3.0 * 1.0_f64.exp();
439 assert!((exponential_ga(&k3()).unwrap() - expected).abs() < 1e-10);
440 }
441
442 #[test]
443 fn ega_k4() {
444 let expected = 6.0 * 1.0_f64.exp();
446 assert!((exponential_ga(&k4()).unwrap() - expected).abs() < 1e-10);
447 }
448
449 #[test]
450 fn ega_path3() {
451 let expected = 2.0 * (2.0 * 2.0_f64.sqrt() / 3.0).exp();
453 assert!((exponential_ga(&path3()).unwrap() - expected).abs() < 1e-10);
454 }
455
456 #[test]
457 fn ega_cycle4() {
458 let expected = 4.0 * 1.0_f64.exp();
460 assert!((exponential_ga(&cycle4()).unwrap() - expected).abs() < 1e-10);
461 }
462
463 #[test]
464 fn ega_cycle5() {
465 let expected = 5.0 * 1.0_f64.exp();
466 assert!((exponential_ga(&cycle5()).unwrap() - expected).abs() < 1e-10);
467 }
468
469 #[test]
470 fn ega_star5() {
471 let expected = 4.0 * 0.8_f64.exp();
473 assert!((exponential_ga(&star5()).unwrap() - expected).abs() < 1e-10);
474 }
475
476 #[test]
477 fn ega_paw() {
478 let expected =
483 1.0_f64.exp() + 2.0 * (2.0 * 6.0_f64.sqrt() / 5.0).exp() + (3.0_f64.sqrt() / 2.0).exp();
484 assert!((exponential_ga(&paw()).unwrap() - expected).abs() < 1e-10);
485 }
486
487 #[test]
490 fn regular_ega_equals_m_times_e() {
491 for g in &[k3(), k4(), cycle4(), cycle5()] {
493 let m = g.ecount() as f64;
494 let expected = m * 1.0_f64.exp();
495 assert!((exponential_ga(g).unwrap() - expected).abs() < 1e-8);
496 }
497 }
498
499 #[test]
500 fn regular_er() {
501 let g = &k4();
503 let m = g.ecount() as f64;
504 let expected = m * (1.0_f64 / 3.0).exp();
505 assert!((exponential_randic(g).unwrap() - expected).abs() < 1e-10);
506 }
507
508 #[test]
509 fn all_positive_for_nonempty() {
510 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
511 assert!(exponential_randic(g).unwrap() > 0.0);
512 assert!(exponential_abc(g).unwrap() > 0.0);
513 assert!(exponential_ga(g).unwrap() > 0.0);
514 }
515 }
516
517 #[test]
518 fn eaz_positive_for_connected_nonedge() {
519 for g in &[path3(), k3(), k4(), cycle4(), star5(), paw()] {
521 assert!(exponential_augmented_zagreb(g).unwrap() > 0.0);
522 }
523 }
524
525 #[test]
526 fn ega_ge_m_for_nontrivial() {
527 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5(), paw()] {
530 let m = g.ecount() as f64;
531 assert!(exponential_ga(g).unwrap() >= m - 1e-10);
532 }
533 }
534}