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 exampleSomeTypecan 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 anAtomicor aCompositenode forHierarchicalGaussianFilter? - Should
x := x1 + x2 + x3 + x4be 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 ~ ¬xbe interpreted as a boolean random variablexwith 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: @modelGraphPPL.DefaultBackend — TypeDefaultBackendA 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 — TypeNodeBehaviourAbstract type representing either Deterministic or Stochastic for a given object. By default is Deterministic unless specified otherwise.
GraphPPL.NodeType — TypeNodeTypeAbstract 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 NReturns 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.
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)
...
endNote 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
endRead 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.