1use std::collections::VecDeque;
13
14use crate::algorithms::connectivity::articulation::articulation_points;
15use crate::algorithms::flow::all_st_mincuts::all_st_mincuts;
16use crate::algorithms::flow::max_flow::max_flow_value;
17use crate::algorithms::flow::vertex_connectivity::vertex_connectivity;
18use crate::algorithms::operators::even_tarjan::even_tarjan_reduction;
19use crate::algorithms::operators::simplify::simplify;
20use crate::algorithms::properties::are_adjacent::are_adjacent;
21use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
22
23pub fn is_separator(graph: &Graph, candidates: &[VertexId]) -> IgraphResult<bool> {
52 if graph.is_directed() {
53 return Err(IgraphError::InvalidArgument(
54 "is_separator: only defined for undirected graphs".into(),
55 ));
56 }
57
58 let n = graph.vcount();
59 for &v in candidates {
60 if v >= n {
61 return Err(IgraphError::InvalidArgument(format!(
62 "is_separator: vertex {v} out of range (vcount={n})"
63 )));
64 }
65 }
66
67 if n == 0 {
68 return Ok(false);
69 }
70
71 let n_us = n as usize;
73 let mut removed = vec![false; n_us];
74 for &v in candidates {
75 removed[v as usize] = true;
76 }
77
78 let remaining = (0..n_us).filter(|&v| !removed[v]).count();
80
81 if remaining == 0 {
82 return Ok(false);
83 }
84
85 let Some(start) = (0..n_us).find(|&v| !removed[v]) else {
88 return Ok(false);
89 };
90 #[allow(clippy::cast_possible_truncation)] let reached = bfs_count(graph, start as u32, &removed)?;
92
93 Ok(reached < remaining)
94}
95
96pub fn is_minimal_separator(graph: &Graph, candidates: &[VertexId]) -> IgraphResult<bool> {
124 if graph.is_directed() {
125 return Err(IgraphError::InvalidArgument(
126 "is_minimal_separator: only defined for undirected graphs".into(),
127 ));
128 }
129
130 let n = graph.vcount();
131 for &v in candidates {
132 if v >= n {
133 return Err(IgraphError::InvalidArgument(format!(
134 "is_minimal_separator: vertex {v} out of range (vcount={n})"
135 )));
136 }
137 }
138
139 if !is_separator(graph, candidates)? {
141 return Ok(false);
142 }
143
144 let n_us = n as usize;
147 for (idx, _) in candidates.iter().enumerate() {
148 let mut removed = vec![false; n_us];
150 for (j, &u) in candidates.iter().enumerate() {
151 if j != idx {
152 removed[u as usize] = true;
153 }
154 }
155
156 let remaining = (0..n_us).filter(|&x| !removed[x]).count();
157 if remaining == 0 {
158 continue;
159 }
160
161 let Some(start) = (0..n_us).find(|&x| !removed[x]) else {
162 continue;
163 };
164 #[allow(clippy::cast_possible_truncation)] let reached = bfs_count(graph, start as u32, &removed)?;
166
167 if reached < remaining {
168 return Ok(false);
170 }
171 }
172
173 Ok(true)
174}
175
176fn bfs_count(graph: &Graph, start: u32, removed: &[bool]) -> IgraphResult<usize> {
178 let n_us = graph.vcount() as usize;
179 let mut visited = vec![false; n_us];
180 let mut queue = VecDeque::new();
181 let mut count = 0usize;
182
183 visited[start as usize] = true;
184 queue.push_back(start);
185 count += 1;
186
187 while let Some(cur) = queue.pop_front() {
188 let neighbors = graph.neighbors(cur)?;
189 for &nb in &neighbors {
190 let nidx = nb as usize;
191 if !visited[nidx] && !removed[nidx] {
192 visited[nidx] = true;
193 queue.push_back(nb);
194 count += 1;
195 }
196 }
197 }
198
199 Ok(count)
200}
201
202pub fn all_minimal_st_separators(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
236 let n = graph.vcount() as usize;
237 if n == 0 {
238 return Ok(Vec::new());
239 }
240
241 let adj = build_adj_undirected(graph)?;
242
243 let mut separators: Vec<Vec<VertexId>> = Vec::new();
244 let mut mark: Vec<u32> = vec![0; n];
245 let mut stamp: u32 = 1;
246
247 for v in 0..n {
250 advance_stamp(&mut mark, &mut stamp, n);
251 mark[v] = stamp;
252 for &nb in &adj[v] {
253 mark[nb as usize] = stamp;
254 }
255
256 let components = find_components_leaveout(&adj, &mark, stamp, n);
257 store_separators(&adj, &components, &mut mark, &mut separators, &mut stamp, n);
258 }
259
260 let mut try_next = 0;
262 while try_next < separators.len() {
263 let basis = separators[try_next].clone();
264 for &x in &basis {
265 advance_stamp(&mut mark, &mut stamp, n);
266 for &sv in &basis {
267 mark[sv as usize] = stamp;
268 }
269 for &nb in &adj[x as usize] {
270 mark[nb as usize] = stamp;
271 }
272
273 let components = find_components_leaveout(&adj, &mark, stamp, n);
274 store_separators(&adj, &components, &mut mark, &mut separators, &mut stamp, n);
275 }
276 try_next += 1;
277 }
278
279 Ok(separators)
280}
281
282fn build_adj_undirected(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
283 let n = graph.vcount() as usize;
284 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
285 let m = graph.ecount();
286 for eid in 0..m {
287 let eid32 = u32::try_from(eid).map_err(|_| {
288 IgraphError::InvalidArgument("all_minimal_st_separators: edge id overflow".into())
289 })?;
290 let (from, to) = graph.edge(eid32)?;
291 adj[from as usize].push(to);
292 if from != to {
293 adj[to as usize].push(from);
294 }
295 }
296 Ok(adj)
297}
298
299fn find_components_leaveout(
302 adj: &[Vec<VertexId>],
303 mark: &[u32],
304 stamp: u32,
305 n: usize,
306) -> Vec<Vec<VertexId>> {
307 let mut visited = vec![false; n];
308 let mut components: Vec<Vec<VertexId>> = Vec::new();
309 let mut queue: VecDeque<VertexId> = VecDeque::new();
310
311 for i in 0..n {
312 if mark[i] == stamp || visited[i] {
313 continue;
314 }
315
316 let mut comp: Vec<VertexId> = Vec::new();
317 #[allow(clippy::cast_possible_truncation)]
318 let i_v = i as VertexId;
319 visited[i] = true;
320 queue.push_back(i_v);
321 comp.push(i_v);
322
323 while let Some(cur) = queue.pop_front() {
324 for &nb in &adj[cur as usize] {
325 let nb_us = nb as usize;
326 if mark[nb_us] == stamp || visited[nb_us] {
327 continue;
328 }
329 visited[nb_us] = true;
330 queue.push_back(nb);
331 comp.push(nb);
332 }
333 }
334
335 components.push(comp);
336 }
337
338 components
339}
340
341fn store_separators(
345 adj: &[Vec<VertexId>],
346 components: &[Vec<VertexId>],
347 mark: &mut [u32],
348 separators: &mut Vec<Vec<VertexId>>,
349 cur_stamp: &mut u32,
350 n: usize,
351) {
352 for comp in components {
353 advance_stamp(mark, cur_stamp, n);
354 let comp_stamp = *cur_stamp;
355
356 for &v in comp {
358 mark[v as usize] = comp_stamp;
359 }
360
361 let mut neighborhood: Vec<VertexId> = Vec::new();
363 for &v in comp {
364 for &nb in &adj[v as usize] {
365 let nb_us = nb as usize;
366 if mark[nb_us] != comp_stamp {
367 mark[nb_us] = comp_stamp;
368 neighborhood.push(nb);
369 }
370 }
371 }
372
373 if neighborhood.is_empty() {
374 continue;
375 }
376
377 neighborhood.sort_unstable();
378
379 if !separators.contains(&neighborhood) {
380 separators.push(neighborhood);
381 }
382 }
383
384 advance_stamp(mark, cur_stamp, n);
385}
386
387fn advance_stamp(mark: &mut [u32], stamp: &mut u32, _n: usize) {
388 *stamp = stamp.wrapping_add(1);
389 if *stamp == 0 {
390 for m in mark.iter_mut() {
391 *m = 0;
392 }
393 *stamp = 1;
394 }
395}
396
397fn topk_degree(graph: &Graph, k: usize) -> IgraphResult<Vec<VertexId>> {
402 let n = graph.vcount();
403 let mut deg: Vec<(usize, VertexId)> = Vec::with_capacity(n as usize);
404 for v in 0..n {
405 deg.push((graph.degree(v)?, v));
406 }
407 deg.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
411 let mut res = Vec::with_capacity(k);
412 for i in 0..k {
413 res.push(deg[n as usize - 1 - i].1);
414 }
415 Ok(res)
416}
417
418fn append_unique(acc: &mut Vec<Vec<VertexId>>, new: Vec<Vec<VertexId>>) {
423 for sep in new {
424 if !acc.iter().any(|existing| existing == &sep) {
425 acc.push(sep);
426 }
427 }
428}
429
430#[allow(clippy::too_many_lines)]
477pub fn minimum_size_separators(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
478 if graph.is_directed() {
479 return Err(IgraphError::InvalidArgument(
480 "minimum_size_separators: only defined for undirected graphs".into(),
481 ));
482 }
483
484 let no_of_nodes = graph.vcount();
485
486 let conn = vertex_connectivity(graph, true)?;
488
489 if conn <= 0 {
494 return Ok(Vec::new());
495 }
496 if conn == 1 {
497 let aps = articulation_points(graph)?;
498 return Ok(aps.into_iter().map(|v| vec![v]).collect());
499 }
500 if conn == i64::from(no_of_nodes) - 1 {
501 let mut separators = Vec::with_capacity(no_of_nodes as usize);
502 for i in 0..no_of_nodes {
503 separators.push((0..no_of_nodes).filter(|&j| j != i).collect());
504 }
505 return Ok(separators);
506 }
507
508 let k = usize::try_from(conn).map_err(|_| {
509 IgraphError::InvalidArgument("minimum_size_separators: connectivity overflow".into())
510 })?;
511
512 let mut graph_copy = simplify(graph, true, true)?;
514
515 let mut separators: Vec<Vec<VertexId>> = Vec::new();
516
517 let x = topk_degree(&graph_copy, k)?;
520 if is_separator(&graph_copy, &x)? {
521 let mut sep = x.clone();
522 sep.sort_unstable();
523 append_unique(&mut separators, vec![sep]);
524 }
525
526 let reduction = even_tarjan_reduction(&graph_copy)?;
534 let mut gbar = reduction.graph;
535 let big = f64::from(no_of_nodes);
536 let mut capacity: Vec<f64> = reduction
537 .capacity
538 .into_iter()
539 .map(|c| if c.is_finite() { c } else { big })
540 .collect();
541
542 let k_f = f64::from(u32::try_from(conn).map_err(|_| {
546 IgraphError::InvalidArgument("minimum_size_separators: connectivity overflow".into())
547 })?);
548 for &xi in &x {
549 for j in 0..no_of_nodes {
550 if xi == j {
551 continue;
552 }
553 if are_adjacent(&graph_copy, xi, j)? {
554 continue;
555 }
556
557 let source = xi + no_of_nodes;
559 let target = j;
560 let phivalue = max_flow_value(&gbar, source, target, Some(&capacity))?;
561
562 if (phivalue - k_f).abs() < 0.5 {
563 let cuts = all_st_mincuts(&gbar, source, target, Some(&capacity))?;
569 let mapped: Vec<Vec<VertexId>> = cuts
570 .cuts
571 .into_iter()
572 .map(|cut| {
573 let mut sep: Vec<VertexId> = cut;
574 sep.sort_unstable();
575 sep
576 })
577 .collect();
578 append_unique(&mut separators, mapped);
579 }
580
581 graph_copy.add_edge(xi, j)?;
583 gbar.add_edge(xi + no_of_nodes, j)?;
584 gbar.add_edge(j + no_of_nodes, xi)?;
585 capacity.push(big);
586 capacity.push(big);
587 }
588 }
589
590 Ok(separators)
591}
592
593#[cfg(test)]
594mod tests {
595 use super::*;
596
597 #[test]
598 fn empty_graph() {
599 let g = Graph::with_vertices(0);
600 assert!(!is_separator(&g, &[]).unwrap());
601 }
602
603 #[test]
604 fn singleton_not_separator() {
605 let g = Graph::with_vertices(1);
606 assert!(!is_separator(&g, &[0]).unwrap());
607 }
608
609 #[test]
610 fn path_middle_is_separator() {
611 let mut g = Graph::with_vertices(3);
612 g.add_edge(0, 1).unwrap();
613 g.add_edge(1, 2).unwrap();
614 assert!(is_separator(&g, &[1]).unwrap());
615 }
616
617 #[test]
618 fn path_leaf_not_separator() {
619 let mut g = Graph::with_vertices(3);
620 g.add_edge(0, 1).unwrap();
621 g.add_edge(1, 2).unwrap();
622 assert!(!is_separator(&g, &[0]).unwrap());
623 assert!(!is_separator(&g, &[2]).unwrap());
624 }
625
626 #[test]
627 fn triangle_no_single_separator() {
628 let mut g = Graph::with_vertices(3);
629 g.add_edge(0, 1).unwrap();
630 g.add_edge(1, 2).unwrap();
631 g.add_edge(2, 0).unwrap();
632 assert!(!is_separator(&g, &[0]).unwrap());
634 assert!(!is_separator(&g, &[1]).unwrap());
635 assert!(!is_separator(&g, &[2]).unwrap());
636 }
637
638 #[test]
639 fn triangle_pair_not_separator() {
640 let mut g = Graph::with_vertices(3);
641 g.add_edge(0, 1).unwrap();
642 g.add_edge(1, 2).unwrap();
643 g.add_edge(2, 0).unwrap();
644 assert!(!is_separator(&g, &[0, 1]).unwrap());
646 }
647
648 #[test]
649 fn cycle_4_opposite_vertices() {
650 let mut g = Graph::with_vertices(4);
652 g.add_edge(0, 1).unwrap();
653 g.add_edge(1, 2).unwrap();
654 g.add_edge(2, 3).unwrap();
655 g.add_edge(3, 0).unwrap();
656 assert!(is_separator(&g, &[1, 3]).unwrap());
657 }
658
659 #[test]
660 fn cycle_4_adjacent_not_separator() {
661 let mut g = Graph::with_vertices(4);
663 g.add_edge(0, 1).unwrap();
664 g.add_edge(1, 2).unwrap();
665 g.add_edge(2, 3).unwrap();
666 g.add_edge(3, 0).unwrap();
667 assert!(!is_separator(&g, &[0, 1]).unwrap());
668 }
669
670 #[test]
671 fn already_disconnected_empty_set_is_separator() {
672 let mut g = Graph::with_vertices(4);
674 g.add_edge(0, 1).unwrap();
675 g.add_edge(2, 3).unwrap();
676 assert!(is_separator(&g, &[]).unwrap());
677 }
678
679 #[test]
680 fn k4_articulation() {
681 let mut g = Graph::with_vertices(4);
687 g.add_edge(0, 1).unwrap();
688 g.add_edge(0, 2).unwrap();
689 g.add_edge(0, 3).unwrap();
690 g.add_edge(1, 2).unwrap();
691 g.add_edge(2, 3).unwrap();
692 assert!(!is_separator(&g, &[0]).unwrap());
693 assert!(!is_separator(&g, &[2]).unwrap());
694 }
695
696 #[test]
697 fn bowtie_articulation() {
698 let mut g = Graph::with_vertices(5);
701 g.add_edge(0, 1).unwrap();
702 g.add_edge(1, 2).unwrap();
703 g.add_edge(2, 0).unwrap();
704 g.add_edge(2, 3).unwrap();
705 g.add_edge(3, 4).unwrap();
706 g.add_edge(4, 2).unwrap();
707 assert!(is_separator(&g, &[2]).unwrap());
708 }
709
710 #[test]
711 fn directed_rejected() {
712 let g = Graph::new(3, true).unwrap();
713 assert!(is_separator(&g, &[0]).is_err());
714 assert!(is_minimal_separator(&g, &[0]).is_err());
715 }
716
717 #[test]
718 fn out_of_range_rejected() {
719 let g = Graph::with_vertices(3);
720 assert!(is_separator(&g, &[5]).is_err());
721 }
722
723 #[test]
726 fn minimal_path_middle() {
727 let mut g = Graph::with_vertices(3);
729 g.add_edge(0, 1).unwrap();
730 g.add_edge(1, 2).unwrap();
731 assert!(is_minimal_separator(&g, &[1]).unwrap());
732 }
733
734 #[test]
735 fn minimal_cycle_4_opposite() {
736 let mut g = Graph::with_vertices(4);
738 g.add_edge(0, 1).unwrap();
739 g.add_edge(1, 2).unwrap();
740 g.add_edge(2, 3).unwrap();
741 g.add_edge(3, 0).unwrap();
742 assert!(is_minimal_separator(&g, &[1, 3]).unwrap());
743 }
744
745 #[test]
746 fn not_minimal_superset() {
747 let mut g = Graph::with_vertices(5);
749 g.add_edge(0, 1).unwrap();
750 g.add_edge(1, 2).unwrap();
751 g.add_edge(2, 3).unwrap();
752 g.add_edge(3, 4).unwrap();
753 assert!(is_separator(&g, &[1, 3]).unwrap());
754 assert!(!is_minimal_separator(&g, &[1, 3]).unwrap());
755 }
756
757 #[test]
758 fn not_separator_not_minimal() {
759 let mut g = Graph::with_vertices(3);
761 g.add_edge(0, 1).unwrap();
762 g.add_edge(1, 2).unwrap();
763 g.add_edge(2, 0).unwrap();
764 assert!(!is_minimal_separator(&g, &[0]).unwrap());
765 }
766
767 #[test]
768 fn minimal_bowtie_articulation() {
769 let mut g = Graph::with_vertices(5);
771 g.add_edge(0, 1).unwrap();
772 g.add_edge(1, 2).unwrap();
773 g.add_edge(2, 0).unwrap();
774 g.add_edge(2, 3).unwrap();
775 g.add_edge(3, 4).unwrap();
776 g.add_edge(4, 2).unwrap();
777 assert!(is_minimal_separator(&g, &[2]).unwrap());
778 }
779
780 #[test]
783 fn all_min_sep_empty_graph() {
784 let g = Graph::with_vertices(0);
785 let seps = all_minimal_st_separators(&g).unwrap();
786 assert!(seps.is_empty());
787 }
788
789 #[test]
790 fn all_min_sep_single_vertex() {
791 let g = Graph::with_vertices(1);
792 let seps = all_minimal_st_separators(&g).unwrap();
793 assert!(seps.is_empty());
794 }
795
796 #[test]
797 fn all_min_sep_single_edge() {
798 let mut g = Graph::with_vertices(2);
799 g.add_edge(0, 1).unwrap();
800 let seps = all_minimal_st_separators(&g).unwrap();
801 assert!(seps.is_empty());
803 }
804
805 #[test]
806 fn all_min_sep_path_3() {
807 let mut g = Graph::with_vertices(3);
809 g.add_edge(0, 1).unwrap();
810 g.add_edge(1, 2).unwrap();
811 let seps = all_minimal_st_separators(&g).unwrap();
812 assert_eq!(seps.len(), 1);
813 assert_eq!(seps[0], vec![1]);
814 }
815
816 #[test]
817 fn all_min_sep_path_5() {
818 let mut g = Graph::with_vertices(5);
820 g.add_edge(0, 1).unwrap();
821 g.add_edge(1, 2).unwrap();
822 g.add_edge(2, 3).unwrap();
823 g.add_edge(3, 4).unwrap();
824 let seps = all_minimal_st_separators(&g).unwrap();
825 assert_eq!(seps.len(), 3);
826 assert!(seps.contains(&vec![1]));
827 assert!(seps.contains(&vec![2]));
828 assert!(seps.contains(&vec![3]));
829 }
830
831 #[test]
832 fn all_min_sep_cycle_4() {
833 let mut g = Graph::with_vertices(4);
835 g.add_edge(0, 1).unwrap();
836 g.add_edge(1, 2).unwrap();
837 g.add_edge(2, 3).unwrap();
838 g.add_edge(3, 0).unwrap();
839 let seps = all_minimal_st_separators(&g).unwrap();
840 assert_eq!(seps.len(), 2);
841 assert!(seps.contains(&vec![0, 2]));
842 assert!(seps.contains(&vec![1, 3]));
843 }
844
845 #[test]
846 fn all_min_sep_pentagon_with_chord() {
847 let mut g = Graph::with_vertices(5);
849 g.add_edge(0, 1).unwrap();
850 g.add_edge(1, 2).unwrap();
851 g.add_edge(2, 3).unwrap();
852 g.add_edge(3, 4).unwrap();
853 g.add_edge(4, 1).unwrap();
854 let seps = all_minimal_st_separators(&g).unwrap();
855 assert_eq!(seps.len(), 3);
857 assert!(seps.contains(&vec![1]));
858 assert!(seps.contains(&vec![2, 4]));
859 assert!(seps.contains(&vec![1, 3]));
860 }
861
862 #[test]
863 fn all_min_sep_bowtie() {
864 let mut g = Graph::with_vertices(5);
866 g.add_edge(0, 1).unwrap();
867 g.add_edge(1, 2).unwrap();
868 g.add_edge(2, 0).unwrap();
869 g.add_edge(2, 3).unwrap();
870 g.add_edge(3, 4).unwrap();
871 g.add_edge(4, 2).unwrap();
872 let seps = all_minimal_st_separators(&g).unwrap();
873 assert_eq!(seps.len(), 1);
875 assert_eq!(seps[0], vec![2]);
876 }
877
878 #[test]
879 fn all_min_sep_complete_graph() {
880 let mut g = Graph::with_vertices(4);
882 g.add_edge(0, 1).unwrap();
883 g.add_edge(0, 2).unwrap();
884 g.add_edge(0, 3).unwrap();
885 g.add_edge(1, 2).unwrap();
886 g.add_edge(1, 3).unwrap();
887 g.add_edge(2, 3).unwrap();
888 let seps = all_minimal_st_separators(&g).unwrap();
889 assert!(seps.is_empty());
890 }
891
892 #[test]
893 fn all_min_sep_disconnected() {
894 let mut g = Graph::with_vertices(4);
896 g.add_edge(0, 1).unwrap();
897 g.add_edge(2, 3).unwrap();
898 let seps = all_minimal_st_separators(&g).unwrap();
899 for s in &seps {
904 assert!(!s.is_empty());
905 }
906 }
907
908 fn canon(mut seps: Vec<Vec<VertexId>>) -> Vec<Vec<VertexId>> {
912 for s in &mut seps {
913 s.sort_unstable();
914 }
915 seps.sort();
916 seps
917 }
918
919 fn undirected(n: u32, edges: &[(u32, u32)]) -> Graph {
920 let mut g = Graph::with_vertices(n);
921 for &(a, b) in edges {
922 g.add_edge(a, b).unwrap();
923 }
924 g
925 }
926
927 #[test]
928 fn mss_directed_rejected() {
929 let g = Graph::new(3, true).unwrap();
930 assert!(minimum_size_separators(&g).is_err());
931 }
932
933 #[test]
934 fn mss_path3() {
935 let g = undirected(3, &[(0, 1), (1, 2)]);
937 assert_eq!(canon(minimum_size_separators(&g).unwrap()), vec![vec![1]]);
938 }
939
940 #[test]
941 fn mss_disconnected_empty() {
942 let g = undirected(4, &[(0, 1), (2, 3)]);
944 assert!(minimum_size_separators(&g).unwrap().is_empty());
945 }
946
947 #[test]
948 fn mss_articulation_singletons() {
949 let g = undirected(5, &[(0, 1), (1, 2), (0, 2), (2, 3), (3, 4), (2, 4)]);
952 assert_eq!(canon(minimum_size_separators(&g).unwrap()), vec![vec![2]]);
953 }
954
955 #[test]
956 fn mss_complete_k4() {
957 let g = undirected(4, &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
960 assert_eq!(
961 canon(minimum_size_separators(&g).unwrap()),
962 vec![vec![0, 1, 2], vec![0, 1, 3], vec![0, 2, 3], vec![1, 2, 3],]
963 );
964 }
965
966 #[test]
967 fn mss_complete_k3() {
968 let g = undirected(3, &[(0, 1), (0, 2), (1, 2)]);
970 assert_eq!(
971 canon(minimum_size_separators(&g).unwrap()),
972 vec![vec![0, 1], vec![0, 2], vec![1, 2]]
973 );
974 }
975
976 #[test]
977 fn mss_cycle5() {
978 let g = undirected(5, &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]);
980 assert_eq!(
981 canon(minimum_size_separators(&g).unwrap()),
982 vec![vec![0, 2], vec![0, 3], vec![1, 3], vec![1, 4], vec![2, 4],]
983 );
984 }
985
986 #[test]
987 fn mss_grid3x3() {
988 let g = undirected(
990 9,
991 &[
992 (0, 1),
993 (1, 2),
994 (3, 4),
995 (4, 5),
996 (6, 7),
997 (7, 8),
998 (0, 3),
999 (3, 6),
1000 (1, 4),
1001 (4, 7),
1002 (2, 5),
1003 (5, 8),
1004 ],
1005 );
1006 assert_eq!(
1007 canon(minimum_size_separators(&g).unwrap()),
1008 vec![vec![1, 3], vec![1, 5], vec![3, 7], vec![5, 7]]
1009 );
1010 }
1011
1012 #[test]
1013 fn mss_complete_bipartite_k23() {
1014 let g = undirected(5, &[(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4)]);
1017 assert_eq!(
1018 canon(minimum_size_separators(&g).unwrap()),
1019 vec![vec![0, 1]]
1020 );
1021 }
1022
1023 #[test]
1024 fn mss_separators_are_valid() {
1025 let g = undirected(
1028 7,
1029 &[
1030 (0, 1),
1031 (1, 2),
1032 (2, 0),
1033 (2, 3),
1034 (3, 4),
1035 (4, 5),
1036 (5, 3),
1037 (5, 6),
1038 (6, 3),
1039 ],
1040 );
1041 let seps = minimum_size_separators(&g).unwrap();
1042 assert!(!seps.is_empty());
1043 let k = seps[0].len();
1044 for s in &seps {
1045 assert_eq!(s.len(), k, "all minimum separators share size");
1046 assert!(is_separator(&g, s).unwrap(), "{s:?} must separate");
1047 }
1048 }
1049}