#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/docs/runtime/handler.md"))]
use crate::core::address::Address;
use crate::core::distribution::Distribution;
use crate::core::model::Model;
use crate::runtime::trace::Trace;
pub trait Handler {
fn on_sample_f64(&mut self, addr: &Address, dist: &dyn Distribution<f64>) -> f64;
fn on_sample_bool(&mut self, addr: &Address, dist: &dyn Distribution<bool>) -> bool;
fn on_sample_u64(&mut self, addr: &Address, dist: &dyn Distribution<u64>) -> u64;
fn on_sample_usize(&mut self, addr: &Address, dist: &dyn Distribution<usize>) -> usize;
fn on_sample_i64(&mut self, addr: &Address, _dist: &dyn Distribution<i64>) -> i64 {
panic!(
"handler does not implement on_sample_i64 (i64 sample site at {})",
addr
)
}
fn on_observe_f64(&mut self, addr: &Address, dist: &dyn Distribution<f64>, value: f64);
fn on_observe_bool(&mut self, addr: &Address, dist: &dyn Distribution<bool>, value: bool);
fn on_observe_u64(&mut self, addr: &Address, dist: &dyn Distribution<u64>, value: u64);
fn on_observe_usize(&mut self, addr: &Address, dist: &dyn Distribution<usize>, value: usize);
fn on_observe_i64(&mut self, addr: &Address, _dist: &dyn Distribution<i64>, _value: i64) {
panic!(
"handler does not implement on_observe_i64 (i64 observe site at {})",
addr
)
}
fn on_factor(&mut self, logw: f64);
fn finish(self) -> Trace
where
Self: Sized;
}
pub fn run<A>(mut h: impl Handler, m: Model<A>) -> (A, Trace) {
let mut m = m;
let a = loop {
m = match m {
Model::Pure(a) => break a,
Model::SampleF64 { addr, dist, k } => {
let x = h.on_sample_f64(&addr, &*dist);
k(x)
}
Model::SampleBool { addr, dist, k } => {
let x = h.on_sample_bool(&addr, &*dist);
k(x)
}
Model::SampleU64 { addr, dist, k } => {
let x = h.on_sample_u64(&addr, &*dist);
k(x)
}
Model::SampleUsize { addr, dist, k } => {
let x = h.on_sample_usize(&addr, &*dist);
k(x)
}
Model::SampleI64 { addr, dist, k } => {
let x = h.on_sample_i64(&addr, &*dist);
k(x)
}
Model::ObserveF64 {
addr,
dist,
value,
k,
} => {
h.on_observe_f64(&addr, &*dist, value);
k(())
}
Model::ObserveBool {
addr,
dist,
value,
k,
} => {
h.on_observe_bool(&addr, &*dist, value);
k(())
}
Model::ObserveU64 {
addr,
dist,
value,
k,
} => {
h.on_observe_u64(&addr, &*dist, value);
k(())
}
Model::ObserveUsize {
addr,
dist,
value,
k,
} => {
h.on_observe_usize(&addr, &*dist, value);
k(())
}
Model::ObserveI64 {
addr,
dist,
value,
k,
} => {
h.on_observe_i64(&addr, &*dist, value);
k(())
}
Model::Factor { logw, k } => {
h.on_factor(logw);
k(())
}
};
};
let t = h.finish();
(a, t)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::addr;
use crate::core::distribution::*;
use crate::core::model::ModelExt;
use crate::runtime::interpreters::PriorHandler;
use rand::rngs::StdRng;
use rand::SeedableRng;
#[test]
fn run_accumulates_logs_for_sample_observe_factor() {
let model = crate::core::model::sample(addr!("x"), Normal::new(0.0, 1.0).unwrap())
.and_then(|x| {
crate::core::model::observe(addr!("y"), Normal::new(x, 1.0).unwrap(), 0.5)
})
.and_then(|_| crate::core::model::factor(-1.0));
let mut rng = StdRng::seed_from_u64(123);
let (_a, trace) = crate::runtime::handler::run(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
model,
);
assert!(trace.choices.contains_key(&addr!("x")));
assert!(trace.log_prior.is_finite());
assert!(trace.log_likelihood.is_finite());
assert!((trace.log_factors + 1.0).abs() < 1e-12);
}
#[test]
fn interpretation_is_stack_safe_for_deep_models() {
fn build(i: usize, n: usize, acc: f64) -> Model<f64> {
if i >= n {
crate::core::model::pure(acc)
} else {
crate::core::model::sample(addr!("x", i), Normal::new(0.0, 1.0).unwrap())
.bind(move |x| build(i + 1, n, acc + x))
}
}
let handle = std::thread::Builder::new()
.stack_size(512 * 1024)
.spawn(|| {
let n = 100_000;
let mut rng = StdRng::seed_from_u64(2024);
let (sum, trace) = crate::runtime::handler::run(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
build(0, n, 0.0),
);
assert!(sum.is_finite());
assert_eq!(trace.choices.len(), n);
assert!(trace.log_prior.is_finite());
})
.expect("spawn thread");
handle
.join()
.expect("deep model interpretation overflowed the stack");
}
#[test]
fn run_handles_large_observe_sequence() {
use crate::core::model::{observe, sequence_vec};
let n = 100_000usize;
let models: Vec<Model<()>> = (0..n)
.map(|i| observe(addr!("obs", i), Bernoulli::new(0.5).unwrap(), true))
.collect();
let model = sequence_vec(models).map(|_| ());
let mut rng = StdRng::seed_from_u64(123);
let (_a, trace) = crate::runtime::handler::run(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
model,
);
assert!(trace.log_likelihood.is_finite());
}
}