Skip to main content

rust_igraph/algorithms/properties/
is_k_degenerate.rs

1//! k-degenerate graph predicate (ALGO-PR-116).
2//!
3//! A graph is k-degenerate if every subgraph has a vertex of degree
4//! at most k. Equivalently, the degeneracy (maximum coreness) is at
5//! most k. 0-degenerate = edgeless, 1-degenerate = forest,
6//! 2-degenerate = every subgraph has a vertex of degree ≤ 2.
7//!
8//! Directed graphs are treated as undirected.
9
10use crate::algorithms::properties::coreness::coreness;
11use crate::core::{Graph, IgraphResult};
12
13/// Check whether a graph is k-degenerate.
14///
15/// A graph is k-degenerate if every non-empty subgraph has a vertex
16/// of degree at most k. Uses the coreness decomposition and checks
17/// that the maximum core number does not exceed k.
18///
19/// Directed graphs are treated as undirected.
20///
21/// # Examples
22///
23/// ```
24/// use rust_igraph::{Graph, is_k_degenerate};
25///
26/// // Tree is 1-degenerate
27/// let mut g = Graph::with_vertices(4);
28/// g.add_edge(0, 1).unwrap();
29/// g.add_edge(1, 2).unwrap();
30/// g.add_edge(1, 3).unwrap();
31/// assert!(is_k_degenerate(&g, 1).unwrap());
32/// assert!(!is_k_degenerate(&g, 0).unwrap());
33///
34/// // `K_4` is 3-degenerate but NOT 2-degenerate
35/// let mut g = Graph::with_vertices(4);
36/// for i in 0..4u32 {
37///     for j in (i+1)..4 {
38///         g.add_edge(i, j).unwrap();
39///     }
40/// }
41/// assert!(is_k_degenerate(&g, 3).unwrap());
42/// assert!(!is_k_degenerate(&g, 2).unwrap());
43/// ```
44pub fn is_k_degenerate(graph: &Graph, k: u32) -> IgraphResult<bool> {
45    let n = graph.vcount();
46    if n == 0 {
47        return Ok(true);
48    }
49
50    let cores = coreness(graph)?;
51    let max_core = cores.iter().copied().max().unwrap_or(0);
52    Ok(max_core <= k)
53}
54
55/// Return the degeneracy of a graph.
56///
57/// The degeneracy is the smallest k such that the graph is
58/// k-degenerate, i.e., the maximum coreness value.
59///
60/// Directed graphs are treated as undirected.
61///
62/// # Examples
63///
64/// ```
65/// use rust_igraph::{Graph, degeneracy};
66///
67/// let mut g = Graph::with_vertices(4);
68/// g.add_edge(0, 1).unwrap();
69/// g.add_edge(1, 2).unwrap();
70/// g.add_edge(1, 3).unwrap();
71/// assert_eq!(degeneracy(&g).unwrap(), 1);
72/// ```
73pub fn degeneracy(graph: &Graph) -> IgraphResult<u32> {
74    let n = graph.vcount();
75    if n == 0 {
76        return Ok(0);
77    }
78
79    let cores = coreness(graph)?;
80    Ok(cores.iter().copied().max().unwrap_or(0))
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn empty_graph() {
89        let g = Graph::with_vertices(0);
90        assert!(is_k_degenerate(&g, 0).unwrap());
91        assert_eq!(degeneracy(&g).unwrap(), 0);
92    }
93
94    #[test]
95    fn single_vertex() {
96        let g = Graph::with_vertices(1);
97        assert!(is_k_degenerate(&g, 0).unwrap());
98        assert_eq!(degeneracy(&g).unwrap(), 0);
99    }
100
101    #[test]
102    fn single_edge() {
103        let mut g = Graph::with_vertices(2);
104        g.add_edge(0, 1).unwrap();
105        assert!(!is_k_degenerate(&g, 0).unwrap());
106        assert!(is_k_degenerate(&g, 1).unwrap());
107        assert_eq!(degeneracy(&g).unwrap(), 1);
108    }
109
110    #[test]
111    fn triangle() {
112        let mut g = Graph::with_vertices(3);
113        g.add_edge(0, 1).unwrap();
114        g.add_edge(1, 2).unwrap();
115        g.add_edge(2, 0).unwrap();
116        assert!(!is_k_degenerate(&g, 1).unwrap());
117        assert!(is_k_degenerate(&g, 2).unwrap());
118        assert_eq!(degeneracy(&g).unwrap(), 2);
119    }
120
121    #[test]
122    fn tree() {
123        let mut g = Graph::with_vertices(5);
124        g.add_edge(0, 1).unwrap();
125        g.add_edge(1, 2).unwrap();
126        g.add_edge(1, 3).unwrap();
127        g.add_edge(3, 4).unwrap();
128        assert!(is_k_degenerate(&g, 1).unwrap());
129        assert!(!is_k_degenerate(&g, 0).unwrap());
130        assert_eq!(degeneracy(&g).unwrap(), 1);
131    }
132
133    #[test]
134    fn complete_k4() {
135        let mut g = Graph::with_vertices(4);
136        for i in 0..4u32 {
137            for j in (i + 1)..4 {
138                g.add_edge(i, j).unwrap();
139            }
140        }
141        assert!(!is_k_degenerate(&g, 2).unwrap());
142        assert!(is_k_degenerate(&g, 3).unwrap());
143        assert_eq!(degeneracy(&g).unwrap(), 3);
144    }
145
146    #[test]
147    fn complete_k5() {
148        let mut g = Graph::with_vertices(5);
149        for i in 0..5u32 {
150            for j in (i + 1)..5 {
151                g.add_edge(i, j).unwrap();
152            }
153        }
154        assert_eq!(degeneracy(&g).unwrap(), 4);
155    }
156
157    #[test]
158    fn edgeless() {
159        let g = Graph::with_vertices(5);
160        assert!(is_k_degenerate(&g, 0).unwrap());
161        assert_eq!(degeneracy(&g).unwrap(), 0);
162    }
163
164    #[test]
165    fn star() {
166        let mut g = Graph::with_vertices(5);
167        g.add_edge(0, 1).unwrap();
168        g.add_edge(0, 2).unwrap();
169        g.add_edge(0, 3).unwrap();
170        g.add_edge(0, 4).unwrap();
171        assert!(is_k_degenerate(&g, 1).unwrap());
172        assert_eq!(degeneracy(&g).unwrap(), 1);
173    }
174
175    #[test]
176    fn c4() {
177        // C_4: coreness 2
178        let mut g = Graph::with_vertices(4);
179        g.add_edge(0, 1).unwrap();
180        g.add_edge(1, 2).unwrap();
181        g.add_edge(2, 3).unwrap();
182        g.add_edge(3, 0).unwrap();
183        assert!(is_k_degenerate(&g, 2).unwrap());
184        assert!(!is_k_degenerate(&g, 1).unwrap());
185        assert_eq!(degeneracy(&g).unwrap(), 2);
186    }
187
188    #[test]
189    fn directed_treated_as_undirected() {
190        let mut g = Graph::new(3, true).unwrap();
191        g.add_edge(0, 1).unwrap();
192        g.add_edge(1, 2).unwrap();
193        g.add_edge(2, 0).unwrap();
194        assert!(is_k_degenerate(&g, 2).unwrap());
195        assert_eq!(degeneracy(&g).unwrap(), 2);
196    }
197
198    #[test]
199    fn large_k_always_true() {
200        let mut g = Graph::with_vertices(3);
201        g.add_edge(0, 1).unwrap();
202        assert!(is_k_degenerate(&g, 100).unwrap());
203    }
204
205    #[test]
206    fn path() {
207        let mut g = Graph::with_vertices(5);
208        g.add_edge(0, 1).unwrap();
209        g.add_edge(1, 2).unwrap();
210        g.add_edge(2, 3).unwrap();
211        g.add_edge(3, 4).unwrap();
212        assert!(is_k_degenerate(&g, 1).unwrap());
213        assert_eq!(degeneracy(&g).unwrap(), 1);
214    }
215}