rust_igraph/algorithms/properties/
perfect.rs1use crate::algorithms::chordality::is_chordal;
27use crate::algorithms::constructors::ring::ring_graph;
28use crate::algorithms::isomorphism::lad::subisomorphic_lad;
29use crate::algorithms::operators::complementer::complementer;
30use crate::algorithms::properties::girth::girth;
31use crate::algorithms::properties::is_bipartite::is_bipartite;
32use crate::algorithms::properties::is_simple::is_simple;
33use crate::core::{Graph, IgraphError, IgraphResult};
34
35pub fn is_perfect(graph: &Graph) -> IgraphResult<bool> {
66 if graph.is_directed() {
67 return Err(IgraphError::InvalidArgument(
68 "The concept of perfect graphs is only defined for undirected graphs.".to_string(),
69 ));
70 }
71
72 if !is_simple(graph)? {
73 return Err(IgraphError::InvalidArgument(
74 "Perfect graph testing is implemented for simple graphs only. Simplify the graph."
75 .to_string(),
76 ));
77 }
78
79 let no_of_nodes = graph.vcount();
80 let no_of_edges = graph.ecount();
81
82 if no_of_nodes < 5 {
84 return Ok(true);
85 }
86
87 let n = u64::from(no_of_nodes);
91 let max_edges = (n - 1) * n / 2;
92 if no_of_nodes < 10_000
93 && (no_of_edges < 5 || (max_edges >= 5 && no_of_edges as u64 > max_edges - 5))
94 {
95 return Ok(true);
96 }
97
98 if is_bipartite(graph)?.is_bipartite {
100 return Ok(true);
101 }
102 if is_chordal(graph, None)?.chordal {
103 return Ok(true);
104 }
105
106 let comp = complementer(graph, false)?;
108
109 if is_bipartite(&comp)?.is_bipartite {
110 return Ok(true);
111 }
112 if is_chordal(&comp, None)?.chordal {
113 return Ok(true);
114 }
115
116 let Some(girth_g) = girth(graph)? else {
119 return Ok(true);
120 };
121 if girth_g > 3 && girth_g % 2 == 1 {
122 return Ok(false);
123 }
124
125 let Some(girth_c) = girth(&comp)? else {
126 return Ok(true);
127 };
128 if girth_c > 3 && girth_c % 2 == 1 {
129 return Ok(false);
130 }
131
132 let min_girth = girth_g.min(girth_c);
135 let mut cycle_len = if min_girth % 2 == 0 {
136 min_girth + 1
137 } else {
138 min_girth + 2
139 };
140
141 while cycle_len <= no_of_nodes {
142 let cycle = ring_graph(cycle_len, false, false, true)?;
143
144 if cycle_len > girth_g && subisomorphic_lad(&cycle, graph, None, true)?.iso {
145 return Ok(false);
146 }
147 if cycle_len > girth_c && subisomorphic_lad(&cycle, &comp, None, true)?.iso {
148 return Ok(false);
149 }
150
151 cycle_len += 2;
152 }
153
154 Ok(true)
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 fn undirected(n: u32, edges: &[(u32, u32)]) -> Graph {
162 let mut g = Graph::new(n, false).expect("graph init");
163 for &(a, b) in edges {
164 g.add_edge(a, b).expect("add edge");
165 }
166 g
167 }
168
169 fn ring(n: u32) -> Graph {
170 ring_graph(n, false, false, true).expect("ring")
171 }
172
173 #[test]
174 fn null_graph_is_perfect() {
175 let g = Graph::new(0, false).expect("graph");
176 assert!(is_perfect(&g).expect("is_perfect"));
177 }
178
179 #[test]
180 fn singleton_is_perfect() {
181 let g = Graph::new(1, false).expect("graph");
182 assert!(is_perfect(&g).expect("is_perfect"));
183 }
184
185 #[test]
186 fn empty_two_vertices_is_perfect() {
187 let g = Graph::new(2, false).expect("graph");
188 assert!(is_perfect(&g).expect("is_perfect"));
189 }
190
191 #[test]
192 fn small_graphs_under_five_vertices_are_perfect() {
193 let g = undirected(4, &[(0, 1), (0, 2), (0, 3), (1, 2), (1, 3), (2, 3)]);
195 assert!(is_perfect(&g).expect("is_perfect"));
196 }
197
198 #[test]
199 fn c5_is_not_perfect() {
200 assert!(!is_perfect(&ring(5)).expect("is_perfect"));
201 }
202
203 #[test]
204 fn c7_is_not_perfect() {
205 assert!(!is_perfect(&ring(7)).expect("is_perfect"));
206 }
207
208 #[test]
209 fn c6_is_perfect() {
210 assert!(is_perfect(&ring(6)).expect("is_perfect"));
212 }
213
214 #[test]
215 fn complement_of_c7_is_not_perfect() {
216 let c7 = ring(7);
217 let comp = complementer(&c7, false).expect("complement");
218 assert!(!is_perfect(&comp).expect("is_perfect"));
219 }
220
221 #[test]
222 fn paley_order_9_is_perfect() {
223 let g = undirected(
225 9,
226 &[
227 (0, 1),
228 (0, 3),
229 (0, 6),
230 (0, 2),
231 (1, 2),
232 (1, 4),
233 (1, 7),
234 (2, 5),
235 (2, 8),
236 (3, 4),
237 (3, 5),
238 (3, 6),
239 (4, 5),
240 (4, 7),
241 (5, 8),
242 (6, 7),
243 (7, 8),
244 (6, 8),
245 ],
246 );
247 assert!(is_perfect(&g).expect("is_perfect"));
248 }
249
250 #[test]
251 fn house_graph_is_perfect() {
252 let g = undirected(5, &[(0, 1), (0, 2), (1, 3), (2, 3), (2, 4), (3, 4)]);
255 assert!(is_perfect(&g).expect("is_perfect"));
256 }
257
258 #[test]
259 fn directed_graph_errors() {
260 let mut g = Graph::new(5, true).expect("graph");
261 g.add_edge(0, 1).expect("edge");
262 assert!(is_perfect(&g).is_err());
263 }
264
265 #[test]
266 fn non_simple_graph_errors() {
267 let mut g = Graph::new(5, false).expect("graph");
268 g.add_edge(0, 1).expect("edge");
269 g.add_edge(0, 1).expect("parallel edge");
270 assert!(is_perfect(&g).is_err());
271 }
272}