#![allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
use std::collections::HashSet;
use rust_igraph::{Graph, degree_sequence_game_vl};
fn observed_degree_sequence(g: &Graph) -> Vec<u32> {
let vcount = g.vcount() as usize;
let mut deg = vec![0u32; vcount];
let ecount = u32::try_from(g.ecount()).expect("ecount fits u32 for demo");
for eid in 0..ecount {
let (src, dst) = g.edge(eid).expect("edge id in bounds for demo");
deg[src as usize] = deg[src as usize].saturating_add(1);
deg[dst as usize] = deg[dst as usize].saturating_add(1);
}
deg
}
fn count_self_loops_and_multi(g: &Graph) -> (u32, u32) {
let ecount = u32::try_from(g.ecount()).expect("ecount fits u32 for demo");
let mut loops = 0u32;
let mut seen: HashSet<(u32, u32)> = HashSet::with_capacity(ecount as usize);
let mut multi = 0u32;
for eid in 0..ecount {
let (a, b) = g.edge(eid).expect("edge id in bounds for demo");
if a == b {
loops = loops.saturating_add(1);
continue;
}
let key = if a < b { (a, b) } else { (b, a) };
if !seen.insert(key) {
multi = multi.saturating_add(1);
}
}
(loops, multi)
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
{
let n: usize = 30;
let d: u32 = 3;
let seq: Vec<u32> = vec![d; n];
let g = degree_sequence_game_vl(&seq, 0xC0DE_C0DE_u64)?;
let observed = observed_degree_sequence(&g);
let (loops, multi) = count_self_loops_and_multi(&g);
println!("Scenario 1: 3-regular undirected, n = {n}");
println!(
" |E| = {} (expected n·d/2 = {})",
g.ecount(),
n * d as usize / 2
);
println!(" degree sequence preserved exactly = {}", observed == seq);
println!(" self-loops = {loops}, multi-edges = {multi} (both must be 0)");
println!();
}
{
let seq: Vec<u32> = vec![5, 4, 4, 3, 3, 3, 2, 2, 2, 2];
let sum: u64 = seq.iter().map(|&d| u64::from(d)).sum();
let g = degree_sequence_game_vl(&seq, 0xFEED_FACE_u64)?;
let observed = observed_degree_sequence(&g);
let (loops, multi) = count_self_loops_and_multi(&g);
println!("Scenario 2: skewed power-law-like, n = {}", seq.len());
println!(
" Σd = {sum}, |E| = {} (expected Σd/2 = {})",
g.ecount(),
sum / 2
);
println!(" observed degrees = {observed:?}");
println!(" expected degrees = {seq:?}");
println!(" preserved exactly = {}", observed == seq);
println!(" self-loops = {loops}, multi-edges = {multi} (both must be 0)");
println!();
}
{
let seq: Vec<u32> = vec![0];
let g = degree_sequence_game_vl(&seq, 0x0000_0001_u64)?;
let observed = observed_degree_sequence(&g);
println!("Scenario 3: isolated singleton, n = 1");
println!(" |E| = {} (expected 0)", g.ecount());
println!(" observed degrees = {observed:?}");
println!();
}
println!("VL guarantees exact degree preservation, simplicity, and weak connectivity;");
println!(
"for fast multigraph realisations (no MCMC) use degree_sequence_game_configuration (ALGO-GN-024)."
);
Ok(())
}