#![allow(clippy::cast_precision_loss, clippy::cast_possible_truncation)]
use std::collections::HashSet;
use rust_igraph::{Graph, degree_sequence_game_configuration_simple};
fn observed_out_in(g: &Graph) -> (Vec<u32>, Vec<u32>) {
let vcount = g.vcount() as usize;
let mut out = vec![0u32; vcount];
let mut inv = 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");
if g.is_directed() {
out[src as usize] = out[src as usize].saturating_add(1);
inv[dst as usize] = inv[dst as usize].saturating_add(1);
} else {
out[src as usize] = out[src as usize].saturating_add(1);
out[dst as usize] = out[dst as usize].saturating_add(1);
}
}
(out, inv)
}
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 g.is_directed() || 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 = 10;
let d: u32 = 3;
let seq: Vec<u32> = vec![d; n];
let g = degree_sequence_game_configuration_simple(&seq, None, 0xC0DE_C0DE_u64)?;
let (out, _) = observed_out_in(&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 = {}", out == seq);
println!(" self-loops = {loops}, multi-edges = {multi} (both must be 0)");
println!();
}
{
let seq: Vec<u32> = vec![4, 3, 3, 3, 2, 2, 2, 2, 2, 1];
let sum: u64 = seq.iter().map(|&d| u64::from(d)).sum();
let g = degree_sequence_game_configuration_simple(&seq, None, 0xFEED_FACE_u64)?;
let (out, _) = observed_out_in(&g);
let (loops, multi) = count_self_loops_and_multi(&g);
println!("Scenario 2: moderate skew, n = {}", seq.len());
println!(
" Σd = {sum}, |E| = {} (expected Σd/2 = {})",
g.ecount(),
sum / 2
);
println!(" observed degrees = {out:?}");
println!(" expected degrees = {seq:?}");
println!(" preserved exactly = {}", out == seq);
println!(" self-loops = {loops}, multi-edges = {multi} (both must be 0)");
println!();
}
{
let out_seq: Vec<u32> = vec![2, 2, 2, 1, 1, 1];
let in_seq: Vec<u32> = vec![2, 1, 2, 1, 2, 1];
let sum_out: u64 = out_seq.iter().map(|&d| u64::from(d)).sum();
let sum_in: u64 = in_seq.iter().map(|&d| u64::from(d)).sum();
assert_eq!(sum_out, sum_in);
let g =
degree_sequence_game_configuration_simple(&out_seq, Some(&in_seq), 0xBEEF_BABE_u64)?;
let (out, inv) = observed_out_in(&g);
let (loops, multi) = count_self_loops_and_multi(&g);
println!("Scenario 3: directed mixed, n = {}", out_seq.len());
println!(" Σout = {sum_out}, |E| = {} (must equal Σout)", g.ecount());
println!(" observed out = {out:?}");
println!(" expected out = {out_seq:?}");
println!(" observed in = {inv:?}");
println!(" expected in = {in_seq:?}");
println!(" preserved exactly = {}", out == out_seq && inv == in_seq);
println!(" self-loops = {loops}, multi-arcs = {multi} (both must be 0)");
println!();
}
{
let seq: Vec<u32> = vec![0];
let g = degree_sequence_game_configuration_simple(&seq, None, 0x0000_0001_u64)?;
let (out, _) = observed_out_in(&g);
println!("Scenario 4: isolated singleton, n = 1");
println!(" |E| = {} (expected 0)", g.ecount());
println!(" observed degrees = {out:?}");
println!();
}
println!("CONFIGURATION_SIMPLE yields a uniformly-distributed simple realisation of the input");
println!(
"degree sequence; expected attempts grow as exp(O((Σd/n)^2)) so keep density moderate."
);
println!("For connectivity, use degree_sequence_game_vl (ALGO-GN-025); for faster heuristic");
println!("sampling, use degree_sequence_game_fast_heur_simple (ALGO-GN-026).");
Ok(())
}