rust_igraph/algorithms/properties/
degree_power_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 general_zeroth_order_randic(graph: &Graph, alpha: f64) -> IgraphResult<f64> {
41 let n = graph.vcount() as usize;
42 let mut result = 0.0_f64;
43
44 for v in 0..n {
45 let d = graph.degree(v as u32)?;
46 if d == 0 {
47 if alpha >= 0.0 {
48 if alpha == 0.0 {
50 result += 1.0;
51 }
52 }
53 continue;
54 }
55 result += (d as f64).powf(alpha);
56 }
57
58 Ok(result)
59}
60
61pub fn variable_sum_exdeg(graph: &Graph, a: f64) -> IgraphResult<f64> {
77 if a <= 0.0 {
78 return Ok(0.0);
79 }
80
81 let n = graph.vcount() as usize;
82 let mut result = 0.0_f64;
83
84 for v in 0..n {
85 let d = graph.degree(v as u32)?;
86 if d == 0 {
87 continue;
88 }
89 result += (d as f64) * a.powf(d as f64);
90 }
91
92 Ok(result)
93}
94
95pub fn inverse_degree_power(graph: &Graph, k: f64) -> IgraphResult<f64> {
112 let n = graph.vcount() as usize;
113 let mut result = 0.0_f64;
114
115 for v in 0..n {
116 let d = graph.degree(v as u32)?;
117 if d == 0 {
118 continue;
119 }
120 result += 1.0 / (d as f64).powf(k);
121 }
122
123 Ok(result)
124}
125
126pub fn variable_first_zagreb(graph: &Graph, p: f64) -> IgraphResult<f64> {
144 let mut result = 0.0_f64;
145
146 for (u, v) in graph.edges() {
147 if u == v {
148 continue;
149 }
150 let du = graph.degree(u)? as f64;
151 let dv = graph.degree(v)? as f64;
152 if p < 0.0 && (du == 0.0 || dv == 0.0) {
153 continue;
154 }
155 result += du.powf(p) + dv.powf(p);
156 }
157
158 Ok(result)
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 fn single_edge() -> Graph {
166 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
167 }
168
169 fn path3() -> Graph {
170 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
171 }
172
173 fn k3() -> Graph {
174 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
175 }
176
177 fn k4() -> Graph {
178 Graph::from_edges(
179 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
180 false,
181 Some(4),
182 )
183 .unwrap()
184 }
185
186 fn cycle4() -> Graph {
187 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
188 }
189
190 fn star5() -> Graph {
191 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
192 }
193
194 fn paw() -> Graph {
195 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
196 }
197
198 #[test]
201 fn gzr_empty() {
202 let g = Graph::with_vertices(0);
203 assert!(general_zeroth_order_randic(&g, 2.0).unwrap().abs() < 1e-10);
204 }
205
206 #[test]
207 fn gzr_isolated() {
208 let g = Graph::with_vertices(5);
209 assert!(general_zeroth_order_randic(&g, 2.0).unwrap().abs() < 1e-10);
210 }
211
212 #[test]
213 fn gzr_alpha2_is_first_zagreb() {
214 assert!((general_zeroth_order_randic(&k3(), 2.0).unwrap() - 12.0).abs() < 1e-10);
217 assert!((general_zeroth_order_randic(&k4(), 2.0).unwrap() - 36.0).abs() < 1e-10);
219 }
220
221 #[test]
222 fn gzr_alpha3_is_forgotten() {
223 assert!((general_zeroth_order_randic(&k3(), 3.0).unwrap() - 24.0).abs() < 1e-10);
226 }
227
228 #[test]
229 fn gzr_alpha1_is_twice_edges() {
230 for g in &[k3(), k4(), cycle4(), star5(), paw()] {
232 let expected = 2.0 * g.ecount() as f64;
233 assert!((general_zeroth_order_randic(g, 1.0).unwrap() - expected).abs() < 1e-10);
234 }
235 }
236
237 #[test]
238 fn gzr_alpha_neg1_is_inverse_degree() {
239 assert!((general_zeroth_order_randic(&k3(), -1.0).unwrap() - 1.5).abs() < 1e-10);
242 }
243
244 #[test]
245 fn gzr_single_edge() {
246 assert!((general_zeroth_order_randic(&single_edge(), 2.0).unwrap() - 2.0).abs() < 1e-10);
248 }
249
250 #[test]
251 fn gzr_star5() {
252 assert!((general_zeroth_order_randic(&star5(), 2.0).unwrap() - 20.0).abs() < 1e-10);
254 }
255
256 #[test]
259 fn vse_empty() {
260 let g = Graph::with_vertices(0);
261 assert!(variable_sum_exdeg(&g, 2.0).unwrap().abs() < 1e-10);
262 }
263
264 #[test]
265 fn vse_isolated() {
266 let g = Graph::with_vertices(5);
267 assert!(variable_sum_exdeg(&g, 2.0).unwrap().abs() < 1e-10);
268 }
269
270 #[test]
271 fn vse_k3_a2() {
272 assert!((variable_sum_exdeg(&k3(), 2.0).unwrap() - 24.0).abs() < 1e-10);
274 }
275
276 #[test]
277 fn vse_single_edge_a2() {
278 assert!((variable_sum_exdeg(&single_edge(), 2.0).unwrap() - 4.0).abs() < 1e-10);
280 }
281
282 #[test]
283 fn vse_path3_a2() {
284 assert!((variable_sum_exdeg(&path3(), 2.0).unwrap() - 12.0).abs() < 1e-10);
286 }
287
288 #[test]
289 fn vse_star5_a2() {
290 assert!((variable_sum_exdeg(&star5(), 2.0).unwrap() - 72.0).abs() < 1e-10);
292 }
293
294 #[test]
295 fn vse_k4_a3() {
296 assert!((variable_sum_exdeg(&k4(), 3.0).unwrap() - 324.0).abs() < 1e-10);
298 }
299
300 #[test]
301 fn vse_invalid_a() {
302 assert!(variable_sum_exdeg(&k3(), 0.0).unwrap().abs() < 1e-10);
304 assert!(variable_sum_exdeg(&k3(), -1.0).unwrap().abs() < 1e-10);
305 }
306
307 #[test]
310 fn idp_empty() {
311 let g = Graph::with_vertices(0);
312 assert!(inverse_degree_power(&g, 1.0).unwrap().abs() < 1e-10);
313 }
314
315 #[test]
316 fn idp_isolated() {
317 let g = Graph::with_vertices(5);
318 assert!(inverse_degree_power(&g, 1.0).unwrap().abs() < 1e-10);
319 }
320
321 #[test]
322 fn idp_k1_is_inverse_degree() {
323 assert!((inverse_degree_power(&k3(), 1.0).unwrap() - 1.5).abs() < 1e-10);
326 }
327
328 #[test]
329 fn idp_k2_k3() {
330 assert!((inverse_degree_power(&k3(), 2.0).unwrap() - 0.75).abs() < 1e-10);
332 }
333
334 #[test]
335 fn idp_single_edge() {
336 assert!((inverse_degree_power(&single_edge(), 2.0).unwrap() - 2.0).abs() < 1e-10);
338 }
339
340 #[test]
341 fn idp_star5() {
342 assert!((inverse_degree_power(&star5(), 1.0).unwrap() - 4.25).abs() < 1e-10);
344 }
345
346 #[test]
347 fn idp_paw() {
348 let expected = 0.5 + 0.5 + 1.0 / 3.0 + 1.0;
350 assert!((inverse_degree_power(&paw(), 1.0).unwrap() - expected).abs() < 1e-10);
351 }
352
353 #[test]
356 fn vfz_empty() {
357 let g = Graph::with_vertices(0);
358 assert!(variable_first_zagreb(&g, 1.0).unwrap().abs() < 1e-10);
359 }
360
361 #[test]
362 fn vfz_p1_is_m1() {
363 assert!((variable_first_zagreb(&k3(), 1.0).unwrap() - 12.0).abs() < 1e-10);
366 }
367
368 #[test]
369 fn vfz_p2_is_forgotten() {
370 assert!((variable_first_zagreb(&k3(), 2.0).unwrap() - 24.0).abs() < 1e-10);
373 assert!((variable_first_zagreb(&k4(), 2.0).unwrap() - 108.0).abs() < 1e-10);
375 }
376
377 #[test]
378 fn vfz_single_edge() {
379 assert!((variable_first_zagreb(&single_edge(), 1.0).unwrap() - 2.0).abs() < 1e-10);
381 }
382
383 #[test]
384 fn vfz_path3() {
385 assert!((variable_first_zagreb(&path3(), 1.0).unwrap() - 6.0).abs() < 1e-10);
387 }
388
389 #[test]
390 fn vfz_star5() {
391 assert!((variable_first_zagreb(&star5(), 1.0).unwrap() - 20.0).abs() < 1e-10);
393 }
394
395 #[test]
396 fn vfz_paw_p2() {
397 let expected = 8.0 + 13.0 + 13.0 + 10.0;
402 assert!((variable_first_zagreb(&paw(), 2.0).unwrap() - expected).abs() < 1e-10);
403 }
404
405 #[test]
408 fn gzr_alpha0_is_vertex_count_nonzero() {
409 let g = &k3();
414 assert!((general_zeroth_order_randic(g, 0.0).unwrap() - 3.0).abs() < 1e-10);
415
416 let iso = Graph::with_vertices(5);
418 assert!((general_zeroth_order_randic(&iso, 0.0).unwrap() - 5.0).abs() < 1e-10);
419 }
420
421 #[test]
422 fn idp_k0_is_nonzero_count() {
423 assert!((inverse_degree_power(&k3(), 0.0).unwrap() - 3.0).abs() < 1e-10);
425 assert!((inverse_degree_power(&star5(), 0.0).unwrap() - 5.0).abs() < 1e-10);
426 }
427}