rust_igraph/algorithms/properties/
girth.rs1use std::collections::VecDeque;
22
23use crate::core::{Graph, IgraphResult, VertexId};
24
25pub fn girth(graph: &Graph) -> IgraphResult<Option<u32>> {
52 let n = graph.vcount();
53 if n < 3 {
54 return Ok(None);
55 }
56
57 let n_us = n as usize;
61 let mut adj: Vec<Vec<VertexId>> = Vec::with_capacity(n_us);
62 for v in 0..n {
63 let raw = graph.neighbors(v)?;
64 let mut simple: Vec<VertexId> = raw.into_iter().filter(|&u| u != v).collect();
65 simple.sort_unstable();
66 simple.dedup();
67 adj.push(simple);
68 }
69
70 let mut mincirc: u32 = u32::MAX;
73 let mut stoplevel: u32 = (n + 1).max(2);
74 let mut triangle = false;
75
76 let mut level: Vec<u32> = vec![0; n_us];
77 let mut q: VecDeque<VertexId> = VecDeque::new();
78
79 let mut node = 0u32;
80 while !triangle && node < n {
81 if node == 1 && mincirc == u32::MAX {
82 let cc = crate::algorithms::connectivity::connected_components(graph)?;
85 if cc.count == 1 {
86 break;
87 }
88 }
89
90 q.clear();
92 level.fill(0);
93 q.push_back(node);
94 level[node as usize] = 1;
95
96 while let Some(actnode) = q.pop_front() {
97 let actlevel = level[actnode as usize];
98 if actlevel >= stoplevel {
99 break;
100 }
101 let neilist = &adj[actnode as usize];
102 for &nei in neilist {
103 let neilevel = level[nei as usize];
104 if neilevel != 0 {
105 if neilevel == actlevel - 1 {
106 continue;
108 }
109 stoplevel = neilevel;
111 let candidate = actlevel + neilevel - 1;
112 if candidate < mincirc {
113 mincirc = candidate;
114 if neilevel == 2 {
115 triangle = true;
117 }
118 }
119 if neilevel == actlevel {
120 break;
121 }
122 } else {
123 q.push_back(nei);
124 level[nei as usize] = actlevel + 1;
125 }
126 }
127 }
128
129 node += 1;
130 }
131
132 if mincirc == u32::MAX {
133 Ok(None)
134 } else {
135 Ok(Some(mincirc))
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn empty_graph_has_no_girth() {
145 let g = Graph::with_vertices(0);
146 assert_eq!(girth(&g).unwrap(), None);
147 }
148
149 #[test]
150 fn singleton_has_no_girth() {
151 let g = Graph::with_vertices(1);
152 assert_eq!(girth(&g).unwrap(), None);
153 }
154
155 #[test]
156 fn isolated_vertices_have_no_girth() {
157 let g = Graph::with_vertices(5);
158 assert_eq!(girth(&g).unwrap(), None);
159 }
160
161 #[test]
162 fn tree_has_no_girth() {
163 let mut g = Graph::with_vertices(5);
165 for i in 0..4 {
166 g.add_edge(i, i + 1).unwrap();
167 }
168 assert_eq!(girth(&g).unwrap(), None);
169 }
170
171 #[test]
172 fn triangle_girth_is_3() {
173 let mut g = Graph::with_vertices(3);
174 g.add_edge(0, 1).unwrap();
175 g.add_edge(1, 2).unwrap();
176 g.add_edge(2, 0).unwrap();
177 assert_eq!(girth(&g).unwrap(), Some(3));
178 }
179
180 #[test]
181 fn square_4_cycle_has_girth_4() {
182 let mut g = Graph::with_vertices(4);
183 for i in 0..4u32 {
184 g.add_edge(i, (i + 1) % 4).unwrap();
185 }
186 assert_eq!(girth(&g).unwrap(), Some(4));
187 }
188
189 #[test]
190 fn pentagon_has_girth_5() {
191 let mut g = Graph::with_vertices(5);
192 for i in 0..5u32 {
193 g.add_edge(i, (i + 1) % 5).unwrap();
194 }
195 assert_eq!(girth(&g).unwrap(), Some(5));
196 }
197
198 #[test]
199 fn k4_has_girth_3() {
200 let mut g = Graph::with_vertices(4);
201 for u in 0..4u32 {
202 for v in (u + 1)..4 {
203 g.add_edge(u, v).unwrap();
204 }
205 }
206 assert_eq!(girth(&g).unwrap(), Some(3));
207 }
208
209 #[test]
210 fn self_loop_does_not_count_as_cycle() {
211 let mut g = Graph::with_vertices(2);
213 g.add_edge(0, 0).unwrap();
214 g.add_edge(0, 1).unwrap();
215 assert_eq!(girth(&g).unwrap(), None);
216 }
217
218 #[test]
219 fn parallel_edges_do_not_count_as_cycle() {
220 let mut g = Graph::with_vertices(2);
223 g.add_edge(0, 1).unwrap();
224 g.add_edge(0, 1).unwrap();
225 assert_eq!(girth(&g).unwrap(), None);
226 }
227
228 #[test]
229 fn two_components_picks_smaller_girth() {
230 let mut g = Graph::with_vertices(7);
234 for i in 0..4u32 {
235 g.add_edge(i, (i + 1) % 4).unwrap();
236 }
237 g.add_edge(4, 5).unwrap();
238 g.add_edge(5, 6).unwrap();
239 g.add_edge(6, 4).unwrap();
240 assert_eq!(girth(&g).unwrap(), Some(3));
241 }
242
243 #[test]
244 fn pendant_does_not_change_cycle_girth() {
245 let mut g = Graph::with_vertices(4);
247 g.add_edge(0, 1).unwrap();
248 g.add_edge(1, 2).unwrap();
249 g.add_edge(2, 0).unwrap();
250 g.add_edge(2, 3).unwrap();
251 assert_eq!(girth(&g).unwrap(), Some(3));
252 }
253}