rust_igraph/algorithms/properties/
degree_distribution.rs1use crate::algorithms::properties::degree::DegreeMode;
8use crate::core::{Graph, IgraphResult};
9
10pub fn degree_distribution(graph: &Graph, mode: DegreeMode) -> IgraphResult<Vec<u32>> {
37 let n = graph.vcount();
38
39 if n == 0 {
40 return Ok(Vec::new());
41 }
42
43 let n_usize = n as usize;
44 let ecount = graph.ecount();
45 let directed = graph.is_directed();
46
47 let mut deg = vec![0u32; n_usize];
48
49 if ecount == 0 {
50 return Ok(vec![n; 1]);
51 }
52
53 let m_u32 = u32::try_from(ecount)
54 .map_err(|_| crate::core::IgraphError::Internal("ecount exceeds u32::MAX"))?;
55
56 for eid in 0..m_u32 {
57 let (from, to) = graph.edge(eid)?;
58 let f = from as usize;
59 let t = to as usize;
60
61 if directed {
62 match mode {
63 DegreeMode::Out => {
64 deg[f] += 1;
65 }
66 DegreeMode::In => {
67 deg[t] += 1;
68 }
69 DegreeMode::All => {
70 deg[f] += 1;
71 deg[t] += 1;
72 }
73 }
74 } else if from == to {
75 deg[f] += 2;
76 } else {
77 deg[f] += 1;
78 deg[t] += 1;
79 }
80 }
81
82 let max_deg = deg.iter().copied().max().unwrap_or(0) as usize;
83 let mut hist = vec![0u32; max_deg + 1];
84
85 for &d in ° {
86 hist[d as usize] += 1;
87 }
88
89 Ok(hist)
90}
91
92#[cfg(test)]
93mod tests {
94 use super::*;
95
96 #[test]
97 fn empty_graph() {
98 let g = Graph::with_vertices(0);
99 let h = degree_distribution(&g, DegreeMode::All).unwrap();
100 assert!(h.is_empty());
101 }
102
103 #[test]
104 fn no_edges() {
105 let g = Graph::with_vertices(5);
106 let h = degree_distribution(&g, DegreeMode::All).unwrap();
107 assert_eq!(h, vec![5]);
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 let h = degree_distribution(&g, DegreeMode::All).unwrap();
117 assert_eq!(h, vec![0, 0, 3]);
118 }
119
120 #[test]
121 fn star_5() {
122 let mut g = Graph::with_vertices(5);
123 g.add_edge(0, 1).unwrap();
124 g.add_edge(0, 2).unwrap();
125 g.add_edge(0, 3).unwrap();
126 g.add_edge(0, 4).unwrap();
127 let h = degree_distribution(&g, DegreeMode::All).unwrap();
128 assert_eq!(h, vec![0, 4, 0, 0, 1]);
129 }
130
131 #[test]
132 fn path_5() {
133 let mut g = Graph::with_vertices(5);
134 g.add_edge(0, 1).unwrap();
135 g.add_edge(1, 2).unwrap();
136 g.add_edge(2, 3).unwrap();
137 g.add_edge(3, 4).unwrap();
138 let h = degree_distribution(&g, DegreeMode::All).unwrap();
139 assert_eq!(h, vec![0, 2, 3]);
141 }
142
143 #[test]
144 fn directed_out_degree() {
145 let mut g = Graph::new(3, true).unwrap();
147 g.add_edge(0, 1).unwrap();
148 g.add_edge(0, 2).unwrap();
149 g.add_edge(1, 2).unwrap();
150 let h = degree_distribution(&g, DegreeMode::Out).unwrap();
151 assert_eq!(h, vec![1, 1, 1]);
153 }
154
155 #[test]
156 fn directed_in_degree() {
157 let mut g = Graph::new(3, true).unwrap();
159 g.add_edge(0, 1).unwrap();
160 g.add_edge(0, 2).unwrap();
161 g.add_edge(1, 2).unwrap();
162 let h = degree_distribution(&g, DegreeMode::In).unwrap();
163 assert_eq!(h, vec![1, 1, 1]);
165 }
166
167 #[test]
168 fn directed_all_degree() {
169 let mut g = Graph::new(3, true).unwrap();
171 g.add_edge(0, 1).unwrap();
172 g.add_edge(0, 2).unwrap();
173 g.add_edge(1, 2).unwrap();
174 let h = degree_distribution(&g, DegreeMode::All).unwrap();
175 assert_eq!(h, vec![0, 0, 3]);
177 }
178
179 #[test]
180 fn self_loop_undirected() {
181 let mut g = Graph::with_vertices(2);
183 g.add_edge(0, 0).unwrap();
184 g.add_edge(0, 1).unwrap();
185 let h = degree_distribution(&g, DegreeMode::All).unwrap();
186 assert_eq!(h, vec![0, 1, 0, 1]);
188 }
189
190 #[test]
191 fn k4_regular() {
192 let mut g = Graph::with_vertices(4);
193 for u in 0..4u32 {
194 for v in (u + 1)..4 {
195 g.add_edge(u, v).unwrap();
196 }
197 }
198 let h = degree_distribution(&g, DegreeMode::All).unwrap();
199 assert_eq!(h, vec![0, 0, 0, 4]);
201 }
202
203 #[test]
204 fn isolated_plus_edges() {
205 let mut g = Graph::with_vertices(5);
207 g.add_edge(0, 1).unwrap();
208 g.add_edge(2, 3).unwrap();
209 let h = degree_distribution(&g, DegreeMode::All).unwrap();
210 assert_eq!(h, vec![1, 4]);
212 }
213}