#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/docs/runtime/delegate.md"))]
use crate::core::address::Address;
use crate::core::distribution::Distribution;
use crate::runtime::handler::Handler;
use crate::runtime::trace::Trace;
pub trait Overrides<H: Handler> {
fn on_sample_f64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<f64>,
) -> f64 {
inner.on_sample_f64(addr, dist)
}
fn on_sample_bool(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<bool>,
) -> bool {
inner.on_sample_bool(addr, dist)
}
fn on_sample_u64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<u64>,
) -> u64 {
inner.on_sample_u64(addr, dist)
}
fn on_sample_usize(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<usize>,
) -> usize {
inner.on_sample_usize(addr, dist)
}
fn on_sample_i64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<i64>,
) -> i64 {
inner.on_sample_i64(addr, dist)
}
fn on_observe_f64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<f64>,
value: f64,
) {
inner.on_observe_f64(addr, dist, value)
}
fn on_observe_bool(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<bool>,
value: bool,
) {
inner.on_observe_bool(addr, dist, value)
}
fn on_observe_u64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<u64>,
value: u64,
) {
inner.on_observe_u64(addr, dist, value)
}
fn on_observe_usize(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<usize>,
value: usize,
) {
inner.on_observe_usize(addr, dist, value)
}
fn on_observe_i64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<i64>,
value: i64,
) {
inner.on_observe_i64(addr, dist, value)
}
fn on_factor(&mut self, inner: &mut H, logw: f64) {
inner.on_factor(logw)
}
fn finish(self, inner: H) -> Trace
where
Self: Sized,
{
inner.finish()
}
}
impl<H: Handler> Overrides<H> for () {}
#[derive(Clone, Debug)]
pub struct Delegate<H, O = ()> {
pub inner: H,
pub overrides: O,
}
impl<H> Delegate<H> {
pub fn new(inner: H) -> Self {
Delegate {
inner,
overrides: (),
}
}
}
impl<H, O> Delegate<H, O> {
pub fn with(inner: H, overrides: O) -> Self {
Delegate { inner, overrides }
}
}
impl<H: Handler, O: Overrides<H>> Handler for Delegate<H, O> {
fn on_sample_f64(&mut self, addr: &Address, dist: &dyn Distribution<f64>) -> f64 {
self.overrides.on_sample_f64(&mut self.inner, addr, dist)
}
fn on_sample_bool(&mut self, addr: &Address, dist: &dyn Distribution<bool>) -> bool {
self.overrides.on_sample_bool(&mut self.inner, addr, dist)
}
fn on_sample_u64(&mut self, addr: &Address, dist: &dyn Distribution<u64>) -> u64 {
self.overrides.on_sample_u64(&mut self.inner, addr, dist)
}
fn on_sample_usize(&mut self, addr: &Address, dist: &dyn Distribution<usize>) -> usize {
self.overrides.on_sample_usize(&mut self.inner, addr, dist)
}
fn on_sample_i64(&mut self, addr: &Address, dist: &dyn Distribution<i64>) -> i64 {
self.overrides.on_sample_i64(&mut self.inner, addr, dist)
}
fn on_observe_f64(&mut self, addr: &Address, dist: &dyn Distribution<f64>, value: f64) {
self.overrides
.on_observe_f64(&mut self.inner, addr, dist, value)
}
fn on_observe_bool(&mut self, addr: &Address, dist: &dyn Distribution<bool>, value: bool) {
self.overrides
.on_observe_bool(&mut self.inner, addr, dist, value)
}
fn on_observe_u64(&mut self, addr: &Address, dist: &dyn Distribution<u64>, value: u64) {
self.overrides
.on_observe_u64(&mut self.inner, addr, dist, value)
}
fn on_observe_usize(&mut self, addr: &Address, dist: &dyn Distribution<usize>, value: usize) {
self.overrides
.on_observe_usize(&mut self.inner, addr, dist, value)
}
fn on_observe_i64(&mut self, addr: &Address, dist: &dyn Distribution<i64>, value: i64) {
self.overrides
.on_observe_i64(&mut self.inner, addr, dist, value)
}
fn on_factor(&mut self, logw: f64) {
self.overrides.on_factor(&mut self.inner, logw)
}
fn finish(self) -> Trace {
self.overrides.finish(self.inner)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::addr;
use crate::core::distribution::*;
use crate::core::model::{factor, observe, pure, sample, Model};
use crate::runtime::handler::run;
use crate::runtime::interpreters::{PriorHandler, ReplayHandler, ScoreGivenTrace};
use crate::runtime::trace::ChoiceValue;
use rand::rngs::StdRng;
use rand::{RngCore, SeedableRng};
use std::cell::RefCell;
type Sites = (f64, bool, u64, usize, i64);
fn all_sites() -> Model<Sites> {
crate::prob! {
let f <- sample(addr!("f"), Normal::new(0.0, 1.0).unwrap());
let b <- sample(addr!("b"), Bernoulli::new(0.3).unwrap());
let u <- sample(addr!("u"), Poisson::new(2.5).unwrap());
let z <- sample(addr!("z"), Categorical::new(vec![0.2, 0.5, 0.3]).unwrap());
let i <- sample(addr!("i"), DiscreteUniform::new(-3, 3).unwrap());
observe(addr!("f_obs"), Normal::new(f, 1.0).unwrap(), 0.4);
observe(addr!("b_obs"), Bernoulli::new(if b { 0.8 } else { 0.2 }).unwrap(), true);
observe(addr!("u_obs"), Poisson::new(1.0 + u as f64).unwrap(), 3);
observe(addr!("z_obs"), Categorical::new(vec![0.6, 0.3, 0.1]).unwrap(), z);
observe(addr!("i_obs"), DiscreteUniform::new(i - 2, i + 4).unwrap(), i + 1);
factor(-0.5 * f * f);
pure((f, b, u, z, i))
}
}
fn assert_same_trace(a: &Trace, b: &Trace) {
let keys = |t: &Trace| t.choices.keys().cloned().collect::<Vec<_>>();
assert_eq!(keys(a), keys(b));
for (addr, ca) in &a.choices {
let cb = &b.choices[addr];
assert_eq!(ca.addr, cb.addr);
assert_eq!(ca.value, cb.value, "value at {}", addr);
assert_eq!(ca.logp.to_bits(), cb.logp.to_bits(), "logp at {}", addr);
}
assert_eq!(a.log_prior.to_bits(), b.log_prior.to_bits());
assert_eq!(a.log_likelihood.to_bits(), b.log_likelihood.to_bits());
assert_eq!(a.log_factors.to_bits(), b.log_factors.to_bits());
}
fn assert_every_site_recorded(t: &Trace) {
assert_eq!(t.choices.len(), 5);
assert!(t.get_f64(&addr!("f")).is_some());
assert!(t.get_bool(&addr!("b")).is_some());
assert!(t.get_u64(&addr!("u")).is_some());
assert!(t.get_usize(&addr!("z")).is_some());
assert!(t.get_i64(&addr!("i")).is_some());
for w in [t.log_prior, t.log_likelihood, t.log_factors] {
assert!(w.is_finite() && w != 0.0, "accumulator {}", w);
}
}
fn prior_run(seed: u64) -> (Sites, Trace) {
let mut rng = StdRng::seed_from_u64(seed);
run(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
all_sites(),
)
}
#[test]
fn delegate_new_matches_prior_handler() {
let mut rng_a = StdRng::seed_from_u64(11);
let (va, ta) = run(
PriorHandler {
rng: &mut rng_a,
trace: Trace::default(),
},
all_sites(),
);
let mut rng_b = StdRng::seed_from_u64(11);
let (vb, tb) = run(
Delegate::new(PriorHandler {
rng: &mut rng_b,
trace: Trace::default(),
}),
all_sites(),
);
assert_every_site_recorded(&ta);
assert_eq!(va, vb);
assert_same_trace(&ta, &tb);
assert_eq!(rng_a.next_u64(), rng_b.next_u64());
}
#[test]
fn delegate_new_matches_replay_handler() {
let (_, mut base) = prior_run(12);
base.choices.remove(&addr!("u"));
base.choices.remove(&addr!("i"));
let mut rng_a = StdRng::seed_from_u64(13);
let (va, ta) = run(
ReplayHandler {
rng: &mut rng_a,
base: base.clone(),
trace: Trace::default(),
},
all_sites(),
);
let mut rng_b = StdRng::seed_from_u64(13);
let (vb, tb) = run(
Delegate::new(ReplayHandler {
rng: &mut rng_b,
base,
trace: Trace::default(),
}),
all_sites(),
);
assert_every_site_recorded(&ta);
assert_eq!(va, vb);
assert_same_trace(&ta, &tb);
assert_eq!(rng_a.next_u64(), rng_b.next_u64());
}
#[test]
fn delegate_new_matches_score_given_trace() {
let (_, base) = prior_run(14);
let (va, ta) = run(
ScoreGivenTrace {
base: base.clone(),
trace: Trace::default(),
},
all_sites(),
);
let (vb, tb) = run(
Delegate::new(ScoreGivenTrace {
base,
trace: Trace::default(),
}),
all_sites(),
);
assert_every_site_recorded(&ta);
assert_eq!(va, vb);
assert_same_trace(&ta, &tb);
}
struct UsizeSites<'a>(&'a mut Vec<String>);
impl<H: Handler> Overrides<H> for UsizeSites<'_> {
fn on_sample_usize(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<usize>,
) -> usize {
let value = inner.on_sample_usize(addr, dist);
self.0.push(format!("sample {} = {}", addr, value));
value
}
fn on_observe_usize(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<usize>,
value: usize,
) {
self.0.push(format!("observe {} = {}", addr, value));
inner.on_observe_usize(addr, dist, value)
}
}
#[test]
fn overrides_see_their_own_sites_and_forward_the_rest() {
let mut seen = Vec::new();
let mut rng = StdRng::seed_from_u64(21);
let (v, t) = run(
Delegate::with(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
UsizeSites(&mut seen),
),
all_sites(),
);
let z = v.3;
assert_eq!(
seen,
[
format!("sample z = {}", z),
format!("observe z_obs = {}", z)
]
);
let (pv, pt) = prior_run(21);
assert_eq!(v, pv);
assert_same_trace(&t, &pt);
}
struct Pick(usize);
impl<R: RngCore> Overrides<PriorHandler<'_, R>> for Pick {
fn on_sample_usize(
&mut self,
inner: &mut PriorHandler<'_, R>,
addr: &Address,
dist: &dyn Distribution<usize>,
) -> usize {
let logp = dist.log_prob(&self.0);
inner.trace.log_prior += logp;
inner
.trace
.insert_choice(addr.clone(), ChoiceValue::Usize(self.0), logp);
self.0
}
}
#[test]
fn an_override_can_answer_a_site_itself() {
for pick in 0..3 {
let mut rng = StdRng::seed_from_u64(31);
let (v, t) = run(
Delegate::with(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
Pick(pick),
),
all_sites(),
);
assert_eq!(v.3, pick);
assert_eq!(t.get_usize(&addr!("z")), Some(pick));
let prior = [0.2_f64, 0.5, 0.3][pick].ln();
assert!((t.choices[&addr!("z")].logp - prior).abs() < 1e-12);
assert_every_site_recorded(&t);
let (_, rescored) = run(
ScoreGivenTrace {
base: t.clone(),
trace: Trace::default(),
},
all_sites(),
);
assert!((rescored.log_prior - t.log_prior).abs() < 1e-12);
assert!((rescored.log_likelihood - t.log_likelihood).abs() < 1e-12);
}
}
struct Tap<'a> {
name: &'static str,
log: &'a RefCell<Vec<String>>,
}
impl<H: Handler> Overrides<H> for Tap<'_> {
fn on_sample_f64(
&mut self,
inner: &mut H,
addr: &Address,
dist: &dyn Distribution<f64>,
) -> f64 {
self.log
.borrow_mut()
.push(format!("{}: sample {}", self.name, addr));
inner.on_sample_f64(addr, dist)
}
fn finish(self, inner: H) -> Trace {
self.log.borrow_mut().push(format!("{}: finish", self.name));
inner.finish()
}
}
#[test]
fn nested_delegates_compose() {
let log = RefCell::new(Vec::new());
let mut seen = Vec::new();
let mut rng = StdRng::seed_from_u64(41);
let prior = PriorHandler {
rng: &mut rng,
trace: Trace::default(),
};
let handler = Delegate::with(
Delegate::with(
Delegate::with(prior, UsizeSites(&mut seen)),
Tap {
name: "inner",
log: &log,
},
),
Tap {
name: "outer",
log: &log,
},
);
let (v, t) = run(handler, all_sites());
assert_eq!(
log.into_inner(),
[
"outer: sample f",
"inner: sample f",
"outer: finish",
"inner: finish"
]
);
assert_eq!(seen.len(), 2);
let (pv, pt) = prior_run(41);
assert_eq!(v, pv);
assert_same_trace(&t, &pt);
}
struct Bonus(f64);
impl<H: Handler> Overrides<H> for Bonus {
fn finish(self, inner: H) -> Trace {
let mut trace = inner.finish();
trace.log_factors += self.0;
trace
}
}
#[test]
fn a_finish_override_runs() {
let mut rng = StdRng::seed_from_u64(51);
let (_, t) = run(
Delegate::with(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
Bonus(2.0),
),
all_sites(),
);
let (_, pt) = prior_run(51);
assert_eq!(t.log_factors, pt.log_factors + 2.0);
assert_eq!(t.log_prior.to_bits(), pt.log_prior.to_bits());
assert_eq!(t.log_likelihood.to_bits(), pt.log_likelihood.to_bits());
assert_eq!(t.choices.len(), pt.choices.len());
}
}