use rand::Rng;
use rand::prelude::SliceRandom;
use rand::rngs::StdRng;
pub fn pdf_to_cdf(pdf: &Vec<f64>) -> Vec<f64> {
let mut cdf = Vec::with_capacity(pdf.len());
let mut acc = 0.0;
for p in pdf.iter() {
acc += p;
cdf.push(acc);
}
if !cdf.is_empty() {
let last_idx = cdf.len() - 1;
cdf[last_idx] = 1.0;
}
cdf
}
pub fn shorten_pdf(pdf: &mut Vec<f64>, k: usize) {
if k == 0 {
pdf.clear();
return;
}
if pdf.len() > k + 1 {
let tail = pdf.iter().skip(k).sum::<f64>();
pdf.truncate(k + 1);
pdf[k] = tail;
}
}
pub fn sampling_indices_with_degree(
degree: usize,
a: usize,
seeded_rng: &mut StdRng,
) -> Vec<usize> {
let mut indices: Vec<usize> = (0..a).collect();
indices.as_mut_slice().shuffle(seeded_rng);
indices.truncate(degree);
indices
}
pub struct DegreeSampling {
cdf: Vec<f64>,
}
impl DegreeSampling {
pub fn new(pdf: Vec<f64>) -> Self {
let cdf = pdf_to_cdf(&pdf);
Self { cdf }
}
pub fn random(&self, seeded_rng: &mut StdRng) -> usize {
let r = seeded_rng.r#gen::<f64>();
self.cdf.iter().position(|&x| r < x).unwrap_or(1)
}
}
#[cfg(test)]
mod tests {
use crate::degree_sets::SolitonDistribution;
use crate::degree_sets::DegreeSetGenerator;
use crate::degree_sets::random::RandomDegreeSet;
use crate::degree_sets::triangular::TriangularDegreeSet;
use crate::parameters::ParamsConfig;
#[test]
fn test_ideal_soliton_pdf_sum_to_one() {
let k = 10;
let dist = SolitonDistribution::IdealSoliton { dmax: k };
let pdf = dist.pdf();
let sum: f64 = pdf.iter().sum();
assert!(
(sum - 1.0).abs() < 1e-8,
"PDF does not sum to 1, got {}",
sum
);
}
#[test]
fn test_ideal_soliton_pdf_is_correct() {
let k = 10;
let dist = SolitonDistribution::IdealSoliton { dmax: k };
let pdf = dist.pdf();
let expected_pdf = vec![
0.0,
0.1,
0.5,
1.0 / 6.0,
1.0 / 12.0,
1.0 / 20.0,
1.0 / 30.0,
1.0 / 42.0,
1.0 / 56.0,
1.0 / 72.0,
1.0 / 90.0,
];
assert_eq!(pdf, expected_pdf);
}
#[test]
fn test_robust_soliton_pdf_sum_to_one() {
let k = 10;
let dist = SolitonDistribution::RobustSoliton {
dmax: k,
c: 0.1,
delta: 0.05,
};
let pdf = dist.pdf();
println!("PDF: {:?}", pdf);
let sum: f64 = pdf.iter().sum();
assert!(
(sum - 1.0).abs() < 1e-8,
"PDF does not sum to 1, got {}",
sum
);
}
#[test]
fn test_ideal_random_degree_set_degree_range() {
let k = 100;
let dmax = 120;
let dist = SolitonDistribution::IdealSoliton { dmax };
let params = ParamsConfig::LT.generate(k);
let mut assoc = RandomDegreeSet::new(&dist, ¶ms);
for coded_id in 0..100 {
let (active_indices, _inactive_indices) = assoc.degree_set(coded_id);
assert!(!active_indices.is_empty() && active_indices.len() <= k);
}
}
#[test]
fn test_robust_random_degree_set_degree_range() {
let k = 100;
let dist = SolitonDistribution::RobustSoliton {
dmax: k,
c: 0.1,
delta: 0.5,
};
let params = ParamsConfig::LT.generate(k);
let mut assoc = RandomDegreeSet::new(&dist, ¶ms);
for coded_id in 0..100 {
let (active_indices, _inactive_indices) = assoc.degree_set(coded_id);
assert!(!active_indices.is_empty() && active_indices.len() <= k);
}
}
#[test]
fn test_ideal_uniform_sampling_of_indices() {
let k = 20;
let ideal = SolitonDistribution::IdealSoliton { dmax: k };
let params = ParamsConfig::LT.generate(k);
let mut assoc = RandomDegreeSet::new(&ideal, ¶ms);
let num_trials = 10_000;
let mut counts = vec![0usize; k];
for coded_id in 0..num_trials {
let set = assoc.degree_set(coded_id);
for &idx in &set.0 {
counts[idx] += 1;
}
}
let avg = counts.iter().sum::<usize>() as f64 / k as f64;
let max_dev = counts
.iter()
.map(|&c| ((c as f64 - avg).abs() / avg))
.fold(0.0, f64::max);
println!("Counts: {:?}", counts);
println!("Average: {}", avg);
println!("Max relative deviation: {:.2}%", max_dev * 100.0);
assert!(max_dev < 0.10, "Sampling is not uniform enough");
}
#[test]
fn test_robust_uniform_sampling_of_indices() {
let k = 20;
let dist = SolitonDistribution::RobustSoliton {
dmax: k,
c: 0.1,
delta: 0.5,
};
let params = ParamsConfig::LT.generate(k);
let mut assoc = RandomDegreeSet::new(&dist, ¶ms);
let num_trials = 10_000;
let mut counts = vec![0usize; k];
for coded_id in 0..num_trials {
let set = assoc.degree_set(coded_id);
for &idx in &set.0 {
counts[idx] += 1;
}
}
let avg = counts.iter().sum::<usize>() as f64 / k as f64;
let max_dev = counts
.iter()
.map(|&c| ((c as f64 - avg).abs() / avg))
.fold(0.0, f64::max);
println!("Counts: {:?}", counts);
println!("Average: {}", avg);
println!("Max relative deviation: {:.2}%", max_dev * 100.0);
}
#[test]
fn test_triangular_degree_set_degree_range() {
let k = 100;
let dmax = 120;
let dist = SolitonDistribution::IdealSoliton { dmax };
let params = ParamsConfig::LT.generate(k);
let mut assoc = TriangularDegreeSet::new(dist, params);
for coded_id in 0..100 {
let (active_indices, _inactive_indices) = assoc.degree_set(coded_id);
assert!(
!active_indices.is_empty() && active_indices.iter().max().unwrap() == &coded_id
);
}
}
}