rust_igraph/algorithms/properties/
reduced_indices.rs1#![allow(
12 clippy::cast_possible_truncation,
13 clippy::cast_precision_loss,
14 clippy::many_single_char_names,
15 clippy::needless_range_loop,
16 clippy::too_many_lines
17)]
18
19use crate::core::{Graph, IgraphResult};
20
21pub fn reduced_reciprocal_randic(graph: &Graph) -> IgraphResult<f64> {
39 let mut result = 0.0_f64;
40
41 for (u, v) in graph.edges() {
42 if u == v {
43 continue;
44 }
45 let du = graph.degree(u)?;
46 let dv = graph.degree(v)?;
47 if du >= 2 && dv >= 2 {
48 let product = ((du - 1) * (dv - 1)) as f64;
49 result += 1.0 / product.sqrt();
50 }
51 }
52
53 Ok(result)
54}
55
56pub fn reduced_sum_connectivity(graph: &Graph) -> IgraphResult<f64> {
74 let mut result = 0.0_f64;
75
76 for (u, v) in graph.edges() {
77 if u == v {
78 continue;
79 }
80 let du = graph.degree(u)?;
81 let dv = graph.degree(v)?;
82 let s = du + dv;
83 if s > 2 {
84 result += 1.0 / ((s - 2) as f64).sqrt();
85 }
86 }
87
88 Ok(result)
89}
90
91pub fn reduced_first_zagreb(graph: &Graph) -> IgraphResult<u64> {
113 let n = graph.vcount() as usize;
114 let mut result = 0_u64;
115
116 for v in 0..n {
117 let d = graph.degree(v as u32)?;
118 if d >= 1 {
119 let rd = (d - 1) as u64;
120 result = result.saturating_add(rd.saturating_mul(rd));
121 }
122 }
123
124 Ok(result)
125}
126
127pub fn reduced_forgotten_index(graph: &Graph) -> IgraphResult<u64> {
145 let n = graph.vcount() as usize;
146 let mut result = 0_u64;
147
148 for v in 0..n {
149 let d = graph.degree(v as u32)?;
150 if d >= 1 {
151 let rd = (d - 1) as u64;
152 result = result.saturating_add(rd.saturating_mul(rd).saturating_mul(rd));
153 }
154 }
155
156 Ok(result)
157}
158
159#[cfg(test)]
160mod tests {
161 use super::*;
162
163 fn single_edge() -> Graph {
164 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
165 }
166
167 fn path3() -> Graph {
168 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
169 }
170
171 fn path4() -> Graph {
172 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
173 }
174
175 fn k3() -> Graph {
176 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
177 }
178
179 fn k4() -> Graph {
180 Graph::from_edges(
181 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
182 false,
183 Some(4),
184 )
185 .unwrap()
186 }
187
188 fn cycle4() -> Graph {
189 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
190 }
191
192 fn cycle5() -> Graph {
193 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
194 }
195
196 fn star5() -> Graph {
197 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
198 }
199
200 fn paw() -> Graph {
201 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
202 }
203
204 #[test]
207 fn rrr_empty() {
208 let g = Graph::with_vertices(0);
209 assert!(reduced_reciprocal_randic(&g).unwrap().abs() < 1e-10);
210 }
211
212 #[test]
213 fn rrr_isolated() {
214 let g = Graph::with_vertices(5);
215 assert!(reduced_reciprocal_randic(&g).unwrap().abs() < 1e-10);
216 }
217
218 #[test]
219 fn rrr_single_edge() {
220 assert!(reduced_reciprocal_randic(&single_edge()).unwrap().abs() < 1e-10);
222 }
223
224 #[test]
225 fn rrr_path3() {
226 assert!(reduced_reciprocal_randic(&path3()).unwrap().abs() < 1e-10);
228 }
229
230 #[test]
231 fn rrr_k3() {
232 assert!((reduced_reciprocal_randic(&k3()).unwrap() - 3.0).abs() < 1e-10);
234 }
235
236 #[test]
237 fn rrr_k4() {
238 assert!((reduced_reciprocal_randic(&k4()).unwrap() - 3.0).abs() < 1e-10);
240 }
241
242 #[test]
243 fn rrr_cycle4() {
244 assert!((reduced_reciprocal_randic(&cycle4()).unwrap() - 4.0).abs() < 1e-10);
246 }
247
248 #[test]
249 fn rrr_cycle5() {
250 assert!((reduced_reciprocal_randic(&cycle5()).unwrap() - 5.0).abs() < 1e-10);
252 }
253
254 #[test]
255 fn rrr_star5() {
256 assert!(reduced_reciprocal_randic(&star5()).unwrap().abs() < 1e-10);
258 }
259
260 #[test]
261 fn rrr_paw() {
262 let expected = 1.0 + 2.0 / 2.0_f64.sqrt();
267 assert!((reduced_reciprocal_randic(&paw()).unwrap() - expected).abs() < 1e-10);
268 }
269
270 #[test]
273 fn rsc_empty() {
274 let g = Graph::with_vertices(0);
275 assert!(reduced_sum_connectivity(&g).unwrap().abs() < 1e-10);
276 }
277
278 #[test]
279 fn rsc_isolated() {
280 let g = Graph::with_vertices(5);
281 assert!(reduced_sum_connectivity(&g).unwrap().abs() < 1e-10);
282 }
283
284 #[test]
285 fn rsc_single_edge() {
286 assert!(reduced_sum_connectivity(&single_edge()).unwrap().abs() < 1e-10);
288 }
289
290 #[test]
291 fn rsc_path3() {
292 assert!((reduced_sum_connectivity(&path3()).unwrap() - 2.0).abs() < 1e-10);
294 }
295
296 #[test]
297 fn rsc_k3() {
298 let expected = 3.0 / 2.0_f64.sqrt();
300 assert!((reduced_sum_connectivity(&k3()).unwrap() - expected).abs() < 1e-10);
301 }
302
303 #[test]
304 fn rsc_k4() {
305 assert!((reduced_sum_connectivity(&k4()).unwrap() - 3.0).abs() < 1e-10);
307 }
308
309 #[test]
310 fn rsc_cycle4() {
311 let expected = 4.0 / 2.0_f64.sqrt();
313 assert!((reduced_sum_connectivity(&cycle4()).unwrap() - expected).abs() < 1e-10);
314 }
315
316 #[test]
317 fn rsc_star5() {
318 let expected = 4.0 / 3.0_f64.sqrt();
320 assert!((reduced_sum_connectivity(&star5()).unwrap() - expected).abs() < 1e-10);
321 }
322
323 #[test]
324 fn rsc_paw() {
325 let expected = 2.0 / 2.0_f64.sqrt() + 2.0 / 3.0_f64.sqrt();
330 assert!((reduced_sum_connectivity(&paw()).unwrap() - expected).abs() < 1e-10);
331 }
332
333 #[test]
336 fn rfz_empty() {
337 let g = Graph::with_vertices(0);
338 assert_eq!(reduced_first_zagreb(&g).unwrap(), 0);
339 }
340
341 #[test]
342 fn rfz_isolated() {
343 let g = Graph::with_vertices(5);
344 assert_eq!(reduced_first_zagreb(&g).unwrap(), 0);
345 }
346
347 #[test]
348 fn rfz_single_edge() {
349 assert_eq!(reduced_first_zagreb(&single_edge()).unwrap(), 0);
351 }
352
353 #[test]
354 fn rfz_path3() {
355 assert_eq!(reduced_first_zagreb(&path3()).unwrap(), 1);
357 }
358
359 #[test]
360 fn rfz_path4() {
361 assert_eq!(reduced_first_zagreb(&path4()).unwrap(), 2);
363 }
364
365 #[test]
366 fn rfz_k3() {
367 assert_eq!(reduced_first_zagreb(&k3()).unwrap(), 3);
369 }
370
371 #[test]
372 fn rfz_k4() {
373 assert_eq!(reduced_first_zagreb(&k4()).unwrap(), 16);
375 }
376
377 #[test]
378 fn rfz_cycle4() {
379 assert_eq!(reduced_first_zagreb(&cycle4()).unwrap(), 4);
381 }
382
383 #[test]
384 fn rfz_cycle5() {
385 assert_eq!(reduced_first_zagreb(&cycle5()).unwrap(), 5);
387 }
388
389 #[test]
390 fn rfz_star5() {
391 assert_eq!(reduced_first_zagreb(&star5()).unwrap(), 9);
393 }
394
395 #[test]
396 fn rfz_paw() {
397 assert_eq!(reduced_first_zagreb(&paw()).unwrap(), 6);
399 }
400
401 #[test]
404 fn rfi_empty() {
405 let g = Graph::with_vertices(0);
406 assert_eq!(reduced_forgotten_index(&g).unwrap(), 0);
407 }
408
409 #[test]
410 fn rfi_isolated() {
411 let g = Graph::with_vertices(5);
412 assert_eq!(reduced_forgotten_index(&g).unwrap(), 0);
413 }
414
415 #[test]
416 fn rfi_single_edge() {
417 assert_eq!(reduced_forgotten_index(&single_edge()).unwrap(), 0);
419 }
420
421 #[test]
422 fn rfi_path3() {
423 assert_eq!(reduced_forgotten_index(&path3()).unwrap(), 1);
425 }
426
427 #[test]
428 fn rfi_k3() {
429 assert_eq!(reduced_forgotten_index(&k3()).unwrap(), 3);
431 }
432
433 #[test]
434 fn rfi_k4() {
435 assert_eq!(reduced_forgotten_index(&k4()).unwrap(), 32);
437 }
438
439 #[test]
440 fn rfi_cycle4() {
441 assert_eq!(reduced_forgotten_index(&cycle4()).unwrap(), 4);
443 }
444
445 #[test]
446 fn rfi_star5() {
447 assert_eq!(reduced_forgotten_index(&star5()).unwrap(), 27);
449 }
450
451 #[test]
452 fn rfi_paw() {
453 assert_eq!(reduced_forgotten_index(&paw()).unwrap(), 10);
455 }
456
457 #[test]
460 fn rfz_le_rfi_for_nontrivial() {
461 for g in &[k4(), star5()] {
465 assert!(reduced_first_zagreb(g).unwrap() <= reduced_forgotten_index(g).unwrap());
466 }
467 }
468
469 #[test]
470 fn regular_rrr_equals_edge_count() {
471 assert!((reduced_reciprocal_randic(&k3()).unwrap() - 3.0).abs() < 1e-10);
474 assert!((reduced_reciprocal_randic(&cycle4()).unwrap() - 4.0).abs() < 1e-10);
476 assert!((reduced_reciprocal_randic(&k4()).unwrap() - 3.0).abs() < 1e-10);
478 }
479}