rust_igraph/algorithms/games/
tree.rs1#![allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
46
47use crate::algorithms::constructors::prufer::from_prufer;
48use crate::core::rng::SplitMix64;
49use crate::core::{Graph, IgraphError, IgraphResult, VertexId};
50
51pub fn tree_game_lerw(n: u32, directed: bool, seed: u64) -> IgraphResult<Graph> {
78 if n < 2 {
79 return Graph::new(n, directed);
80 }
81
82 let n_usize = n as usize;
83 let no_edges = n_usize - 1;
84
85 let mut vertices: Vec<VertexId> = (0..n).collect();
86 let mut visited: Vec<bool> = vec![false; n_usize];
87 let mut edges: Vec<(VertexId, VertexId)> = Vec::with_capacity(no_edges);
88
89 let mut rng = SplitMix64::new(seed);
90
91 let i0 = rng.gen_index(n_usize);
93 visited[vertices[i0] as usize] = true;
94 vertices.swap(0, i0);
95
96 let mut prev: VertexId = vertices[0];
97
98 for k in 1..n_usize {
99 let mut j = rng.gen_index(n_usize);
101 if visited[vertices[j] as usize] {
102 prev = vertices[j];
105 j = k + rng.gen_index(n_usize - k);
106 }
107 visited[vertices[j] as usize] = true;
108 vertices.swap(k, j);
109 let new_v = vertices[k];
110 edges.push((prev, new_v));
111 prev = new_v;
112 }
113
114 let mut g = Graph::new(n, directed)?;
115 g.add_edges(edges)?;
116 Ok(g)
117}
118
119pub fn tree_game_prufer(n: u32, seed: u64) -> IgraphResult<Graph> {
153 if n < 2 {
154 return Graph::new(n, false);
155 }
156
157 let seq_len = (n - 2) as usize;
158 let mut rng = SplitMix64::new(seed);
159 let n_usize = n as usize;
160
161 let mut prufer: Vec<u32> = Vec::with_capacity(seq_len);
162 for _ in 0..seq_len {
163 let val = rng.gen_index(n_usize);
164 let val_u32 = u32::try_from(val).map_err(|_| {
165 IgraphError::InvalidArgument("tree_game_prufer: vertex index overflow".to_string())
166 })?;
167 prufer.push(val_u32);
168 }
169
170 from_prufer(&prufer)
171}
172
173#[cfg(test)]
174mod tests {
175 use super::*;
176
177 fn collect_edges(g: &Graph) -> Vec<(VertexId, VertexId)> {
178 let n_edges = u32::try_from(g.ecount()).expect("ecount fits in u32 in tests");
179 (0..n_edges)
180 .map(|eid| g.edge(eid).expect("edge id in bounds"))
181 .collect()
182 }
183
184 struct UnionFind {
186 parent: Vec<u32>,
187 }
188 impl UnionFind {
189 fn new(n: usize) -> Self {
190 Self {
191 parent: (0..n as u32).collect(),
192 }
193 }
194 fn find(&mut self, mut x: u32) -> u32 {
195 while self.parent[x as usize] != x {
196 let p = self.parent[x as usize];
197 self.parent[x as usize] = self.parent[p as usize];
198 x = self.parent[x as usize];
199 }
200 x
201 }
202 fn union(&mut self, a: u32, b: u32) -> bool {
205 let ra = self.find(a);
206 let rb = self.find(b);
207 if ra == rb {
208 false
209 } else {
210 self.parent[ra as usize] = rb;
211 true
212 }
213 }
214 }
215
216 #[test]
217 fn n_zero_returns_empty_graph() {
218 let g = tree_game_lerw(0, false, 1).unwrap();
219 assert_eq!(g.vcount(), 0);
220 assert_eq!(g.ecount(), 0);
221 }
222
223 #[test]
224 fn n_one_returns_singleton() {
225 let g = tree_game_lerw(1, false, 1).unwrap();
226 assert_eq!(g.vcount(), 1);
227 assert_eq!(g.ecount(), 0);
228 }
229
230 #[test]
231 fn n_two_returns_single_edge() {
232 let g = tree_game_lerw(2, false, 0xBEEF).unwrap();
233 assert_eq!(g.vcount(), 2);
234 assert_eq!(g.ecount(), 1);
235 let (a, b) = g.edge(0).unwrap();
236 assert_ne!(a, b, "tree edge must not be a self-loop");
237 let lo = a.min(b);
238 let hi = a.max(b);
239 assert_eq!(lo, 0);
240 assert_eq!(hi, 1);
241 }
242
243 #[test]
244 fn exact_edge_count() {
245 for &n in &[5u32, 10, 50, 200, 1000] {
246 let g = tree_game_lerw(n, false, 0xC0FF_EE00 + u64::from(n)).unwrap();
247 assert_eq!(g.vcount(), n);
248 assert_eq!(g.ecount() as u32, n - 1);
249 }
250 }
251
252 #[test]
253 fn no_self_loops() {
254 let g = tree_game_lerw(150, false, 0xFACE).unwrap();
255 for (a, b) in collect_edges(&g) {
256 assert_ne!(a, b, "Wilson LERW must not emit self-loops");
257 }
258 }
259
260 #[test]
261 fn output_is_acyclic_and_connected_undirected() {
262 let g = tree_game_lerw(200, false, 0xCAFE).unwrap();
263 let n = g.vcount() as usize;
264 let mut uf = UnionFind::new(n);
265 for (a, b) in collect_edges(&g) {
266 assert!(
267 uf.union(a, b),
268 "edge ({a}, {b}) closed a cycle — output is not a tree"
269 );
270 }
271 let root = uf.find(0);
273 for v in 1..n as u32 {
274 assert_eq!(
275 uf.find(v),
276 root,
277 "vertex {v} is in a disconnected component"
278 );
279 }
280 }
281
282 #[test]
283 fn output_is_acyclic_and_connected_directed() {
284 let g = tree_game_lerw(150, true, 0xDEAD).unwrap();
288 assert!(g.is_directed());
289 let n = g.vcount() as usize;
290 let mut uf = UnionFind::new(n);
291 for (a, b) in collect_edges(&g) {
292 assert!(
293 uf.union(a, b),
294 "directed tree edge ({a}, {b}) closed a cycle"
295 );
296 }
297 let root = uf.find(0);
298 for v in 1..n as u32 {
299 assert_eq!(uf.find(v), root);
300 }
301 }
302
303 #[test]
304 fn directed_mode_has_no_duplicate_targets() {
305 let g = tree_game_lerw(100, true, 0xBAD_F00D).unwrap();
308 let n = g.vcount() as usize;
309 let mut indeg = vec![0u32; n];
310 for (_a, b) in collect_edges(&g) {
311 indeg[b as usize] += 1;
312 }
313 let roots: Vec<_> = indeg.iter().enumerate().filter(|&(_, &d)| d == 0).collect();
314 assert_eq!(roots.len(), 1, "directed Wilson tree has exactly one root");
315 for (v, &d) in indeg.iter().enumerate() {
316 if v == roots[0].0 {
317 continue;
318 }
319 assert_eq!(d, 1, "non-root vertex {v} must have in-degree 1, got {d}");
320 }
321 }
322
323 #[test]
324 fn deterministic_with_seed() {
325 let a = tree_game_lerw(80, false, 0xABCD).unwrap();
326 let b = tree_game_lerw(80, false, 0xABCD).unwrap();
327 assert_eq!(a.vcount(), b.vcount());
328 assert_eq!(a.ecount(), b.ecount());
329 assert_eq!(collect_edges(&a), collect_edges(&b));
330 }
331
332 #[test]
333 fn different_seeds_yield_different_trees() {
334 let a = tree_game_lerw(60, false, 1).unwrap();
335 let b = tree_game_lerw(60, false, 2).unwrap();
336 assert_ne!(
337 collect_edges(&a),
338 collect_edges(&b),
339 "different seeds must produce different trees"
340 );
341 }
342
343 #[test]
344 fn all_vertices_appear_in_some_edge_for_n_ge_2() {
345 let g = tree_game_lerw(40, false, 0x5EED).unwrap();
347 let mut seen = vec![false; g.vcount() as usize];
348 for (a, b) in collect_edges(&g) {
349 seen[a as usize] = true;
350 seen[b as usize] = true;
351 }
352 for (v, &s) in seen.iter().enumerate() {
353 assert!(s, "vertex {v} missing from spanning tree");
354 }
355 }
356
357 #[test]
360 fn prufer_n_zero() {
361 let g = tree_game_prufer(0, 1).unwrap();
362 assert_eq!(g.vcount(), 0);
363 assert_eq!(g.ecount(), 0);
364 }
365
366 #[test]
367 fn prufer_n_one() {
368 let g = tree_game_prufer(1, 1).unwrap();
369 assert_eq!(g.vcount(), 1);
370 assert_eq!(g.ecount(), 0);
371 }
372
373 #[test]
374 fn prufer_n_two() {
375 let g = tree_game_prufer(2, 0xBEEF).unwrap();
376 assert_eq!(g.vcount(), 2);
377 assert_eq!(g.ecount(), 1);
378 assert!(!g.is_directed());
379 }
380
381 #[test]
382 fn prufer_always_undirected() {
383 let g = tree_game_prufer(50, 0xFACE).unwrap();
384 assert!(!g.is_directed());
385 }
386
387 #[test]
388 fn prufer_exact_edge_count() {
389 for &n in &[5u32, 10, 50, 200, 1000] {
390 let g = tree_game_prufer(n, 0xC0DE + u64::from(n)).unwrap();
391 assert_eq!(g.vcount(), n);
392 assert_eq!(g.ecount() as u32, n - 1);
393 }
394 }
395
396 #[test]
397 fn prufer_no_self_loops() {
398 let g = tree_game_prufer(150, 0xFACE).unwrap();
399 for (a, b) in collect_edges(&g) {
400 assert_ne!(a, b, "Prüfer tree must not emit self-loops");
401 }
402 }
403
404 #[test]
405 fn prufer_is_acyclic_and_connected() {
406 let g = tree_game_prufer(200, 0xCAFE).unwrap();
407 let n = g.vcount() as usize;
408 let mut uf = UnionFind::new(n);
409 for (a, b) in collect_edges(&g) {
410 assert!(
411 uf.union(a, b),
412 "edge ({a}, {b}) closed a cycle — not a tree"
413 );
414 }
415 let root = uf.find(0);
416 for v in 1..n as u32 {
417 assert_eq!(uf.find(v), root, "vertex {v} in disconnected component");
418 }
419 }
420
421 #[test]
422 fn prufer_deterministic_with_seed() {
423 let a = tree_game_prufer(80, 0xABCD).unwrap();
424 let b = tree_game_prufer(80, 0xABCD).unwrap();
425 assert_eq!(collect_edges(&a), collect_edges(&b));
426 }
427
428 #[test]
429 fn prufer_different_seeds_yield_different_trees() {
430 let a = tree_game_prufer(60, 1).unwrap();
431 let b = tree_game_prufer(60, 2).unwrap();
432 assert_ne!(
433 collect_edges(&a),
434 collect_edges(&b),
435 "different seeds must produce different trees"
436 );
437 }
438
439 #[test]
440 fn prufer_all_vertices_touched() {
441 let g = tree_game_prufer(40, 0x5EED).unwrap();
442 let mut seen = vec![false; g.vcount() as usize];
443 for (a, b) in collect_edges(&g) {
444 seen[a as usize] = true;
445 seen[b as usize] = true;
446 }
447 for (v, &s) in seen.iter().enumerate() {
448 assert!(s, "vertex {v} missing from spanning tree");
449 }
450 }
451}