rust_igraph/algorithms/properties/
basic.rs1use crate::algorithms::paths::distances::distances;
17use crate::core::{Graph, IgraphResult};
18
19pub fn density(graph: &Graph) -> IgraphResult<Option<f64>> {
42 let n = graph.vcount();
43 if n == 0 || n == 1 {
44 return Ok(None);
45 }
46 let m = graph.ecount();
47 let directed = graph.is_directed();
48
49 let n_f = f64::from(n);
51 #[allow(clippy::cast_precision_loss)]
52 let m_f = m as f64;
53
54 let result = if directed {
58 m_f / n_f / (n_f - 1.0)
59 } else {
60 m_f / n_f * 2.0 / (n_f - 1.0)
61 };
62 Ok(Some(result))
63}
64
65pub fn mean_distance(graph: &Graph) -> IgraphResult<Option<f64>> {
84 let n = graph.vcount();
85 if n < 2 {
86 return Ok(None);
87 }
88 let mut sum: u64 = 0;
89 let mut count: u64 = 0;
90 for v in 0..n {
91 let d = distances(graph, v)?;
92 let v_us = v as usize;
93 for (target, &val) in d.iter().enumerate() {
94 if target == v_us {
95 continue;
96 }
97 if let Some(dist) = val {
98 sum += u64::from(dist);
99 count += 1;
100 }
101 }
102 }
103 if count == 0 {
104 return Ok(None);
105 }
106 #[allow(clippy::cast_precision_loss)]
107 let mean = (sum as f64) / (count as f64);
108 Ok(Some(mean))
109}
110
111pub fn mean_degree(graph: &Graph, count_loops: bool) -> IgraphResult<Option<f64>> {
134 let n = graph.vcount();
135 if n == 0 {
136 return Ok(None);
137 }
138
139 let mut ecount = graph.ecount();
140
141 if !count_loops {
142 let loop_count = crate::algorithms::properties::multiplicity::count_loops(graph)?;
143 ecount -= loop_count;
144 }
145
146 let directed = graph.is_directed();
147 let n_f = f64::from(n);
148 #[allow(clippy::cast_precision_loss)]
149 let m_f = ecount as f64;
150
151 let result = if directed { m_f / n_f } else { 2.0 * m_f / n_f };
152 Ok(Some(result))
153}
154
155#[cfg(test)]
156mod tests {
157 use super::*;
158
159 #[test]
160 fn density_empty_graph_is_none() {
161 let g = Graph::with_vertices(0);
162 assert_eq!(density(&g).unwrap(), None);
163 }
164
165 #[test]
166 fn density_singleton_is_none() {
167 let g = Graph::with_vertices(1);
168 assert_eq!(density(&g).unwrap(), None);
169 }
170
171 #[test]
172 fn density_complete_undirected_is_one() {
173 let mut g = Graph::with_vertices(4);
174 for u in 0..4u32 {
175 for v in (u + 1)..4 {
176 g.add_edge(u, v).unwrap();
177 }
178 }
179 assert_eq!(density(&g).unwrap(), Some(1.0));
180 }
181
182 #[test]
183 fn density_no_edges_is_zero() {
184 let g = Graph::with_vertices(5);
185 assert_eq!(density(&g).unwrap(), Some(0.0));
186 }
187
188 #[test]
189 fn density_directed_complete_is_one() {
190 let mut g = Graph::new(3, true).unwrap();
192 for u in 0..3u32 {
193 for v in 0..3u32 {
194 if u != v {
195 g.add_edge(u, v).unwrap();
196 }
197 }
198 }
199 assert_eq!(density(&g).unwrap(), Some(1.0));
200 }
201
202 #[test]
203 fn density_path_5_is_2_over_10() {
204 let mut g = Graph::with_vertices(5);
206 for i in 0..4 {
207 g.add_edge(i, i + 1).unwrap();
208 }
209 assert_eq!(density(&g).unwrap(), Some(0.4));
210 }
211
212 #[test]
213 fn mean_distance_n_lt_2_is_none() {
214 let g = Graph::with_vertices(0);
215 assert_eq!(mean_distance(&g).unwrap(), None);
216 let g = Graph::with_vertices(1);
217 assert_eq!(mean_distance(&g).unwrap(), None);
218 }
219
220 #[test]
221 fn mean_distance_isolated_vertices_is_none() {
222 let g = Graph::with_vertices(5);
223 assert_eq!(mean_distance(&g).unwrap(), None);
224 }
225
226 #[test]
227 fn mean_distance_path_5_is_2() {
228 let mut g = Graph::with_vertices(5);
229 for i in 0..4 {
230 g.add_edge(i, i + 1).unwrap();
231 }
232 assert_eq!(mean_distance(&g).unwrap(), Some(2.0));
233 }
234
235 #[test]
236 fn mean_distance_triangle_is_1() {
237 let mut g = Graph::with_vertices(3);
238 g.add_edge(0, 1).unwrap();
239 g.add_edge(1, 2).unwrap();
240 g.add_edge(2, 0).unwrap();
241 assert_eq!(mean_distance(&g).unwrap(), Some(1.0));
242 }
243
244 #[test]
245 fn mean_distance_two_components_only_within_components() {
246 let mut g = Graph::with_vertices(5);
251 g.add_edge(0, 1).unwrap();
252 g.add_edge(1, 2).unwrap();
253 g.add_edge(3, 4).unwrap();
254 assert_eq!(mean_distance(&g).unwrap(), Some(1.25));
255 }
256
257 #[test]
258 fn mean_distance_directed_uses_out_edges() {
259 let mut g = Graph::new(3, true).unwrap();
262 g.add_edge(0, 1).unwrap();
263 g.add_edge(1, 2).unwrap();
264 let four_thirds = 4.0_f64 / 3.0;
265 assert_eq!(mean_distance(&g).unwrap(), Some(four_thirds));
266 }
267
268 #[test]
269 fn mean_degree_empty_graph() {
270 let g = Graph::with_vertices(0);
271 assert_eq!(mean_degree(&g, true).unwrap(), None);
272 }
273
274 #[test]
275 fn mean_degree_no_edges() {
276 let g = Graph::with_vertices(5);
277 let md = mean_degree(&g, true).unwrap().unwrap();
278 assert!((md - 0.0).abs() < 1e-10);
279 }
280
281 #[test]
282 fn mean_degree_undirected_path() {
283 let mut g = Graph::with_vertices(4);
284 g.add_edge(0, 1).unwrap();
285 g.add_edge(1, 2).unwrap();
286 g.add_edge(2, 3).unwrap();
287 let md = mean_degree(&g, true).unwrap().unwrap();
289 assert!((md - 1.5).abs() < 1e-10);
290 }
291
292 #[test]
293 fn mean_degree_directed() {
294 let mut g = Graph::new(3, true).unwrap();
295 g.add_edge(0, 1).unwrap();
296 g.add_edge(0, 2).unwrap();
297 g.add_edge(1, 2).unwrap();
298 let md = mean_degree(&g, true).unwrap().unwrap();
300 assert!((md - 1.0).abs() < 1e-10);
301 }
302
303 #[test]
304 fn mean_degree_with_self_loops() {
305 let mut g = Graph::with_vertices(3);
306 g.add_edge(0, 1).unwrap();
307 g.add_edge(1, 2).unwrap();
308 g.add_edge(0, 0).unwrap(); let md_with = mean_degree(&g, true).unwrap().unwrap();
311 assert!((md_with - 2.0).abs() < 1e-10);
312 let md_without = mean_degree(&g, false).unwrap().unwrap();
314 assert!((md_without - 4.0 / 3.0).abs() < 1e-10);
315 }
316
317 #[test]
318 fn mean_degree_complete_undirected() {
319 let mut g = Graph::with_vertices(4);
321 for u in 0..4u32 {
322 for v in (u + 1)..4 {
323 g.add_edge(u, v).unwrap();
324 }
325 }
326 let md = mean_degree(&g, true).unwrap().unwrap();
327 assert!((md - 3.0).abs() < 1e-10);
328 }
329}