Developers guide
This page is aimed at developers of inference backends who aim to integrate GraphPPL
into their packages. GraphPPL
uses the MetaGraphsNext
package to represent a factor graph model as a graph. In GraphPPL
, both variables and factors are represented by nodes, and the edges denote the inclusion of variables in factors.
Model Creation
A model in GraphPPL
is represented by the GraphPPL.Model
structure.
GraphPPL.Model
— TypeModel(graph::MetaGraph)
A structure representing a probabilistic graphical model. It contains a MetaGraph
object representing the factor graph and a Base.RefValue{Int64}
object to keep track of the number of nodes in the graph.
Fields:
graph
: AMetaGraph
object representing the factor graph.plugins
: APluginsCollection
object representing the plugins enabled in the model.backend
: ABackend
object representing the backend used in the model.counter
: ABase.RefValue{Int64}
object keeping track of the number of nodes in the graph.
Any model is a bipartite graph of variable and factor nodes, with edges denoting which variables are used in which factors. Models can be indexed with GraphPPL.NodeLabel
structures, which is a unique identifier of every variable and factor node composed of its name and a global counter. Every GraphPPL.NodeLabel
points to a GraphPPL.NodeData
, which contains all relevant information to do Bethe Free Energy minimization in the factor graph. Edges in the graph can be accessed by querying the model with a NodeLabel
pair of an edge that exists. Note that both variable and factor nodes are represented by GraphPPL.NodeData
structures. To retrieve whether or not a node is a variable or a factor, we can use the is_variable
and is_factor
functions:
GraphPPL.is_variable
— Functionis_variable(nodedata::NodeData)
Returns true
if the node data is associated with a variable node, false
otherwise. See also: is_factor
,
GraphPPL.is_factor
— Functionis_factor(nodedata::NodeData)
Returns true
if the node data is associated with a factor node, false
otherwise. See also: is_variable
,
The representation of a GraphPPL.Model
is a bipartite graph where both variables and factor functions are represented as nodes. The internal representation is therefore not a Forney-style factor graph.
Contexts, Submodels and retrieving NodeLabels
After creating a GraphPPL.Model
structure, it is important to know about the attached Context
. The Context
structure contains all variable and factor nodes in the scope of the model, and contains a Context
stucture for all submodels. The context of a model can be accessed by the GraphPPL.getcontext()
function:
GraphPPL.getcontext
— Functiongetcontext(model::Model)
Retrieves the context of a model. The context of a model contains the complete hierarchy of variables and factor nodes. Additionally, contains all child submodels and their respective contexts. The Context supplies a mapping from symbols to GraphPPL.NodeLabel
structures with which the model can be queried.
Contexts can be accessed like dictionaries, and will point to NodeLabel
structures that can be used to query the graph. As a variable with the same name can also live in submodels, we nest the Context
structures in the same hierarchy as the submodels themselves. Variables can be retrieved from the Context using a Symbol
, whereas factors and submodels can be retrieved with their type and index. For example, to access the first Normal
factor in the context, we can use the following syntax:
context[Normal, 1]
Distributions.Normal_4
Because on any level, submodels are treated as factors, we can also access submodels in the same way. For example, to access the Normal
factor in the first submodel, we can use the following syntax:
context[submodel_name, 1][Normal, 1]
Variable Creation
Variables in the graph can be created by the GraphPPL.getorcreate!
function, that takes the model, the context, the name and the index of the variable, as well as node creation options (such as additional information that should be saved in nodes).
GraphPPL.getorcreate!
— Functiongetorcreate!(model::Model, context::Context, options::NodeCreationOptions, name, index)
Get or create a variable (name) from a factor graph model and context, using an index if provided.
This function searches for a variable (name) in the factor graph model and context specified by the arguments model
and context
. If the variable exists, it returns it. Otherwise, it creates a new variable and returns it.
Arguments
model::Model
: The factor graph model to search for or create the variable in.context::Context
: The context to search for or create the variable in.options::NodeCreationOptions
: Options for creating the variable. Must be aNodeCreationOptions
object.name
: The variable (name) to search for or create. Must be a symbol.index
: Optional index for the variable. Can be an integer, a collection of integers, ornothing
. If the index isnothing
creates a single variable.
If the index is an integer creates a vector-like variable. If the index is a collection of integers creates a tensor-like variable.
Returns
The variable (name) found or created in the factor graph model and context.
Model macro
The GraphPPL.jl
does not export the @model
macro by default. For interactive usages (e.g. testing or plotting) GraphPPL.jl
implements GraphPPL.DefaultBackend
, but any downstream packages must define their own @model
macro and implement their custom backend.
import GraphPPL: @model
Piecing everying together
In this section we will create a factor graph from scratch, materializing the underlying factor graph and applying constraints. First, let's define a model, we'll use the gcv
model from the Nested Models section:
using GraphPPL, Distributions
@model function gcv(κ, ω, z, x, y)
log_σ := κ * z + ω
σ := exp(log_σ)
y ~ Normal(x, σ)
end
Let's also define a mean-field constraint around the Normal
node:
constraints = @constraints begin
q(x, y, σ) = q(x)q(y)q(σ)
end
Constraints:
q(x, y, σ) = q(x)q(y)q(σ)
This defines the gcv
submodel, but now we have to materialize this model. Let's greate a model and hook up all interfaces to variables that will later have to be supplied by the user.
GraphPPL.create_model
— Functioncreate_model([callback], generator::ModelGenerator)
Create a model from the ModelGenerator
. Accepts an optional callback that can be used to inject extra keyword arguments into the model creation process by downstream packages. For example:
using GraphPPL, Distributions
GraphPPL.@model function beta_bernoulli(y, a, b)
θ ~ Beta(a, b)
for i in eachindex(y)
y[i] ~ Bernoulli(θ)
end
end
data_for_y = rand(Bernoulli(0.5), 100)
model = GraphPPL.create_model(beta_bernoulli(a = 1, b = 1)) do model, ctx
# Inject the data into the model
y = GraphPPL.datalabel(model, ctx, GraphPPL.NodeCreationOptions(kind = GraphPPL.VariableKindData), :y, data_for_y)
return (; y = y, )
end
model isa GraphPPL.Model
# output
true
model = GraphPPL.create_model(GraphPPL.with_plugins(gcv(), GraphPPL.PluginsCollection(GraphPPL.VariationalConstraintsPlugin(constraints)))) do model, context
return (;
κ = GraphPPL.getorcreate!(model, context, GraphPPL.NodeCreationOptions(kind = :data, factorized = true), :κ, nothing),
ω = GraphPPL.getorcreate!(model, context, GraphPPL.NodeCreationOptions(kind = :data, factorized = true), :ω, nothing),
z = GraphPPL.getorcreate!(model, context, GraphPPL.NodeCreationOptions(kind = :data, factorized = true), :z, nothing),
x = GraphPPL.getorcreate!(model, context, GraphPPL.NodeCreationOptions(kind = :data, factorized = true), :x, nothing),
y = GraphPPL.getorcreate!(model, context, GraphPPL.NodeCreationOptions(kind = :data, factorized = true), :y, nothing)
)
end;
GraphPPL.Model{MetaGraphsNext.MetaGraph{Int64, Graphs.SimpleGraphs.SimpleGraph{Int64}, GraphPPL.NodeLabel, GraphPPL.NodeData, GraphPPL.EdgeLabel, GraphPPL.Context, MetaGraphsNext.var"#4#8", Float64}, GraphPPL.PluginsCollection{Tuple{GraphPPL.VariationalConstraintsPlugin{GraphPPL.Constraints{Vector{GraphPPL.FactorizationConstraint}, Vector{GraphPPL.MarginalFormConstraint}, Vector{GraphPPL.MessageFormConstraint}, Dict{Function, GraphPPL.GeneralSubModelConstraints}, Dict{GraphPPL.FactorID, GraphPPL.SpecificSubModelConstraints}}}}}, GraphPPL.DefaultBackend}(Meta graph based on a Graphs.SimpleGraphs.SimpleGraph{Int64} with vertex labels of type GraphPPL.NodeLabel, vertex metadata of type GraphPPL.NodeData, edge metadata of type GraphPPL.EdgeLabel, graph metadata given by GraphPPL.Context(0, Main.var"Main".gcv, "", nothing, {exp = 1, Distributions.Normal = 1, + = 1}, {}, {(+, 1) = +_8, (Distributions.Normal, 1) = Distributions.Normal_11, (exp, 1) = exp_10}, {:σ = σ_9, :anonymous_var_graphppl = anonymous_var_graphppl_7, :y = y_5, :κ = κ_1, :ω = ω_2, :log_σ = log_σ_6, :z = z_3, :x = x_4}, {}, {}, {}, Base.RefValue{Any}(y_5)), and default weight 1.0, GraphPPL.PluginsCollection{Tuple{GraphPPL.VariationalConstraintsPlugin{GraphPPL.Constraints{Vector{GraphPPL.FactorizationConstraint}, Vector{GraphPPL.MarginalFormConstraint}, Vector{GraphPPL.MessageFormConstraint}, Dict{Function, GraphPPL.GeneralSubModelConstraints}, Dict{GraphPPL.FactorID, GraphPPL.SpecificSubModelConstraints}}}}}((GraphPPL.VariationalConstraintsPlugin{GraphPPL.Constraints{Vector{GraphPPL.FactorizationConstraint}, Vector{GraphPPL.MarginalFormConstraint}, Vector{GraphPPL.MessageFormConstraint}, Dict{Function, GraphPPL.GeneralSubModelConstraints}, Dict{GraphPPL.FactorID, GraphPPL.SpecificSubModelConstraints}}}(Constraints:
q(x, y, σ) = q(x)q(y)q(σ)
),)), GraphPPL.DefaultBackend(), Base.RefValue{Int64}(11))
Now we have a fully materialized model that can be passed to an inference engine. Factorization constraints are saved in two ways: as a tuple of lists of indices of interfaces that represent the individual clusters (e.g. ([1], [2, 3])
) and as a BoundedBitSetTuple. The BoundedBitSetTuple
is a more efficient way to store the factorization constraints, which stores a BitMatrix
under the hood representing the factorization clusters. Both can be accessed by the GraphPPL.getextra
function:
context = GraphPPL.getcontext(model)
node = context[Normal, 1]
@show GraphPPL.getextra(model[node], :factorization_constraint_indices)
@show GraphPPL.getextra(model[node], :factorization_constraint_bitset)
GraphPPL.getextra(model[node], :factorization_constraint_indices) = ([1], [2], [3])
GraphPPL.getextra(model[node], :factorization_constraint_bitset) = BitSetTuples.BoundedBitSetTuple(Bool[1 0 0; 0 1 0; 0 0 1])
ResizableArrays
GraphPPL
uses ResizableArrays
to store arrays of random variables. A ResizableArray
is a mutable array that can grow dynamically when data is assigned to it. This is why the x[i] ~ Normal(0, 1)
syntax is allowed in GraphPPL
; the ResizableArray
x
will check if i
is a valid index and grow the array if necessary.
GraphPPL.ResizableArray
— TypeResizableArray{T, V, N} <: AbstractArray{T, N}
A ResizableArray is an array that can grow in any dimension. It handles like a regular AbstractArray when calling getindex
, but for setindex!
it will automatically resize the array if the index is out of bounds. It is also possible to iterate over the array in the same way as for a regular array. The data is stored internally as a recursive vector. The depth of the recursion is fixed at construction time and cannot be changed.
Constructor
ResizableArray(::Type{T})
: Create an empty resizable array of typeT
with depth 1, similar to a vector.ResizableArray(::Type{T}, ::Val{N})
: Create an empty resizable array of typeT
with depthN
, similar to AbstractArray{T, N}.
ResizableArray
is a subtype of AbstractArray
, and implements all the functions that are expected from an array. Note that size
returns the largest size of the array across each dimension, so an array of size (2, 3)
does not necessarily has to have an element stored at index (2, 3)
, instead there exists a vector of length 3 along the second dimension.
Model creation engine internal
GraphPPL.Context
— TypeContext
Contains all information about a submodel in a probabilistic graphical model.
GraphPPL.ModelGenerator
— TypeModelGenerator(model, kwargs, plugins)
The ModelGenerator
structure is used to lazily create the model with the given model
and kwargs
and plugins
.
GraphPPL.FactorID
— TypeFactorID(fform, index)
A unique identifier for a factor node in a probabilistic graphical model.
GraphPPL.NodeData
— TypeNodeData(context, properties, plugins)
Data associated with a node in a probabilistic graphical model. The context
field stores the context of the node. The properties
field stores the properties of the node. The plugins
field stores additional properties of the node depending on which plugins were enabled.
GraphPPL.NodeLabel
— TypeNodeLabel(name, global_counter::Int64)
Unique identifier for a node (factor or variable) in a probabilistic graphical model.
GraphPPL.EdgeLabel
— TypeEdgeLabel(symbol, index)
A unique identifier for an edge in a probabilistic graphical model.
GraphPPL.ProxyLabel
— TypeProxyLabel(name, index, proxied)
A label that proxies another label in a probabilistic graphical model. The proxied objects must implement the is_proxied(::Type) = True()
. The proxy labels may spawn new variables in a model, if maycreate
is set to True()
.
GraphPPL.Splat
— TypeSplat{T}
A type used to represent splatting in the model macro. Any call on the right hand side of ~ that uses splatting will be wrapped in this type.
GraphPPL.indexed_last
— FunctionSimilar to Base.last
when applied on ProxyLabel
, but also applies checked_getindex
while unrolling
GraphPPL.lift_index
— FunctionThe lift_index
function "lifts" (or tracks) the index that is going to be used to determine the shape of the container upon creation for a variable during the unrolling of the ProxyLabel
. This index is used only if the container is set to be created and is not used if variable container already exists.
GraphPPL.datalabel
— Functiondatalabel(model, context, options, name, collection = MissingCollection())
A function for creating proxy data labels to pass into the model upon creation. Can be useful in combination with ModelGenerator
and create_model
.
GraphPPL.StaticInterfaces
— TypeA structure that holds interfaces of a node in the type argument I
. Used for dispatch.
GraphPPL.VariableRef
— TypeVariableRef(model::Model, context::Context, name::Symbol, index, external_collection = nothing)
VariableRef
implements a lazy reference to a variable in the model. The reference does not create an actual variable in the model immediatelly, but postpones the creation until strictly necessarily, which is hapenning inside the unroll
function. The postponed creation allows users to define pass a single variable into a submodel, e.g. y ~ submodel(x = x)
, but use it as an array inside the submodel, e.g. y[i] ~ Normal(x[i], 1.0)
.
Optionally accepts an external_collection
, which defines the upper limit on the shape of the underlying collection. For example, an external collection [ 1, 2, 3 ]
can be used both as y ~ ...
and y[i] ~ ...
, but not as y[i, j] ~ ...
. By default, the MissingCollection
is used for the external_collection
, which does not restrict the shape of the underlying collection.
The index
is always a Tuple
. By default, (nothing, )
is used, to indicate empty indices with no restrictions on the shape of the underlying collection. If "non-nothing" index is supplied, e.g. (1, )
the shape of the udnerlying collection will be fixed to match the index (1-dimensional in case of (1, )
, 2-dimensional in case of (1, 1)
and so on).
GraphPPL.makevarref
— Functionmakevarref(fform::F, model::Model, context::Context, options::NodeCreationOptions, name::Symbol, index::Tuple)
A function that creates VariableRef
, but takes the fform
into account. When fform
happens to be Atomic
creates the underlying variable immediatelly without postponing. When fform
is Composite
does not create the actual variable, but waits until strictly necessarily.
GraphPPL.MissingCollection
— TypeA placeholder collection for VariableRef
when the actual external collection is not yet available.
GraphPPL.VariableNodeProperties
— TypeVariableNodeProperties(name, index, kind, link, value)
Data associated with a variable node in a probabilistic graphical model.
GraphPPL.FactorNodeProperties
— TypeFactorNodeProperties(fform, neighbours)
Data associated with a factor node in a probabilistic graphical model.
GraphPPL.VarDict
— TypeVarDict
A recursive dictionary structure that contains all variables in a probabilistic graphical model. Iterates over all variables in the model and their children in a linear fashion, but preserves the recursive nature of the actual model.
GraphPPL.AnonymousVariable
— TypeAnonymousVariable(model, context)
Defines a lazy structure for anonymous variables. The actual anonymous variables materialize only in make_node!
upon calling, because it needs arguments to the make_node!
in order to create proper links.
GraphPPL.NodeDataExtraKey
— TypeA compile time key to access the extra
properties of the NodeData
structure.
GraphPPL.IndexedVariable
— TypeIndexedVariable(name, index)
IndexedVariable
represents a reference to a variable named name
with index index
.
GraphPPL.VariableKindRandom
— ConstantDefines a random
(or latent
) kind for a variable in a probabilistic graphical model.
GraphPPL.VariableKindData
— ConstantDefines a data
kind for a variable in a probabilistic graphical model.
GraphPPL.VariableKindConstant
— ConstantDefines a constant
kind for a variable in a probabilistic graphical model.
GraphPPL.VariableKindUnknown
— ConstantPlaceholder for a variable kind in a probabilistic graphical model.
GraphPPL.Deterministic
— TypeDeterministic
Deterministic
object used to parametrize factor node object with determinstic type of relationship between variables.
GraphPPL.Stochastic
— TypeStochastic
Stochastic
object used to parametrize factor node object with stochastic type of relationship between variables.
GraphPPL.Atomic
— TypeAtomic
Atomic
object used as a trait of structs and functions that are composed of a single node and are therefore materialized as a single node in the factor graph.
GraphPPL.Composite
— TypeComposite
Composite
object used as a trait of structs and functions that are composed of multiple nodes and therefore implement make_node!
.
GraphPPL.NodeCreationOptions
— TypeNodeCreationOptions(namedtuple)
Options for creating a node in a probabilistic graphical model. These are typically coming from the where {}
block in the @model
macro, but can also be created manually. Expects a NamedTuple
as an input.
GraphPPL.variable_nodes
— FunctionA version variable_nodes(model)
that uses a callback function to process the variable nodes. The callback function accepts both the label and the node data.
GraphPPL.factor_nodes
— FunctionA version factor_nodes(model)
that uses a callback function to process the factor nodes. The callback function accepts both the label and the node data.
GraphPPL.missing_interfaces
— Functionmissing_interfaces(node_type, val, known_interfaces)
Returns the interfaces that are missing for a node. This is used when inferring the interfaces for a node that is composite.
Arguments
node_type
: The type of the node as a Function object.val
: The value of the amount of interfaces the node is supposed to have. This is aStatic.StaticInt
object.known_interfaces
: The known interfaces for the node.
Returns
missing_interfaces
: AVector
of the missing interfaces.
GraphPPL.hasextra
— Functionhasextra(node::NodeData, key::Symbol)
Checks if NodeData
has an extra property with the given key.
GraphPPL.getextra
— Functiongetextra(node::NodeData, key::Symbol, [ default ])
Returns the extra property with the given key. Optionally, if the property does not exist, returns the default value.
GraphPPL.setextra!
— Functionsetextra!(node::NodeData, key::Symbol, value)
Sets the extra property with the given key to the given value.
GraphPPL.make_node!
— Functionmake_node!
Make a new factor node in the Model and specified Context, attach it to the specified interfaces, and return the interface that is on the lhs of the ~
operator.
Arguments
model::Model
: The model to add the node to.ctx::Context
: The context in which to add the node.fform
: The function that the node represents.lhs_interface
: The interface that is on the lhs of the~
operator.rhs_interfaces
: The interfaces that are the arguments of fform on the rhs of the~
operator.__parent_options__::NamedTuple = nothing
: The options to attach to the node.__debug__::Bool = false
: Whether to attach debug information to the factor node.
GraphPPL.add_atomic_factor_node!
— Functionadd_atomic_factor_node!(model::Model, context::Context, options::NodeCreationOptions, fform)
Add an atomic factor node to the model with the given name. The function generates a new symbol for the node and adds it to the model with the generated symbol as the key and a FactorNodeData
struct.
Args: - model::Model
: The model to which the node is added. - context::Context
: The context to which the symbol is added. - options::NodeCreationOptions
: The options for the creation process. - fform::Any
: The functional form of the node.
Returns: - The generated label for the node.
GraphPPL.add_toplevel_model!
— FunctionAdd the fform
as the toplevel model to the model
and context
with the specified interfaces
. Calls the postprocess logic for the attached plugins of the model. Should be called only once for a given Model
object.
GraphPPL.add_variable_node!
— Functionadd_variable_node!(model::Model, context::Context, options::NodeCreationOptions, name::Symbol, index)
Add a variable node to the model with the given name
and index
. This function is unsafe (doesn't check if a variable with the given name already exists in the model).
Args: - model::Model
: The model to which the node is added. - context::Context
: The context to which the symbol is added. - options::NodeCreationOptions
: The options for the creation process. - name::Symbol
: The ID of the variable. - index
: The index of the variable.
Returns: - The generated symbol for the variable.
GraphPPL.add_composite_factor_node!
— FunctionAdd a composite factor node to the model with the given name.
The function generates a new symbol for the node and adds it to the model with the generated symbol as the key and a NodeData
struct with is_variable
set to false
and node_name
set to the given name.
Args: - model::Model
: The model to which the node is added. - parent_context::Context
: The context to which the symbol is added. - context::Context
: The context of the composite factor node. - node_name::Symbol
: The name of the node.
Returns: - The generated id for the node.
GraphPPL.copy_markov_blanket_to_child_context
— Functioncopy_markov_blanket_to_child_context(child_context::Context, interfaces::NamedTuple)
Copy the variables in the Markov blanket of a parent context to a child context, using a mapping specified by a named tuple.
The Markov blanket of a node or model in a Factor Graph is defined as the set of its outgoing interfaces. This function copies the variables in the Markov blanket of the parent context specified by the named tuple interfaces
to the child context child_context
, by setting each child variable in child_context.individual_variables
to its corresponding parent variable in interfaces
.
Arguments
child_context::Context
: The child context to which to copy the Markov blanket variables.interfaces::NamedTuple
: A named tuple that maps child variable names to parent variable names.
GraphPPL.generate_nodelabel
— Functiongenerate_nodelabel(model::Model, name::Symbol)
Generate a new NodeLabel
object with a unique identifier based on the specified name and the number of nodes already in the model.
Arguments:
model
: AModel
object representing the probabilistic graphical model.name
: A symbol representing the name of the node.variable_type
: A UInt8 representing the type of the variable. 0 = factor, 1 = individual variable, 2 = vector variable, 3 = tensor variableindex
: An integer or tuple of integers representing the index of the variable.
GraphPPL.FunctionalIndex
— TypeFunctionalIndex
A special type of an index that represents a function that can be used only in pair with a collection. An example of a FunctionalIndex
can be firstindex
or lastindex
, but more complex use cases are possible too, e.g. firstindex + 1
. Important part of the implementation is that the resulting structure is isbitstype(...) = true
, that allows to store it in parametric type as valtype. One use case for this structure is to dispatch on and to replace begin
or end
(or more complex use cases, e.g. begin + 1
).
GraphPPL.FunctionalRange
— TypeFunctionalRange(left, range)
A range can handle FunctionalIndex
as one of (or both) the bounds.
julia> first = GraphPPL.FunctionalIndex{:begin}(firstindex)
(begin)
julia> last = GraphPPL.FunctionalIndex{:end}(lastindex)
(end)
julia> range = GraphPPL.FunctionalRange(first + 1, last - 1)
((begin) + 1):((end) - 1)
julia> [ 1.0, 2.0, 3.0, 4.0 ][range]
2-element Vector{Float64}:
2.0
3.0
Model macro internals
GraphPPL.combine_args
— Functioncombine_args(args::Vector, kwargs::Nothing)
Combines a vector of arguments into a single expression.
Arguments
args::Vector
: The vector of arguments.kwargs::Nothing
: The keyword arguments. This argument is alwaysnothing
.
Returns
An Expr
with the combined arguments.
combine_args(args::Vector, kwargs::Vector)
Combines a vector of arguments and a vector of keyword arguments into a single expression.
Arguments
args::Vector
: The vector of arguments.kwargs::Vector
: The vector of keyword arguments.
Returns
An Expr
with the combined arguments and keyword arguments.
combine_args(args::Nothing, kwargs::Nothing)
Returns nothing
.
Arguments
args::Nothing
: The arguments. This argument is alwaysnothing
.kwargs::Nothing
: The keyword arguments. This argument is alwaysnothing
.
GraphPPL.convert_local_statement
— Functionconvert_local_statement(expr::Expr)
Converts a statement with the local
keyword to the creation of an additional variable and the inclusion of thie variable in the subsequent tilde expression.
GraphPPL.add_get_or_create_expression
— Functionadd_get_or_create_expression(e::Expr)
Add code to get or create a variable in the graph. The code generated by this function ensures that the left-hand-side is always defined in the local scope and can be used in make_node!
afterwards.
Arguments
e::Expr
: The expression to modify.
Returns
A quote
block with the modified expression.
GraphPPL.keyword_expressions_to_named_tuple
— Functionkeyword_expressions_to_named_tuple(keywords::Vector)
Converts a vector of keyword expressions to a named tuple.
Arguments
keywords::Vector
: The vector of keyword expressions.
Returns
NamedTuple
: The named tuple.
Examples
julia> keyword_expressions_to_named_tuple([:($(Expr(:kw, :in1, :y))), :($(Expr(:kw, :in2, :z)))])
(in1 = y, in2 = z)
GraphPPL.convert_anonymous_variables
— Functionconvert_anonymous_variables(e::Expr)
Convert a function argument in the right-hand side of an expression to an anonymous variable. This function is used to convert function calls in the arguments of node creations to anonymous variables in the graph.
Example
```jldoctest
julia> convert_anonymous_variables(:(x ~ Normal(μ, sqrt(σ2)) where {created_by=:(Normal(μ, sqrt(σ2)))}))
:(x ~ (Normal(μ, anon_1 ~ (sqrt(σ2) where {anonymous = true, created_by = :(Normal(μ, sqrt(σ2)))})) where (created_by = :(Normal(μ, sqrt(σ2))))))
```
GraphPPL.is_kwargs_expression
— Functionis_kwargs_expression(e::Expr)
Returns true
if the given expression e
is a keyword argument expression, i.e., if its head is either :kw
or :parameters
.
GraphPPL.convert_to_kwargs_expression
— Functionconvert_to_kwargs_expression(expr::Expr)
Convert an expression to a keyword argument expression. This function is used in the conversion of tilde and dot-tilde expressions to ensure that the arguments are passed as keyword arguments.
GraphPPL.convert_deterministic_statement
— Functionconvert_deterministic_statement(expr::Expr)
Convert a deterministic statement to a tilde statement with the is_deterministic
option set to true
.
GraphPPL.proxy_args
— FunctionConverts an expression into its proxied equivalent. Used to pass variables in sub-models and create a chain of proxied labels.
julia> x = GraphPPL.NodeLabel(:x, 1)
x_1
julia> GraphPPL.proxy_args(:(y = x))
:(y = GraphPPL.proxylabel(:x, x, nothing, GraphPPL.False()))
GraphPPL.save_expression_in_tilde
— Functionsave_expression_in_tilde(expr::Expr)
Save the expression found in the tilde syntax in the created_by
field of the expression. This function also ensures that the where
clause is always present in the tilde syntax.
GraphPPL.convert_meta_object
— Functionconvert_meta_object(e::Expr)
Converts a variable meta or a factor meta call on the left hand side of a meta specification to a GraphPPL.MetaObject
.
Arguments
e::Expr
: The expression to convert.
Returns
Expr
: The resulting expression with the variable reference or factor function call converted to aGraphPPL.MetaObject
.
Examples
GraphPPL.get_boilerplate_functions
— Functionget_boilerplate_functions(ms_name, ms_args, num_interfaces)
Returns a quote block containing boilerplate functions for a model macro.
Arguments
ms_name
: The name of the model macro.ms_args
: The arguments of the model macro.num_interfaces
: The number of interfaces of the model macro.
Returns
quote
: A quote block containing the boilerplate functions for the model macro.
GraphPPL.apply_pipeline_collection
— Functionapply_pipeline_collection(e::Expr, collection)
Similar to apply_pipeline
, but applies a collection of pipeline functions to an expression.
Arguments
e::Expr
: An expression to apply the pipeline to.collection
: A collection of functions to apply to the expressions ine
.
Returns
The result of applying the pipeline function to e
.
GraphPPL.convert_tilde_expression
— Functionconvert_tilde_expression(e::Expr)
Converts a tilde expression to a make_node!
call. Converts broadcasted tile expressions to make_node!
calls with nothing as the lhs to indicate that a variable should be created on every broadcasted pass.
Arguments
e::Expr
: The expression to convert.
Returns
Expr
: The converted expression.
Examples
julia> convert_tilde_expression(:(x ~ Normal(0, 1) where {created_by = (x~Normal(0,1))}))
quote
GraphPPL.make_node!(__model__, __context__, Normal, x, [0, 1]; options = Dict{Any, Any}(:created_by => :(x ~ Normal(0, 1))), debug = debug)
end
GraphPPL.check_reserved_variable_names_model
— Functioncheck_reserved_variable_names_model(expr::Expr)
Check if any of the variable names in the given expression are reserved in the model macro. Reserved variable names are:
__parent_options__
__debug__
__model__
__context__
__parent_context__
__lhs_interface__
__rhs_interfaces__
__interfaces__
Arguments
expr::Expr
: The expression to check for reserved variable names.
Examples
jldoctest julia> check_reserved_variable_names_model(:(__parent_options__ ~ Normal(μ, σ)) ERROR: Variable name in __parent_options__ ~ Normal(μ, σ) cannot be used as it is a reserved variable name in the model macro.
`
GraphPPL.generate_get_or_create
— Functiongenerate_get_or_create(s::Symbol, lhs::Symbol, index::Nothing)
Generates code to get or create a variable in the graph. This function is used to generate code for variables that are not indexed.
Arguments
s::Symbol
: The symbol representing the variable.index::Nothing
: The index of the variable. This argument is alwaysnothing
.
Returns
A quote
block with the code to get or create the variable in the graph.
generate_get_or_create(s::Symbol, lhs::Expr, index::AbstractArray)
Generates code to get or create a variable in the graph. This function is used to generate code for variables that are indexed.
Arguments
s::Symbol
: The symbol representing the variable.index::AbstractArray
: The index of the variable.
Returns
A quote
block with the code to get or create the variable in the graph.
GraphPPL.add_meta_construction
— Functionadd_meta_construction(e::Expr)
Add a meta construction to the given expression e
. This function creates a new GraphPPL.MetaSpecification
object and assigns it to the __meta__
variable. It then evaluates the given expression e
in the context of this new GraphPPL.MetaSpecification
object, and returns the resulting GraphPPL.MetaSpecification
object.
Arguments
e::Expr
: The expression to evaluate in the context of the newGraphPPL.MetaSpecification
object.
Returns
e::Expr
: The expression that will generate theGraphPPL.MetaSpecification
object.
GraphPPL.apply_pipeline
— Functionapply_pipeline(e::Expr, pipeline)
Apply a pipeline function to an expression.
The apply_pipeline
function takes an expression e
and a pipeline
function and applies the function in the pipeline to e
when walking over it. The walk utilized can be specified by implementing what_walk
for a pipeline funciton.
Arguments
e::Expr
: An expression to apply the pipeline to.pipeline
: A function to apply to the expressions ine
.
Returns
The result of applying the pipeline function to e
.
GraphPPL.options_vector_to_named_tuple
— Functionoptions_vector_to_named_tuple(options::AbstractArray)
Converts the array found by pattern matching on the where clause in a tilde expression into a named tuple.
Arguments
options::AbstractArray
: An array of options.
Returns
result
: A named tuple of options.
GraphPPL.get_created_by
— Functionget_created_by(options::AbstractArray)
Retrieve the created_by
option from the given options. Expects the options to be retrieved using the MacroTools.@capture
macro.
GraphPPL.convert_to_anonymous
— Functionconvert_to_anonymous(e::Expr, created_by)
Convert an expression to an anonymous variable. This function is used to convert function calls in the arguments of node creations to anonymous variables in the graph.
Auxiliary functionality
GraphPPL.prune!
— Functionprune!(m::Model)
Remove all nodes from the model that are not connected to any other node.