modppl 0.3.1

a experimental library for probabilistic programming in Rust.
Documentation
# modppl

[<img alt="crates.io" src="https://img.shields.io/crates/v/modppl.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/modppl)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-modppl-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/modppl)
[<img alt="status" src="https://img.shields.io/github/actions/workflow/status/agarret7/modppl/test.yml?branch=main&style=for-the-badge" height="20">](https://github.com/agarret7/modppl/actions?query=branch%3Amain)

⚠ ️This is a research prototype, not intended for production use.

# What is modppl?

`modppl` is probabilistic programming written natively in Rust. Modeling and inference are separated via a trait interface called `GenFn`, whose implementations can simulate, generate, update, and regenerate `Trace` data structures, supporting composable inference kernels.


## Modeling & Inference

Probabilistic programming differs from deep learning in that it emphasizes developing inference algorithms rather than training weights. Different languages make different trade-offs in the inference algorithms they support, and designing structures that accommodate the widest possible set of probabilistic models is ongoing research.

Several inference procedures are supported:

- Importance Sampling and Resampling
- Proposal-based and Regenerative Metropolis-Hastings
- Sequential Monte Carlo (SMC)

ModPPL supports a compact, expressive, macro-based modeling DSL with the `dyngen!` macro that generates `Trace`s holding values of `Any` type at runtime. This includes support for `Unfold`, a sequential inference combinator. See [examples](https://github.com/agarret7/modppl/tree/main/modppl/tests/dyngenfns) for usage.

A robust observation model: most points are explained by tight noise, but outliers are drawn from a wide distribution.
```rust
use modppl::prelude::*;

dyngen!(
pub fn observe(mu: Real) -> Real {
    let is_outlier = bernoulli(0.1) %= "is_outlier";
    let noise = if is_outlier { 5.0 } else { 0.5 };
    normal(mu, noise) %= "y"
});
```

Linear regression that traces each observation into a namespaced submodel via `/=`.
```rust
dyngen!(
pub fn linear_model(xs: Vec<Real>) -> Vec<Real> {
    let a = normal(0., 2.) %= "a";
    let b = normal(0., 2.) %= "b";
    xs.iter().enumerate().map(|(i, x)| {
        observe(a * x + b) /= &format!("obs/{}", i)
    }).collect()
});
```

A drift proposal perturbs the regression coefficients, leaving everything else fixed.
```rust
dyngen!(
pub fn drift_proposal(tr: Weak<DynTrace<Vec<Real>, Vec<Real>>>, drift: Real) {
    let tr = tr.upgrade().unwrap();
    normal(tr.data.read::<Real>("a"), drift) %= "a";
    normal(tr.data.read::<Real>("b"), drift) %= "b";
});
```

Composable inference alternating between drifting `a` and `b` and regenerating `outlier` labels.
```rust
let (mut trace, _) = linear_model.generate(xs.clone(), constraints);
for _ in 0..1000 {
    (trace, _) = mh(&linear_model, trace, &drift_proposal, 0.25);
    for i in 0..xs.len() {
        let mut mask = AddrMap::new();
        mask.visit(&format!("obs/{}/is_outlier", i));
        (trace, _) = regen_mh(&linear_model, trace, &mask);
    }
}
```

## Gallery

Generate visualizations to `visualizations` with:
```shell
python -m venv venv && source venv/bin/activate && pip install matplotlib
cargo test --release && python visualization/visualizer.py
```


## Inspiration

`modppl` was inspired by the Generative Function Interface (GFI) as described in the Gen.jl whitepaper.

  Gen: A General-Purpose Probabilistic Programming System with Programmable Inference. Cusumano-Towner, M. F.; Saad, F. A.; Lew, A.; and Mansinghka, V. K. In Proceedings of the 40th ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI ‘19). ([pdf](https://dl.acm.org/doi/10.1145/3314221.3314642)) ([bibtex](https://www.gen.dev/assets/gen-pldi.txt)).

`modppl` does not exactly implement the GFI. More precisely, it does not support _retdiff_ or _choice gradients_.