rust_igraph/algorithms/constructors/
realize_degree_sequence.rs1use crate::core::error::IgraphError;
10use crate::core::{Graph, IgraphResult, VertexId};
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum RealizeDegseqMethod {
15 Largest,
18 Smallest,
21 Index,
23}
24
25pub fn realize_degree_sequence(
53 degrees: &[u32],
54 method: RealizeDegseqMethod,
55) -> IgraphResult<Graph> {
56 let n = degrees.len();
57
58 if n == 0 {
59 return Graph::new(0, false);
60 }
61
62 let n_u32 = u32::try_from(n)
64 .map_err(|_| IgraphError::InvalidArgument("vertex count exceeds u32::MAX".to_string()))?;
65
66 for (i, &d) in degrees.iter().enumerate() {
67 if d >= n_u32 {
68 return Err(IgraphError::InvalidArgument(format!(
69 "degree[{i}] = {d} >= n = {n_u32}, cannot realize as simple graph"
70 )));
71 }
72 }
73
74 let deg_sum: u64 = degrees.iter().map(|&d| u64::from(d)).sum();
76 if deg_sum % 2 != 0 {
77 return Err(IgraphError::InvalidArgument(
78 "degree sum must be even for an undirected graph".to_string(),
79 ));
80 }
81
82 let num_edges = (deg_sum / 2) as usize;
83
84 if num_edges == 0 {
85 return Graph::new(n_u32, false);
86 }
87
88 let mut vd: Vec<(u32, u32)> = degrees
90 .iter()
91 .enumerate()
92 .map(|(i, &d)| {
93 #[allow(clippy::cast_possible_truncation)]
94 let idx = i as u32;
95 (idx, d)
96 })
97 .collect();
98
99 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(num_edges);
100
101 for _ in 0..n {
102 let hub_pos = select_hub(&vd, method);
104 if hub_pos.is_none() {
105 break;
106 }
107 let hub_pos = hub_pos.unwrap();
108 let (hub_vertex, hub_degree) = vd[hub_pos];
109
110 if hub_degree == 0 {
111 break;
112 }
113
114 vd.swap_remove(hub_pos);
116
117 vd.sort_unstable_by_key(|x| std::cmp::Reverse(x.1));
119
120 let d = hub_degree as usize;
121 if d > vd.len() {
122 return Err(IgraphError::InvalidArgument(
123 "degree sequence is not graphical (not realizable as simple graph)".to_string(),
124 ));
125 }
126
127 if vd[d - 1].1 == 0 {
129 return Err(IgraphError::InvalidArgument(
130 "degree sequence is not graphical (not realizable as simple graph)".to_string(),
131 ));
132 }
133
134 for item in vd.iter_mut().take(d) {
136 edges.push((hub_vertex, item.0));
137 item.1 -= 1;
138 }
139 }
140
141 for &(v, d) in &vd {
143 if d != 0 {
144 return Err(IgraphError::InvalidArgument(format!(
145 "degree sequence is not graphical: vertex {v} has {d} remaining degree"
146 )));
147 }
148 }
149
150 let mut graph = Graph::new(n_u32, false)?;
151 graph.add_edges(edges)?;
152 Ok(graph)
153}
154
155fn select_hub(vd: &[(u32, u32)], method: RealizeDegseqMethod) -> Option<usize> {
156 if vd.is_empty() {
157 return None;
158 }
159
160 match method {
161 RealizeDegseqMethod::Largest => {
162 let mut best = 0;
163 for (i, item) in vd.iter().enumerate().skip(1) {
164 if item.1 > vd[best].1 {
165 best = i;
166 }
167 }
168 if vd[best].1 == 0 { None } else { Some(best) }
169 }
170 RealizeDegseqMethod::Smallest => {
171 let mut best: Option<usize> = None;
172 for (i, item) in vd.iter().enumerate() {
173 if item.1 == 0 {
174 continue;
175 }
176 match best {
177 None => best = Some(i),
178 Some(b) => {
179 if item.1 < vd[b].1 {
180 best = Some(i);
181 }
182 }
183 }
184 }
185 best
186 }
187 RealizeDegseqMethod::Index => {
188 let mut best: Option<usize> = None;
190 for (i, item) in vd.iter().enumerate() {
191 if item.1 == 0 {
192 continue;
193 }
194 match best {
195 None => best = Some(i),
196 Some(b) => {
197 if item.0 < vd[b].0 {
198 best = Some(i);
199 }
200 }
201 }
202 }
203 best
204 }
205 }
206}
207
208#[cfg(test)]
209mod tests {
210 use super::*;
211
212 fn verify_degrees(graph: &Graph, expected: &[u32]) {
213 let n = graph.vcount();
214 assert_eq!(n as usize, expected.len());
215 for v in 0..n {
216 let deg = graph.degree(v).unwrap();
217 assert_eq!(
218 deg, expected[v as usize] as usize,
219 "vertex {v}: got degree {deg}, expected {}",
220 expected[v as usize]
221 );
222 }
223 }
224
225 #[test]
226 fn empty_sequence() {
227 let g = realize_degree_sequence(&[], RealizeDegseqMethod::Largest).unwrap();
228 assert_eq!(g.vcount(), 0);
229 assert_eq!(g.ecount(), 0);
230 }
231
232 #[test]
233 fn all_zeros() {
234 let g = realize_degree_sequence(&[0, 0, 0], RealizeDegseqMethod::Largest).unwrap();
235 assert_eq!(g.vcount(), 3);
236 assert_eq!(g.ecount(), 0);
237 }
238
239 #[test]
240 fn single_edge() {
241 let g = realize_degree_sequence(&[1, 1], RealizeDegseqMethod::Largest).unwrap();
242 assert_eq!(g.vcount(), 2);
243 assert_eq!(g.ecount(), 1);
244 verify_degrees(&g, &[1, 1]);
245 }
246
247 #[test]
248 fn path_graph() {
249 let g = realize_degree_sequence(&[1, 2, 2, 1], RealizeDegseqMethod::Largest).unwrap();
250 assert_eq!(g.vcount(), 4);
251 assert_eq!(g.ecount(), 3);
252 verify_degrees(&g, &[1, 2, 2, 1]);
253 }
254
255 #[test]
256 fn complete_graph_k4() {
257 let g = realize_degree_sequence(&[3, 3, 3, 3], RealizeDegseqMethod::Largest).unwrap();
258 assert_eq!(g.vcount(), 4);
259 assert_eq!(g.ecount(), 6);
260 verify_degrees(&g, &[3, 3, 3, 3]);
261 }
262
263 #[test]
264 fn star_graph() {
265 let g = realize_degree_sequence(&[4, 1, 1, 1, 1], RealizeDegseqMethod::Largest).unwrap();
266 assert_eq!(g.vcount(), 5);
267 assert_eq!(g.ecount(), 4);
268 verify_degrees(&g, &[4, 1, 1, 1, 1]);
269 }
270
271 #[test]
272 fn regular_graph_3() {
273 let g = realize_degree_sequence(&[3, 3, 3, 3], RealizeDegseqMethod::Smallest).unwrap();
275 assert_eq!(g.ecount(), 6);
276 verify_degrees(&g, &[3, 3, 3, 3]);
277 }
278
279 #[test]
280 fn odd_sum_fails() {
281 let result = realize_degree_sequence(&[1, 2, 2], RealizeDegseqMethod::Largest);
282 assert!(result.is_err());
283 }
284
285 #[test]
286 fn degree_too_large_fails() {
287 let result = realize_degree_sequence(&[4, 1, 1, 1], RealizeDegseqMethod::Largest);
289 assert!(result.is_err());
290 }
291
292 #[test]
293 fn non_graphical_sequence_fails() {
294 let result = realize_degree_sequence(&[3, 3, 3, 1], RealizeDegseqMethod::Largest);
296 assert!(result.is_err());
297 }
298
299 #[test]
300 fn method_smallest_connected() {
301 let g = realize_degree_sequence(&[2, 2, 2, 2, 2], RealizeDegseqMethod::Smallest).unwrap();
303 assert_eq!(g.vcount(), 5);
304 assert_eq!(g.ecount(), 5);
305 verify_degrees(&g, &[2, 2, 2, 2, 2]);
306 }
307
308 #[test]
309 fn method_index() {
310 let g = realize_degree_sequence(&[2, 2, 1, 1], RealizeDegseqMethod::Index).unwrap();
311 assert_eq!(g.ecount(), 3);
312 verify_degrees(&g, &[2, 2, 1, 1]);
313 }
314
315 #[test]
316 fn larger_graph() {
317 let g = realize_degree_sequence(&[3, 3, 2, 2, 2, 2, 1, 1], RealizeDegseqMethod::Largest)
319 .unwrap();
320 assert_eq!(g.vcount(), 8);
321 assert_eq!(g.ecount(), 8);
322 verify_degrees(&g, &[3, 3, 2, 2, 2, 2, 1, 1]);
323 }
324
325 #[test]
326 fn single_vertex_zero_degree() {
327 let g = realize_degree_sequence(&[0], RealizeDegseqMethod::Largest).unwrap();
328 assert_eq!(g.vcount(), 1);
329 assert_eq!(g.ecount(), 0);
330 }
331}