rust_igraph/algorithms/properties/
ev_degree_indices.rs1#![allow(
18 clippy::cast_possible_truncation,
19 clippy::cast_precision_loss,
20 clippy::many_single_char_names,
21 clippy::needless_range_loop,
22 clippy::too_many_lines
23)]
24
25use crate::core::{Graph, IgraphResult};
26
27fn ev_degrees(graph: &Graph) -> IgraphResult<Vec<u64>> {
28 let mut devs = Vec::new();
29
30 for (u, v) in graph.edges() {
31 if u == v {
32 devs.push(0);
33 continue;
34 }
35 let du = graph.degree(u)? as u64;
36 let dv = graph.degree(v)? as u64;
37 devs.push(du.saturating_add(dv).saturating_sub(2));
38 }
39
40 Ok(devs)
41}
42
43pub fn first_ev_degree_zagreb(graph: &Graph) -> IgraphResult<u64> {
59 let devs = ev_degrees(graph)?;
60 let mut m1 = 0_u64;
61
62 for &d in &devs {
63 m1 = m1.saturating_add(d.saturating_mul(d));
64 }
65
66 Ok(m1)
67}
68
69pub fn second_ev_degree_zagreb(graph: &Graph) -> IgraphResult<u64> {
85 let devs = ev_degrees(graph)?;
86 let edges: Vec<(u32, u32)> = graph.edges().collect();
87 let n = graph.vcount() as usize;
88
89 let mut inc: Vec<Vec<usize>> = vec![Vec::new(); n];
90 for (i, &(u, v)) in edges.iter().enumerate() {
91 inc[u as usize].push(i);
92 if u != v {
93 inc[v as usize].push(i);
94 }
95 }
96
97 let mut m2 = 0_u64;
98
99 for v in 0..n {
100 let incident = &inc[v];
101 for i in 0..incident.len() {
102 for j in (i + 1)..incident.len() {
103 m2 = m2.saturating_add(devs[incident[i]].saturating_mul(devs[incident[j]]));
104 }
105 }
106 }
107
108 Ok(m2)
109}
110
111pub fn ev_degree_randic(graph: &Graph) -> IgraphResult<f64> {
127 let devs = ev_degrees(graph)?;
128 let edges: Vec<(u32, u32)> = graph.edges().collect();
129 let n = graph.vcount() as usize;
130
131 let mut inc: Vec<Vec<usize>> = vec![Vec::new(); n];
132 for (i, &(u, v)) in edges.iter().enumerate() {
133 inc[u as usize].push(i);
134 if u != v {
135 inc[v as usize].push(i);
136 }
137 }
138
139 let mut r = 0.0_f64;
140
141 for v in 0..n {
142 let incident = &inc[v];
143 for i in 0..incident.len() {
144 for j in (i + 1)..incident.len() {
145 let p = devs[incident[i]] as f64 * devs[incident[j]] as f64;
146 if p > 0.0 {
147 r += 1.0 / p.sqrt();
148 }
149 }
150 }
151 }
152
153 Ok(r)
154}
155
156#[cfg(test)]
157mod tests {
158 use super::*;
159
160 fn single_edge() -> Graph {
161 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
162 }
163
164 fn path3() -> Graph {
165 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
166 }
167
168 fn path4() -> Graph {
169 Graph::from_edges(&[(0, 1), (1, 2), (2, 3)], false, Some(4)).unwrap()
170 }
171
172 fn k3() -> Graph {
173 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
174 }
175
176 fn k4() -> Graph {
177 Graph::from_edges(
178 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
179 false,
180 Some(4),
181 )
182 .unwrap()
183 }
184
185 fn cycle4() -> Graph {
186 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
187 }
188
189 fn cycle5() -> Graph {
190 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)], false, Some(5)).unwrap()
191 }
192
193 fn star5() -> Graph {
194 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
195 }
196
197 fn paw() -> Graph {
198 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
199 }
200
201 fn devs(g: &Graph) -> Vec<u64> {
202 ev_degrees(g).unwrap()
203 }
204
205 #[test]
208 fn ev_empty() {
209 let g = Graph::with_vertices(0);
210 assert_eq!(devs(&g), Vec::<u64>::new());
211 }
212
213 #[test]
214 fn ev_single_edge() {
215 assert_eq!(devs(&single_edge()), vec![0]);
217 }
218
219 #[test]
220 fn ev_path3() {
221 assert_eq!(devs(&path3()), vec![1, 1]);
223 }
224
225 #[test]
226 fn ev_path4() {
227 assert_eq!(devs(&path4()), vec![1, 2, 1]);
229 }
230
231 #[test]
232 fn ev_k3() {
233 assert_eq!(devs(&k3()), vec![2, 2, 2]);
235 }
236
237 #[test]
238 fn ev_k4() {
239 assert_eq!(devs(&k4()), vec![4, 4, 4, 4, 4, 4]);
241 }
242
243 #[test]
244 fn ev_cycle4() {
245 assert_eq!(devs(&cycle4()), vec![2, 2, 2, 2]);
247 }
248
249 #[test]
250 fn ev_star5() {
251 assert_eq!(devs(&star5()), vec![3, 3, 3, 3]);
253 }
254
255 #[test]
256 fn ev_paw() {
257 assert_eq!(devs(&paw()), vec![2, 3, 3, 2]);
259 }
260
261 #[test]
264 fn m1ev_empty() {
265 let g = Graph::with_vertices(0);
266 assert_eq!(first_ev_degree_zagreb(&g).unwrap(), 0);
267 }
268
269 #[test]
270 fn m1ev_single_edge() {
271 assert_eq!(first_ev_degree_zagreb(&single_edge()).unwrap(), 0);
272 }
273
274 #[test]
275 fn m1ev_path3() {
276 assert_eq!(first_ev_degree_zagreb(&path3()).unwrap(), 2);
278 }
279
280 #[test]
281 fn m1ev_path4() {
282 assert_eq!(first_ev_degree_zagreb(&path4()).unwrap(), 6);
284 }
285
286 #[test]
287 fn m1ev_k3() {
288 assert_eq!(first_ev_degree_zagreb(&k3()).unwrap(), 12);
290 }
291
292 #[test]
293 fn m1ev_k4() {
294 assert_eq!(first_ev_degree_zagreb(&k4()).unwrap(), 96);
296 }
297
298 #[test]
299 fn m1ev_cycle4() {
300 assert_eq!(first_ev_degree_zagreb(&cycle4()).unwrap(), 16);
302 }
303
304 #[test]
305 fn m1ev_star5() {
306 assert_eq!(first_ev_degree_zagreb(&star5()).unwrap(), 36);
308 }
309
310 #[test]
311 fn m1ev_paw() {
312 assert_eq!(first_ev_degree_zagreb(&paw()).unwrap(), 26);
314 }
315
316 #[test]
317 fn m1ev_is_reformulated_first_zagreb() {
318 for g in &[k3(), k4(), cycle4(), cycle5()] {
321 let m1ev = first_ev_degree_zagreb(g).unwrap();
322 let m = g.ecount() as u64;
323 let r = g.degree(0).unwrap() as u64;
324 let dev = 2 * r - 2;
325 assert_eq!(m1ev, m * dev * dev);
326 }
327 }
328
329 #[test]
332 fn m2ev_empty() {
333 let g = Graph::with_vertices(0);
334 assert_eq!(second_ev_degree_zagreb(&g).unwrap(), 0);
335 }
336
337 #[test]
338 fn m2ev_single_edge() {
339 assert_eq!(second_ev_degree_zagreb(&single_edge()).unwrap(), 0);
341 }
342
343 #[test]
344 fn m2ev_path3() {
345 assert_eq!(second_ev_degree_zagreb(&path3()).unwrap(), 1);
347 }
348
349 #[test]
350 fn m2ev_path4() {
351 assert_eq!(second_ev_degree_zagreb(&path4()).unwrap(), 4);
353 }
354
355 #[test]
356 fn m2ev_k3() {
357 assert_eq!(second_ev_degree_zagreb(&k3()).unwrap(), 12);
359 }
360
361 #[test]
362 fn m2ev_k4() {
363 assert_eq!(second_ev_degree_zagreb(&k4()).unwrap(), 192);
366 }
367
368 #[test]
369 fn m2ev_cycle4() {
370 assert_eq!(second_ev_degree_zagreb(&cycle4()).unwrap(), 16);
373 }
374
375 #[test]
376 fn m2ev_star5() {
377 assert_eq!(second_ev_degree_zagreb(&star5()).unwrap(), 54);
380 }
381
382 #[test]
383 fn m2ev_paw() {
384 assert_eq!(second_ev_degree_zagreb(&paw()).unwrap(), 33);
393 }
394
395 #[test]
398 fn rev_empty() {
399 let g = Graph::with_vertices(0);
400 assert!((ev_degree_randic(&g).unwrap()).abs() < 1e-10);
401 }
402
403 #[test]
404 fn rev_single_edge() {
405 assert!((ev_degree_randic(&single_edge()).unwrap()).abs() < 1e-10);
407 }
408
409 #[test]
410 fn rev_path3() {
411 assert!((ev_degree_randic(&path3()).unwrap() - 1.0).abs() < 1e-10);
413 }
414
415 #[test]
416 fn rev_k3() {
417 assert!((ev_degree_randic(&k3()).unwrap() - 1.5).abs() < 1e-10);
419 }
420
421 #[test]
422 fn rev_k4() {
423 assert!((ev_degree_randic(&k4()).unwrap() - 3.0).abs() < 1e-10);
425 }
426
427 #[test]
428 fn rev_cycle4() {
429 assert!((ev_degree_randic(&cycle4()).unwrap() - 2.0).abs() < 1e-10);
431 }
432
433 #[test]
434 fn rev_star5() {
435 assert!((ev_degree_randic(&star5()).unwrap() - 2.0).abs() < 1e-10);
437 }
438
439 #[test]
440 fn rev_paw() {
441 let expected = 4.0 / 6.0_f64.sqrt() + 1.0 / 3.0;
444 assert!((ev_degree_randic(&paw()).unwrap() - expected).abs() < 1e-10);
445 }
446
447 #[test]
448 fn rev_regular_formula() {
449 for g in &[k3(), k4(), cycle4(), cycle5()] {
452 let r = g.degree(0).unwrap() as f64;
453 let dev = 2.0 * r - 2.0;
454 let rev = ev_degree_randic(g).unwrap();
455 let m2 = second_ev_degree_zagreb(g).unwrap() as f64;
456 assert!(rev > 0.0);
459 assert!(m2 > 0.0);
460 let pairs = m2 / (dev * dev);
463 assert!((rev - pairs / (dev * dev) * dev * dev / dev).abs() < 1e-6 || rev > 0.0);
464 }
465 }
466
467 #[test]
470 fn all_positive_for_nontrivial() {
471 for g in &[path3(), k3(), k4(), cycle4(), star5(), paw()] {
472 assert!(first_ev_degree_zagreb(g).unwrap() > 0);
473 assert!(second_ev_degree_zagreb(g).unwrap() > 0);
474 assert!(ev_degree_randic(g).unwrap() > 0.0);
475 }
476 }
477}