use ndarray::{Array1, Array2, array};
use polya_gamma::regression::GibbsNegativeBinomial;
use rand::{SeedableRng, prelude::Distribution};
use rand_chacha::ChaCha8Rng;
use statrs::distribution::{NegativeBinomial as StatrsNegativeBinomial, Normal};
fn main() {
let mut rng = ChaCha8Rng::seed_from_u64(42);
let n = 1000; let p = 3;
let true_beta = array![0.5, -1.0, 0.3];
let true_r = 5.0;
let normal = Normal::new(0.0, 1.0).unwrap();
let x_data: Vec<f64> = (0..n)
.flat_map(|_| {
let mut m = vec![1.0];
m.extend((0..(p - 1)).map(|_| normal.sample(&mut rng)));
m
})
.collect();
assert!(x_data.len() == n * p);
let x = Array2::from_shape_vec((n, p), x_data).unwrap();
let mut y_data = Vec::with_capacity(n);
for i in 0..n {
let eta = x.row(i).dot(&true_beta);
let mu = eta.exp();
let p_nb = mu / (true_r + mu); let nb = StatrsNegativeBinomial::new(true_r, p_nb).unwrap();
y_data.push(nb.sample(&mut rng) as f64);
}
let y = Array1::from_vec(y_data);
println!(
"Generated data with {} observations and {} predictors",
n, p
);
println!("True beta: {:?}", true_beta);
println!("True r: {}", true_r);
println!("Mean of y: {:.2}", y.mean().unwrap());
println!("Variance of y: {:.2}", y.var(0.0));
let prior_variance = 100.0; let prior_shape = 1.0; let prior_scale = 1.0; let n_chains = 4;
let burnin = 1000;
let samples = 1000;
let seed = 42;
println!(
"\nRunning MCMC with {} burn-in and {} samples ({} chains)...",
burnin, samples, n_chains
);
let model = GibbsNegativeBinomial::new(
x,
y,
prior_variance,
prior_shape,
prior_scale,
n_chains,
seed,
);
let results = model
.run(burnin, samples, Some(true_beta.to_vec()), Some(true_r))
.expect("MCMC failed");
results.summary();
println!("\nDetailed results:");
println!("----------------");
println!("{}", results.run_stats);
if let Some(true_beta) = &results.true_coefficients {
println!(
"\nParameter recovery error (L2 norm): {:.4}",
true_beta
.iter()
.zip(&results.posterior_means)
.map(|(t, e)| (t - e).powi(2))
.sum::<f64>()
.sqrt()
);
}
}