Skip to main content

rust_igraph/algorithms/properties/
is_claw_free.rs

1//! Claw-free graph predicate (ALGO-PR-096).
2//!
3//! A graph is claw-free if it contains no induced subgraph
4//! isomorphic to `K_{1,3}` (the complete bipartite graph with parts
5//! of size 1 and 3, also called the "claw" or "star on 3 edges").
6//!
7//! Equivalently, for every vertex v, the open neighborhood N(v) does
8//! not contain three mutually non-adjacent vertices (i.e., no
9//! independent set of size 3 in N(v)).
10//!
11//! Claw-free graphs include line graphs and complements of
12//! triangle-free graphs.
13//!
14//! For directed graphs, the function returns `false`.
15
16use crate::core::{Graph, IgraphResult};
17
18/// Check whether a graph is claw-free.
19///
20/// A claw-free graph has no induced `K_{1,3}`. This is checked by
21/// verifying that for every vertex, no three of its neighbors are
22/// mutually non-adjacent.
23///
24/// Returns `false` for directed graphs.
25///
26/// # Examples
27///
28/// ```
29/// use rust_igraph::{Graph, is_claw_free};
30///
31/// // Triangle is claw-free
32/// let mut g = Graph::with_vertices(3);
33/// g.add_edge(0, 1).unwrap();
34/// g.add_edge(1, 2).unwrap();
35/// g.add_edge(2, 0).unwrap();
36/// assert!(is_claw_free(&g).unwrap());
37///
38/// // Star `K_{1,3}` IS a claw
39/// let mut g = Graph::with_vertices(4);
40/// g.add_edge(0, 1).unwrap();
41/// g.add_edge(0, 2).unwrap();
42/// g.add_edge(0, 3).unwrap();
43/// assert!(!is_claw_free(&g).unwrap());
44/// ```
45pub fn is_claw_free(graph: &Graph) -> IgraphResult<bool> {
46    if graph.is_directed() {
47        return Ok(false);
48    }
49
50    let n = graph.vcount();
51    if n <= 3 {
52        // Can't have K_{1,3} with fewer than 4 vertices
53        return Ok(true);
54    }
55
56    // Build adjacency for fast pair lookup
57    let n_usize = n as usize;
58    let mut adj = vec![vec![false; n_usize]; n_usize];
59    for v in 0..n {
60        let nbrs = graph.neighbors(v)?;
61        for &w in &nbrs {
62            adj[v as usize][w as usize] = true;
63        }
64    }
65
66    // For each vertex v, check if N(v) contains an independent set of size 3
67    for v in 0..n {
68        let nbrs = graph.neighbors(v)?;
69        let deg = nbrs.len();
70        if deg < 3 {
71            continue;
72        }
73
74        // Check all triples of neighbors
75        for i in 0..deg {
76            for j in (i + 1)..deg {
77                if adj[nbrs[i] as usize][nbrs[j] as usize] {
78                    continue;
79                }
80                // nbrs[i] and nbrs[j] are non-adjacent
81                for k in (j + 1)..deg {
82                    if !adj[nbrs[i] as usize][nbrs[k] as usize]
83                        && !adj[nbrs[j] as usize][nbrs[k] as usize]
84                    {
85                        // Found independent triple in N(v) → induced K_{1,3}
86                        return Ok(false);
87                    }
88                }
89            }
90        }
91    }
92
93    Ok(true)
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn empty_graph() {
102        let g = Graph::with_vertices(0);
103        assert!(is_claw_free(&g).unwrap());
104    }
105
106    #[test]
107    fn single_vertex() {
108        let g = Graph::with_vertices(1);
109        assert!(is_claw_free(&g).unwrap());
110    }
111
112    #[test]
113    fn single_edge() {
114        let mut g = Graph::with_vertices(2);
115        g.add_edge(0, 1).unwrap();
116        assert!(is_claw_free(&g).unwrap());
117    }
118
119    #[test]
120    fn triangle() {
121        let mut g = Graph::with_vertices(3);
122        g.add_edge(0, 1).unwrap();
123        g.add_edge(1, 2).unwrap();
124        g.add_edge(2, 0).unwrap();
125        assert!(is_claw_free(&g).unwrap());
126    }
127
128    #[test]
129    fn k4() {
130        let mut g = Graph::with_vertices(4);
131        g.add_edge(0, 1).unwrap();
132        g.add_edge(0, 2).unwrap();
133        g.add_edge(0, 3).unwrap();
134        g.add_edge(1, 2).unwrap();
135        g.add_edge(1, 3).unwrap();
136        g.add_edge(2, 3).unwrap();
137        assert!(is_claw_free(&g).unwrap());
138    }
139
140    #[test]
141    fn star_k13_is_claw() {
142        let mut g = Graph::with_vertices(4);
143        g.add_edge(0, 1).unwrap();
144        g.add_edge(0, 2).unwrap();
145        g.add_edge(0, 3).unwrap();
146        assert!(!is_claw_free(&g).unwrap());
147    }
148
149    #[test]
150    fn star_k14_not_claw_free() {
151        let mut g = Graph::with_vertices(5);
152        g.add_edge(0, 1).unwrap();
153        g.add_edge(0, 2).unwrap();
154        g.add_edge(0, 3).unwrap();
155        g.add_edge(0, 4).unwrap();
156        assert!(!is_claw_free(&g).unwrap());
157    }
158
159    #[test]
160    fn path_claw_free() {
161        // Path 0-1-2-3: max degree 2, can't have K_{1,3}
162        let mut g = Graph::with_vertices(4);
163        g.add_edge(0, 1).unwrap();
164        g.add_edge(1, 2).unwrap();
165        g.add_edge(2, 3).unwrap();
166        assert!(is_claw_free(&g).unwrap());
167    }
168
169    #[test]
170    fn cycle_c5_claw_free() {
171        let mut g = Graph::with_vertices(5);
172        g.add_edge(0, 1).unwrap();
173        g.add_edge(1, 2).unwrap();
174        g.add_edge(2, 3).unwrap();
175        g.add_edge(3, 4).unwrap();
176        g.add_edge(4, 0).unwrap();
177        assert!(is_claw_free(&g).unwrap());
178    }
179
180    #[test]
181    fn line_graph_of_k4() {
182        // Line graph of K4 has 6 vertices (one per edge of K4)
183        // Line graphs are always claw-free (Whitney's theorem)
184        // Edges of K4: 01, 02, 03, 12, 13, 23
185        // Two edges adjacent iff they share a vertex
186        let mut g = Graph::with_vertices(6);
187        // 01-02, 01-03, 01-12, 01-13
188        g.add_edge(0, 1).unwrap(); // 01-02
189        g.add_edge(0, 2).unwrap(); // 01-03
190        g.add_edge(0, 3).unwrap(); // 01-12
191        g.add_edge(0, 4).unwrap(); // 01-13
192        // 02-03, 02-12, 02-23
193        g.add_edge(1, 2).unwrap(); // 02-03
194        g.add_edge(1, 3).unwrap(); // 02-12
195        g.add_edge(1, 5).unwrap(); // 02-23
196        // 03-13, 03-23
197        g.add_edge(2, 4).unwrap(); // 03-13
198        g.add_edge(2, 5).unwrap(); // 03-23
199        // 12-13, 12-23
200        g.add_edge(3, 4).unwrap(); // 12-13
201        g.add_edge(3, 5).unwrap(); // 12-23
202        // 13-23
203        g.add_edge(4, 5).unwrap(); // 13-23
204        assert!(is_claw_free(&g).unwrap());
205    }
206
207    #[test]
208    fn directed_returns_false() {
209        let mut g = Graph::new(3, true).unwrap();
210        g.add_edge(0, 1).unwrap();
211        g.add_edge(1, 2).unwrap();
212        g.add_edge(2, 0).unwrap();
213        assert!(!is_claw_free(&g).unwrap());
214    }
215
216    #[test]
217    fn diamond_claw_free() {
218        // Diamond: K4 minus one edge
219        let mut g = Graph::with_vertices(4);
220        g.add_edge(0, 1).unwrap();
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(2, 3).unwrap();
225        // Vertex 0 has neighbors {1, 2, 3}; 1-2 adjacent, 2-3 adjacent,
226        // but 1-3 not adjacent. That's only 2 non-adjacent, need 3.
227        // No vertex has 3 mutually non-adjacent neighbors.
228        assert!(is_claw_free(&g).unwrap());
229    }
230
231    #[test]
232    fn petersen_not_claw_free() {
233        // Petersen graph is NOT claw-free (3-regular, 10 vertices)
234        let mut g = Graph::with_vertices(10);
235        // Outer cycle
236        g.add_edge(0, 1).unwrap();
237        g.add_edge(1, 2).unwrap();
238        g.add_edge(2, 3).unwrap();
239        g.add_edge(3, 4).unwrap();
240        g.add_edge(4, 0).unwrap();
241        // Inner pentagram
242        g.add_edge(5, 7).unwrap();
243        g.add_edge(7, 9).unwrap();
244        g.add_edge(9, 6).unwrap();
245        g.add_edge(6, 8).unwrap();
246        g.add_edge(8, 5).unwrap();
247        // Spokes
248        g.add_edge(0, 5).unwrap();
249        g.add_edge(1, 6).unwrap();
250        g.add_edge(2, 7).unwrap();
251        g.add_edge(3, 8).unwrap();
252        g.add_edge(4, 9).unwrap();
253        assert!(!is_claw_free(&g).unwrap());
254    }
255
256    #[test]
257    fn two_triangles_sharing_edge_claw_free() {
258        // 0-1-2-0, 1-2-3-1
259        let mut g = Graph::with_vertices(4);
260        g.add_edge(0, 1).unwrap();
261        g.add_edge(1, 2).unwrap();
262        g.add_edge(2, 0).unwrap();
263        g.add_edge(1, 3).unwrap();
264        g.add_edge(2, 3).unwrap();
265        assert!(is_claw_free(&g).unwrap());
266    }
267}