rust_igraph/algorithms/properties/
multiplicity.rs1use crate::core::cache::CachedProperty;
21use crate::core::graph::EdgeId;
22use crate::core::{Graph, IgraphResult};
23
24pub fn has_loop(graph: &Graph) -> IgraphResult<bool> {
38 if let Some(v) = graph.cache_get(CachedProperty::HasLoop) {
39 return Ok(v);
40 }
41 let m = u32::try_from(graph.ecount())
42 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
43 for e in 0..m {
44 let (u, v) = graph.edge(e as EdgeId)?;
45 if u == v {
46 graph.cache_set(CachedProperty::HasLoop, true);
47 return Ok(true);
48 }
49 }
50 graph.cache_set(CachedProperty::HasLoop, false);
51 Ok(false)
52}
53
54pub fn has_multiple(graph: &Graph) -> IgraphResult<bool> {
79 if let Some(v) = graph.cache_get(CachedProperty::HasMulti) {
80 return Ok(v);
81 }
82 let m = u32::try_from(graph.ecount())
83 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
84 if m < 2 {
85 graph.cache_set(CachedProperty::HasMulti, false);
86 return Ok(false);
87 }
88 let mut pairs: Vec<(u32, u32)> = Vec::with_capacity(m as usize);
89 for e in 0..m {
90 pairs.push(graph.edge(e as EdgeId)?);
91 }
92 pairs.sort_unstable();
93 for i in 1..pairs.len() {
94 if pairs[i] == pairs[i - 1] {
95 graph.cache_set(CachedProperty::HasMulti, true);
96 return Ok(true);
97 }
98 }
99 graph.cache_set(CachedProperty::HasMulti, false);
100 Ok(false)
101}
102
103pub fn is_loop(graph: &Graph) -> IgraphResult<Vec<bool>> {
121 let m = u32::try_from(graph.ecount())
122 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
123 let mut out = Vec::with_capacity(m as usize);
124 for e in 0..m {
125 let (u, v) = graph.edge(e as EdgeId)?;
126 out.push(u == v);
127 }
128 Ok(out)
129}
130
131pub fn is_multiple(graph: &Graph) -> IgraphResult<Vec<bool>> {
156 let m = u32::try_from(graph.ecount())
157 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
158 let m_us = m as usize;
159 if m_us == 0 {
160 return Ok(Vec::new());
161 }
162 let mut pairs: Vec<((u32, u32), u32)> = Vec::with_capacity(m_us);
165 for e in 0..m {
166 pairs.push((graph.edge(e as EdgeId)?, e));
167 }
168 pairs.sort_unstable_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
169 let mut out = vec![false; m_us];
170 let mut i = 0usize;
171 while i < m_us {
172 let mut j = i + 1;
173 while j < m_us && pairs[j].0 == pairs[i].0 {
174 j += 1;
175 }
176 for entry in &pairs[i + 1..j] {
178 out[entry.1 as usize] = true;
179 }
180 i = j;
181 }
182 Ok(out)
183}
184
185pub fn count_loops(graph: &Graph) -> IgraphResult<usize> {
208 let m = u32::try_from(graph.ecount())
209 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
210 let mut count = 0usize;
211 for e in 0..m {
212 let (u, v) = graph.edge(e as EdgeId)?;
213 if u == v {
214 count += 1;
215 }
216 }
217 Ok(count)
218}
219
220pub fn count_multiple(graph: &Graph) -> IgraphResult<Vec<usize>> {
250 let m = u32::try_from(graph.ecount())
251 .map_err(|_| crate::IgraphError::Internal("ecount exceeds u32::MAX"))?;
252 let m_us = m as usize;
253 if m_us == 0 {
254 return Ok(Vec::new());
255 }
256 let mut pairs: Vec<((u32, u32), u32)> = Vec::with_capacity(m_us);
257 for e in 0..m {
258 pairs.push((graph.edge(e as EdgeId)?, e));
259 }
260 pairs.sort_unstable_by_key(|p| p.0);
261 let mut out = vec![0usize; m_us];
262 let mut i = 0usize;
263 while i < m_us {
264 let mut j = i + 1;
265 while j < m_us && pairs[j].0 == pairs[i].0 {
266 j += 1;
267 }
268 let group_size = j - i;
269 for entry in &pairs[i..j] {
270 out[entry.1 as usize] = group_size;
271 }
272 i = j;
273 }
274 Ok(out)
275}
276
277#[cfg(test)]
278mod tests {
279 use super::*;
280
281 #[test]
282 fn empty_graph_has_no_loop() {
283 let g = Graph::with_vertices(0);
284 assert!(!has_loop(&g).unwrap());
285 }
286
287 #[test]
288 fn empty_graph_has_no_multi() {
289 let g = Graph::with_vertices(0);
290 assert!(!has_multiple(&g).unwrap());
291 }
292
293 #[test]
294 fn no_edge_graph_has_neither() {
295 let g = Graph::with_vertices(5);
296 assert!(!has_loop(&g).unwrap());
297 assert!(!has_multiple(&g).unwrap());
298 }
299
300 #[test]
301 fn path_has_neither() {
302 let mut g = Graph::with_vertices(4);
303 g.add_edge(0, 1).unwrap();
304 g.add_edge(1, 2).unwrap();
305 g.add_edge(2, 3).unwrap();
306 assert!(!has_loop(&g).unwrap());
307 assert!(!has_multiple(&g).unwrap());
308 }
309
310 #[test]
311 fn detects_self_loop() {
312 let mut g = Graph::with_vertices(2);
313 g.add_edge(0, 0).unwrap();
314 assert!(has_loop(&g).unwrap());
315 assert!(!has_multiple(&g).unwrap());
317 }
318
319 #[test]
320 fn detects_parallel_undirected() {
321 let mut g = Graph::with_vertices(2);
322 g.add_edge(0, 1).unwrap();
323 g.add_edge(1, 0).unwrap();
324 assert!(!has_loop(&g).unwrap());
325 assert!(has_multiple(&g).unwrap());
326 }
327
328 #[test]
329 fn detects_parallel_directed() {
330 let mut g = Graph::new(2, true).unwrap();
331 g.add_edge(0, 1).unwrap();
332 g.add_edge(0, 1).unwrap();
333 assert!(has_multiple(&g).unwrap());
334 }
335
336 #[test]
337 fn directed_mutual_pair_not_parallel() {
338 let mut g = Graph::new(2, true).unwrap();
340 g.add_edge(0, 1).unwrap();
341 g.add_edge(1, 0).unwrap();
342 assert!(!has_multiple(&g).unwrap());
343 }
344
345 #[test]
346 fn duplicate_self_loops_count_as_parallel() {
347 let mut g = Graph::with_vertices(1);
350 g.add_edge(0, 0).unwrap();
351 g.add_edge(0, 0).unwrap();
352 assert!(has_loop(&g).unwrap());
353 assert!(has_multiple(&g).unwrap());
354 }
355
356 #[test]
357 fn is_loop_per_edge_marks_self_loops_only() {
358 let mut g = Graph::with_vertices(3);
359 g.add_edge(0, 1).unwrap();
360 g.add_edge(2, 2).unwrap();
361 g.add_edge(1, 2).unwrap();
362 assert_eq!(is_loop(&g).unwrap(), vec![false, true, false]);
363 }
364
365 #[test]
366 fn is_loop_empty_graph() {
367 let g = Graph::with_vertices(0);
368 assert!(is_loop(&g).unwrap().is_empty());
369 }
370
371 #[test]
372 fn is_multiple_per_edge_marks_only_duplicates_after_first() {
373 let mut g = Graph::with_vertices(3);
377 g.add_edge(0, 1).unwrap();
378 g.add_edge(0, 1).unwrap();
379 g.add_edge(1, 2).unwrap();
380 assert_eq!(is_multiple(&g).unwrap(), vec![false, true, false]);
381 }
382
383 #[test]
384 fn is_multiple_directed_mutual_pair_not_multiple() {
385 let mut g = Graph::new(2, true).unwrap();
386 g.add_edge(0, 1).unwrap();
387 g.add_edge(1, 0).unwrap();
388 assert_eq!(is_multiple(&g).unwrap(), vec![false, false]);
389 }
390
391 #[test]
392 fn is_multiple_three_copies_first_canonical_only() {
393 let mut g = Graph::with_vertices(2);
396 for _ in 0..3 {
397 g.add_edge(0, 1).unwrap();
398 }
399 assert_eq!(is_multiple(&g).unwrap(), vec![false, true, true]);
400 }
401
402 #[test]
403 fn is_multiple_empty_graph() {
404 let g = Graph::with_vertices(0);
405 assert!(is_multiple(&g).unwrap().is_empty());
406 }
407
408 #[test]
409 fn matches_is_simple_negation_for_simple_graphs() {
410 let mut g = Graph::with_vertices(4);
412 for u in 0..4u32 {
413 for v in (u + 1)..4 {
414 g.add_edge(u, v).unwrap();
415 }
416 }
417 assert!(!has_loop(&g).unwrap());
418 assert!(!has_multiple(&g).unwrap());
419 assert!(crate::is_simple(&g).unwrap());
420 }
421
422 #[test]
423 fn count_loops_empty_graph() {
424 let g = Graph::with_vertices(0);
425 assert_eq!(count_loops(&g).unwrap(), 0);
426 }
427
428 #[test]
429 fn count_loops_no_loops() {
430 let mut g = Graph::with_vertices(4);
431 for (u, v) in [(0, 1), (1, 2), (2, 3)] {
432 g.add_edge(u, v).unwrap();
433 }
434 assert_eq!(count_loops(&g).unwrap(), 0);
435 }
436
437 #[test]
438 fn count_loops_counts_each_self_loop() {
439 let mut g = Graph::with_vertices(3);
440 g.add_edge(0, 1).unwrap();
441 g.add_edge(1, 1).unwrap();
442 g.add_edge(2, 2).unwrap();
443 g.add_edge(2, 2).unwrap();
444 assert_eq!(count_loops(&g).unwrap(), 3);
445 }
446
447 #[test]
448 fn count_loops_directed_self_loops() {
449 let mut g = Graph::new(3, true).unwrap();
450 g.add_edge(0, 0).unwrap();
451 g.add_edge(0, 1).unwrap();
452 g.add_edge(2, 2).unwrap();
453 assert_eq!(count_loops(&g).unwrap(), 2);
454 }
455
456 #[test]
457 fn count_multiple_empty_graph() {
458 let g = Graph::with_vertices(0);
459 assert!(count_multiple(&g).unwrap().is_empty());
460 }
461
462 #[test]
463 fn count_multiple_simple_graph_all_ones() {
464 let mut g = Graph::with_vertices(4);
465 for (u, v) in [(0, 1), (1, 2), (2, 3)] {
466 g.add_edge(u, v).unwrap();
467 }
468 assert_eq!(count_multiple(&g).unwrap(), vec![1, 1, 1]);
469 }
470
471 #[test]
472 fn count_multiple_groups_parallel_undirected() {
473 let mut g = Graph::with_vertices(3);
474 g.add_edge(0, 1).unwrap();
475 g.add_edge(1, 0).unwrap();
476 g.add_edge(1, 2).unwrap();
477 assert_eq!(count_multiple(&g).unwrap(), vec![2, 2, 1]);
479 }
480
481 #[test]
482 fn count_multiple_directed_mutual_pair_distinct() {
483 let mut g = Graph::new(2, true).unwrap();
485 g.add_edge(0, 1).unwrap();
486 g.add_edge(1, 0).unwrap();
487 assert_eq!(count_multiple(&g).unwrap(), vec![1, 1]);
488 }
489
490 #[test]
491 fn count_multiple_three_parallel_copies() {
492 let mut g = Graph::with_vertices(2);
493 for _ in 0..3 {
494 g.add_edge(0, 1).unwrap();
495 }
496 assert_eq!(count_multiple(&g).unwrap(), vec![3, 3, 3]);
497 }
498
499 #[test]
500 fn count_multiple_self_loops_grouped_per_vertex() {
501 let mut g = Graph::with_vertices(3);
504 g.add_edge(0, 0).unwrap();
505 g.add_edge(0, 0).unwrap();
506 g.add_edge(2, 2).unwrap();
507 assert_eq!(count_multiple(&g).unwrap(), vec![2, 2, 1]);
508 }
509
510 #[test]
511 fn count_multiple_mixed_graph() {
512 let mut g = Graph::with_vertices(4);
514 g.add_edge(0, 1).unwrap(); g.add_edge(1, 2).unwrap(); g.add_edge(0, 1).unwrap(); g.add_edge(3, 3).unwrap(); g.add_edge(2, 3).unwrap(); assert_eq!(count_multiple(&g).unwrap(), vec![2, 1, 2, 1, 1]);
520 }
521
522 #[test]
523 fn count_multiple_length_matches_ecount() {
524 let mut g = Graph::with_vertices(5);
525 for (u, v) in [(0, 1), (1, 2), (2, 3), (3, 4), (0, 4)] {
526 g.add_edge(u, v).unwrap();
527 }
528 assert_eq!(count_multiple(&g).unwrap().len(), g.ecount());
529 }
530
531 #[test]
532 fn count_multiple_consistent_with_is_multiple_and_has_multiple() {
533 let mut g = Graph::with_vertices(3);
534 g.add_edge(0, 1).unwrap();
535 g.add_edge(0, 1).unwrap();
536 g.add_edge(1, 2).unwrap();
537 let mults = count_multiple(&g).unwrap();
538 let is_mult = is_multiple(&g).unwrap();
539 let has_mult_via_count = mults.iter().any(|&m| m > 1);
542 assert_eq!(has_mult_via_count, has_multiple(&g).unwrap());
543 for (e, &flag) in is_mult.iter().enumerate() {
545 if flag {
546 assert!(mults[e] > 1, "is_multiple but mult = {}", mults[e]);
547 }
548 }
549 }
550
551 #[test]
552 fn count_loops_consistent_with_is_loop() {
553 let mut g = Graph::with_vertices(3);
554 g.add_edge(0, 1).unwrap();
555 g.add_edge(2, 2).unwrap();
556 g.add_edge(1, 2).unwrap();
557 let n_loops = count_loops(&g).unwrap();
558 let is_l = is_loop(&g).unwrap();
559 assert_eq!(n_loops, is_l.iter().filter(|&&b| b).count());
560 }
561}