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 for nb in graph.neighbors_iter(cur)? {
189 let nidx = nb as usize;
190 if !visited[nidx] && !removed[nidx] {
191 visited[nidx] = true;
192 queue.push_back(nb);
193 count += 1;
194 }
195 }
196 }
197
198 Ok(count)
199}
200
201pub fn all_minimal_st_separators(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
235 let n = graph.vcount() as usize;
236 if n == 0 {
237 return Ok(Vec::new());
238 }
239
240 let adj = build_adj_undirected(graph)?;
241
242 let mut separators: Vec<Vec<VertexId>> = Vec::new();
243 let mut mark: Vec<u32> = vec![0; n];
244 let mut stamp: u32 = 1;
245
246 for v in 0..n {
249 advance_stamp(&mut mark, &mut stamp, n);
250 mark[v] = stamp;
251 for &nb in &adj[v] {
252 mark[nb as usize] = stamp;
253 }
254
255 let components = find_components_leaveout(&adj, &mark, stamp, n);
256 store_separators(&adj, &components, &mut mark, &mut separators, &mut stamp, n);
257 }
258
259 let mut try_next = 0;
261 while try_next < separators.len() {
262 let basis = separators[try_next].clone();
263 for &x in &basis {
264 advance_stamp(&mut mark, &mut stamp, n);
265 for &sv in &basis {
266 mark[sv as usize] = stamp;
267 }
268 for &nb in &adj[x as usize] {
269 mark[nb as usize] = stamp;
270 }
271
272 let components = find_components_leaveout(&adj, &mark, stamp, n);
273 store_separators(&adj, &components, &mut mark, &mut separators, &mut stamp, n);
274 }
275 try_next += 1;
276 }
277
278 Ok(separators)
279}
280
281fn build_adj_undirected(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
282 let n = graph.vcount() as usize;
283 let mut adj: Vec<Vec<VertexId>> = vec![Vec::new(); n];
284 let m = graph.ecount();
285 for eid in 0..m {
286 let eid32 = u32::try_from(eid).map_err(|_| {
287 IgraphError::InvalidArgument("all_minimal_st_separators: edge id overflow".into())
288 })?;
289 let (from, to) = graph.edge(eid32)?;
290 adj[from as usize].push(to);
291 if from != to {
292 adj[to as usize].push(from);
293 }
294 }
295 Ok(adj)
296}
297
298fn find_components_leaveout(
301 adj: &[Vec<VertexId>],
302 mark: &[u32],
303 stamp: u32,
304 n: usize,
305) -> Vec<Vec<VertexId>> {
306 let mut visited = vec![false; n];
307 let mut components: Vec<Vec<VertexId>> = Vec::new();
308 let mut queue: VecDeque<VertexId> = VecDeque::new();
309
310 for i in 0..n {
311 if mark[i] == stamp || visited[i] {
312 continue;
313 }
314
315 let mut comp: Vec<VertexId> = Vec::new();
316 #[allow(clippy::cast_possible_truncation)]
317 let i_v = i as VertexId;
318 visited[i] = true;
319 queue.push_back(i_v);
320 comp.push(i_v);
321
322 while let Some(cur) = queue.pop_front() {
323 for &nb in &adj[cur as usize] {
324 let nb_us = nb as usize;
325 if mark[nb_us] == stamp || visited[nb_us] {
326 continue;
327 }
328 visited[nb_us] = true;
329 queue.push_back(nb);
330 comp.push(nb);
331 }
332 }
333
334 components.push(comp);
335 }
336
337 components
338}
339
340fn store_separators(
344 adj: &[Vec<VertexId>],
345 components: &[Vec<VertexId>],
346 mark: &mut [u32],
347 separators: &mut Vec<Vec<VertexId>>,
348 cur_stamp: &mut u32,
349 n: usize,
350) {
351 for comp in components {
352 advance_stamp(mark, cur_stamp, n);
353 let comp_stamp = *cur_stamp;
354
355 for &v in comp {
357 mark[v as usize] = comp_stamp;
358 }
359
360 let mut neighborhood: Vec<VertexId> = Vec::new();
362 for &v in comp {
363 for &nb in &adj[v as usize] {
364 let nb_us = nb as usize;
365 if mark[nb_us] != comp_stamp {
366 mark[nb_us] = comp_stamp;
367 neighborhood.push(nb);
368 }
369 }
370 }
371
372 if neighborhood.is_empty() {
373 continue;
374 }
375
376 neighborhood.sort_unstable();
377
378 if !separators.contains(&neighborhood) {
379 separators.push(neighborhood);
380 }
381 }
382
383 advance_stamp(mark, cur_stamp, n);
384}
385
386fn advance_stamp(mark: &mut [u32], stamp: &mut u32, _n: usize) {
387 *stamp = stamp.wrapping_add(1);
388 if *stamp == 0 {
389 for m in mark.iter_mut() {
390 *m = 0;
391 }
392 *stamp = 1;
393 }
394}
395
396fn topk_degree(graph: &Graph, k: usize) -> IgraphResult<Vec<VertexId>> {
401 let n = graph.vcount();
402 let mut deg: Vec<(usize, VertexId)> = Vec::with_capacity(n as usize);
403 for v in 0..n {
404 deg.push((graph.degree(v)?, v));
405 }
406 deg.sort_by(|a, b| a.0.cmp(&b.0).then(a.1.cmp(&b.1)));
410 let mut res = Vec::with_capacity(k);
411 for i in 0..k {
412 res.push(deg[n as usize - 1 - i].1);
413 }
414 Ok(res)
415}
416
417fn append_unique(acc: &mut Vec<Vec<VertexId>>, new: Vec<Vec<VertexId>>) {
422 for sep in new {
423 if !acc.iter().any(|existing| existing == &sep) {
424 acc.push(sep);
425 }
426 }
427}
428
429#[allow(clippy::too_many_lines)]
476pub fn minimum_size_separators(graph: &Graph) -> IgraphResult<Vec<Vec<VertexId>>> {
477 if graph.is_directed() {
478 return Err(IgraphError::InvalidArgument(
479 "minimum_size_separators: only defined for undirected graphs".into(),
480 ));
481 }
482
483 let no_of_nodes = graph.vcount();
484
485 let conn = vertex_connectivity(graph, true)?;
487
488 if conn <= 0 {
493 return Ok(Vec::new());
494 }
495 if conn == 1 {
496 let aps = articulation_points(graph)?;
497 return Ok(aps.into_iter().map(|v| vec![v]).collect());
498 }
499 if conn == i64::from(no_of_nodes) - 1 {
500 let mut separators = Vec::with_capacity(no_of_nodes as usize);
501 for i in 0..no_of_nodes {
502 separators.push((0..no_of_nodes).filter(|&j| j != i).collect());
503 }
504 return Ok(separators);
505 }
506
507 let k = usize::try_from(conn).map_err(|_| {
508 IgraphError::InvalidArgument("minimum_size_separators: connectivity overflow".into())
509 })?;
510
511 let mut graph_copy = simplify(graph, true, true)?;
513
514 let mut separators: Vec<Vec<VertexId>> = Vec::new();
515
516 let x = topk_degree(&graph_copy, k)?;
519 if is_separator(&graph_copy, &x)? {
520 let mut sep = x.clone();
521 sep.sort_unstable();
522 append_unique(&mut separators, vec![sep]);
523 }
524
525 let reduction = even_tarjan_reduction(&graph_copy)?;
533 let mut gbar = reduction.graph;
534 let big = f64::from(no_of_nodes);
535 let mut capacity: Vec<f64> = reduction
536 .capacity
537 .into_iter()
538 .map(|c| if c.is_finite() { c } else { big })
539 .collect();
540
541 let k_f = f64::from(u32::try_from(conn).map_err(|_| {
545 IgraphError::InvalidArgument("minimum_size_separators: connectivity overflow".into())
546 })?);
547 for &xi in &x {
548 for j in 0..no_of_nodes {
549 if xi == j {
550 continue;
551 }
552 if are_adjacent(&graph_copy, xi, j)? {
553 continue;
554 }
555
556 let source = xi + no_of_nodes;
558 let target = j;
559 let phivalue = max_flow_value(&gbar, source, target, Some(&capacity))?;
560
561 if (phivalue - k_f).abs() < 0.5 {
562 let cuts = all_st_mincuts(&gbar, source, target, Some(&capacity))?;
568 let mapped: Vec<Vec<VertexId>> = cuts
569 .cuts
570 .into_iter()
571 .map(|cut| {
572 let mut sep: Vec<VertexId> = cut;
573 sep.sort_unstable();
574 sep
575 })
576 .collect();
577 append_unique(&mut separators, mapped);
578 }
579
580 graph_copy.add_edge(xi, j)?;
582 gbar.add_edge(xi + no_of_nodes, j)?;
583 gbar.add_edge(j + no_of_nodes, xi)?;
584 capacity.push(big);
585 capacity.push(big);
586 }
587 }
588
589 Ok(separators)
590}
591
592#[cfg(test)]
593mod tests {
594 use super::*;
595
596 #[test]
597 fn empty_graph() {
598 let g = Graph::with_vertices(0);
599 assert!(!is_separator(&g, &[]).unwrap());
600 }
601
602 #[test]
603 fn singleton_not_separator() {
604 let g = Graph::with_vertices(1);
605 assert!(!is_separator(&g, &[0]).unwrap());
606 }
607
608 #[test]
609 fn path_middle_is_separator() {
610 let mut g = Graph::with_vertices(3);
611 g.add_edge(0, 1).unwrap();
612 g.add_edge(1, 2).unwrap();
613 assert!(is_separator(&g, &[1]).unwrap());
614 }
615
616 #[test]
617 fn path_leaf_not_separator() {
618 let mut g = Graph::with_vertices(3);
619 g.add_edge(0, 1).unwrap();
620 g.add_edge(1, 2).unwrap();
621 assert!(!is_separator(&g, &[0]).unwrap());
622 assert!(!is_separator(&g, &[2]).unwrap());
623 }
624
625 #[test]
626 fn triangle_no_single_separator() {
627 let mut g = Graph::with_vertices(3);
628 g.add_edge(0, 1).unwrap();
629 g.add_edge(1, 2).unwrap();
630 g.add_edge(2, 0).unwrap();
631 assert!(!is_separator(&g, &[0]).unwrap());
633 assert!(!is_separator(&g, &[1]).unwrap());
634 assert!(!is_separator(&g, &[2]).unwrap());
635 }
636
637 #[test]
638 fn triangle_pair_not_separator() {
639 let mut g = Graph::with_vertices(3);
640 g.add_edge(0, 1).unwrap();
641 g.add_edge(1, 2).unwrap();
642 g.add_edge(2, 0).unwrap();
643 assert!(!is_separator(&g, &[0, 1]).unwrap());
645 }
646
647 #[test]
648 fn cycle_4_opposite_vertices() {
649 let mut g = Graph::with_vertices(4);
651 g.add_edge(0, 1).unwrap();
652 g.add_edge(1, 2).unwrap();
653 g.add_edge(2, 3).unwrap();
654 g.add_edge(3, 0).unwrap();
655 assert!(is_separator(&g, &[1, 3]).unwrap());
656 }
657
658 #[test]
659 fn cycle_4_adjacent_not_separator() {
660 let mut g = Graph::with_vertices(4);
662 g.add_edge(0, 1).unwrap();
663 g.add_edge(1, 2).unwrap();
664 g.add_edge(2, 3).unwrap();
665 g.add_edge(3, 0).unwrap();
666 assert!(!is_separator(&g, &[0, 1]).unwrap());
667 }
668
669 #[test]
670 fn already_disconnected_empty_set_is_separator() {
671 let mut g = Graph::with_vertices(4);
673 g.add_edge(0, 1).unwrap();
674 g.add_edge(2, 3).unwrap();
675 assert!(is_separator(&g, &[]).unwrap());
676 }
677
678 #[test]
679 fn k4_articulation() {
680 let mut g = Graph::with_vertices(4);
686 g.add_edge(0, 1).unwrap();
687 g.add_edge(0, 2).unwrap();
688 g.add_edge(0, 3).unwrap();
689 g.add_edge(1, 2).unwrap();
690 g.add_edge(2, 3).unwrap();
691 assert!(!is_separator(&g, &[0]).unwrap());
692 assert!(!is_separator(&g, &[2]).unwrap());
693 }
694
695 #[test]
696 fn bowtie_articulation() {
697 let mut g = Graph::with_vertices(5);
700 g.add_edge(0, 1).unwrap();
701 g.add_edge(1, 2).unwrap();
702 g.add_edge(2, 0).unwrap();
703 g.add_edge(2, 3).unwrap();
704 g.add_edge(3, 4).unwrap();
705 g.add_edge(4, 2).unwrap();
706 assert!(is_separator(&g, &[2]).unwrap());
707 }
708
709 #[test]
710 fn directed_rejected() {
711 let g = Graph::new(3, true).unwrap();
712 assert!(is_separator(&g, &[0]).is_err());
713 assert!(is_minimal_separator(&g, &[0]).is_err());
714 }
715
716 #[test]
717 fn out_of_range_rejected() {
718 let g = Graph::with_vertices(3);
719 assert!(is_separator(&g, &[5]).is_err());
720 }
721
722 #[test]
725 fn minimal_path_middle() {
726 let mut g = Graph::with_vertices(3);
728 g.add_edge(0, 1).unwrap();
729 g.add_edge(1, 2).unwrap();
730 assert!(is_minimal_separator(&g, &[1]).unwrap());
731 }
732
733 #[test]
734 fn minimal_cycle_4_opposite() {
735 let mut g = Graph::with_vertices(4);
737 g.add_edge(0, 1).unwrap();
738 g.add_edge(1, 2).unwrap();
739 g.add_edge(2, 3).unwrap();
740 g.add_edge(3, 0).unwrap();
741 assert!(is_minimal_separator(&g, &[1, 3]).unwrap());
742 }
743
744 #[test]
745 fn not_minimal_superset() {
746 let mut g = Graph::with_vertices(5);
748 g.add_edge(0, 1).unwrap();
749 g.add_edge(1, 2).unwrap();
750 g.add_edge(2, 3).unwrap();
751 g.add_edge(3, 4).unwrap();
752 assert!(is_separator(&g, &[1, 3]).unwrap());
753 assert!(!is_minimal_separator(&g, &[1, 3]).unwrap());
754 }
755
756 #[test]
757 fn not_separator_not_minimal() {
758 let mut g = Graph::with_vertices(3);
760 g.add_edge(0, 1).unwrap();
761 g.add_edge(1, 2).unwrap();
762 g.add_edge(2, 0).unwrap();
763 assert!(!is_minimal_separator(&g, &[0]).unwrap());
764 }
765
766 #[test]
767 fn minimal_bowtie_articulation() {
768 let mut g = Graph::with_vertices(5);
770 g.add_edge(0, 1).unwrap();
771 g.add_edge(1, 2).unwrap();
772 g.add_edge(2, 0).unwrap();
773 g.add_edge(2, 3).unwrap();
774 g.add_edge(3, 4).unwrap();
775 g.add_edge(4, 2).unwrap();
776 assert!(is_minimal_separator(&g, &[2]).unwrap());
777 }
778
779 #[test]
782 fn all_min_sep_empty_graph() {
783 let g = Graph::with_vertices(0);
784 let seps = all_minimal_st_separators(&g).unwrap();
785 assert!(seps.is_empty());
786 }
787
788 #[test]
789 fn all_min_sep_single_vertex() {
790 let g = Graph::with_vertices(1);
791 let seps = all_minimal_st_separators(&g).unwrap();
792 assert!(seps.is_empty());
793 }
794
795 #[test]
796 fn all_min_sep_single_edge() {
797 let mut g = Graph::with_vertices(2);
798 g.add_edge(0, 1).unwrap();
799 let seps = all_minimal_st_separators(&g).unwrap();
800 assert!(seps.is_empty());
802 }
803
804 #[test]
805 fn all_min_sep_path_3() {
806 let mut g = Graph::with_vertices(3);
808 g.add_edge(0, 1).unwrap();
809 g.add_edge(1, 2).unwrap();
810 let seps = all_minimal_st_separators(&g).unwrap();
811 assert_eq!(seps.len(), 1);
812 assert_eq!(seps[0], vec![1]);
813 }
814
815 #[test]
816 fn all_min_sep_path_5() {
817 let mut g = Graph::with_vertices(5);
819 g.add_edge(0, 1).unwrap();
820 g.add_edge(1, 2).unwrap();
821 g.add_edge(2, 3).unwrap();
822 g.add_edge(3, 4).unwrap();
823 let seps = all_minimal_st_separators(&g).unwrap();
824 assert_eq!(seps.len(), 3);
825 assert!(seps.contains(&vec![1]));
826 assert!(seps.contains(&vec![2]));
827 assert!(seps.contains(&vec![3]));
828 }
829
830 #[test]
831 fn all_min_sep_cycle_4() {
832 let mut g = Graph::with_vertices(4);
834 g.add_edge(0, 1).unwrap();
835 g.add_edge(1, 2).unwrap();
836 g.add_edge(2, 3).unwrap();
837 g.add_edge(3, 0).unwrap();
838 let seps = all_minimal_st_separators(&g).unwrap();
839 assert_eq!(seps.len(), 2);
840 assert!(seps.contains(&vec![0, 2]));
841 assert!(seps.contains(&vec![1, 3]));
842 }
843
844 #[test]
845 fn all_min_sep_pentagon_with_chord() {
846 let mut g = Graph::with_vertices(5);
848 g.add_edge(0, 1).unwrap();
849 g.add_edge(1, 2).unwrap();
850 g.add_edge(2, 3).unwrap();
851 g.add_edge(3, 4).unwrap();
852 g.add_edge(4, 1).unwrap();
853 let seps = all_minimal_st_separators(&g).unwrap();
854 assert_eq!(seps.len(), 3);
856 assert!(seps.contains(&vec![1]));
857 assert!(seps.contains(&vec![2, 4]));
858 assert!(seps.contains(&vec![1, 3]));
859 }
860
861 #[test]
862 fn all_min_sep_bowtie() {
863 let mut g = Graph::with_vertices(5);
865 g.add_edge(0, 1).unwrap();
866 g.add_edge(1, 2).unwrap();
867 g.add_edge(2, 0).unwrap();
868 g.add_edge(2, 3).unwrap();
869 g.add_edge(3, 4).unwrap();
870 g.add_edge(4, 2).unwrap();
871 let seps = all_minimal_st_separators(&g).unwrap();
872 assert_eq!(seps.len(), 1);
874 assert_eq!(seps[0], vec![2]);
875 }
876
877 #[test]
878 fn all_min_sep_complete_graph() {
879 let mut g = Graph::with_vertices(4);
881 g.add_edge(0, 1).unwrap();
882 g.add_edge(0, 2).unwrap();
883 g.add_edge(0, 3).unwrap();
884 g.add_edge(1, 2).unwrap();
885 g.add_edge(1, 3).unwrap();
886 g.add_edge(2, 3).unwrap();
887 let seps = all_minimal_st_separators(&g).unwrap();
888 assert!(seps.is_empty());
889 }
890
891 #[test]
892 fn all_min_sep_disconnected() {
893 let mut g = Graph::with_vertices(4);
895 g.add_edge(0, 1).unwrap();
896 g.add_edge(2, 3).unwrap();
897 let seps = all_minimal_st_separators(&g).unwrap();
898 for s in &seps {
903 assert!(!s.is_empty());
904 }
905 }
906
907 fn canon(mut seps: Vec<Vec<VertexId>>) -> Vec<Vec<VertexId>> {
911 for s in &mut seps {
912 s.sort_unstable();
913 }
914 seps.sort();
915 seps
916 }
917
918 fn undirected(n: u32, edges: &[(u32, u32)]) -> Graph {
919 let mut g = Graph::with_vertices(n);
920 for &(a, b) in edges {
921 g.add_edge(a, b).unwrap();
922 }
923 g
924 }
925
926 #[test]
927 fn mss_directed_rejected() {
928 let g = Graph::new(3, true).unwrap();
929 assert!(minimum_size_separators(&g).is_err());
930 }
931
932 #[test]
933 fn mss_path3() {
934 let g = undirected(3, &[(0, 1), (1, 2)]);
936 assert_eq!(canon(minimum_size_separators(&g).unwrap()), vec![vec![1]]);
937 }
938
939 #[test]
940 fn mss_disconnected_empty() {
941 let g = undirected(4, &[(0, 1), (2, 3)]);
943 assert!(minimum_size_separators(&g).unwrap().is_empty());
944 }
945
946 #[test]
947 fn mss_articulation_singletons() {
948 let g = undirected(5, &[(0, 1), (1, 2), (0, 2), (2, 3), (3, 4), (2, 4)]);
951 assert_eq!(canon(minimum_size_separators(&g).unwrap()), vec![vec![2]]);
952 }
953
954 #[test]
955 fn mss_complete_k4() {
956 let g = undirected(4, &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
959 assert_eq!(
960 canon(minimum_size_separators(&g).unwrap()),
961 vec![vec![0, 1, 2], vec![0, 1, 3], vec![0, 2, 3], vec![1, 2, 3],]
962 );
963 }
964
965 #[test]
966 fn mss_complete_k3() {
967 let g = undirected(3, &[(0, 1), (0, 2), (1, 2)]);
969 assert_eq!(
970 canon(minimum_size_separators(&g).unwrap()),
971 vec![vec![0, 1], vec![0, 2], vec![1, 2]]
972 );
973 }
974
975 #[test]
976 fn mss_cycle5() {
977 let g = undirected(5, &[(0, 1), (1, 2), (2, 3), (3, 4), (4, 0)]);
979 assert_eq!(
980 canon(minimum_size_separators(&g).unwrap()),
981 vec![vec![0, 2], vec![0, 3], vec![1, 3], vec![1, 4], vec![2, 4],]
982 );
983 }
984
985 #[test]
986 fn mss_grid3x3() {
987 let g = undirected(
989 9,
990 &[
991 (0, 1),
992 (1, 2),
993 (3, 4),
994 (4, 5),
995 (6, 7),
996 (7, 8),
997 (0, 3),
998 (3, 6),
999 (1, 4),
1000 (4, 7),
1001 (2, 5),
1002 (5, 8),
1003 ],
1004 );
1005 assert_eq!(
1006 canon(minimum_size_separators(&g).unwrap()),
1007 vec![vec![1, 3], vec![1, 5], vec![3, 7], vec![5, 7]]
1008 );
1009 }
1010
1011 #[test]
1012 fn mss_complete_bipartite_k23() {
1013 let g = undirected(5, &[(0, 2), (0, 3), (0, 4), (1, 2), (1, 3), (1, 4)]);
1016 assert_eq!(
1017 canon(minimum_size_separators(&g).unwrap()),
1018 vec![vec![0, 1]]
1019 );
1020 }
1021
1022 #[test]
1023 fn mss_separators_are_valid() {
1024 let g = undirected(
1027 7,
1028 &[
1029 (0, 1),
1030 (1, 2),
1031 (2, 0),
1032 (2, 3),
1033 (3, 4),
1034 (4, 5),
1035 (5, 3),
1036 (5, 6),
1037 (6, 3),
1038 ],
1039 );
1040 let seps = minimum_size_separators(&g).unwrap();
1041 assert!(!seps.is_empty());
1042 let k = seps[0].len();
1043 for s in &seps {
1044 assert_eq!(s.len(), k, "all minimum separators share size");
1045 assert!(is_separator(&g, s).unwrap(), "{s:?} must separate");
1046 }
1047 }
1048}