Rules implementation

Message update rules

ReactiveMP.ruleFunction
rule(fform, on, vconstraint, mnames, messages, qnames, marginals, meta, __node)

This function is used to compute an outbound message for a given node

Arguments

  • fform: Functional form of the node in form of a type of the node, e.g. ::Type{ <: NormalMeanVariance } or ::typeof(+)
  • on: Outbound interface's tag for which a message has to be computed, e.g. ::Val{:out} or ::Val{:μ}
  • vconstraint: Variable constraints for an outbound interface, e.g. Marginalisation or MomentMatching
  • mnames: Ordered messages names in form of the Val type, eg. ::Val{ (:mean, :precision) }
  • messages: Tuple of message of the same length as mnames used to compute an outbound message
  • qnames: Ordered marginal names in form of the Val type, eg. ::Val{ (:mean, :precision) }
  • marginals: Tuple of marginals of the same length as qnames used to compute an outbound message
  • meta: Extra meta information
  • addons: Extra addons information
  • __node: Node reference

See also: @rule, marginalrule, @marginalrule

source
ReactiveMP.@ruleMacro
@rule NodeType(:Edge, Constraint) (Arguments..., [ meta::MetaType ]) = begin
    # rule body
    return ...
end

The @rule macro help to define new methods for the rule function. It works particularly well in combination with the @node macro. It has a specific structure, which must specify:

  • NodeType: must be a valid Julia type. If some attempt to define a rule for a Julia function (for example +), use typeof(+)
  • Edge: edge label, usually edge labels are defined with the @node macro
  • Constrain: DEPRECATED, please just use the Marginalisation label
  • Arguments: defines a list of the input arguments for the rule
    • m_* prefix indicates that the argument is of type Message from the edge *
    • q_* prefix indicates that the argument is of type Marginal on the edge *
  • Meta::MetaType - optionally, a user can specify a Meta object of type MetaType. This can be useful is some attempts to try different rules with different approximation methods or if the rule itself requires some temporary storage or cache. The default meta is nothing.

Here are various examples of the @rule macro usage:

  1. Belief-Propagation (or Sum-Product) message update rule for the NormalMeanVariance node toward the edge with the Marginalisation constraint. Input arguments are m_out and m_v, which are the messages from the corresponding edges out and v and have the type PointMass.
@rule NormalMeanVariance(:μ, Marginalisation) (m_out::PointMass, m_v::PointMass) = NormalMeanVariance(mean(m_out), mean(m_v))
  1. Mean-field message update rule for the NormalMeanVariance node towards the edge with the Marginalisation constraint. Input arguments are q_out and q_v, which are the marginals on the corresponding edges out and v of type Any.
@rule NormalMeanVariance(:μ, Marginalisation) (q_out::Any, q_v::Any) = NormalMeanVariance(mean(q_out), mean(q_v))
  1. Structured Variational message update rule for the NormalMeanVariance node towards the :out edge with the Marginalisation constraint. Input arguments are m_μ, which is a message from the μ edge of type UnivariateNormalDistributionsFamily, and q_v, which is a marginal on the v edge of type Any.
@rule NormalMeanVariance(:out, Marginalisation) (m_μ::UnivariateNormalDistributionsFamily, q_v::Any) = begin
    m_μ_mean, m_μ_cov = mean_cov(m_μ)
    return NormalMeanVariance(m_μ_mean, m_μ_cov + mean(q_v))
end

See also: rule, marginalrule, [@marginalrule], @call_rule

source
ReactiveMP.@call_ruleMacro
@call_rule NodeType(:edge, Constraint) (argument1 = value1, argument2 = value2, ..., [ meta = ... ])

The @call_rule macro helps to call the rule method with an easier syntax. The structure of the macro is almost the same as in the @rule macro, but there is no begin ... end block, but instead each argument must have a specified value with the = operator.

See also: @rule, rule, @call_marginalrule

source

Marginal update rules

ReactiveMP.marginalruleFunction
marginalrule(fform, on, mnames, messages, qnames, marginals, meta, __node)

This function is used to compute a local joint marginal for a given node

Arguments

  • fform: Functional form of the node in form of a type of the node, e.g. ::Type{ <: NormalMeanVariance } or ::typeof(+)
  • on: Local joint marginal tag , e.g. ::Val{ :mean_precision } or ::Val{ :out_mean_precision }
  • mnames: Ordered messages names in form of the Val type, eg. ::Val{ (:mean, :precision) }
  • messages: Tuple of message of the same length as mnames used to compute an outbound message
  • qnames: Ordered marginal names in form of the Val type, eg. ::Val{ (:mean, :precision) }
  • marginals: Tuple of marginals of the same length as qnames used to compute an outbound message
  • meta: Extra meta information
  • __node: Node reference

See also: rule, @rule @marginalrule

source
ReactiveMP.@marginalruleMacro
@marginalrule NodeType(:Cluster) (Arguments..., [ meta::MetaType ]) = begin
    # rule body
    return ...
end

