GCVMessagePassingRules
The Gaussian controlled variance node, GCV, and its rules for reactive message passing: a normal whose log-variance is linear in other variables of the model. Use it for a variance that is itself inferred and changes over time, as in hierarchical Gaussian filters, where the state of one layer sets the volatility of the layer below.
GCVMessagePassingRules — Module
GCVMessagePassingRulesThe Gaussian controlled variance node, GCV: a normal whose log-variance is linear in other variables of the model,
\[p(y \mid x, z, κ, ω) = \mathcal{N}\bigl(y \mid x, \exp(κ z + ω)\bigr),\]
the building block of hierarchical Gaussian filters, where one layer's state sets the volatility of the layer below.
GCV: the node, with interfacesy,x,z,κandω. Its rules are variational, under the structured factorisationq(y, x) q(z) q(κ) q(ω)or the mean field; every rule reads the marginals ofz,κandω.GCVApproximation: its algorithm, which carries the Gauss–Hermite cubature of the messages towardsz,κandω.ExponentialLinearQuadratic: the density of those messages, which has no closed-form moments.
The package also adds rules to NormalMeanVariance and NormalMeanPrecision (their nodes are in StandardMessagePassingRules) for an ExponentialLinearQuadratic message on out, so that a GCV's z or ω can be the output of a normal node.
Examples
julia> using MessagePassingRulesBase, ExponentialFamily, BayesBase
julia> q_z, q_κ, q_ω = PointMass(0.0), PointMass(1.0), PointMass(0.0); # a noise variance exp(0) = 1
julia> result = @call_message_update_rule(
node = GCV, target = :y, m = (x = NormalMeanVariance(1.0, 2.0),), q = (z = q_z, κ = q_κ, ω = q_ω),
);
julia> mean_var(getresult(result)) .≈ (1.0, 3.0)
(true, true)The ReactiveMP engine runs these rules on a factor graph, which RxInfer builds from a model. The examples here call the rules directly.
Overview
A GCV node relates an output y to its mean x with a variance exp(κz + ω). Here z is a latent variable, often a random walk of its own. κ scales the effect of z on the log-variance, and ω is an offset.
The rules are variational: each rule sends the exponentiated expected log-density of the node, the expectation taken under the marginals of the variables outside the target's cluster. Every rule reads the marginals of z, κ and ω. The messages towards z, κ and ω are ExponentialLinearQuadratic densities, whose moments a Gauss–Hermite cubature computes.
The package also adds rules to the NormalMeanVariance and NormalMeanPrecision nodes of StandardMessagePassingRules. These rules accept an ExponentialLinearQuadratic message on the normal's out. With them, z or ω can itself be the output of a normal node, such as a step of a random walk.
Definition
\[p(y \mid x, z, κ, ω) = \mathcal{N}\bigl(y \mid x, \exp(κ z + ω)\bigr) = \frac{1}{\sqrt{2π \exp(κ z + ω)}} \exp\Bigl(-\frac{(y - x)^2}{2 \exp(κ z + ω)}\Bigr).\]
The rules need the expected noise precision ⟨exp(-(κz + ω))⟩ = ⟨exp(-ω)⟩ ⟨exp(-κz)⟩. For a normal q(ω) the first factor is the mean of a lognormal, exp(-⟨ω⟩ + Var(ω)/2), which is exact. The second treats κz as normal, with Var(κz) = ⟨κ⟩² Var(z) + ⟨z⟩² Var(κ) + Var(κ) Var(z), which is exact only when κ or z is a point mass.
Interfaces
using GCVMessagePassingRules, MessagePassingRulesBase, ExponentialFamily, BayesBase
MessagePassingRulesBase.nodespec(GCV)| interface | aliases | |
|---|---|---|
y | ||
x | ||
z | ||
κ | ||
ω |
| default algorithm | GCVApproximation{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}}(GaussHermiteCubature(20)) |
|---|---|
| static inputs | none |
| defined | src/node.jl:93 |
| name | meaning | messages its rules take | marginals its rules take |
|---|---|---|---|
y | the output, normal about x | univariate normal or ExponentialLinearQuadratic (structured) | univariate normal |
x | the mean of y | univariate normal or ExponentialLinearQuadratic (structured) | univariate normal |
z | the variable controlling the log-variance | none | a normal |
κ | the coupling of z to the log-variance | none | anything with a mean and a variance |
ω | the offset of the log-variance | none | anything with a mean and a variance |
No interface of GCV has an alias. Under the structured factorisation y and x share one cluster, and so a joint marginal q(y, x), a bivariate normal.
Algorithm
GCVApproximation(; method = GaussHermiteCubature(20)) is the node's algorithm. It carries the GaussHermiteCubature that computes the moments of the messages towards z, κ and ω. The node declares its default instance, so a model does not need to name it, and names it only to change the number of points. The rules are declared for the default instance's type, so another approximation method finds no rule.
The algorithm declares no dependencies of its own. The rules take what the default scheme delivers: messages from the target's cluster and marginals of the other clusters.
Supported rules
MessagePassingRulesBase.rule_coverage(GCV)| GCVApproximation{GaussHermiteCubature} | |
|---|---|
| → y | ✓×2 |
| → x | ✓×2 |
| → z | ✓×2 |
| → κ | ✓×2 |
| → ω | ✓×2 |
| average energy | ✓×2 |
| q(y, x) | ✓ |
Each target of GCV has two rules. One is for the structured factorisation q(y, x) q(z) q(κ) q(ω), and one is for the mean field, which puts every variable in a cluster of its own. Under the structured factorisation:
- the rules towards
yandxtake the message on the other one and the marginals ofz,κandω; - the rules towards
z,κandωtake the jointq(y, x); - the row
q(y, x)is the marginal rule of the joint.
There are no belief propagation rules. The average energy is defined for both factorisations.
The rules that the package adds to the normal nodes of StandardMessagePassingRules take an ExponentialLinearQuadratic message on out, which they reduce to a normal with its moments:
| node | target | inputs |
|---|---|---|
NormalMeanVariance | μ | m(out), and m(v) a point mass or q(v) |
NormalMeanVariance | q(out, μ, v) | m(out), a univariate normal m(μ), a point-mass m(v) |
NormalMeanVariance | q(out, μ) | m(out), a univariate normal m(μ), q(v) |
NormalMeanPrecision | μ | m(out), and m(τ) a point mass or q(τ) |
NormalMeanPrecision | q(out, μ, τ) | m(out), a univariate normal m(μ), a point-mass m(τ) |
NormalMeanPrecision | q(out, μ) | m(out), a univariate normal m(μ), q(τ) |
Example
The message towards y from a normal message on x, with point-mass marginals that make the noise variance exp(1 ⋅ 0 + 0) = 1:
@call_message_update_rule(
node = GCV, target = :y,
m = (x = NormalMeanVariance(1.0, 2.0),),
q = (z = PointMass(0.0), κ = PointMass(1.0), ω = PointMass(0.0)),
)Result
| value | ExponentialFamily.NormalMeanVariance{Float64}(μ=1.0, v=3.0) |
|---|---|
| type | ExponentialFamily.NormalMeanVariance{Float64} |
| log scale | undefined: the message rule for GCV towards :y under GCVApproximation{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}} declares no `logscale` |
Inputs
| edge | value | |
|---|---|---|
| z | q | PointMass{Float64}(0.0) |
| κ | q | PointMass{Float64}(1.0) |
| ω | q | PointMass{Float64}(0.0) |
Rule
| declared inputs | m[:x]::Union{ExponentialLinearQuadratic, ExponentialFamily.UnivariateNormalDistributionsFamily, ExponentialFamily.UnivariateGaussianDistributionsFamily}, q[:z]::Any, q[:κ]::Any, q[:ω]::Any |
|---|---|
| algorithm | GCVApproximation{GaussHermiteCubature{Vector{Float64}, Vector{Float64}}}(GaussHermiteCubature(20)) |
| log scale | none |
| defined | src/rules.jl:12 |
| body | args->through_noise(args.m[:x], args.q[:z], args.q[:κ], args.q[:ω]) |
Other rules for this target (1)
src/rules.jl:17| ✓ | algorithm GCVApproximation{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}} |
| ✗ | q[:x]::Any not provided |
| ✓ | q[:z]::Any got BayesBase.PointMass{Float64} |
| ✓ | q[:κ]::Any got BayesBase.PointMass{Float64} |
| ✓ | q[:ω]::Any got BayesBase.PointMass{Float64} |
| ✗ | m[:x]::ExponentialFamily.NormalMeanVariance{Float64} provided but not consumed |
The card draws the message on x as a solid input and the marginals of z, κ and ω as dashed ones. The result adds the noise variance to the variance of x, 2 + 1 = 3:
julia> using MessagePassingRulesBase, GCVMessagePassingRules, ExponentialFamily, BayesBase
julia> result = @call_message_update_rule(
node = GCV, target = :y,
m = (x = NormalMeanVariance(1.0, 2.0),),
q = (z = PointMass(0.0), κ = PointMass(1.0), ω = PointMass(0.0)),
);
julia> mean_var(getresult(result)) .≈ (1.0, 3.0)
(true, true)The message towards ω under the mean field is an ExponentialLinearQuadratic, whose moments the cubature of the default algorithm computes. Its coefficient b is ⟨(y - x)²⟩ = (1 - 0)² + 0.5 + 0.5 = 2:
julia> using MessagePassingRulesBase, GCVMessagePassingRules, ExponentialFamily, BayesBase
julia> result = @call_message_update_rule(
node = GCV, target = :ω,
q = (y = NormalMeanVariance(1.0, 0.5), x = NormalMeanVariance(0.0, 0.5), z = PointMass(0.0), κ = PointMass(1.0)),
);
julia> message = getresult(result);
julia> message isa ExponentialLinearQuadratic
true
julia> params(message) == (1.0, 2.0, -1.0, 0.0)
trueA step of a hierarchical Gaussian filter
In a two-layer hierarchical Gaussian filter, the upper layer z is a random walk, zₜ ~ N(zₜ₋₁, 1), and it sets the volatility of the lower random walk, xₜ ~ GCV(xₜ₋₁, zₜ, κ, ω). The update of the upper layer passes through two nodes. First the GCV node sends its message towards zₜ, from the marginals of the lower layer:
towards_z = @call_message_update_rule(
node = GCV, target = :z,
q = (y = NormalMeanVariance(1.0, 0.5), x = NormalMeanVariance(0.0, 0.5), κ = PointMass(1.0), ω = PointMass(0.0)),
)
m_z = getresult(towards_z)
mean_var(m_z)(1.814142762171816, 3.673218039648464)The message is an ExponentialLinearQuadratic, and its moments come from the cubature. Then the random walk's NormalMeanVariance node carries it back towards zₜ₋₁. This is one of the rules the package adds to that node:
@call_message_update_rule(node = NormalMeanVariance, target = :μ, m = (out = m_z, v = PointMass(1.0)))Result
| value | ExponentialFamily.NormalMeanVariance{Float64}(μ=1.81414, v=4.67322) |
|---|---|
| type | ExponentialFamily.NormalMeanVariance{Float64} |
| log scale | undefined: the message rule for NormalMeanVariance towards :μ under MessagePassingRulesBase.DefaultAlgorithm declares no `logscale` |
Inputs
| edge | value |
|---|
Rule
| declared inputs | m[:out]::ExponentialLinearQuadratic, m[:v]::BayesBase.PointMass |
|---|---|
| algorithm | DefaultAlgorithm() |
| log scale | none |
| defined | src/gaussian_extension.jl:8 |
| body | args->begin
(m, v) = mean_var(args.m[:out])
NormalMeanVariance(m, v + mean(args.m[:v]))
end |
Other rules for this target (8)
src/gaussian_extension.jl:13| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✓ | m[:out]::ExponentialLinearQuadratic got ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} |
| ✗ | q[:v]::Any not provided |
| ✗ | m[:v]::BayesBase.PointMass{Float64} provided but not consumed |
normal_mean_variance/mean.jl:1| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | m[:out]::BayesBase.PointMass got ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} |
| ✓ | m[:v]::BayesBase.PointMass got BayesBase.PointMass{Float64} |
normal_mean_variance/mean.jl:8| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | m[:out]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} |
| ✓ | m[:v]::BayesBase.PointMass got BayesBase.PointMass{Float64} |
normal_mean_variance/mean.jl:18| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | q[:out]::BayesBase.PointMass not provided |
| ✗ | q[:v]::BayesBase.PointMass not provided |
| ✗ | m[:out]::ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} provided but not consumed |
| ✗ | m[:v]::BayesBase.PointMass{Float64} provided but not consumed |
normal_mean_variance/mean.jl:25| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | q[:out]::Any not provided |
| ✗ | q[:v]::Any not provided |
| ✗ | m[:out]::ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} provided but not consumed |
| ✗ | m[:v]::BayesBase.PointMass{Float64} provided but not consumed |
normal_mean_variance/mean.jl:31| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | m[:out]::BayesBase.PointMass got ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} |
| ✗ | q[:v]::Any not provided |
| ✗ | m[:v]::BayesBase.PointMass{Float64} provided but not consumed |
normal_mean_variance/mean.jl:38| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | m[:out]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} |
| ✗ | q[:v]::Any not provided |
| ✗ | m[:v]::BayesBase.PointMass{Float64} provided but not consumed |
normal_mean_variance/mean.jl:49| ✓ | algorithm MessagePassingRulesBase.DefaultAlgorithm |
| ✗ | m[:out]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got ExponentialLinearQuadratic{MessagePassingRulesApproximations.GaussHermiteCubature{Vector{Float64}, Vector{Float64}}, Float64} |
| ✗ | q[:v]::BayesBase.PointMass not provided |
| ✗ | m[:v]::BayesBase.PointMass{Float64} provided but not consumed |
The rule reduces the message on out to a normal with its moments and adds the random walk's variance, 1.
Limitations
- There are no belief-propagation rules: every rule needs the marginals of
z,κandω, so a model must initialise them. - The node is univariate; the messages on
yandxmust be univariate normals orExponentialLinearQuadratics. ⟨exp(-κz)⟩treatsκzas normal, which is an approximation unlessκorzis a point mass.- The approximation method must be a Gauss–Hermite cubature.
- The average energy needs a normal
q(z), and a multivariate normalq(y, x)or normalq(y)andq(x). - The rules declare no log scale.
API
The node and its algorithm:
GCVMessagePassingRules.GCV — Type
GCVThe Gaussian controlled variance node, a normal whose log-variance is linear in its inputs:
\[p(y \mid x, z, κ, ω) = \mathcal{N}\bigl(y \mid x, \exp(κ z + ω)\bigr).\]
The variance of y about x is exp(κz + ω), so its precision is exp(-(κz + ω)). It is stochastic, with the interfaces, in order:
y: the output, normal aboutx;x: the mean ofy;z: the variable that controls the log-variance, scaled byκ;κ: the coupling ofzto the log-variance;ω: the offset of the log-variance.
Its rules run under its own algorithm, GCVApproximation, whose default instance the node declares, so a model need not name it. They are variational and read q(z), q(κ) and q(ω) always, under one of two factorisations:
- structured,
q(y, x) q(z) q(κ) q(ω): the rules towardsyandxtake the message on the other one, the rules towardsz,κandωthe jointq(y, x), and there is a marginal rule forq(y, x); - mean field,
q(y) q(x) q(z) q(κ) q(ω): every rule takes marginals.
Towards y and x a rule returns a NormalMeanVariance; towards z, κ and ω an ExponentialLinearQuadratic. The expectation ⟨exp(-κz)⟩ treats κz as normal, which is exact only when κ or z is a point mass; ⟨exp(-ω)⟩ is exact for a normal q(ω).
The average energy,
\[\tfrac12\bigl[\log 2π + ⟨κ⟩⟨z⟩ + ⟨ω⟩ + ⟨(y - x)^2⟩ ⟨e^{-(κz + ω)}⟩\bigr],\]
is defined for both factorisations, with a multivariate normal q(y, x) or normal q(y) and q(x), and a normal q(z); q(κ) and q(ω) need only a mean and a variance.
Limitations
- There are no belief-propagation rules: every rule needs the marginals of
z,κandω. - The messages on
yandxmust be univariate normals orExponentialLinearQuadratics; the node is univariate. - The rules declare no log scale.
See also GCVApproximation, ExponentialLinearQuadratic.
GCVMessagePassingRules.GCVApproximation — Type
GCVApproximation(; method = GaussHermiteCubature(20))
GCVApproximation(method)The algorithm of GCV's rules: it carries the cubature that computes the moments of the ExponentialLinearQuadratic messages towards z, κ and ω. The rules towards y and x do not use it.
Keywords
method: aGaussHermiteCubature, of any number of points. DefaultGaussHermiteCubature(20). More points give more accurate moments at a higher cost.
A model names this algorithm only to change the number of points.
Throws
An ArgumentError for a method other than a GaussHermiteCubature: the rules take the moments by cubature, through its points and weights.
Examples
julia> using MessagePassingRulesApproximations: GaussHermiteCubature
julia> GCVApproximation(method = GaussHermiteCubature(32)) isa GCVApproximation
trueThe density of the messages towards z, κ and ω:
GCVMessagePassingRules.ExponentialLinearQuadratic — Type
ExponentialLinearQuadratic(approximation, a, b, c, d)The univariate density
\[p(x) ∝ \exp\Bigl(-\tfrac12\bigl(a x + b \exp(c x + d x^2 / 2)\bigr)\Bigr),\]
the form of GCV's messages towards z, κ and ω. It has no closed-form normalisation or moments: approximation, a GaussHermiteCubature, computes mean, var and the other moments by weighting a standard normal.
Arguments
approximation: the cubature of the moments;a,b,c,d: real coefficients, promoted to a common floating-point type.
logpdf and pdf are unnormalised. Its product with a univariate normal is a NormalMeanVariance with the moments of the product, computed by the same cubature around the normal's mean and variance.
Examples
julia> using MessagePassingRulesApproximations: GaussHermiteCubature
julia> using ExponentialFamily, BayesBase
julia> d = ExponentialLinearQuadratic(GaussHermiteCubature(20), 1.0, 2.0, -1.0, 0.0);
julia> logpdf(d, 0.0) ≈ -1.0
true
julia> prod(GenericProd(), NormalMeanVariance(0.0, 1.0), d) isa NormalMeanVariance
true