#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/docs/macros/README.md"))]
#[macro_export]
macro_rules! prob {
(@let [$($pat:tt)+] <- $expr:expr; $($rest:tt)*) => {
$crate::core::model::ModelExt::bind($expr, move |__prob_bound| {
let $($pat)+ = __prob_bound;
$crate::prob!($($rest)*)
})
};
(@let [$($pat:tt)+] = $expr:expr; $($rest:tt)*) => {
{ let $($pat)+ = $expr; $crate::prob!($($rest)*) }
};
(@let [$($pat:tt)*] $next:tt $($rest:tt)*) => {
$crate::prob!(@let [$($pat)* $next] $($rest)*)
};
(let $($rest:tt)*) => {
$crate::prob!(@let [] $($rest)*)
};
($expr:expr; $($rest:tt)*) => {
$crate::core::model::ModelExt::bind($expr, move |_| $crate::prob!($($rest)*))
};
($e:expr) => { $e };
}
#[macro_export]
macro_rules! plate {
($var:ident in $range:expr => $body:expr) => {
$crate::core::model::traverse_vec($range.collect::<Vec<_>>(), move |$var| $body)
};
}
#[macro_export]
macro_rules! scoped_addr {
($scope:expr, $name:expr) => {
$crate::core::address::Address::new(format!(
"{}::{}",
$scope,
$crate::core::address::escape_addr_segment(&format!("{}", $name))
))
};
($scope:expr, $name:expr, $($indices:expr),+) => {
$crate::core::address::Address::new(format!(
"{}::{}#{}",
$scope,
$crate::core::address::escape_addr_segment(&format!("{}", $name)),
$crate::core::address::escape_addr_segment(&format!("{}", format_args!($($indices),+)))
))
};
}
#[cfg(test)]
mod tests {
use crate::addr;
use crate::core::distribution::*;
use crate::core::model::{observe, pure, sample};
use crate::runtime::handler::run;
use crate::runtime::interpreters::PriorHandler;
use crate::runtime::trace::Trace;
use rand::rngs::StdRng;
use rand::SeedableRng;
#[test]
fn prob_macro_chains_computations() {
let model = prob!({
sample(addr!("x"), Normal::new(0.0, 1.0).unwrap());
let _x = pure(());
observe(addr!("y"), Normal::new(0.0, 1.0).unwrap(), 0.1);
pure(1)
});
let mut rng = StdRng::seed_from_u64(30);
let (val, trace) = run(
PriorHandler {
rng: &mut rng,
trace: Trace::default(),
},
model,
);
assert_eq!(val, 1);
assert!(trace.log_prior.is_finite());
assert!(trace.log_likelihood.is_finite());
}
#[test]
fn plate_macro_traverses_range() {
let xs = 0..5;
let model = plate!(i in xs => pure(i));
let (vals, _t) = run(
PriorHandler {
rng: &mut StdRng::seed_from_u64(31),
trace: Trace::default(),
},
model,
);
assert_eq!(vals, vec![0, 1, 2, 3, 4]);
}
#[test]
fn scoped_addr_formats_with_scope_and_indices() {
let a = scoped_addr!("scope", "name");
assert_eq!(a.as_str(), "scope::name");
let b = scoped_addr!("scope", "name", "{}", 3);
assert_eq!(b.as_str(), "scope::name#3");
}
#[test]
fn prob_macro_binds_tuple_patterns() {
let model = prob! {
let (a, b) <- pure((1i32, 2i32));
let c <- pure(a + b);
pure(c)
};
let (val, _t) = run(
PriorHandler {
rng: &mut StdRng::seed_from_u64(40),
trace: Trace::default(),
},
model,
);
assert_eq!(val, 3);
}
#[test]
fn prob_macro_binds_struct_and_mut_patterns() {
struct Point {
x: i32,
y: i32,
}
let model = prob! {
let Point { x, y } <- pure(Point { x: 3, y: 4 });
let mut acc <- pure(x);
let sum = {
acc += y;
acc
};
pure(sum)
};
let (val, _t) = run(
PriorHandler {
rng: &mut StdRng::seed_from_u64(41),
trace: Trace::default(),
},
model,
);
assert_eq!(val, 7);
}
#[test]
fn prob_macro_tuple_pattern_with_sampling() {
let model = prob! {
let x <- sample(addr!("x"), Normal::new(0.0, 1.0).unwrap());
let (lo, hi) <- pure((x - 1.0, x + 1.0));
pure(hi - lo)
};
let (val, _t) = run(
PriorHandler {
rng: &mut StdRng::seed_from_u64(42),
trace: Trace::default(),
},
model,
);
assert!((val - 2.0).abs() < 1e-12);
}
}