library(monty)
10 The monty DSL
The monty
DSL provides a more intuitive way to define statistical models with monty
. It is currently relatively basic and focuses on providing support for defining priors in Bayesian models. It fully support differentiability allowing to use gradient based samplers on these models.
10.1 A simple example
In chapter 4 of Statistical Rethinking, we build a regression model of height with parameters \(\alpha\), \(\beta\) and \(\sigma\). We can define the model for the prior probability of this model in monty by running
<- monty_dsl({
prior ~ Normal(178, 20)
alpha ~ Normal(0, 10)
beta ~ Uniform(0, 50)
sigma })
This will define a new monty_model()
object that represents the prior, but with all the bits that we might need depending on how we want to use it:
We have model parameters
$parameters
prior#> [1] "alpha" "beta" "sigma"
These are defined in the order that they appear in your definition (so alpha
is first and sigma
is last)
We can compute the domain for your model:
$domain
prior#> [,1] [,2]
#> alpha -Inf Inf
#> beta -Inf Inf
#> sigma 0 50
We can draw samples from the model if we provide a monty_rng
object
<- monty_rng_create()
rng <- monty_model_direct_sample(prior, rng)
theta
theta#> [1] 155.30396 -12.79361 27.66180
We can compute the (log) density at a point in parameter space
$density(theta)
prior#> [1] -12.51049
The computed properties for the model are:
$properties
prior#>
#> ── <monty_model_properties> ────────────────────────────────────────────────────
#> • has_gradient: `TRUE`
#> • has_direct_sample: `TRUE`
#> • is_stochastic: `FALSE`
#> • has_parameter_groups: `FALSE`
#> • has_observer: `FALSE`
#> • allow_multiple_parameters: `TRUE`
10.2 Distribution functions
In the above example we use distribution functions for the normal and uniform distributions. The distribution functions available for the monty
DSL are the same as those for the odin
DSL, the full list of which can be found here.
10.3 Dependent distributions
It is possible within the DSL to have the distribution of parameters to depend upon the value of other parameters:
<- monty_dsl({
m ~ Normal(0, 1)
a ~ Normal(a, 1)
b })
This is particularly useful for the implementation of hyperpriors when using the DSL to define priors.
Order of equations is important when using dependent distributions in the monty
DSL! You cannot have the distribution of a parameter depend upon a parameter that is defined later. Thus rewriting the above example as
m <- monty_dsl({
b ~ Normal(a, 1)
a ~ Normal(0, 1)
})
would produce an error.
10.4 Calculations in the DSL
Sometimes it will be useful to perform calculations in the code; you can do this with assignments. Most trivially, giving names to numbers may help make code more understandable:
<- monty_dsl({
m <- 10
mu <- 2
sd ~ Normal(mu, sd)
a })
You can also use this to do things like:
<- monty_dsl({
m ~ Normal(0, 1)
a ~ Normal(0, 1)
b <- (a + b) / 2
mu ~ Normal(mu, 1)
c })
Where c
is drawn from a normal distribution with a mean that is the average of a
and b
.
10.5 Pass in fixed data
You can also pass in a list of data with values that should be available in the DSL code. For example, our first example:
<- monty_dsl({
prior ~ Normal(178, 20)
alpha ~ Normal(0, 10)
beta ~ Uniform(0, 50)
sigma })
Might be written as
<- list(alpha_mean = 170, alpha_sd = 20,
fixed beta_mean = 0, beta_sd = 10,
sigma_max = 50)
<- monty_dsl({
prior ~ Normal(alpha_mean, alpha_sd)
alpha ~ Normal(beta_mean, beta_sd)
beta ~ Uniform(0, sigma_max)
sigma fixed = fixed) },
Values you pass in this way are fixed (hence the name!) and cannot be modified after the model object is created.