rust_igraph/algorithms/properties/
edge_degree_pair.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 edge_degree_min_sum(graph: &Graph) -> IgraphResult<u64> {
39 let mut result = 0_u64;
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 result += du.min(dv) as u64;
48 }
49
50 Ok(result)
51}
52
53pub fn edge_degree_max_sum(graph: &Graph) -> IgraphResult<u64> {
70 let mut result = 0_u64;
71
72 for (u, v) in graph.edges() {
73 if u == v {
74 continue;
75 }
76 let du = graph.degree(u)?;
77 let dv = graph.degree(v)?;
78 result += du.max(dv) as u64;
79 }
80
81 Ok(result)
82}
83
84pub fn edge_degree_log_product(graph: &Graph) -> IgraphResult<f64> {
102 let mut result = 0.0_f64;
103
104 for (u, v) in graph.edges() {
105 if u == v {
106 continue;
107 }
108 let du = graph.degree(u)?;
109 let dv = graph.degree(v)?;
110 if du == 0 || dv == 0 {
111 continue;
112 }
113 result += ((du * dv) as f64).ln();
114 }
115
116 Ok(result)
117}
118
119pub fn edge_degree_mean_sum(graph: &Graph) -> IgraphResult<f64> {
136 let mut result = 0.0_f64;
137
138 for (u, v) in graph.edges() {
139 if u == v {
140 continue;
141 }
142 let du = graph.degree(u)? as f64;
143 let dv = graph.degree(v)? as f64;
144 result += f64::midpoint(du, dv);
145 }
146
147 Ok(result)
148}
149
150#[cfg(test)]
151mod tests {
152 use super::*;
153
154 fn single_edge() -> Graph {
155 Graph::from_edges(&[(0, 1)], false, Some(2)).unwrap()
156 }
157
158 fn path3() -> Graph {
159 Graph::from_edges(&[(0, 1), (1, 2)], false, Some(3)).unwrap()
160 }
161
162 fn k3() -> Graph {
163 Graph::from_edges(&[(0, 1), (1, 2), (0, 2)], false, Some(3)).unwrap()
164 }
165
166 fn k4() -> Graph {
167 Graph::from_edges(
168 &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)],
169 false,
170 Some(4),
171 )
172 .unwrap()
173 }
174
175 fn cycle4() -> Graph {
176 Graph::from_edges(&[(0, 1), (1, 2), (2, 3), (3, 0)], false, Some(4)).unwrap()
177 }
178
179 fn star5() -> Graph {
180 Graph::from_edges(&[(0, 1), (0, 2), (0, 3), (0, 4)], false, Some(5)).unwrap()
181 }
182
183 fn paw() -> Graph {
184 Graph::from_edges(&[(0, 1), (1, 2), (0, 2), (2, 3)], false, Some(4)).unwrap()
185 }
186
187 #[test]
190 fn edmin_empty() {
191 let g = Graph::with_vertices(0);
192 assert_eq!(edge_degree_min_sum(&g).unwrap(), 0);
193 }
194
195 #[test]
196 fn edmin_isolated() {
197 let g = Graph::with_vertices(5);
198 assert_eq!(edge_degree_min_sum(&g).unwrap(), 0);
199 }
200
201 #[test]
202 fn edmin_regular() {
203 assert_eq!(edge_degree_min_sum(&k3()).unwrap(), 6); assert_eq!(edge_degree_min_sum(&k4()).unwrap(), 18); assert_eq!(edge_degree_min_sum(&cycle4()).unwrap(), 8); }
208
209 #[test]
210 fn edmin_single_edge() {
211 assert_eq!(edge_degree_min_sum(&single_edge()).unwrap(), 1);
212 }
213
214 #[test]
215 fn edmin_star5() {
216 assert_eq!(edge_degree_min_sum(&star5()).unwrap(), 4);
218 }
219
220 #[test]
221 fn edmin_path3() {
222 assert_eq!(edge_degree_min_sum(&path3()).unwrap(), 2);
224 }
225
226 #[test]
227 fn edmin_paw() {
228 assert_eq!(edge_degree_min_sum(&paw()).unwrap(), 7);
230 }
231
232 #[test]
235 fn edmax_empty() {
236 let g = Graph::with_vertices(0);
237 assert_eq!(edge_degree_max_sum(&g).unwrap(), 0);
238 }
239
240 #[test]
241 fn edmax_isolated() {
242 let g = Graph::with_vertices(5);
243 assert_eq!(edge_degree_max_sum(&g).unwrap(), 0);
244 }
245
246 #[test]
247 fn edmax_regular() {
248 assert_eq!(edge_degree_max_sum(&k3()).unwrap(), 6);
249 assert_eq!(edge_degree_max_sum(&k4()).unwrap(), 18);
250 assert_eq!(edge_degree_max_sum(&cycle4()).unwrap(), 8);
251 }
252
253 #[test]
254 fn edmax_single_edge() {
255 assert_eq!(edge_degree_max_sum(&single_edge()).unwrap(), 1);
256 }
257
258 #[test]
259 fn edmax_star5() {
260 assert_eq!(edge_degree_max_sum(&star5()).unwrap(), 16);
262 }
263
264 #[test]
265 fn edmax_path3() {
266 assert_eq!(edge_degree_max_sum(&path3()).unwrap(), 4);
268 }
269
270 #[test]
271 fn edmax_paw() {
272 assert_eq!(edge_degree_max_sum(&paw()).unwrap(), 11);
274 }
275
276 #[test]
279 fn min_plus_max_equals_m1() {
280 for g in &[single_edge(), path3(), k3(), k4(), star5(), paw()] {
283 let min_val = edge_degree_min_sum(g).unwrap();
284 let max_val = edge_degree_max_sum(g).unwrap();
285 let m1: u64 = g
286 .edges()
287 .filter(|&(u, v)| u != v)
288 .map(|(u, v)| (g.degree(u).unwrap() + g.degree(v).unwrap()) as u64)
289 .sum();
290 assert_eq!(min_val + max_val, m1);
291 }
292 }
293
294 #[test]
297 fn edlp_empty() {
298 let g = Graph::with_vertices(0);
299 assert!(edge_degree_log_product(&g).unwrap().abs() < 1e-10);
300 }
301
302 #[test]
303 fn edlp_isolated() {
304 let g = Graph::with_vertices(5);
305 assert!(edge_degree_log_product(&g).unwrap().abs() < 1e-10);
306 }
307
308 #[test]
309 fn edlp_k3() {
310 let expected = 3.0 * 4.0_f64.ln();
312 assert!((edge_degree_log_product(&k3()).unwrap() - expected).abs() < 1e-10);
313 }
314
315 #[test]
316 fn edlp_single_edge() {
317 assert!(edge_degree_log_product(&single_edge()).unwrap().abs() < 1e-10);
319 }
320
321 #[test]
322 fn edlp_star5() {
323 let expected = 4.0 * 4.0_f64.ln();
325 assert!((edge_degree_log_product(&star5()).unwrap() - expected).abs() < 1e-10);
326 }
327
328 #[test]
329 fn edlp_path3() {
330 let expected = 2.0 * 2.0_f64.ln();
332 assert!((edge_degree_log_product(&path3()).unwrap() - expected).abs() < 1e-10);
333 }
334
335 #[test]
336 fn edlp_paw() {
337 let expected = 4.0_f64.ln() + 2.0 * 6.0_f64.ln() + 3.0_f64.ln();
339 assert!((edge_degree_log_product(&paw()).unwrap() - expected).abs() < 1e-10);
340 }
341
342 #[test]
345 fn edmean_empty() {
346 let g = Graph::with_vertices(0);
347 assert!(edge_degree_mean_sum(&g).unwrap().abs() < 1e-10);
348 }
349
350 #[test]
351 fn edmean_isolated() {
352 let g = Graph::with_vertices(5);
353 assert!(edge_degree_mean_sum(&g).unwrap().abs() < 1e-10);
354 }
355
356 #[test]
357 fn edmean_regular() {
358 assert!((edge_degree_mean_sum(&k3()).unwrap() - 6.0).abs() < 1e-10);
360 assert!((edge_degree_mean_sum(&k4()).unwrap() - 18.0).abs() < 1e-10);
361 assert!((edge_degree_mean_sum(&cycle4()).unwrap() - 8.0).abs() < 1e-10);
362 }
363
364 #[test]
365 fn edmean_single_edge() {
366 assert!((edge_degree_mean_sum(&single_edge()).unwrap() - 1.0).abs() < 1e-10);
367 }
368
369 #[test]
370 fn edmean_star5() {
371 assert!((edge_degree_mean_sum(&star5()).unwrap() - 10.0).abs() < 1e-10);
373 }
374
375 #[test]
376 fn edmean_paw() {
377 assert!((edge_degree_mean_sum(&paw()).unwrap() - 9.0).abs() < 1e-10);
379 }
380
381 #[test]
384 fn edmin_le_edmean_le_edmax() {
385 for g in &[single_edge(), path3(), k3(), k4(), star5(), paw()] {
386 let min_val = edge_degree_min_sum(g).unwrap() as f64;
387 let mean_val = edge_degree_mean_sum(g).unwrap();
388 let max_val = edge_degree_max_sum(g).unwrap() as f64;
389 assert!(min_val <= mean_val + 1e-10);
390 assert!(mean_val <= max_val + 1e-10);
391 }
392 }
393
394 #[test]
395 fn edmean_is_half_m1() {
396 for g in &[single_edge(), path3(), k3(), k4(), star5(), paw()] {
398 let min_val = edge_degree_min_sum(g).unwrap();
399 let max_val = edge_degree_max_sum(g).unwrap();
400 let m1 = (min_val + max_val) as f64;
401 let mean_val = edge_degree_mean_sum(g).unwrap();
402 assert!((mean_val - m1 / 2.0).abs() < 1e-10);
403 }
404 }
405}