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 start = (0..n_us).find(|&v| !removed[v]).unwrap();
88 #[allow(clippy::cast_possible_truncation)] let reached = bfs_count(graph, start as u32, &removed)?;
90
91 Ok(reached < remaining)
92}
93
94pub fn is_minimal_separator(graph: &Graph, candidates: &[VertexId]) -> IgraphResult<bool> {
122 if graph.is_directed() {
123 return Err(IgraphError::InvalidArgument(
124 "is_minimal_separator: only defined for undirected graphs".into(),
125 ));
126 }
127
128 let n = graph.vcount();
129 for &v in candidates {
130 if v >= n {
131 return Err(IgraphError::InvalidArgument(format!(
132 "is_minimal_separator: vertex {v} out of range (vcount={n})"
133 )));
134 }
135 }
136
137 if !is_separator(graph, candidates)? {
139 return Ok(false);
140 }
141
142 let n_us = n as usize;
145 for (idx, _) in candidates.iter().enumerate() {
146 let mut removed = vec![false; n_us];
148 for (j, &u) in candidates.iter().enumerate() {
149 if j != idx {
150 removed[u as usize] = true;
151 }
152 }
153
154 let remaining = (0..n_us).filter(|&x| !removed[x]).count();
155 if remaining == 0 {
156 continue;
157 }
158
159 let start = (0..n_us).find(|&x| !removed[x]).unwrap();
160 #[allow(clippy::cast_possible_truncation)] let reached = bfs_count(graph, start as u32, &removed)?;
162
163 if reached < remaining {
164 return Ok(false);
166 }
167 }
168
169 Ok(true)
170}
171
172fn bfs_count(graph: &Graph, start: u32, removed: &[bool]) -> IgraphResult<usize> {
174 let n_us = graph.vcount() as usize;
175 let mut visited = vec![false; n_us];
176 let mut queue = VecDeque::new();
177 let mut count = 0usize;
178
179 visited[start as usize] = true;
180 queue.push_back(start);
181 count += 1;
182
183 while let Some(cur) = queue.pop_front() {
184 let neighbors = graph.neighbors(cur)?;
185 for &nb in &neighbors {
186 let nidx = nb as usize;
187 if !visited[nidx] && !removed[nidx] {
188 visited[nidx] = true;
189 queue.push_back(nb);
190 count += 1;
191 }
192 }
193 }
194
195 Ok(count)
196}
197
198pub fn all_minimal_st_separators(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
232 let n = graph.vcount() as usize;
233 if n == 0 {
234 return Ok(Vec::new());
235 }
236
237 let adj = build_adj_undirected(graph)?;
238
239 let mut separators: Vec<Vec<VertexId>> = Vec::new();
240 let mut mark: Vec<u32> = vec![0; n];
241 let mut stamp: u32 = 1;
242
243 for v in 0..n {
246 advance_stamp(&mut mark, &mut stamp, n);
247 mark[v] = stamp;
248 for &nb in &adj[v] {
249 mark[nb as usize] = stamp;
250 }
251
252 let components = find_components_leaveout(&adj, &mark, stamp, n);
253 store_separators(&adj, &components, &mut mark, &mut separators, &mut stamp, n);
254 }
255
256 let mut try_next = 0;
258 while try_next < separators.len() {
259 let basis = separators[try_next].clone();
260 for &x in &basis {
261 advance_stamp(&mut mark, &mut stamp, n);
262 for &sv in &basis {
263 mark[sv as usize] = stamp;
264 }
265 for &nb in &adj[x as usize] {
266 mark[nb as usize] = stamp;
267 }
268
269 let components = find_components_leaveout(&adj, &mark, stamp, n);
270 store_separators(&adj, &components, &mut mark, &mut separators, &mut stamp, n);
271 }
272 try_next += 1;
273 }
274
275 Ok(separators)
276}
277
278fn build_adj_undirected(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
279 let n = graph.vcount() as usize;
280 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
281 let m = graph.ecount();
282 for eid in 0..m {
283 let eid32 = u32::try_from(eid).map_err(|_| {
284 IgraphError::InvalidArgument("all_minimal_st_separators: edge id overflow".into())
285 })?;
286 let (from, to) = graph.edge(eid32)?;
287 adj[from as usize].push(to);
288 if from != to {
289 adj[to as usize].push(from);
290 }
291 }
292 Ok(adj)
293}
294
295fn find_components_leaveout(
298 adj: &[Vec<VertexId>],
299 mark: &[u32],
300 stamp: u32,
301 n: usize,
302) -> Vec<Vec<VertexId>> {
303 let mut visited = vec![false; n];
304 let mut components: Vec<Vec<VertexId>> = Vec::new();
305 let mut queue: VecDeque<VertexId> = VecDeque::new();
306
307 for i in 0..n {
308 if mark[i] == stamp || visited[i] {
309 continue;
310 }
311
312 let mut comp: Vec<VertexId> = Vec::new();
313 #[allow(clippy::cast_possible_truncation)]
314 let i_v = i as VertexId;
315 visited[i] = true;
316 queue.push_back(i_v);
317 comp.push(i_v);
318
319 while let Some(cur) = queue.pop_front() {
320 for &nb in &adj[cur as usize] {
321 let nb_us = nb as usize;
322 if mark[nb_us] == stamp || visited[nb_us] {
323 continue;
324 }
325 visited[nb_us] = true;
326 queue.push_back(nb);
327 comp.push(nb);
328 }
329 }
330
331 components.push(comp);
332 }
333
334 components
335}
336
337fn store_separators(
341 adj: &[Vec<VertexId>],
342 components: &[Vec<VertexId>],
343 mark: &mut [u32],
344 separators: &mut Vec<Vec<VertexId>>,
345 cur_stamp: &mut u32,
346 n: usize,
347) {
348 for comp in components {
349 advance_stamp(mark, cur_stamp, n);
350 let comp_stamp = *cur_stamp;
351
352 for &v in comp {
354 mark[v as usize] = comp_stamp;
355 }
356
357 let mut neighborhood: Vec<VertexId> = Vec::new();
359 for &v in comp {
360 for &nb in &adj[v as usize] {
361 let nb_us = nb as usize;
362 if mark[nb_us] != comp_stamp {
363 mark[nb_us] = comp_stamp;
364 neighborhood.push(nb);
365 }
366 }
367 }
368
369 if neighborhood.is_empty() {
370 continue;
371 }
372
373 neighborhood.sort_unstable();
374
375 if !separators.contains(&neighborhood) {
376 separators.push(neighborhood);
377 }
378 }
379
380 advance_stamp(mark, cur_stamp, n);
381}
382
383fn advance_stamp(mark: &mut [u32], stamp: &mut u32, _n: usize) {
384 *stamp = stamp.wrapping_add(1);
385 if *stamp == 0 {
386 for m in mark.iter_mut() {
387 *m = 0;
388 }
389 *stamp = 1;
390 }
391}
392
393fn topk_degree(graph: &Graph, k: usize) -> IgraphResult<Vec<VertexId>> {
398 let n = graph.vcount();
399 let mut deg: Vec<(usize, VertexId)> = Vec::with_capacity(n as usize);
400 for v in 0..n {
401 deg.push((graph.degree(v)?, v));
402 }
403 deg.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
407 let mut res = Vec::with_capacity(k);
408 for i in 0..k {
409 res.push(deg[n as usize - 1 - i].1);
410 }
411 Ok(res)
412}
413
414fn append_unique(acc: &mut Vec<Vec<VertexId>>, new: Vec<Vec<VertexId>>) {
419 for sep in new {
420 if !acc.iter().any(|existing| existing == &sep) {
421 acc.push(sep);
422 }
423 }
424}
425
426#[allow(clippy::too_many_lines)]
473pub fn minimum_size_separators(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
474 if graph.is_directed() {
475 return Err(IgraphError::InvalidArgument(
476 "minimum_size_separators: only defined for undirected graphs".into(),
477 ));
478 }
479
480 let no_of_nodes = graph.vcount();
481
482 let conn = vertex_connectivity(graph, true)?;
484
485 if conn <= 0 {
490 return Ok(Vec::new());
491 }
492 if conn == 1 {
493 let aps = articulation_points(graph)?;
494 return Ok(aps.into_iter().map(|v| vec![v]).collect());
495 }
496 if conn == i64::from(no_of_nodes) - 1 {
497 let mut separators = Vec::with_capacity(no_of_nodes as usize);
498 for i in 0..no_of_nodes {
499 separators.push((0..no_of_nodes).filter(|&j| j != i).collect());
500 }
501 return Ok(separators);
502 }
503
504 let k = usize::try_from(conn).map_err(|_| {
505 IgraphError::InvalidArgument("minimum_size_separators: connectivity overflow".into())
506 })?;
507
508 let mut graph_copy = simplify(graph, true, true)?;
510
511 let mut separators: Vec<Vec<VertexId>> = Vec::new();
512
513 let x = topk_degree(&graph_copy, k)?;
516 if is_separator(&graph_copy, &x)? {
517 let mut sep = x.clone();
518 sep.sort_unstable();
519 append_unique(&mut separators, vec![sep]);
520 }
521
522 let reduction = even_tarjan_reduction(&graph_copy)?;
530 let mut gbar = reduction.graph;
531 let big = f64::from(no_of_nodes);
532 let mut capacity: Vec<f64> = reduction
533 .capacity
534 .into_iter()
535 .map(|c| if c.is_finite() { c } else { big })
536 .collect();
537
538 let k_f = f64::from(u32::try_from(conn).map_err(|_| {
542 IgraphError::InvalidArgument("minimum_size_separators: connectivity overflow".into())
543 })?);
544 for &xi in &x {
545 for j in 0..no_of_nodes {
546 if xi == j {
547 continue;
548 }
549 if are_adjacent(&graph_copy, xi, j)? {
550 continue;
551 }
552
553 let source = xi + no_of_nodes;
555 let target = j;
556 let phivalue = max_flow_value(&gbar, source, target, Some(&capacity))?;
557
558 if (phivalue - k_f).abs() < 0.5 {
559 let cuts = all_st_mincuts(&gbar, source, target, Some(&capacity))?;
565 let mapped: Vec<Vec<VertexId>> = cuts
566 .cuts
567 .into_iter()
568 .map(|cut| {
569 let mut sep: Vec<VertexId> = cut;
570 sep.sort_unstable();
571 sep
572 })
573 .collect();
574 append_unique(&mut separators, mapped);
575 }
576
577 graph_copy.add_edge(xi, j)?;
579 gbar.add_edge(xi + no_of_nodes, j)?;
580 gbar.add_edge(j + no_of_nodes, xi)?;
581 capacity.push(big);
582 capacity.push(big);
583 }
584 }
585
586 Ok(separators)
587}
588
589#[cfg(test)]
590mod tests {
591 use super::*;
592
593 #[test]
594 fn empty_graph() {
595 let g = Graph::with_vertices(0);
596 assert!(!is_separator(&g, &[]).unwrap());
597 }
598
599 #[test]
600 fn singleton_not_separator() {
601 let g = Graph::with_vertices(1);
602 assert!(!is_separator(&g, &[0]).unwrap());
603 }
604
605 #[test]
606 fn path_middle_is_separator() {
607 let mut g = Graph::with_vertices(3);
608 g.add_edge(0, 1).unwrap();
609 g.add_edge(1, 2).unwrap();
610 assert!(is_separator(&g, &[1]).unwrap());
611 }
612
613 #[test]
614 fn path_leaf_not_separator() {
615 let mut g = Graph::with_vertices(3);
616 g.add_edge(0, 1).unwrap();
617 g.add_edge(1, 2).unwrap();
618 assert!(!is_separator(&g, &[0]).unwrap());
619 assert!(!is_separator(&g, &[2]).unwrap());
620 }
621
622 #[test]
623 fn triangle_no_single_separator() {
624 let mut g = Graph::with_vertices(3);
625 g.add_edge(0, 1).unwrap();
626 g.add_edge(1, 2).unwrap();
627 g.add_edge(2, 0).unwrap();
628 assert!(!is_separator(&g, &[0]).unwrap());
630 assert!(!is_separator(&g, &[1]).unwrap());
631 assert!(!is_separator(&g, &[2]).unwrap());
632 }
633
634 #[test]
635 fn triangle_pair_not_separator() {
636 let mut g = Graph::with_vertices(3);
637 g.add_edge(0, 1).unwrap();
638 g.add_edge(1, 2).unwrap();
639 g.add_edge(2, 0).unwrap();
640 assert!(!is_separator(&g, &[0, 1]).unwrap());
642 }
643
644 #[test]
645 fn cycle_4_opposite_vertices() {
646 let mut g = Graph::with_vertices(4);
648 g.add_edge(0, 1).unwrap();
649 g.add_edge(1, 2).unwrap();
650 g.add_edge(2, 3).unwrap();
651 g.add_edge(3, 0).unwrap();
652 assert!(is_separator(&g, &[1, 3]).unwrap());
653 }
654
655 #[test]
656 fn cycle_4_adjacent_not_separator() {
657 let mut g = Graph::with_vertices(4);
659 g.add_edge(0, 1).unwrap();
660 g.add_edge(1, 2).unwrap();
661 g.add_edge(2, 3).unwrap();
662 g.add_edge(3, 0).unwrap();
663 assert!(!is_separator(&g, &[0, 1]).unwrap());
664 }
665
666 #[test]
667 fn already_disconnected_empty_set_is_separator() {
668 let mut g = Graph::with_vertices(4);
670 g.add_edge(0, 1).unwrap();
671 g.add_edge(2, 3).unwrap();
672 assert!(is_separator(&g, &[]).unwrap());
673 }
674
675 #[test]
676 fn k4_articulation() {
677 let mut g = Graph::with_vertices(4);
683 g.add_edge(0, 1).unwrap();
684 g.add_edge(0, 2).unwrap();
685 g.add_edge(0, 3).unwrap();
686 g.add_edge(1, 2).unwrap();
687 g.add_edge(2, 3).unwrap();
688 assert!(!is_separator(&g, &[0]).unwrap());
689 assert!(!is_separator(&g, &[2]).unwrap());
690 }
691
692 #[test]
693 fn bowtie_articulation() {
694 let mut g = Graph::with_vertices(5);
697 g.add_edge(0, 1).unwrap();
698 g.add_edge(1, 2).unwrap();
699 g.add_edge(2, 0).unwrap();
700 g.add_edge(2, 3).unwrap();
701 g.add_edge(3, 4).unwrap();
702 g.add_edge(4, 2).unwrap();
703 assert!(is_separator(&g, &[2]).unwrap());
704 }
705
706 #[test]
707 fn directed_rejected() {
708 let g = Graph::new(3, true).unwrap();
709 assert!(is_separator(&g, &[0]).is_err());
710 assert!(is_minimal_separator(&g, &[0]).is_err());
711 }
712
713 #[test]
714 fn out_of_range_rejected() {
715 let g = Graph::with_vertices(3);
716 assert!(is_separator(&g, &[5]).is_err());
717 }
718
719 #[test]
722 fn minimal_path_middle() {
723 let mut g = Graph::with_vertices(3);
725 g.add_edge(0, 1).unwrap();
726 g.add_edge(1, 2).unwrap();
727 assert!(is_minimal_separator(&g, &[1]).unwrap());
728 }
729
730 #[test]
731 fn minimal_cycle_4_opposite() {
732 let mut g = Graph::with_vertices(4);
734 g.add_edge(0, 1).unwrap();
735 g.add_edge(1, 2).unwrap();
736 g.add_edge(2, 3).unwrap();
737 g.add_edge(3, 0).unwrap();
738 assert!(is_minimal_separator(&g, &[1, 3]).unwrap());
739 }
740
741 #[test]
742 fn not_minimal_superset() {
743 let mut g = Graph::with_vertices(5);
745 g.add_edge(0, 1).unwrap();
746 g.add_edge(1, 2).unwrap();
747 g.add_edge(2, 3).unwrap();
748 g.add_edge(3, 4).unwrap();
749 assert!(is_separator(&g, &[1, 3]).unwrap());
750 assert!(!is_minimal_separator(&g, &[1, 3]).unwrap());
751 }
752
753 #[test]
754 fn not_separator_not_minimal() {
755 let mut g = Graph::with_vertices(3);
757 g.add_edge(0, 1).unwrap();
758 g.add_edge(1, 2).unwrap();
759 g.add_edge(2, 0).unwrap();
760 assert!(!is_minimal_separator(&g, &[0]).unwrap());
761 }
762
763 #[test]
764 fn minimal_bowtie_articulation() {
765 let mut g = Graph::with_vertices(5);
767 g.add_edge(0, 1).unwrap();
768 g.add_edge(1, 2).unwrap();
769 g.add_edge(2, 0).unwrap();
770 g.add_edge(2, 3).unwrap();
771 g.add_edge(3, 4).unwrap();
772 g.add_edge(4, 2).unwrap();
773 assert!(is_minimal_separator(&g, &[2]).unwrap());
774 }
775
776 #[test]
779 fn all_min_sep_empty_graph() {
780 let g = Graph::with_vertices(0);
781 let seps = all_minimal_st_separators(&g).unwrap();
782 assert!(seps.is_empty());
783 }
784
785 #[test]
786 fn all_min_sep_single_vertex() {
787 let g = Graph::with_vertices(1);
788 let seps = all_minimal_st_separators(&g).unwrap();
789 assert!(seps.is_empty());
790 }
791
792 #[test]
793 fn all_min_sep_single_edge() {
794 let mut g = Graph::with_vertices(2);
795 g.add_edge(0, 1).unwrap();
796 let seps = all_minimal_st_separators(&g).unwrap();
797 assert!(seps.is_empty());
799 }
800
801 #[test]
802 fn all_min_sep_path_3() {
803 let mut g = Graph::with_vertices(3);
805 g.add_edge(0, 1).unwrap();
806 g.add_edge(1, 2).unwrap();
807 let seps = all_minimal_st_separators(&g).unwrap();
808 assert_eq!(seps.len(), 1);
809 assert_eq!(seps[0], vec![1]);
810 }
811
812 #[test]
813 fn all_min_sep_path_5() {
814 let mut g = Graph::with_vertices(5);
816 g.add_edge(0, 1).unwrap();
817 g.add_edge(1, 2).unwrap();
818 g.add_edge(2, 3).unwrap();
819 g.add_edge(3, 4).unwrap();
820 let seps = all_minimal_st_separators(&g).unwrap();
821 assert_eq!(seps.len(), 3);
822 assert!(seps.contains(&vec![1]));
823 assert!(seps.contains(&vec![2]));
824 assert!(seps.contains(&vec![3]));
825 }
826
827 #[test]
828 fn all_min_sep_cycle_4() {
829 let mut g = Graph::with_vertices(4);
831 g.add_edge(0, 1).unwrap();
832 g.add_edge(1, 2).unwrap();
833 g.add_edge(2, 3).unwrap();
834 g.add_edge(3, 0).unwrap();
835 let seps = all_minimal_st_separators(&g).unwrap();
836 assert_eq!(seps.len(), 2);
837 assert!(seps.contains(&vec![0, 2]));
838 assert!(seps.contains(&vec![1, 3]));
839 }
840
841 #[test]
842 fn all_min_sep_pentagon_with_chord() {
843 let mut g = Graph::with_vertices(5);
845 g.add_edge(0, 1).unwrap();
846 g.add_edge(1, 2).unwrap();
847 g.add_edge(2, 3).unwrap();
848 g.add_edge(3, 4).unwrap();
849 g.add_edge(4, 1).unwrap();
850 let seps = all_minimal_st_separators(&g).unwrap();
851 assert_eq!(seps.len(), 3);
853 assert!(seps.contains(&vec![1]));
854 assert!(seps.contains(&vec![2, 4]));
855 assert!(seps.contains(&vec![1, 3]));
856 }
857
858 #[test]
859 fn all_min_sep_bowtie() {
860 let mut g = Graph::with_vertices(5);
862 g.add_edge(0, 1).unwrap();
863 g.add_edge(1, 2).unwrap();
864 g.add_edge(2, 0).unwrap();
865 g.add_edge(2, 3).unwrap();
866 g.add_edge(3, 4).unwrap();
867 g.add_edge(4, 2).unwrap();
868 let seps = all_minimal_st_separators(&g).unwrap();
869 assert_eq!(seps.len(), 1);
871 assert_eq!(seps[0], vec![2]);
872 }
873
874 #[test]
875 fn all_min_sep_complete_graph() {
876 let mut g = Graph::with_vertices(4);
878 g.add_edge(0, 1).unwrap();
879 g.add_edge(0, 2).unwrap();
880 g.add_edge(0, 3).unwrap();
881 g.add_edge(1, 2).unwrap();
882 g.add_edge(1, 3).unwrap();
883 g.add_edge(2, 3).unwrap();
884 let seps = all_minimal_st_separators(&g).unwrap();
885 assert!(seps.is_empty());
886 }
887
888 #[test]
889 fn all_min_sep_disconnected() {
890 let mut g = Graph::with_vertices(4);
892 g.add_edge(0, 1).unwrap();
893 g.add_edge(2, 3).unwrap();
894 let seps = all_minimal_st_separators(&g).unwrap();
895 for s in &seps {
900 assert!(!s.is_empty());
901 }
902 }
903
904 fn canon(mut seps: Vec<Vec<VertexId>>) -> Vec<Vec<VertexId>> {
908 for s in &mut seps {
909 s.sort_unstable();
910 }
911 seps.sort();
912 seps
913 }
914
915 fn undirected(n: u32, edges: &[(u32, u32)]) -> Graph {
916 let mut g = Graph::with_vertices(n);
917 for &(a, b) in edges {
918 g.add_edge(a, b).unwrap();
919 }
920 g
921 }
922
923 #[test]
924 fn mss_directed_rejected() {
925 let g = Graph::new(3, true).unwrap();
926 assert!(minimum_size_separators(&g).is_err());
927 }
928
929 #[test]
930 fn mss_path3() {
931 let g = undirected(3, &[(0, 1), (1, 2)]);
933 assert_eq!(canon(minimum_size_separators(&g).unwrap()), vec![vec![1]]);
934 }
935
936 #[test]
937 fn mss_disconnected_empty() {
938 let g = undirected(4, &[(0, 1), (2, 3)]);
940 assert!(minimum_size_separators(&g).unwrap().is_empty());
941 }
942
943 #[test]
944 fn mss_articulation_singletons() {
945 let g = undirected(5, &[(0, 1), (1, 2), (0, 2), (2, 3), (3, 4), (2, 4)]);
948 assert_eq!(canon(minimum_size_separators(&g).unwrap()), vec![vec![2]]);
949 }
950
951 #[test]
952 fn mss_complete_k4() {
953 let g = undirected(4, &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
956 assert_eq!(
957 canon(minimum_size_separators(&g).unwrap()),
958 vec![vec![0, 1, 2], vec![0, 1, 3], vec![0, 2, 3], vec![1, 2, 3],]
959 );
960 }
961
962 #[test]
963 fn mss_complete_k3() {
964 let g = undirected(3, &[(0, 1), (0, 2), (1, 2)]);
966 assert_eq!(
967 canon(minimum_size_separators(&g).unwrap()),
968 vec![vec![0, 1], vec![0, 2], vec![1, 2]]
969 );
970 }
971
972 #[test]
973 fn mss_cycle5() {
974 let g = undirected(5, &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]);
976 assert_eq!(
977 canon(minimum_size_separators(&g).unwrap()),
978 vec![vec![0, 2], vec![0, 3], vec![1, 3], vec![1, 4], vec![2, 4],]
979 );
980 }
981
982 #[test]
983 fn mss_grid3x3() {
984 let g = undirected(
986 9,
987 &[
988 (0, 1),
989 (1, 2),
990 (3, 4),
991 (4, 5),
992 (6, 7),
993 (7, 8),
994 (0, 3),
995 (3, 6),
996 (1, 4),
997 (4, 7),
998 (2, 5),
999 (5, 8),
1000 ],
1001 );
1002 assert_eq!(
1003 canon(minimum_size_separators(&g).unwrap()),
1004 vec![vec![1, 3], vec![1, 5], vec![3, 7], vec![5, 7]]
1005 );
1006 }
1007
1008 #[test]
1009 fn mss_complete_bipartite_k23() {
1010 let g = undirected(5, &[(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4)]);
1013 assert_eq!(
1014 canon(minimum_size_separators(&g).unwrap()),
1015 vec![vec![0, 1]]
1016 );
1017 }
1018
1019 #[test]
1020 fn mss_separators_are_valid() {
1021 let g = undirected(
1024 7,
1025 &[
1026 (0, 1),
1027 (1, 2),
1028 (2, 0),
1029 (2, 3),
1030 (3, 4),
1031 (4, 5),
1032 (5, 3),
1033 (5, 6),
1034 (6, 3),
1035 ],
1036 );
1037 let seps = minimum_size_separators(&g).unwrap();
1038 assert!(!seps.is_empty());
1039 let k = seps[0].len();
1040 for s in &seps {
1041 assert_eq!(s.len(), k, "all minimum separators share size");
1042 assert!(is_separator(&g, s).unwrap(), "{s:?} must separate");
1043 }
1044 }
1045}