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 Some(hub_pos) = select_hub(&vd, method) else {
104 break;
105 };
106 let (hub_vertex, hub_degree) = vd[hub_pos];
107
108 if hub_degree == 0 {
109 break;
110 }
111
112 vd.swap_remove(hub_pos);
114
115 vd.sort_unstable_by_key(|x| std::cmp::Reverse(x.1));
117
118 let d = hub_degree as usize;
119 if d > vd.len() {
120 return Err(IgraphError::InvalidArgument(
121 "degree sequence is not graphical (not realizable as simple graph)".to_string(),
122 ));
123 }
124
125 if vd[d - 1].1 == 0 {
127 return Err(IgraphError::InvalidArgument(
128 "degree sequence is not graphical (not realizable as simple graph)".to_string(),
129 ));
130 }
131
132 for item in vd.iter_mut().take(d) {
134 edges.push((hub_vertex, item.0));
135 item.1 -= 1;
136 }
137 }
138
139 for &(v, d) in &vd {
141 if d != 0 {
142 return Err(IgraphError::InvalidArgument(format!(
143 "degree sequence is not graphical: vertex {v} has {d} remaining degree"
144 )));
145 }
146 }
147
148 let mut graph = Graph::new(n_u32, false)?;
149 graph.add_edges(edges)?;
150 Ok(graph)
151}
152
153fn select_hub(vd: &[(u32, u32)], method: RealizeDegseqMethod) -> Option<usize> {
154 if vd.is_empty() {
155 return None;
156 }
157
158 match method {
159 RealizeDegseqMethod::Largest => {
160 let mut best = 0;
161 for (i, item) in vd.iter().enumerate().skip(1) {
162 if item.1 > vd[best].1 {
163 best = i;
164 }
165 }
166 if vd[best].1 == 0 { None } else { Some(best) }
167 }
168 RealizeDegseqMethod::Smallest => {
169 let mut best: Option<usize> = None;
170 for (i, item) in vd.iter().enumerate() {
171 if item.1 == 0 {
172 continue;
173 }
174 match best {
175 None => best = Some(i),
176 Some(b) => {
177 if item.1 < vd[b].1 {
178 best = Some(i);
179 }
180 }
181 }
182 }
183 best
184 }
185 RealizeDegseqMethod::Index => {
186 let mut best: Option<usize> = None;
188 for (i, item) in vd.iter().enumerate() {
189 if item.1 == 0 {
190 continue;
191 }
192 match best {
193 None => best = Some(i),
194 Some(b) => {
195 if item.0 < vd[b].0 {
196 best = Some(i);
197 }
198 }
199 }
200 }
201 best
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 fn verify_degrees(graph: &Graph, expected: &[u32]) {
211 let n = graph.vcount();
212 assert_eq!(n as usize, expected.len());
213 for v in 0..n {
214 let deg = graph.degree(v).unwrap();
215 assert_eq!(
216 deg, expected[v as usize] as usize,
217 "vertex {v}: got degree {deg}, expected {}",
218 expected[v as usize]
219 );
220 }
221 }
222
223 #[test]
224 fn empty_sequence() {
225 let g = realize_degree_sequence(&[], RealizeDegseqMethod::Largest).unwrap();
226 assert_eq!(g.vcount(), 0);
227 assert_eq!(g.ecount(), 0);
228 }
229
230 #[test]
231 fn all_zeros() {
232 let g = realize_degree_sequence(&[0, 0, 0], RealizeDegseqMethod::Largest).unwrap();
233 assert_eq!(g.vcount(), 3);
234 assert_eq!(g.ecount(), 0);
235 }
236
237 #[test]
238 fn single_edge() {
239 let g = realize_degree_sequence(&[1, 1], RealizeDegseqMethod::Largest).unwrap();
240 assert_eq!(g.vcount(), 2);
241 assert_eq!(g.ecount(), 1);
242 verify_degrees(&g, &[1, 1]);
243 }
244
245 #[test]
246 fn path_graph() {
247 let g = realize_degree_sequence(&[1, 2, 2, 1], RealizeDegseqMethod::Largest).unwrap();
248 assert_eq!(g.vcount(), 4);
249 assert_eq!(g.ecount(), 3);
250 verify_degrees(&g, &[1, 2, 2, 1]);
251 }
252
253 #[test]
254 fn complete_graph_k4() {
255 let g = realize_degree_sequence(&[3, 3, 3, 3], RealizeDegseqMethod::Largest).unwrap();
256 assert_eq!(g.vcount(), 4);
257 assert_eq!(g.ecount(), 6);
258 verify_degrees(&g, &[3, 3, 3, 3]);
259 }
260
261 #[test]
262 fn star_graph() {
263 let g = realize_degree_sequence(&[4, 1, 1, 1, 1], RealizeDegseqMethod::Largest).unwrap();
264 assert_eq!(g.vcount(), 5);
265 assert_eq!(g.ecount(), 4);
266 verify_degrees(&g, &[4, 1, 1, 1, 1]);
267 }
268
269 #[test]
270 fn regular_graph_3() {
271 let g = realize_degree_sequence(&[3, 3, 3, 3], RealizeDegseqMethod::Smallest).unwrap();
273 assert_eq!(g.ecount(), 6);
274 verify_degrees(&g, &[3, 3, 3, 3]);
275 }
276
277 #[test]
278 fn odd_sum_fails() {
279 let result = realize_degree_sequence(&[1, 2, 2], RealizeDegseqMethod::Largest);
280 assert!(result.is_err());
281 }
282
283 #[test]
284 fn degree_too_large_fails() {
285 let result = realize_degree_sequence(&[4, 1, 1, 1], RealizeDegseqMethod::Largest);
287 assert!(result.is_err());
288 }
289
290 #[test]
291 fn non_graphical_sequence_fails() {
292 let result = realize_degree_sequence(&[3, 3, 3, 1], RealizeDegseqMethod::Largest);
294 assert!(result.is_err());
295 }
296
297 #[test]
298 fn method_smallest_connected() {
299 let g = realize_degree_sequence(&[2, 2, 2, 2, 2], RealizeDegseqMethod::Smallest).unwrap();
301 assert_eq!(g.vcount(), 5);
302 assert_eq!(g.ecount(), 5);
303 verify_degrees(&g, &[2, 2, 2, 2, 2]);
304 }
305
306 #[test]
307 fn method_index() {
308 let g = realize_degree_sequence(&[2, 2, 1, 1], RealizeDegseqMethod::Index).unwrap();
309 assert_eq!(g.ecount(), 3);
310 verify_degrees(&g, &[2, 2, 1, 1]);
311 }
312
313 #[test]
314 fn larger_graph() {
315 let g = realize_degree_sequence(&[3, 3, 2, 2, 2, 2, 1, 1], RealizeDegseqMethod::Largest)
317 .unwrap();
318 assert_eq!(g.vcount(), 8);
319 assert_eq!(g.ecount(), 8);
320 verify_degrees(&g, &[3, 3, 2, 2, 2, 2, 1, 1]);
321 }
322
323 #[test]
324 fn single_vertex_zero_degree() {
325 let g = realize_degree_sequence(&[0], RealizeDegseqMethod::Largest).unwrap();
326 assert_eq!(g.vcount(), 1);
327 assert_eq!(g.ecount(), 0);
328 }
329}