Skip to main content

rust_igraph/algorithms/properties/
is_co_chordal.rs

1//! Co-chordal graph predicate (ALGO-PR-124).
2//!
3//! A graph is co-chordal if its complement is chordal. Every
4//! co-chordal graph is weakly chordal. Every complete graph is
5//! co-chordal (complement is edgeless, hence chordal). Every
6//! edgeless graph is co-chordal (complement is complete, hence
7//! chordal).
8//!
9//! Directed graphs are treated as undirected.
10
11use crate::algorithms::chordality::is_chordal;
12use crate::core::{Graph, IgraphResult};
13
14/// Check whether a graph is co-chordal (complement is chordal).
15///
16/// Builds the complement graph and runs the chordality test on it.
17///
18/// Directed graphs are treated as undirected.
19///
20/// # Examples
21///
22/// ```
23/// use rust_igraph::{Graph, is_co_chordal};
24///
25/// // `K_5`: complement is edgeless (chordal) → co-chordal
26/// let mut g = Graph::with_vertices(5);
27/// for i in 0..5u32 {
28///     for j in (i+1)..5 {
29///         g.add_edge(i, j).unwrap();
30///     }
31/// }
32/// assert!(is_co_chordal(&g).unwrap());
33///
34/// // `C_5`: complement is also `C_5` → not chordal → not co-chordal
35/// let mut g = Graph::with_vertices(5);
36/// g.add_edge(0, 1).unwrap();
37/// g.add_edge(1, 2).unwrap();
38/// g.add_edge(2, 3).unwrap();
39/// g.add_edge(3, 4).unwrap();
40/// g.add_edge(4, 0).unwrap();
41/// assert!(!is_co_chordal(&g).unwrap());
42/// ```
43pub fn is_co_chordal(graph: &Graph) -> IgraphResult<bool> {
44    let n = graph.vcount();
45    if n <= 3 {
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            adj[w as usize][v as usize] = true;
56        }
57    }
58
59    let mut comp = Graph::with_vertices(n);
60    for v in 0..n {
61        for w in (v + 1)..n {
62            if !adj[v as usize][w as usize] {
63                comp.add_edge(v, w)?;
64            }
65        }
66    }
67
68    Ok(is_chordal(&comp, None)?.chordal)
69}
70
71#[cfg(test)]
72mod tests {
73    use super::*;
74
75    #[test]
76    fn empty_graph() {
77        let g = Graph::with_vertices(0);
78        assert!(is_co_chordal(&g).unwrap());
79    }
80
81    #[test]
82    fn single_vertex() {
83        let g = Graph::with_vertices(1);
84        assert!(is_co_chordal(&g).unwrap());
85    }
86
87    #[test]
88    fn single_edge() {
89        let mut g = Graph::with_vertices(2);
90        g.add_edge(0, 1).unwrap();
91        assert!(is_co_chordal(&g).unwrap());
92    }
93
94    #[test]
95    fn triangle() {
96        let mut g = Graph::with_vertices(3);
97        g.add_edge(0, 1).unwrap();
98        g.add_edge(1, 2).unwrap();
99        g.add_edge(2, 0).unwrap();
100        assert!(is_co_chordal(&g).unwrap());
101    }
102
103    #[test]
104    fn k4() {
105        let mut g = Graph::with_vertices(4);
106        for i in 0..4u32 {
107            for j in (i + 1)..4 {
108                g.add_edge(i, j).unwrap();
109            }
110        }
111        assert!(is_co_chordal(&g).unwrap());
112    }
113
114    #[test]
115    fn k5() {
116        let mut g = Graph::with_vertices(5);
117        for i in 0..5u32 {
118            for j in (i + 1)..5 {
119                g.add_edge(i, j).unwrap();
120            }
121        }
122        assert!(is_co_chordal(&g).unwrap());
123    }
124
125    #[test]
126    fn edgeless_5() {
127        // Complement is K_5 (chordal)
128        let g = Graph::with_vertices(5);
129        assert!(is_co_chordal(&g).unwrap());
130    }
131
132    #[test]
133    fn c4() {
134        // C_4: complement is 2 disjoint edges (K_2 + K_2), which is chordal
135        let mut g = Graph::with_vertices(4);
136        g.add_edge(0, 1).unwrap();
137        g.add_edge(1, 2).unwrap();
138        g.add_edge(2, 3).unwrap();
139        g.add_edge(3, 0).unwrap();
140        assert!(is_co_chordal(&g).unwrap());
141    }
142
143    #[test]
144    fn c5_not_co_chordal() {
145        // C_5 is self-complementary; complement is also C_5 (not chordal)
146        let mut g = Graph::with_vertices(5);
147        g.add_edge(0, 1).unwrap();
148        g.add_edge(1, 2).unwrap();
149        g.add_edge(2, 3).unwrap();
150        g.add_edge(3, 4).unwrap();
151        g.add_edge(4, 0).unwrap();
152        assert!(!is_co_chordal(&g).unwrap());
153    }
154
155    #[test]
156    fn c6_not_co_chordal() {
157        // C_6 complement has induced C_4: 0-3-5-2-0 (no chords 0-5 or 3-2).
158        let mut g = Graph::with_vertices(6);
159        for i in 0..6u32 {
160            g.add_edge(i, (i + 1) % 6).unwrap();
161        }
162        assert!(!is_co_chordal(&g).unwrap());
163    }
164
165    #[test]
166    fn path_p5() {
167        // P_5: complement has edges 0-2,0-3,0-4,1-3,1-4,2-4.
168        // 6 edges on 5 vertices. Degree: 0→3, 1→2, 2→2, 3→2, 4→3.
169        // Is it chordal? Need to check for induced C_k (k≥4).
170        // Try 0-2-4-1-3-0: edges 0-2✓,2-4✓,4-1✓,1-3✓,3-0✓. All present!
171        // Chord: 0-4 ✓. So not an induced C_5.
172        // Any induced C_4? 4 vertices. 0-2-4-1: 0-2✓,2-4✓,4-1✓,1-0? No edge.
173        // 0-3-1-4: 0-3✓,3-1✓,1-4✓,4-0✓. Chord 0-1? No. Chord 3-4? No.
174        // So 0-3-1-4-0 is an induced C_4! Not chordal.
175        // P_5 is NOT co-chordal.
176        let mut g = Graph::with_vertices(5);
177        g.add_edge(0, 1).unwrap();
178        g.add_edge(1, 2).unwrap();
179        g.add_edge(2, 3).unwrap();
180        g.add_edge(3, 4).unwrap();
181        assert!(!is_co_chordal(&g).unwrap());
182    }
183
184    #[test]
185    fn star_s4() {
186        // Star S_4: center 0, leaves 1,2,3,4.
187        // Complement: 0 isolated, leaves form K_4 (chordal).
188        // So star IS co-chordal.
189        let mut g = Graph::with_vertices(5);
190        g.add_edge(0, 1).unwrap();
191        g.add_edge(0, 2).unwrap();
192        g.add_edge(0, 3).unwrap();
193        g.add_edge(0, 4).unwrap();
194        assert!(is_co_chordal(&g).unwrap());
195    }
196
197    #[test]
198    fn directed_treated_as_undirected() {
199        let mut g = Graph::new(4, true).unwrap();
200        g.add_edge(0, 1).unwrap();
201        g.add_edge(1, 2).unwrap();
202        g.add_edge(2, 3).unwrap();
203        g.add_edge(3, 0).unwrap();
204        assert!(is_co_chordal(&g).unwrap());
205    }
206}