Customizing the behaviour of the @model
with a custom backend
When creating the graphical model, GraphPPL
package uses several functions to decide what to do in specific situations, for example
- Is
x ~ SomeType(y, z)
a stochastic or deterministic relationship? For exampleSomeType
can be aGaussian
, in which case the answer
is obvious, but what is SomeType
is a Matrix
? Or what if a user specified const Matrix = Gaussian
?
- Should
x ~ Normal(a, b)
be interpreted asx ~ Normal(mean = a, variance = b)
orNormal(mean = a, standard_deviation = b)
? - Should
x_next ~ HierarchicalGaussianFilter(x_prev, tau)
create anAtomic
or aComposite
node forHierarchicalGaussianFilter
? - Should
x := x1 + x2 + x3 + x4
be replaced withx := sum(x1, x2, x3, x4)
orx := sum(sum(sum(x1, x2), x3), x4)
. Or left untouched? - What extra syntax transformations are allowed? For example should
not_x ~ ¬x
be interpreted as a boolean random variablex
with the¬
as a stochastic node
or it is just a function call?
It is not possible to resolve these issues on a syntax level, thus GraphPPL
requires a specific backend to resolve this information at run-time.
Default backend
For interactive purposes (plotting or testing) GraphPPL
implements a DefaultBackend
, which properly handles objects from Distributions.jl
. The @model
macro by itself is not exported by default. To use it explicitly simply call:
import GraphPPL: @model
GraphPPL.DefaultBackend
— TypeDefaultBackend
A default backend that is used in the GraphPPL.@model
macro when no backend is specified explicitly.
Recommended way of using GraphPPL
from a backend-specific inference package
A backend-specific inference package should implement its own backend structure together with its own @model
macro (or a different name) that would call the @model
macro from GraphPPL
with a specific package. Below is the list of backend-specific functions, each of which should be implemented in order for backend to be fully specified.
GraphPPL.model_macro_interior_pipelines
— Functionmodel_macro_interior_pipelines(backend)
Returns a collection of syntax transformation functions for the apply_pipeline
function based on a specific backend. The functions are being applied to the model in the model_macro_interior
macro body in the exact same order they are returned.
GraphPPL.NodeBehaviour
— TypeNodeBehaviour
Abstract type representing either Deterministic
or Stochastic
for a given object. By default is Deterministic
unless specified otherwise.
GraphPPL.NodeType
— TypeNodeType
Abstract type representing either Composite
or Atomic
trait for a given object. By default is Atomic
unless specified otherwise.
GraphPPL.aliases
— Functionaliases(backend, fform)
Returns a collection of aliases for fform
depending on the backend
.
GraphPPL.interfaces
— Functioninterfaces(backend, fform, ::StaticInt{N}) where N
Returns the interfaces for a given fform
and backend
with a given amount of interfaces N
.
GraphPPL.factor_alias
— Functionfactor_alias(backend, fform, interfaces)
Returns the alias for a given fform
and interfaces
with a given backend
.
GraphPPL.interface_aliases
— Functioninterface_aliases(backend, fform)
Returns the aliases for a given fform
and backend
.
GraphPPL.default_parametrization
— Functiondefault_parametrization(backend, fform, rhs)
Returns the default parametrization for a given fform
and backend
with a given rhs
.
GraphPPL.instantiate
— Functioninstantiate(::Type{Backend})
Instantiates a default backend object of the specified type. Should be implemented for all backends.
The GraphPPL.model_macro_interior
automatically creates a method for GraphPPL.default_backend
.
GraphPPL.default_backend
— Functiondefault_backend(model_function)
Returns a default backend for the given model function.
For inference backends, we recommend to implement the @model
macro using the following pattern:
GraphPPL.@model
— Macro@model function model_name(model_arguments)
...
end
Note that the @model
macro is not exported by default and the recommended way of using it is in the combination with some inference backend. The GraphPPL
package provides the DefaultGraphPPLBackend
structure for plotting and test purposes, but some backends may specify different behaviour for different structures. For example, the interface names of a node Normal
or its behaviour may (and should) depend on the specified backend.
The recommended way of using the GraphPPL.@model
macro from other backend-based packages is to define their own @model
macro, which will call the GraphPPL.model_macro_interior
function with the specified backend. For example
module SamplingBasedInference
struct SamplingBasedBackend end
macro model(model_specification)
return esc(GraphPPL.model_macro_interior(SamplingBasedBackend(), model_specification))
end
end
Read more about the backend inteface in the corresponding section of the documentation.
To use GraphPPL
package as a standalone package for plotting and testing, use the import GraphPPL: @model
explicitly to add the @model
macro to the current scope.
GraphPPL.model_macro_interior
— Functionmodel_macro_interior(backend, model_specification)
The function that translates the model_specification
code into a Julia compatible code block given some backend
. This function must be used within the @model
macro.