modppl 0.3.0

a experimental library for probabilistic programming in Rust.
Documentation
use rand::rngs::ThreadRng;
use nalgebra::{dvector,dmatrix};

use modppl::ParticleSystem;

mod hmm;



#[test]
fn test_hmm_forward_algorithm() {
    let prior = dvector![0.4, 0.6];
    let emission_dists = dmatrix![0.1, 0.9; 0.7, 0.3].transpose();
    let transition_dists = dmatrix![0.5, 0.5; 0.2, 0.8].transpose();
    let obs = vec![1, 0];

    let mut true_marginal_likelihood = 0.0;
    // z = [0, 0]
    true_marginal_likelihood += prior[0]                * emission_dists[(obs[0] as usize, 0)]
                              * transition_dists[(0,0)] * emission_dists[(obs[1] as usize, 0)];
    // z = [0, 1]
    true_marginal_likelihood += prior[0]                * emission_dists[(obs[0] as usize, 0)]
                              * transition_dists[(1,0)] * emission_dists[(obs[1] as usize, 1)];
    // z = [1, 0]
    true_marginal_likelihood += prior[1]                * emission_dists[(obs[0] as usize, 1)]
                              * transition_dists[(0,1)] * emission_dists[(obs[1] as usize, 0)];
    // z = [1, 1]
    true_marginal_likelihood += prior[1]                * emission_dists[(obs[0] as usize, 1)]
                              * transition_dists[(1,1)] * emission_dists[(obs[1] as usize, 1)];

    let empirical_marginal_likelihood = hmm::hmm_forward_alg(prior, emission_dists, transition_dists, &obs);
    approx::assert_abs_diff_eq!(empirical_marginal_likelihood, true_marginal_likelihood, epsilon = 1e-16);
}

#[test]
fn test_particle_filter() -> std::io::Result<()> {
    let rng = ThreadRng::default();
    const NUM_PARTICLES: usize = 10000;

    let prior = dvector![0.2, 0.3, 0.5];
    let emission_matrix = dmatrix![
        0.1, 0.2, 0.7;
        0.2, 0.7, 0.1;
        0.7, 0.2, 0.1
    ].transpose();
    let transition_matrix = dmatrix![
        0.4, 0.4, 0.2;
        0.2, 0.3, 0.5;
        0.9, 0.05, 0.05
    ].transpose();
    let params = hmm::HMMParams::new(prior.clone(), emission_matrix.clone(), transition_matrix.clone());

    let model = hmm::HMM::new(params);

    let data = vec![0, 0, 1, 2];
    let expected = hmm::hmm_forward_alg(prior, emission_matrix, transition_matrix, &data).ln();

    let mut filter = ParticleSystem::new(model, NUM_PARTICLES, rng);

    let store = hmm::ParamStore { };
    let mut data_it = data.into_iter();
    filter.init_step(store, (vec![None], vec![data_it.next()]));
    println!("T = {}", 1);

    for (t, obs) in data_it.enumerate() {
        println!("T = {}", t+2);  // time is 1-indexed and init_step used "1 => observation" (1 + 1 = 2)
        let constraints = (vec![None], vec![Some(obs)]);
        filter = filter.step(constraints);
        let ess = filter.effective_sample_size();
        dbg!(ess);
        let log_weight = filter.resample();
        dbg!(log_weight);
    }
    let lml_estimate = filter.log_marginal_likelihood_estimate();

    approx::assert_abs_diff_eq!(lml_estimate, expected, epsilon = 0.03);

    Ok(())
}