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, false}(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 as well (a bit less generally) with the macro-free version

using DynamicPPL
using DynamicPPL.VarNamedTuples: NoTemplate

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

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

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

    # The final return statement should comprise both the original return
    # value and the updated varinfo.
    return nothing, varinfo
end

# The `false` type parameter here indicates that this model does not need
# threadsafe evaluation (see the threadsafe evaluation page for details)
gdemo2(x) = DynamicPPL.Model{false}(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, false}(gdemo2, (x = [1.5, 2.0],), NamedTuple(), DefaultContext())

We can sample from this model in the same way:

chain = sample(model2, MH(), 1000; progress=false)
╭─FlexiChain (1000 iterations, 1 chain) ───────────────────────────────────────
 ↓ iter  = 1:1000                                                             
 → chain = 1:1                                                                
                                                                              
 Parameters (2) ── AbstractPPL.VarName                                        
  Float64  s², m                                                              
                                                                              
 Extras (4)                                                                   
  Bool     accepted                                                           
  Float64  logprior, loglikelihood, logjoint                                  
╰──────────────────────────────────────────────────────────────────────────────╯

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

Back to top