rust_igraph/algorithms/properties/
is_cograph.rs1use crate::algorithms::connectivity::components::connected_components;
17use crate::algorithms::operators::complementer::complementer;
18use crate::core::{Graph, IgraphResult};
19
20pub fn is_cograph(graph: &Graph) -> IgraphResult<bool> {
56 if graph.is_directed() {
57 return Ok(false);
58 }
59
60 let n = graph.vcount();
61 if n <= 3 {
62 return Ok(true);
63 }
64
65 is_cograph_recursive(graph)
66}
67
68fn is_cograph_recursive(graph: &Graph) -> IgraphResult<bool> {
69 let n = graph.vcount();
70 if n <= 3 {
71 return Ok(true);
72 }
73
74 let cc = connected_components(graph)?;
75
76 if cc.count > 1 {
77 return check_components_are_cographs(graph, &cc);
79 }
80
81 let comp = complementer(graph, false)?;
83 let cc_comp = connected_components(&comp)?;
84
85 if cc_comp.count <= 1 {
86 return Ok(false);
88 }
89
90 check_components_are_cographs(&comp, &cc_comp)
92}
93
94fn check_components_are_cographs(
95 graph: &Graph,
96 cc: &crate::algorithms::connectivity::components::ConnectedComponents,
97) -> IgraphResult<bool> {
98 for comp_id in 0..cc.count {
99 let comp_verts: Vec<u32> = (0..graph.vcount())
100 .filter(|&v| cc.membership[v as usize] == comp_id)
101 .collect();
102
103 if comp_verts.len() <= 3 {
104 continue;
105 }
106
107 let subgraph = extract_induced_subgraph(graph, &comp_verts)?;
108 if !is_cograph_recursive(&subgraph)? {
109 return Ok(false);
110 }
111 }
112
113 Ok(true)
114}
115
116fn extract_induced_subgraph(graph: &Graph, vertices: &[u32]) -> IgraphResult<Graph> {
117 let n = vertices.len();
118 let mut sub = Graph::with_vertices(u32::try_from(n).unwrap_or(u32::MAX));
119
120 let mut old_to_new = vec![u32::MAX; graph.vcount() as usize];
122 for (new_id, &old_id) in vertices.iter().enumerate() {
123 old_to_new[old_id as usize] = u32::try_from(new_id).unwrap_or(u32::MAX);
124 }
125
126 for &v in vertices {
128 let nbrs = graph.neighbors(v)?;
129 for w in nbrs {
130 if w > v && old_to_new[w as usize] != u32::MAX {
131 sub.add_edge(old_to_new[v as usize], old_to_new[w as usize])?;
132 }
133 }
134 }
135
136 Ok(sub)
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn empty_graph() {
145 let g = Graph::with_vertices(0);
146 assert!(is_cograph(&g).unwrap());
147 }
148
149 #[test]
150 fn single_vertex() {
151 let g = Graph::with_vertices(1);
152 assert!(is_cograph(&g).unwrap());
153 }
154
155 #[test]
156 fn single_edge() {
157 let mut g = Graph::with_vertices(2);
158 g.add_edge(0, 1).unwrap();
159 assert!(is_cograph(&g).unwrap());
160 }
161
162 #[test]
163 fn triangle_is_cograph() {
164 let mut g = Graph::with_vertices(3);
165 g.add_edge(0, 1).unwrap();
166 g.add_edge(1, 2).unwrap();
167 g.add_edge(2, 0).unwrap();
168 assert!(is_cograph(&g).unwrap());
169 }
170
171 #[test]
172 fn k4_is_cograph() {
173 let mut g = Graph::with_vertices(4);
174 for u in 0..4u32 {
175 for v in (u + 1)..4 {
176 g.add_edge(u, v).unwrap();
177 }
178 }
179 assert!(is_cograph(&g).unwrap());
180 }
181
182 #[test]
183 fn k5_is_cograph() {
184 let mut g = Graph::with_vertices(5);
185 for u in 0..5u32 {
186 for v in (u + 1)..5 {
187 g.add_edge(u, v).unwrap();
188 }
189 }
190 assert!(is_cograph(&g).unwrap());
191 }
192
193 #[test]
194 fn edgeless_is_cograph() {
195 let g = Graph::with_vertices(5);
196 assert!(is_cograph(&g).unwrap());
197 }
198
199 #[test]
200 fn p4_not_cograph() {
201 let mut g = Graph::with_vertices(4);
203 g.add_edge(0, 1).unwrap();
204 g.add_edge(1, 2).unwrap();
205 g.add_edge(2, 3).unwrap();
206 assert!(!is_cograph(&g).unwrap());
207 }
208
209 #[test]
210 fn c4_is_cograph() {
211 let mut g = Graph::with_vertices(4);
214 g.add_edge(0, 1).unwrap();
215 g.add_edge(1, 2).unwrap();
216 g.add_edge(2, 3).unwrap();
217 g.add_edge(3, 0).unwrap();
218 assert!(is_cograph(&g).unwrap());
219 }
220
221 #[test]
222 fn c5_not_cograph() {
223 let mut g = Graph::with_vertices(5);
224 g.add_edge(0, 1).unwrap();
225 g.add_edge(1, 2).unwrap();
226 g.add_edge(2, 3).unwrap();
227 g.add_edge(3, 4).unwrap();
228 g.add_edge(4, 0).unwrap();
229 assert!(!is_cograph(&g).unwrap());
230 }
231
232 #[test]
233 fn star_is_cograph() {
234 let mut g = Graph::with_vertices(5);
236 for i in 1..5u32 {
237 g.add_edge(0, i).unwrap();
238 }
239 assert!(is_cograph(&g).unwrap());
240 }
241
242 #[test]
243 fn path_3_is_cograph() {
244 let mut g = Graph::with_vertices(3);
246 g.add_edge(0, 1).unwrap();
247 g.add_edge(1, 2).unwrap();
248 assert!(is_cograph(&g).unwrap());
249 }
250
251 #[test]
252 fn disjoint_k3_k2_is_cograph() {
253 let mut g = Graph::with_vertices(5);
255 g.add_edge(0, 1).unwrap();
256 g.add_edge(1, 2).unwrap();
257 g.add_edge(2, 0).unwrap();
258 g.add_edge(3, 4).unwrap();
259 assert!(is_cograph(&g).unwrap());
260 }
261
262 #[test]
263 fn complement_of_p4_not_cograph() {
264 let mut g = Graph::with_vertices(4);
266 g.add_edge(0, 2).unwrap();
269 g.add_edge(0, 3).unwrap();
270 g.add_edge(1, 3).unwrap();
271 assert!(!is_cograph(&g).unwrap());
272 }
273
274 #[test]
275 fn k2_union_k2_is_cograph() {
276 let mut g = Graph::with_vertices(4);
279 g.add_edge(0, 1).unwrap();
280 g.add_edge(2, 3).unwrap();
281 assert!(is_cograph(&g).unwrap());
282 }
283
284 #[test]
285 fn petersen_not_cograph() {
286 let mut g = Graph::with_vertices(10);
288 g.add_edge(0, 1).unwrap();
289 g.add_edge(1, 2).unwrap();
290 g.add_edge(2, 3).unwrap();
291 g.add_edge(3, 4).unwrap();
292 g.add_edge(4, 0).unwrap();
293 g.add_edge(5, 7).unwrap();
294 g.add_edge(7, 9).unwrap();
295 g.add_edge(9, 6).unwrap();
296 g.add_edge(6, 8).unwrap();
297 g.add_edge(8, 5).unwrap();
298 g.add_edge(0, 5).unwrap();
299 g.add_edge(1, 6).unwrap();
300 g.add_edge(2, 7).unwrap();
301 g.add_edge(3, 8).unwrap();
302 g.add_edge(4, 9).unwrap();
303 assert!(!is_cograph(&g).unwrap());
304 }
305
306 #[test]
307 fn directed_returns_false() {
308 let mut g = Graph::new(3, true).unwrap();
309 g.add_edge(0, 1).unwrap();
310 g.add_edge(1, 2).unwrap();
311 assert!(!is_cograph(&g).unwrap());
312 }
313
314 #[test]
315 fn join_of_two_edgeless_is_cograph() {
316 let g = Graph::with_vertices(5);
319 assert!(is_cograph(&g).unwrap());
320 }
321
322 #[test]
323 fn k23_is_cograph() {
324 let mut g = Graph::with_vertices(5);
326 g.add_edge(0, 2).unwrap();
327 g.add_edge(0, 3).unwrap();
328 g.add_edge(0, 4).unwrap();
329 g.add_edge(1, 2).unwrap();
330 g.add_edge(1, 3).unwrap();
331 g.add_edge(1, 4).unwrap();
332 assert!(is_cograph(&g).unwrap());
333 }
334
335 #[test]
336 fn threshold_graph_is_cograph() {
337 let mut g = Graph::with_vertices(5);
345 g.add_edge(2, 0).unwrap();
346 g.add_edge(2, 1).unwrap();
347 g.add_edge(4, 0).unwrap();
348 g.add_edge(4, 1).unwrap();
349 g.add_edge(4, 2).unwrap();
350 g.add_edge(4, 3).unwrap();
351 assert!(is_cograph(&g).unwrap());
352 }
353}