use drawset::{gumbel_topk_sample_with_rng, WeightedReservoirSampler};
use rand::SeedableRng;
use rand_chacha::ChaCha8Rng;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let n = 20;
let weights: Vec<f64> = (0..n).map(|i| 1.0 / (1.0 + (i as f64)).powf(1.3)).collect();
let eps = 1e-12f64;
let logits: Vec<f32> = weights.iter().map(|&w| (w + eps).ln() as f32).collect();
let k = 5usize;
let n_trials = 10_000usize;
let mut rng_g = ChaCha8Rng::seed_from_u64(7);
let pick_g = gumbel_topk_sample_with_rng(&logits, k, &mut rng_g);
let mut rng_r = ChaCha8Rng::seed_from_u64(7);
let mut rs = WeightedReservoirSampler::new(k);
for (i, &w) in weights.iter().enumerate() {
rs.add_with_rng(i, w, &mut rng_r)?;
}
let pick_r: Vec<usize> = rs.samples().to_vec();
println!("weights (first 10 of {n}):");
for (i, w) in weights.iter().enumerate().take(10) {
println!(" i={i:2} w={w:.6}");
}
println!();
println!("Single draw (seed=7, k={k}):");
println!(" Gumbel-top-k (Plackett-Luce): {pick_g:?}");
println!(" Weighted reservoir (A-Res): {pick_r:?}");
let mut freq_gumbel = vec![0u64; n];
let mut freq_reservoir = vec![0u64; n];
for trial in 0..n_trials {
let seed = trial as u64;
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let selected = gumbel_topk_sample_with_rng(&logits, k, &mut rng);
for idx in selected {
freq_gumbel[idx] += 1;
}
let mut rng = ChaCha8Rng::seed_from_u64(seed);
let mut sampler = WeightedReservoirSampler::new(k);
for (i, &w) in weights.iter().enumerate() {
sampler.add_with_rng(i, w, &mut rng)?;
}
for &idx in sampler.samples() {
freq_reservoir[idx] += 1;
}
}
println!();
println!("Empirical selection frequency ({n_trials} trials, k={k}, n={n}):");
println!(
"{:>5} {:>8} {:>12} {:>12} {:>10}",
"idx", "weight", "gumbel_freq", "reserv_freq", "delta"
);
println!("{}", "-".repeat(51));
for i in 0..n {
let gf = freq_gumbel[i] as f64 / n_trials as f64;
let rf = freq_reservoir[i] as f64 / n_trials as f64;
let delta = gf - rf;
println!(
"{:>5} {:>8.4} {:>12.4} {:>12.4} {:>+10.4}",
i, weights[i], gf, rf, delta
);
}
println!();
println!("Observation: for k>1, Gumbel-top-k (Plackett-Luce) and A-Res produce");
println!("different marginal inclusion probabilities, especially for mid-weight items.");
println!("For k=1 both reduce to sampling proportional to w_i.");
Ok(())
}