rust_igraph/algorithms/properties/
augmented_zagreb.rs1#![allow(
14 clippy::cast_possible_truncation,
15 clippy::cast_precision_loss,
16 clippy::many_single_char_names,
17 clippy::needless_range_loop,
18 clippy::too_many_lines
19)]
20
21use crate::core::{Graph, IgraphResult};
22
23pub fn augmented_zagreb_index(graph: &Graph) -> IgraphResult<f64> {
42 let n = graph.vcount();
43 if n == 0 {
44 return Ok(0.0);
45 }
46
47 let mut azi = 0.0_f64;
48
49 for (u, v) in graph.edges() {
50 if u == v {
51 continue;
52 }
53 let du = graph.degree(u)? as f64;
54 let dv = graph.degree(v)? as f64;
55 let denom = du + dv - 2.0;
56 if denom <= 0.0 {
57 continue;
58 }
59 let frac = du * dv / denom;
60 azi += frac * frac * frac;
61 }
62
63 Ok(azi)
64}
65
66pub fn atom_bond_sum_connectivity(graph: &Graph) -> IgraphResult<f64> {
85 let n = graph.vcount();
86 if n == 0 {
87 return Ok(0.0);
88 }
89
90 let mut abs_val = 0.0_f64;
91
92 for (u, v) in graph.edges() {
93 if u == v {
94 continue;
95 }
96 let du = graph.degree(u)? as f64;
97 let dv = graph.degree(v)? as f64;
98 let sum_d = du + dv;
99 if sum_d <= 2.0 {
100 continue;
101 }
102 abs_val += ((sum_d - 2.0) / sum_d).sqrt();
103 }
104
105 Ok(abs_val)
106}
107
108pub fn geometric_arithmetic_index(graph: &Graph) -> IgraphResult<f64> {
126 let n = graph.vcount();
127 if n == 0 {
128 return Ok(0.0);
129 }
130
131 let mut ga = 0.0_f64;
132
133 for (u, v) in graph.edges() {
134 if u == v {
135 continue;
136 }
137 let du = graph.degree(u)? as f64;
138 let dv = graph.degree(v)? as f64;
139 let sum_d = du + dv;
140 if sum_d <= 0.0 {
141 continue;
142 }
143 ga += 2.0 * (du * dv).sqrt() / sum_d;
144 }
145
146 Ok(ga)
147}
148
149#[cfg(test)]
150mod tests {
151 use super::*;
152
153 fn single_edge() -> Graph {
154 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
155 }
156
157 fn path3() -> Graph {
158 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
159 }
160
161 fn path4() -> Graph {
162 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
163 }
164
165 fn k3() -> Graph {
166 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
167 }
168
169 fn k4() -> Graph {
170 Graph::from_edges(
171 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
172 false,
173 Some(4),
174 )
175 .unwrap()
176 }
177
178 fn cycle4() -> Graph {
179 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
180 }
181
182 fn cycle5() -> Graph {
183 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
184 }
185
186 fn star5() -> Graph {
187 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
188 }
189
190 #[test]
193 fn azi_empty() {
194 let g = Graph::with_vertices(0);
195 assert!((augmented_zagreb_index(&g).unwrap() - 0.0).abs() < 1e-10);
196 }
197
198 #[test]
199 fn azi_single_vertex() {
200 let g = Graph::with_vertices(1);
201 assert!((augmented_zagreb_index(&g).unwrap() - 0.0).abs() < 1e-10);
202 }
203
204 #[test]
205 fn azi_no_edges() {
206 let g = Graph::with_vertices(3);
207 assert!((augmented_zagreb_index(&g).unwrap() - 0.0).abs() < 1e-10);
208 }
209
210 #[test]
211 fn azi_single_edge() {
212 assert!((augmented_zagreb_index(&single_edge()).unwrap() - 0.0).abs() < 1e-10);
214 }
215
216 #[test]
217 fn azi_path3() {
218 assert!((augmented_zagreb_index(&path3()).unwrap() - 16.0).abs() < 1e-10);
222 }
223
224 #[test]
225 fn azi_path4() {
226 assert!((augmented_zagreb_index(&path4()).unwrap() - 24.0).abs() < 1e-10);
231 }
232
233 #[test]
234 fn azi_k3() {
235 assert!((augmented_zagreb_index(&k3()).unwrap() - 24.0).abs() < 1e-10);
238 }
239
240 #[test]
241 fn azi_k4() {
242 let expected = 6.0 * (9.0_f64 / 4.0).powi(3);
245 assert!((augmented_zagreb_index(&k4()).unwrap() - expected).abs() < 1e-10);
246 }
247
248 #[test]
249 fn azi_cycle4() {
250 assert!((augmented_zagreb_index(&cycle4()).unwrap() - 32.0).abs() < 1e-10);
253 }
254
255 #[test]
256 fn azi_cycle5() {
257 assert!((augmented_zagreb_index(&cycle5()).unwrap() - 40.0).abs() < 1e-10);
259 }
260
261 #[test]
262 fn azi_star5() {
263 let expected = 4.0 * (4.0_f64 / 3.0).powi(3);
267 assert!((augmented_zagreb_index(&star5()).unwrap() - expected).abs() < 1e-10);
268 }
269
270 #[test]
271 fn azi_positive_for_path() {
272 for g in &[path3(), path4(), k3(), k4(), cycle4(), star5()] {
273 assert!(augmented_zagreb_index(g).unwrap() > 0.0);
274 }
275 }
276
277 #[test]
278 fn azi_regular_formula() {
279 for g in &[k3(), k4(), cycle4(), cycle5()] {
281 let m = g.ecount() as f64;
282 let r = g.degree(0).unwrap() as f64;
283 let expected = m * (r * r / (2.0 * r - 2.0)).powi(3);
284 assert!((augmented_zagreb_index(g).unwrap() - expected).abs() < 1e-8);
285 }
286 }
287
288 #[test]
291 fn abs_empty() {
292 let g = Graph::with_vertices(0);
293 assert!((atom_bond_sum_connectivity(&g).unwrap() - 0.0).abs() < 1e-10);
294 }
295
296 #[test]
297 fn abs_single_edge() {
298 assert!((atom_bond_sum_connectivity(&single_edge()).unwrap() - 0.0).abs() < 1e-10);
300 }
301
302 #[test]
303 fn abs_path3() {
304 let expected = 2.0 / 3.0_f64.sqrt();
308 assert!((atom_bond_sum_connectivity(&path3()).unwrap() - expected).abs() < 1e-10);
309 }
310
311 #[test]
312 fn abs_k3() {
313 let expected = 3.0 / 2.0_f64.sqrt();
316 assert!((atom_bond_sum_connectivity(&k3()).unwrap() - expected).abs() < 1e-10);
317 }
318
319 #[test]
320 fn abs_k4() {
321 let expected = 6.0 * (2.0_f64 / 3.0).sqrt();
324 assert!((atom_bond_sum_connectivity(&k4()).unwrap() - expected).abs() < 1e-10);
325 }
326
327 #[test]
328 fn abs_cycle4() {
329 let expected = 4.0 / 2.0_f64.sqrt();
332 assert!((atom_bond_sum_connectivity(&cycle4()).unwrap() - expected).abs() < 1e-10);
333 }
334
335 #[test]
336 fn abs_star5() {
337 let expected = 4.0 * (3.0_f64 / 5.0).sqrt();
340 assert!((atom_bond_sum_connectivity(&star5()).unwrap() - expected).abs() < 1e-10);
341 }
342
343 #[test]
344 fn abs_leq_m() {
345 for g in &[path3(), k3(), k4(), cycle4(), star5()] {
347 let abs_val = atom_bond_sum_connectivity(g).unwrap();
348 assert!(abs_val < g.ecount() as f64 + 1e-10);
349 }
350 }
351
352 #[test]
355 fn ga_empty() {
356 let g = Graph::with_vertices(0);
357 assert!((geometric_arithmetic_index(&g).unwrap() - 0.0).abs() < 1e-10);
358 }
359
360 #[test]
361 fn ga_single_edge() {
362 assert!((geometric_arithmetic_index(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
364 }
365
366 #[test]
367 fn ga_path3() {
368 let expected = 4.0 * 2.0_f64.sqrt() / 3.0;
370 assert!((geometric_arithmetic_index(&path3()).unwrap() - expected).abs() < 1e-10);
371 }
372
373 #[test]
374 fn ga_k3() {
375 assert!((geometric_arithmetic_index(&k3()).unwrap() - 3.0).abs() < 1e-10);
377 }
378
379 #[test]
380 fn ga_k4() {
381 assert!((geometric_arithmetic_index(&k4()).unwrap() - 6.0).abs() < 1e-10);
383 }
384
385 #[test]
386 fn ga_cycle4() {
387 assert!((geometric_arithmetic_index(&cycle4()).unwrap() - 4.0).abs() < 1e-10);
389 }
390
391 #[test]
392 fn ga_star5() {
393 let expected = 4.0 * 4.0 / 5.0;
395 assert!((geometric_arithmetic_index(&star5()).unwrap() - expected).abs() < 1e-10);
396 }
397
398 #[test]
399 fn ga_leq_m() {
400 for g in &[single_edge(), path3(), k3(), k4(), cycle4(), star5()] {
402 let ga = geometric_arithmetic_index(g).unwrap();
403 assert!(ga <= g.ecount() as f64 + 1e-10);
404 }
405 }
406
407 #[test]
408 fn ga_equals_m_for_regular() {
409 for g in &[k3(), k4(), cycle4(), cycle5()] {
412 let ga = geometric_arithmetic_index(g).unwrap();
413 assert!((ga - g.ecount() as f64).abs() < 1e-10);
414 }
415 }
416
417 #[test]
418 fn ga_path4() {
419 let expected = 4.0 * 2.0_f64.sqrt() / 3.0 + 1.0;
424 assert!((geometric_arithmetic_index(&path4()).unwrap() - expected).abs() < 1e-10);
425 }
426
427 #[test]
428 fn ga_diamond() {
429 let g =
435 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3)], false, Some(4)).unwrap();
436 let expected = 1.0 + 8.0 * 6.0_f64.sqrt() / 5.0;
437 assert!((geometric_arithmetic_index(&g).unwrap() - expected).abs() < 1e-10);
438 }
439
440 #[test]
443 fn all_positive_for_connected() {
444 for g in &[path3(), k3(), k4(), cycle4(), star5()] {
445 assert!(augmented_zagreb_index(g).unwrap() > 0.0);
446 assert!(atom_bond_sum_connectivity(g).unwrap() > 0.0);
447 assert!(geometric_arithmetic_index(g).unwrap() > 0.0);
448 }
449 }
450
451 #[test]
452 fn with_isolated_vertex() {
453 let g = Graph::from_edges(&[(0, 1)], false, Some(3)).unwrap();
455 assert!((augmented_zagreb_index(&g).unwrap() - 0.0).abs() < 1e-10);
457 assert!((atom_bond_sum_connectivity(&g).unwrap() - 0.0).abs() < 1e-10);
458 assert!((geometric_arithmetic_index(&g).unwrap() - 1.0).abs() < 1e-10);
460 }
461}