The @marginalrule macro help to define new methods for the marginalrule function. It works particularly well in combination with the @node macro. It has a specific structure, which must specify:

  • NodeType: must be a valid Julia type. If some attempt to define a rule for a Julia function (for example +), use typeof(+)
  • Cluster: edge cluster that contains joined edge labels with the _ symbol. Usually edge labels are defined with the @node macro
  • Arguments: defines a list of the input arguments for the rule
    • m_* prefix indicates that the argument is of type Message from the edge *
    • q_* prefix indicates that the argument is of type Marginal on the edge *
  • Meta::MetaType - optionally, a user can specify a Meta object of type MetaType. This can be useful is some attempts to try different rules with different approximation methods or if the rule itself requires some temporary storage or cache. The default meta is nothing.

The @marginalrule can return a NamedTuple in the return statement. This would indicate some variables in the joint marginal for the Cluster are independent and the joint itself is factorised. For example if some attempts to compute a marginal for the q(x, y) it is possible to return (x = ..., y = ...) as the result of the computation to indicate that q(x, y) = q(x)q(y).

Here are various examples of the @marginalrule macro usage:

  1. Marginal computation rule around the NormalMeanPrecision node for the q(out, μ). The rule accepts arguments m_out and m_μ, which are the messages

from the out and μ edges respectively, and q_τ which is the marginal on the edge τ.

@marginalrule NormalMeanPrecision(:out_μ) (m_out::UnivariateNormalDistributionsFamily, m_μ::UnivariateNormalDistributionsFamily, q_τ::Any) = begin
    xi_out, W_out = weightedmean_precision(m_out)
    xi_μ, W_μ     = weightedmean_precision(m_μ)

    W_bar = mean(q_τ)

    W  = [W_out+W_bar -W_bar; -W_bar W_μ+W_bar]
    xi = [xi_out; xi_μ]

    return MvNormalWeightedMeanPrecision(xi, W)
end
  1. Marginal computation rule around the NormalMeanPrecision node for the q(out, μ). The rule accepts arguments m_out and m_μ, which are the messages from the

out and μ edges respectively, and q_τ which is the marginal on the edge τ. In this example the result of the computation is a NamedTuple

@marginalrule NormalMeanPrecision(:out_μ) (m_out::PointMass, m_μ::UnivariateNormalDistributionsFamily, q_τ::Any) = begin
    return (out = m_out, μ = prod(ProdAnalytical(), NormalMeanPrecision(mean(m_out), mean(q_τ)), m_μ))
end
source
ReactiveMP.@call_marginalruleMacro
@call_marginalrule NodeType(:edge) (argument1 = value1, argument2 = value2, ..., [ meta = ... ])

The @call_marginalrule macro helps to call the marginalrule method with an easier syntax. The structure of the macro is almost the same as in the @marginalrule macro, but there is no begin ... end block, but instead each argument must have a specified value with the = operator.

See also: @marginalrule, marginalrule, @call_rule

source

Testing utilities for the update rules

ReactiveMP.@test_rulesMacro
@test_rules [options] rule [ test_entries... ]

The @test_rules macro generates test cases for message update rules for probabilistic programming models that follow the "message passing" paradigm. It takes a rule specification as input and generates a set of tests based on that specification. This macro is provided by ReactiveMP.

Note: The Test module must be imported explicitly. The @test_rules macro tries to use the @test macro, which must be defined globally.

Arguments

The macro takes three arguments:

  • options: An optional argument that specifies the options for the test generation process. See below for details.
  • rule: A rule specification in the same format as the @rule macro, e.g. Beta(:out, Marginalisation) or NormalMeanVariance(:μ, Marginalisation).
  • test_entries: An array of named tuples (input = ..., output = ...). The input entry has the same format as the input for the @rule macro. The output entry specifies the expected output.

Options

The following options are available:

  • check_type_promotion: By default, this option is set to false. If set to true, the macro generates an extensive list of extra tests that aim to check the correct type promotion within the tests. For example, if all inputs are of type Float32, then the expected output should also be of type Float32. See the paramfloattype and convert_paramfloattype functions for details.
  • atol: Sets the desired accuracy for the tests. The tests use the custom_isapprox function from ReactiveMP to check if outputs are approximately the same. This argument can be either a single number or an array of key => value pairs.
  • extra_float_types: A set of extra float types to be used in the check_type_promotion tests. This argument has no effect if check_type_promotion is set to false.

The default values for the atol option are:

  • Float32: 1e-4
  • Float64: 1e-6
  • BigFloat: 1e-8

Examples


@test_rules [check_type_promotion = true] Beta(:out, Marginalisation) [
    (input = (m_a = PointMass(1.0), m_b = PointMass(2.0)), output = Beta(1.0, 2.0)),
    (input = (m_a = PointMass(2.0), m_b = PointMass(2.0)), output = Beta(2.0, 2.0)),
    (input = (m_a = PointMass(3.0), m_b = PointMass(3.0)), output = Beta(3.0, 3.0))
]

@test_rules [check_type_promotion = true] Beta(:out, Marginalisation) [
    (input = (q_a = PointMass(1.0), q_b = PointMass(2.0)), output = Beta(1.0, 2.0)),
    (input = (q_a = PointMass(2.0), q_b = PointMass(2.0)), output = Beta(2.0, 2.0)),
    (input = (q_a = PointMass(3.0), q_b = PointMass(3.0)), output = Beta(3.0, 3.0))
]

See also: ReactiveMP.@test_marginalrules

source