Skip to main content

rust_igraph/algorithms/properties/
is_co_bipartite.rs

1//! Co-bipartite graph predicate (ALGO-PR-110).
2//!
3//! A graph is co-bipartite if its complement is bipartite. Equivalently,
4//! the vertex set can be partitioned into two cliques (each part is a
5//! complete subgraph).
6//!
7//! For directed graphs, the function returns `false`.
8
9use crate::core::{Graph, IgraphResult};
10
11/// Check whether a graph is co-bipartite.
12///
13/// A co-bipartite graph's vertex set can be partitioned into two
14/// cliques. This is equivalent to the complement being bipartite.
15///
16/// Returns `false` for directed graphs.
17///
18/// # Examples
19///
20/// ```
21/// use rust_igraph::{Graph, is_co_bipartite};
22///
23/// // `K_3`: single clique → co-bipartite (other part empty)
24/// let mut g = Graph::with_vertices(3);
25/// g.add_edge(0, 1).unwrap();
26/// g.add_edge(1, 2).unwrap();
27/// g.add_edge(2, 0).unwrap();
28/// assert!(is_co_bipartite(&g).unwrap());
29///
30/// // `C_5` is NOT co-bipartite (complement `C_5` is not bipartite)
31/// let mut g = Graph::with_vertices(5);
32/// g.add_edge(0, 1).unwrap();
33/// g.add_edge(1, 2).unwrap();
34/// g.add_edge(2, 3).unwrap();
35/// g.add_edge(3, 4).unwrap();
36/// g.add_edge(4, 0).unwrap();
37/// assert!(!is_co_bipartite(&g).unwrap());
38/// ```
39pub fn is_co_bipartite(graph: &Graph) -> IgraphResult<bool> {
40    if graph.is_directed() {
41        return Ok(false);
42    }
43
44    let n = graph.vcount();
45    if n <= 2 {
46        return Ok(true);
47    }
48
49    let n_usize = n as usize;
50    let mut adj = vec![vec![false; n_usize]; n_usize];
51    for v in 0..n {
52        let nbrs = graph.neighbors(v)?;
53        for &w in &nbrs {
54            adj[v as usize][w as usize] = true;
55        }
56    }
57
58    // 2-color the complement graph. If the complement is bipartite,
59    // the original is co-bipartite. Use BFS on complement edges.
60    let mut color: Vec<i8> = vec![-1; n_usize];
61
62    for start in 0..n_usize {
63        if color[start] != -1 {
64            continue;
65        }
66        color[start] = 0;
67        let mut queue = std::collections::VecDeque::new();
68        queue.push_back(start);
69
70        while let Some(u) = queue.pop_front() {
71            let u_color = color[u];
72            let next_color = 1 - u_color;
73            for (v, c) in color.iter_mut().enumerate() {
74                if v == u || adj[u][v] {
75                    continue;
76                }
77                if *c == -1 {
78                    *c = next_color;
79                    queue.push_back(v);
80                } else if *c == u_color {
81                    return Ok(false);
82                }
83            }
84        }
85    }
86
87    Ok(true)
88}
89
90#[cfg(test)]
91mod tests {
92    use super::*;
93
94    #[test]
95    fn empty_graph() {
96        let g = Graph::with_vertices(0);
97        assert!(is_co_bipartite(&g).unwrap());
98    }
99
100    #[test]
101    fn single_vertex() {
102        let g = Graph::with_vertices(1);
103        assert!(is_co_bipartite(&g).unwrap());
104    }
105
106    #[test]
107    fn two_vertices_no_edge() {
108        // Complement is `K_2` which is bipartite
109        let g = Graph::with_vertices(2);
110        assert!(is_co_bipartite(&g).unwrap());
111    }
112
113    #[test]
114    fn two_vertices_with_edge() {
115        let mut g = Graph::with_vertices(2);
116        g.add_edge(0, 1).unwrap();
117        assert!(is_co_bipartite(&g).unwrap());
118    }
119
120    #[test]
121    fn triangle() {
122        // `K_3`: single clique, other part empty
123        let mut g = Graph::with_vertices(3);
124        g.add_edge(0, 1).unwrap();
125        g.add_edge(1, 2).unwrap();
126        g.add_edge(2, 0).unwrap();
127        assert!(is_co_bipartite(&g).unwrap());
128    }
129
130    #[test]
131    fn two_cliques() {
132        // `K_2` union `K_3`: partition {0,1} and {2,3,4}
133        let mut g = Graph::with_vertices(5);
134        g.add_edge(0, 1).unwrap();
135        g.add_edge(2, 3).unwrap();
136        g.add_edge(3, 4).unwrap();
137        g.add_edge(2, 4).unwrap();
138        assert!(is_co_bipartite(&g).unwrap());
139    }
140
141    #[test]
142    fn complete_graph() {
143        // `K_5`: single clique → co-bipartite
144        let mut g = Graph::with_vertices(5);
145        for i in 0..5u32 {
146            for j in (i + 1)..5 {
147                g.add_edge(i, j).unwrap();
148            }
149        }
150        assert!(is_co_bipartite(&g).unwrap());
151    }
152
153    #[test]
154    fn c4_co_bipartite() {
155        // `C_4` complement: {0,1,2,3} with edges 0-2, 1-3 → two disjoint
156        // edges → bipartite. So `C_4` IS co-bipartite.
157        let mut g = Graph::with_vertices(4);
158        g.add_edge(0, 1).unwrap();
159        g.add_edge(1, 2).unwrap();
160        g.add_edge(2, 3).unwrap();
161        g.add_edge(3, 0).unwrap();
162        assert!(is_co_bipartite(&g).unwrap());
163    }
164
165    #[test]
166    fn c5_not_co_bipartite() {
167        // `C_5` complement is also `C_5` (self-complementary), which is
168        // odd cycle → not bipartite → not co-bipartite
169        let mut g = Graph::with_vertices(5);
170        g.add_edge(0, 1).unwrap();
171        g.add_edge(1, 2).unwrap();
172        g.add_edge(2, 3).unwrap();
173        g.add_edge(3, 4).unwrap();
174        g.add_edge(4, 0).unwrap();
175        assert!(!is_co_bipartite(&g).unwrap());
176    }
177
178    #[test]
179    fn path_p3_co_bipartite() {
180        // `P_3`: 0-1-2. Complement has edge 0-2 only → bipartite.
181        let mut g = Graph::with_vertices(3);
182        g.add_edge(0, 1).unwrap();
183        g.add_edge(1, 2).unwrap();
184        assert!(is_co_bipartite(&g).unwrap());
185    }
186
187    #[test]
188    fn independent_set_3() {
189        // 3 isolated vertices. Complement is `K_3` → bipartite? No,
190        // `K_3` is a triangle → not bipartite (odd cycle).
191        // So 3 isolated vertices is NOT co-bipartite.
192        let g = Graph::with_vertices(3);
193        assert!(!is_co_bipartite(&g).unwrap());
194    }
195
196    #[test]
197    fn directed_returns_false() {
198        let mut g = Graph::new(3, true).unwrap();
199        g.add_edge(0, 1).unwrap();
200        g.add_edge(1, 2).unwrap();
201        g.add_edge(2, 0).unwrap();
202        assert!(!is_co_bipartite(&g).unwrap());
203    }
204
205    #[test]
206    fn star_not_co_bipartite() {
207        // `S_4`: center 0, leaves 1,2,3,4. Complement: 0 isolated, 1-2-3-4
208        // complete (`K_4`). Is `K_4` bipartite? No (triangle) → not co-bipartite.
209        let mut g = Graph::with_vertices(5);
210        g.add_edge(0, 1).unwrap();
211        g.add_edge(0, 2).unwrap();
212        g.add_edge(0, 3).unwrap();
213        g.add_edge(0, 4).unwrap();
214        assert!(!is_co_bipartite(&g).unwrap());
215    }
216
217    #[test]
218    fn complete_bipartite_k22() {
219        // `K_{2,2}` = `C_4` → co-bipartite (see c4 test)
220        let mut g = Graph::with_vertices(4);
221        g.add_edge(0, 2).unwrap();
222        g.add_edge(0, 3).unwrap();
223        g.add_edge(1, 2).unwrap();
224        g.add_edge(1, 3).unwrap();
225        assert!(is_co_bipartite(&g).unwrap());
226    }
227}