rust_igraph/algorithms/properties/
is_chordal_bipartite.rs1use crate::algorithms::connectivity::{ConnectednessMode, is_connected};
11use crate::algorithms::properties::is_bipartite::is_bipartite;
12use crate::core::{Graph, IgraphResult};
13
14pub fn is_chordal_bipartite(graph: &Graph) -> IgraphResult<bool> {
49 if graph.is_directed() {
50 return Ok(false);
51 }
52
53 let n = graph.vcount();
54 if n <= 4 {
55 return is_bipartite(graph).map(|r| r.is_bipartite);
58 }
59
60 let bip = is_bipartite(graph)?;
61 if !bip.is_bipartite {
62 return Ok(false);
63 }
64
65 if !is_connected(graph, ConnectednessMode::Weak)? {
67 return check_components_chordal_bipartite(graph);
68 }
69
70 check_single_component(graph, n)
71}
72
73fn check_components_chordal_bipartite(graph: &Graph) -> IgraphResult<bool> {
75 let n = graph.vcount();
76 let n_usize = n as usize;
77 let mut visited = vec![false; n_usize];
78 let mut nbrs_cache: Vec<Vec<u32>> = Vec::with_capacity(n_usize);
79 for v in 0..n {
80 nbrs_cache.push(graph.neighbors(v)?);
81 }
82
83 for start in 0..n_usize {
84 if visited[start] {
85 continue;
86 }
87 let mut component = Vec::new();
89 let mut queue = std::collections::VecDeque::new();
90 visited[start] = true;
91 queue.push_back(start);
92 while let Some(u) = queue.pop_front() {
93 component.push(u);
94 for &w in &nbrs_cache[u] {
95 let wi = w as usize;
96 if !visited[wi] {
97 visited[wi] = true;
98 queue.push_back(wi);
99 }
100 }
101 }
102
103 if component.len() >= 6 && !check_component_chordal_bipartite(&nbrs_cache, &component) {
104 return Ok(false);
105 }
106 }
107
108 Ok(true)
109}
110
111fn check_component_chordal_bipartite(nbrs_cache: &[Vec<u32>], component: &[usize]) -> bool {
113 let max_id = component.iter().copied().max().unwrap_or(0);
115 let mut global_to_local = vec![usize::MAX; max_id + 1];
116 for (i, &v) in component.iter().enumerate() {
117 global_to_local[v] = i;
118 }
119
120 let cn = component.len();
121 let mut adj = vec![vec![false; cn]; cn];
122 for &v in component {
123 let li = global_to_local[v];
124 for &w in &nbrs_cache[v] {
125 let wi = w as usize;
126 if wi <= max_id && global_to_local[wi] != usize::MAX {
127 adj[li][global_to_local[wi]] = true;
128 }
129 }
130 }
131
132 is_chordal_bipartite_from_adj(&adj, cn)
133}
134
135fn check_single_component(graph: &Graph, n: u32) -> IgraphResult<bool> {
137 let n_usize = n as usize;
138 let mut adj = vec![vec![false; n_usize]; n_usize];
139 for v in 0..n {
140 let nbrs = graph.neighbors(v)?;
141 for &w in &nbrs {
142 adj[v as usize][w as usize] = true;
143 }
144 }
145 Ok(is_chordal_bipartite_from_adj(&adj, n_usize))
146}
147
148fn is_chordal_bipartite_from_adj(adj: &[Vec<bool>], n: usize) -> bool {
158 let mut removed = vec![false; n];
169 let mut remaining = n;
170
171 while remaining > 0 {
172 let mut found = false;
173 for v in 0..n {
174 if removed[v] {
175 continue;
176 }
177
178 if is_bisimplicial(adj, v, &removed, n) {
179 removed[v] = true;
180 remaining -= 1;
181 found = true;
182 break;
183 }
184 }
185
186 if !found {
187 return false;
188 }
189 }
190
191 true
192}
193
194fn is_bisimplicial(adj: &[Vec<bool>], v: usize, removed: &[bool], n: usize) -> bool {
199 let nbrs_v: Vec<usize> = (0..n).filter(|&u| !removed[u] && adj[v][u]).collect();
200
201 if nbrs_v.is_empty() {
202 return true;
203 }
204
205 for &u in &nbrs_v {
208 for x in 0..n {
209 if removed[x] || x == v || !adj[u][x] {
210 continue;
211 }
212 for &w in &nbrs_v {
214 if !adj[x][w] {
215 return false;
216 }
217 }
218 }
219 }
220
221 true
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn empty_graph() {
230 let g = Graph::with_vertices(0);
231 assert!(is_chordal_bipartite(&g).unwrap());
232 }
233
234 #[test]
235 fn single_vertex() {
236 let g = Graph::with_vertices(1);
237 assert!(is_chordal_bipartite(&g).unwrap());
238 }
239
240 #[test]
241 fn single_edge() {
242 let mut g = Graph::with_vertices(2);
243 g.add_edge(0, 1).unwrap();
244 assert!(is_chordal_bipartite(&g).unwrap());
245 }
246
247 #[test]
248 fn c4_chordal_bipartite() {
249 let mut g = Graph::with_vertices(4);
251 g.add_edge(0, 1).unwrap();
252 g.add_edge(1, 2).unwrap();
253 g.add_edge(2, 3).unwrap();
254 g.add_edge(3, 0).unwrap();
255 assert!(is_chordal_bipartite(&g).unwrap());
256 }
257
258 #[test]
259 fn c6_not_chordal_bipartite() {
260 let mut g = Graph::with_vertices(6);
261 g.add_edge(0, 1).unwrap();
262 g.add_edge(1, 2).unwrap();
263 g.add_edge(2, 3).unwrap();
264 g.add_edge(3, 4).unwrap();
265 g.add_edge(4, 5).unwrap();
266 g.add_edge(5, 0).unwrap();
267 assert!(!is_chordal_bipartite(&g).unwrap());
268 }
269
270 #[test]
271 fn complete_bipartite_k23() {
272 let mut g = Graph::with_vertices(5);
273 g.add_edge(0, 2).unwrap();
274 g.add_edge(0, 3).unwrap();
275 g.add_edge(0, 4).unwrap();
276 g.add_edge(1, 2).unwrap();
277 g.add_edge(1, 3).unwrap();
278 g.add_edge(1, 4).unwrap();
279 assert!(is_chordal_bipartite(&g).unwrap());
280 }
281
282 #[test]
283 fn complete_bipartite_k33() {
284 let mut g = Graph::with_vertices(6);
285 for i in 0..3u32 {
286 for j in 3..6u32 {
287 g.add_edge(i, j).unwrap();
288 }
289 }
290 assert!(is_chordal_bipartite(&g).unwrap());
291 }
292
293 #[test]
294 fn tree_chordal_bipartite() {
295 let mut g = Graph::with_vertices(5);
297 g.add_edge(0, 1).unwrap();
298 g.add_edge(1, 2).unwrap();
299 g.add_edge(1, 3).unwrap();
300 g.add_edge(3, 4).unwrap();
301 assert!(is_chordal_bipartite(&g).unwrap());
302 }
303
304 #[test]
305 fn triangle_not_bipartite() {
306 let mut g = Graph::with_vertices(3);
308 g.add_edge(0, 1).unwrap();
309 g.add_edge(1, 2).unwrap();
310 g.add_edge(2, 0).unwrap();
311 assert!(!is_chordal_bipartite(&g).unwrap());
312 }
313
314 #[test]
315 fn directed_returns_false() {
316 let mut g = Graph::new(3, true).unwrap();
317 g.add_edge(0, 1).unwrap();
318 g.add_edge(1, 2).unwrap();
319 assert!(!is_chordal_bipartite(&g).unwrap());
320 }
321
322 #[test]
323 fn path_chordal_bipartite() {
324 let mut g = Graph::with_vertices(6);
325 g.add_edge(0, 1).unwrap();
326 g.add_edge(1, 2).unwrap();
327 g.add_edge(2, 3).unwrap();
328 g.add_edge(3, 4).unwrap();
329 g.add_edge(4, 5).unwrap();
330 assert!(is_chordal_bipartite(&g).unwrap());
331 }
332
333 #[test]
334 fn star_chordal_bipartite() {
335 let mut g = Graph::with_vertices(5);
336 g.add_edge(0, 1).unwrap();
337 g.add_edge(0, 2).unwrap();
338 g.add_edge(0, 3).unwrap();
339 g.add_edge(0, 4).unwrap();
340 assert!(is_chordal_bipartite(&g).unwrap());
341 }
342
343 #[test]
344 fn c8_not_chordal_bipartite() {
345 let mut g = Graph::with_vertices(8);
346 for i in 0..8u32 {
347 g.add_edge(i, (i + 1) % 8).unwrap();
348 }
349 assert!(!is_chordal_bipartite(&g).unwrap());
350 }
351
352 #[test]
353 fn disconnected_bipartite() {
354 let mut g = Graph::with_vertices(8);
356 g.add_edge(0, 1).unwrap();
357 g.add_edge(1, 2).unwrap();
358 g.add_edge(2, 3).unwrap();
359 g.add_edge(3, 0).unwrap();
360 g.add_edge(4, 5).unwrap();
361 g.add_edge(5, 6).unwrap();
362 g.add_edge(6, 7).unwrap();
363 g.add_edge(7, 4).unwrap();
364 assert!(is_chordal_bipartite(&g).unwrap());
365 }
366}