|
| 1 | +//! Conjugate Bayesian model examples. |
| 2 | +//! |
| 3 | +//! These examples use closed-form posterior updates, then build the matching |
| 4 | +//! bayes-rs distributions for posterior summaries and simple plug-in predictive |
| 5 | +//! checks. The plug-in checks are intentionally lightweight examples; they are |
| 6 | +//! not substitutes for the full posterior-predictive distributions that these |
| 7 | +//! conjugate pairs also provide in closed form. They are deterministic and quick |
| 8 | +//! enough to use as executable docs. |
| 9 | +
|
| 10 | +use bayes_rs::distributions::{Beta, Binomial, DiscreteDistribution, Distribution, Gamma, Poisson}; |
| 11 | + |
| 12 | +fn beta_binomial_posterior( |
| 13 | + prior_alpha: f64, |
| 14 | + prior_beta: f64, |
| 15 | + successes: u64, |
| 16 | + trials: u64, |
| 17 | +) -> bayes_rs::Result<Beta> { |
| 18 | + let failures = trials |
| 19 | + .checked_sub(successes) |
| 20 | + .ok_or_else(|| bayes_rs::BayesError::invalid_parameter("successes cannot exceed trials"))?; |
| 21 | + |
| 22 | + Beta::new(prior_alpha + successes as f64, prior_beta + failures as f64) |
| 23 | +} |
| 24 | + |
| 25 | +fn gamma_poisson_posterior( |
| 26 | + prior_shape: f64, |
| 27 | + prior_rate: f64, |
| 28 | + counts: &[u64], |
| 29 | +) -> bayes_rs::Result<Gamma> { |
| 30 | + let observed_events: u64 = counts.iter().sum(); |
| 31 | + // bayes-rs parameterizes Gamma as shape/rate, so exposure increments rate. |
| 32 | + Gamma::new( |
| 33 | + prior_shape + observed_events as f64, |
| 34 | + prior_rate + counts.len() as f64, |
| 35 | + ) |
| 36 | +} |
| 37 | + |
| 38 | +fn main() -> bayes_rs::Result<()> { |
| 39 | + let conversion_posterior = beta_binomial_posterior(2.0, 2.0, 42, 120)?; |
| 40 | + let posterior_mean = conversion_posterior.mean(); |
| 41 | + let predictive_successes = Binomial::new(25, posterior_mean)?; |
| 42 | + |
| 43 | + println!("Beta-binomial conversion-rate update"); |
| 44 | + println!(" Posterior mean success probability: {posterior_mean:.3}"); |
| 45 | + println!( |
| 46 | + " Plug-in predictive probability of at least 8 successes in 25 trials: {:.3}", |
| 47 | + (8..=25) |
| 48 | + .map(|successes| predictive_successes.pmf(successes)) |
| 49 | + .sum::<f64>() |
| 50 | + ); |
| 51 | + |
| 52 | + let daily_defects = [3, 4, 2, 5, 4, 1, 3]; |
| 53 | + let defect_rate_posterior = gamma_poisson_posterior(1.5, 1.0, &daily_defects)?; |
| 54 | + let posterior_rate = defect_rate_posterior.mean(); |
| 55 | + let next_day_defects = Poisson::new(posterior_rate)?; |
| 56 | + |
| 57 | + println!("Gamma-Poisson count-rate update"); |
| 58 | + println!(" Posterior mean event rate: {posterior_rate:.3}"); |
| 59 | + println!( |
| 60 | + " Plug-in predictive probability of at most 2 events tomorrow: {:.3}", |
| 61 | + (0..=2) |
| 62 | + .map(|count| next_day_defects.pmf(count)) |
| 63 | + .sum::<f64>() |
| 64 | + ); |
| 65 | + println!( |
| 66 | + " Posterior density at rate 3.0: {:.3}", |
| 67 | + defect_rate_posterior.pdf(3.0) |
| 68 | + ); |
| 69 | + |
| 70 | + Ok(()) |
| 71 | +} |
0 commit comments