Nested model specification

GraphPPL supports nested model specification, allowing hierarchical modeling and model specification. This means that any model that is defined in GraphPPL can be used as a submodel in another model. This allows us to write models that are more modular and reusable. This page will go over the syntax for nested model specification in GraphPPL and how to use it.

Markov Blankets

In GraphPPL, a model is defined as a collection of random variables and their dependencies. This means that there are internal variables of the model, and variables that communicate with the outside of the model. These boundary variables are called the Markov Blanket of a model, and we have to specify them when we use a model as a submodel. To specify the Markov Blanket of a model, we include their names in the model function definition. For example, we can define the well-known Gaussian-with-Controlled-Variance model as follows:

using GraphPPL
import GraphPPL: @model

@model function gcv(κ, ω, z, x, y)
    log_σ := κ * z + ω
    σ := exp(log_σ)
    y ~ Normal(x, σ)
end

Here, we see that the κ, ω, z, x and y variables define the boundary of the gcv submodel, with σ and log_σ as internal variables.

Invoking submodels

If we want to chain these gcv submodels together into a Hierarchical Gaussian Filter, we still use the ~ operator. Here, in the arguments to gcv, we specify all-but-one interface. GraphPPL will interpolate which interface is missing and assign it to the left-hand-side:

@model function hgf(κ, ω, θ, prior_x, depth)
    for i = 1:depth
        if i == 0
            means[i] ~ gcv(κ = κ, ω = ω, θ = θ, x = prior_x)
        else
            means[i] ~ gcv(κ = κ, ω = ω, θ = θ, x = means[i - 1])
        end
    end
end

Note that in our invocations of gcv, we haven't specified the y argument of the Markov Blanket. This is what is being recognized as the missing interface and GraphPPL will assign means[i] to y.