Manually Defining a Model

Traditionally, models in Turing are defined using the @model macro:

using Turing

@model function gdemo(x)
    # Set priors.
~ InverseGamma(2, 3)
    m ~ Normal(0, sqrt(s²))

    # Observe each value of x.
    x .~ Normal(m, sqrt(s²))

    return nothing
end

model = gdemo([1.5, 2.0])
DynamicPPL.Model{typeof(gdemo), (:x,), (), (), Tuple{Vector{Float64}}, Tuple{}, DynamicPPL.DefaultContext}(gdemo, (x = [1.5, 2.0],), NamedTuple(), DynamicPPL.DefaultContext())

The @model macro accepts a function definition and rewrites it such that call of the function generates a Model struct for use by the sampler.

However, models can be constructed by hand without the use of a macro. Taking the gdemo model above as an example, the macro-based definition can be implemented also (a bit less generally) with the macro-free version

using DynamicPPL

# Create the model function.
function gdemo2(model, varinfo, context, x)
    # Assume s² has an InverseGamma distribution.
    s², varinfo = DynamicPPL.tilde_assume!!(
        context, InverseGamma(2, 3), Turing.@varname(s²), varinfo
    )

    # Assume m has a Normal distribution.
    m, varinfo = DynamicPPL.tilde_assume!!(
        context, Normal(0, sqrt(s²)), Turing.@varname(m), varinfo
    )

    # Observe each value of x[i] according to a Normal distribution.
    for i in eachindex(x)
        _retval, varinfo = DynamicPPL.tilde_observe!!(
            context, Normal(m, sqrt(s²)), x[i], Turing.@varname(x[i]), varinfo
        )
    end

    # The final return statement should comprise both the original return
    # value and the updated varinfo.
    return nothing, varinfo
end
gdemo2(x) = Turing.Model(gdemo2, (; x))

# Instantiate a Model object with our data variables.
model2 = gdemo2([1.5, 2.0])
Model{typeof(gdemo2), (:x,), (), (), Tuple{Vector{Float64}}, Tuple{}, DefaultContext}(gdemo2, (x = [1.5, 2.0],), NamedTuple(), DefaultContext())

We can sample from this model in the same way:

chain = sample(model2, NUTS(), 1000; progress=false)
┌ Info: Found initial step size
└   ϵ = 0.80625
Chains MCMC chain (1000×14×1 Array{Float64, 3}):

Iterations        = 501:1:1500
Number of chains  = 1
Samples per chain = 1000
Wall duration     = 6.82 seconds
Compute duration  = 6.82 seconds
parameters        = s², m
internals         = lp, n_steps, is_accept, acceptance_rate, log_density, hamiltonian_energy, hamiltonian_energy_error, max_hamiltonian_energy_error, tree_depth, numerical_error, step_size, nom_step_size

Summary Statistics
  parameters      mean       std      mcse   ess_bulk   ess_tail      rhat   e ⋯
      Symbol   Float64   Float64   Float64    Float64    Float64   Float64     ⋯

          s²    1.9300    1.5977    0.0809   484.2915   498.0668    1.0039     ⋯
           m    1.1994    0.7943    0.0415   408.7467   254.5830    1.0069     ⋯
                                                                1 column omitted

Quantiles
  parameters      2.5%     25.0%     50.0%     75.0%     97.5%
      Symbol   Float64   Float64   Float64   Float64   Float64

          s²    0.6071    1.0266    1.4945    2.2515    6.3826
           m   -0.4355    0.7312    1.1992    1.6738    2.8152

The subsequent pages in this section will show how the @model macro does this behind-the-scenes.

Back to top