ContinuousTransitionMessagePassingRules

The ContinuousTransition node and its variational rules. Use it for a linear Gaussian transition y ~ N(A x, W⁻¹) whose matrix A = f(a) and noise precision W are learned along with the states: a state-space model with an unknown transition, a rotation by an unknown angle, a regression with an unknown matrix of coefficients.

ContinuousTransitionMessagePassingRules — Module
ContinuousTransitionMessagePassingRules

The ContinuousTransition node, y ~ N(f(a) x, W⁻¹): a linear Gaussian transition from x to y through a matrix built from the vector a, with the noise precision W, where a and W are learned alongside the states. Its rules are variational, mean-field or structured q(y, x), with average energies, and run under CTVMP, which carries f and which the model must give. A nonlinear f is linearised with ForwardDiff.

Examples

julia> result = @call_message_update_rule(
           node = ContinuousTransition, target = :y, algorithm = CTVMP(a -> reshape(a, 1, 1)),
           q = (x = MvNormalMeanCovariance([2.0], [1.0;;]), a = MvNormalMeanCovariance([3.0], [1.0;;]), W = Wishart(2, [0.5;;])),
       );

julia> m, W = mean_precision(getresult(result));

julia> m ≈ [6.0] && W ≈ [1.0;;]
true

See also ContinuousTransition, CTransition.

source
Where these rules run

ReactiveMP runs these rules on a factor graph, which RxInfer builds from a GraphPPL model. The examples here call the rules directly, as a test does.

The site is one page: this one. It follows the node from its definition to its rules, an example and its limitations, and ends with the API.

Overview

ContinuousTransition carries an m-dimensional x to an n-dimensional y through the n × m matrix f(a), with Gaussian noise of precision W. The transformation f makes the matrix as free or as structured as the model needs: a -> reshape(a, n, m) learns every entry, while a rotation a -> [cos(a[1]) -sin(a[1]); sin(a[1]) cos(a[1])] learns one angle.

The node is a stochastic node, a density over its variables, and its rules are those of variational message passing. They update the marginals q(a) and q(W) from the states, and the states from them.

Definition

\[p(y \mid x, a, W) = \mathcal{N}\big(y \mid f(a)\, x,\; W^{-1}\big)\]

The output y is normal around the transformed input f(a) x, with covariance W⁻¹.

A nonlinear f is linearised around the mean of q(a): each row of f(a) is taken as linear in a, through its Jacobian, plus an offset that is zero for an f linear through the origin. The docstring of ContinuousTransition gives the expansion and the messages towards a and W in full.

Interfaces

using ContinuousTransitionMessagePassingRules, MessagePassingRulesBase, BayesBase, ExponentialFamily, LinearAlgebra
MessagePassingRulesBase.nodespec(ContinuousTransition)
NodeSpec: ContinuousTransition
xaWyContinuousTransitionstochastic
interfacealiases
y
x
a
W
default algorithmMessagePassingRulesBase.DefaultAlgorithm()
static inputsnone
definedsrc/node.jl:139

The node has four interfaces.

namemeaningmessages and marginals the rules take
ythe output, n-dimensionalMvNormal message; MvNormal joint q(y, x) or marginal q(y)
xthe input, m-dimensionalMvNormal message; MvNormal joint q(y, x) or marginal q(x)
athe parameters of A = f(a), a vectorMvNormal marginal q(a)
Wthe n × n noise precisiona marginal with a mean and E[logdet W], such as a Wishart

The rules send an MvNormal message towards y, x and a, and a Wishart towards W.

Algorithm

ContinuousTransitionMessagePassingRules.CTVMP — Type
CTVMP(f)

The algorithm of ContinuousTransition, carrying the transformation f that takes the vector a to the matrix A = f(a). The node declares no algorithm of its own, so a model must give this one; its rules are variational.

Arguments

  • f: a function of a vector returning an n × m matrix, differentiable by ForwardDiff. The rules differentiate it at the mean of q(a), and a nonlinear f is linearised there.

Examples

julia> using ContinuousTransitionMessagePassingRules

julia> CTVMP(a -> reshape(a, 2, 2)).f([1.0, 2.0, 3.0, 4.0])
2×2 Matrix{Float64}:
 1.0  3.0
 2.0  4.0

A rotation by one angle, a single parameter:

CTVMP(a -> [cos(a[1]) -sin(a[1]); sin(a[1]) cos(a[1])])
source

CTVMP(f) is the node's algorithm. It takes the transformation as its only argument and has no keywords. The node declares no algorithm of its own: a model must name CTVMP for every ContinuousTransition, and under the default one, DefaultAlgorithm, no rule is found.

Under CTVMP the node declares its dependencies, the inputs each target's rule takes:

MessagePassingRulesBase.dependencies_spec(ContinuousTransition, CTVMP(a -> reshape(a, 2, 2)))
DependenciesSpec: ContinuousTransitionunder CTVMP
defaultyContinuousTransitiondefaultxContinuousTransitiondefaultq[:a]aContinuousTransitiondefaultWContinuousTransition
━▶ target┄▶ marginal q··· the default scheme's inputs
free-energy partitionfrom the factorisation

Every target takes the inputs of the default scheme, which follow the factorisation. The rule towards a also reads q(a), the point it expands f around.

Supported rules

MessagePassingRulesBase.rule_coverage(ContinuousTransition)
Rule coverage for ContinuousTransition
DefaultAlgorithmCTVMP
→ y✓×2
→ x✓×2
→ a✓×2
→ W✓×2
q(y, x)✓
average energy✓×2

Each target has two rules under CTVMP, one per factorisation:

  • the structured q(y, x) q(a) q(W), reading the messages on y and x or the joint q(y, x);
  • the mean field q(y) q(x) q(a) q(W), reading marginals only.

The marginal rule computes the joint q(y, x). The average energy exists in both factorisations, so the Bethe free energy is available. There is no belief propagation rule.

Example

The message towards y under a known identity transition: the mean is A times the input's, the covariance the input's plus the inverse of the mean of q(W). When a is uncertain, its spread adds to the precision of x before the transition, as it does in the joint q(y, x).

@call_message_update_rule(
    node = ContinuousTransition, target = :y, algorithm = CTVMP(a -> reshape(a, 2, 2)),
    m = (x = MvNormalMeanCovariance([1.0, 2.0], [1.0 0.0; 0.0 1.0]),),
    q = (a = MvNormalMeanCovariance([1.0, 0.0, 0.0, 1.0], 1e-12 * Matrix(1.0I, 4, 4)), W = Wishart(3, [1.0 0.0; 0.0 1.0])),
)
RuleResult: message of ContinuousTransition towards :ymessages and marginals
xaWyContinuousTransition
Result
valueMvNormalMeanCovariance( μ: [1.0, 2.0] Σ: [1.33333 0.0; 0.0 1.33333] )
typeExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
log scaleundefined: the message rule for ContinuousTransition towards :y under CTVMP declares no `logscale`
Inputs
edgevalue
xmMvNormalMeanCovariance( μ: [1.0, 2.0] Σ: [1.0 0.0; 0.0 1.0] )
aqMvNormalMeanCovariance( μ: [1.0, 0.0, 0.0, 1.0] Σ: [1.0e-12 0.0 0.0 0.0; 0.0 1.0e-12 0.0 0.0; 0.0 0.0 1.0e-12 0.0; 0.0 …
WqDistributions.Wishart{Float64, PDMats.PDMat{Float64, Matrix{Float64}, LinearAlgebra.Cholesky{Float64, Matrix{Float64}}}…
Rule
declared inputsm[:x]::Union{ExponentialFamily.MultivariateNormalDistributionsFamily{T}, ExponentialFamily.MultivariateGaussianDistributionsFamily{T}} where T, q[:a]::Union{ExponentialFamily.MultivariateNormalDistributionsFamily{T}, ExponentialFamily.MultivariateGaussianDistributionsFamily{T}} where T, q[:W]::Any
algorithmCTVMP{var"#5#6"}(#5)
log scalenone
definedsrc/rules.jl:14
body
(algo, args)->begin
        (mx, Vx) = mean_cov(args.m[:x])
        mA = ct_matrix(algo, args.q[:a])
        mW = mean(args.q[:W])
        G = lu(I + Vx * ct_uncertainty(algo, args.q[:a], mW))
        MvNormalMeanCovariance(mA * (G \ mx), mA * (G \ Vx) * mA' + cholinv(mW))
    end
Other rules for this target (1)
src/rules.jl:26
✓algorithm CTVMP
✗q[:x]::Any not provided
✓q[:a]::Any got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
✓q[:W]::Any got Distributions.Wishart{Float64, PDMats.PDMat{Float64, Matrix{Float64}, LinearAlgebra.Cholesky{Float64, Matrix{Float64}}}, Int64}
✗m[:x]::ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}} provided but not consumed

@call_message_update_rule runs the rule and draws its inputs: the message on x and the marginals of a and W. The mean of q(W) is 3 I, so the covariance is I + I/3, which getresult and mean_cov confirm:

julia> using ContinuousTransitionMessagePassingRules, MessagePassingRulesBase, ExponentialFamily, BayesBase, LinearAlgebra

julia> result = @call_message_update_rule(
           node = ContinuousTransition, target = :y, algorithm = CTVMP(a -> reshape(a, 2, 2)),
           m = (x = MvNormalMeanCovariance([1.0, 2.0], [1.0 0.0; 0.0 1.0]),),
           q = (a = MvNormalMeanCovariance([1.0, 0.0, 0.0, 1.0], 1e-12 * Matrix(1.0I, 4, 4)), W = Wishart(3, [1.0 0.0; 0.0 1.0])),
       );

julia> m, V = mean_cov(getresult(result));

julia> m ≈ [1.0, 2.0] && V ≈ (1 + 1 / 3) * I
true

Limitations

  • Variational only. Every rule needs CTVMP, which the model must give, and marginals on a and W, which need initialising.
  • Multivariate normals only on y, x and a: a scalar state is a vector of length one.
  • A nonlinear f is linearised, around the mean of q(a), so its rules and its average energy are approximate; for an f linear in a they are exact.

API

The node and its alias.

ContinuousTransitionMessagePassingRules.ContinuousTransition — Type
ContinuousTransition

The stochastic node of a linear Gaussian transition whose matrix is itself learned:

\[p(y \mid x, a, W) = \mathcal{N}\big(y \mid A x,\; W^{-1}\big), \qquad A = f(a),\]

taking an m-dimensional x to an n-dimensional y through the n × m matrix f(a). The transformation f is carried by the node's algorithm, CTVMP, which the model must give. CTransition is an alias.

Interfaces

  • y: the output, an n-dimensional vector;
  • x: the input, an m-dimensional vector;
  • a: the parameters of the matrix, always a vector, of length one for a single parameter;
  • W: the n × n precision of the transition noise.

Rules

Every rule is variational and runs under CTVMP, in one of two factorisations:

  • structured, q(y, x) q(a) q(W): messages towards y and x from the messages on x and y (MvNormal), messages towards a and W and the average energy from the joint q(y, x) (MvNormal), and the marginal rule for q(y, x);
  • mean-field, q(y) q(x) q(a) q(W): every message and the average energy from the marginals.

q(a) must have a mean and a covariance, q(W) a mean and, for the average energy, the expectation of logdet, as a Wishart has. The rule towards a also reads q(a), its expansion point, besides the inputs its factorisation gives it, so q(a) needs an initial marginal.

Linearisation

The rules treat each row of A as linear in a. With Fᵢ the Jacobian of the i-th row of f at the mean mₐ of q(a), and Ā the matrix they use for f(mₐ),

\[A(a) \approx O + \begin{bmatrix} (F_1 a)^\top \\ \vdots \\ (F_n a)^\top \end{bmatrix}, \qquad O = \bar{A} - \begin{bmatrix} (F_1 m_a)^\top \\ \vdots \\ (F_n m_a)^\top \end{bmatrix}.\]

Ā is f linearised at mₐ + σₐ, the mean plus one standard deviation of q(a) per component, and evaluated at mₐ, which is f(mₐ) exactly for a linear f. The offset O is zero for an f linear through the origin, such as reshape, and the rules towards a and W and the average energy keep it for any other f, such as a rotation. The expectation of AᵀWA then has the closed form ĀᵀW̄Ā + Σᵢⱼ W̄ᵢⱼ Fᵢ Σₐ Fⱼᵀ, W̄ being the mean of q(W) and Σₐ the covariance of q(a), which every rule uses.

The message towards a is a normal with weighted mean and precision

\[\xi = \sum_i F_i^\top \, \mathbb{E}\big[x (y - O x)^\top\big] \, \bar{W}_{:, i}, \qquad \Lambda = \sum_i F_i^\top \, \mathbb{E}[x x^\top] \sum_j \bar{W}_{j i} F_j,\]

and the message towards W is a Wishart with n + 2 degrees of freedom and the inverse scale E[(y - A x)(y - A x)ᵀ].

Limitations

  • Variational only: under the default algorithm, DefaultAlgorithm, no rule exists, and a model that does not name CTVMP finds none.
  • Multivariate normal messages and marginals on y, x and a only.
  • A nonlinear f is linearised as above, so its rules are approximate.

Examples

A 2 × 2 transition learned entry by entry, with a known almost exactly to be the identity:

julia> using ContinuousTransitionMessagePassingRules, MessagePassingRulesBase, ExponentialFamily, BayesBase, LinearAlgebra

julia> algorithm = CTVMP(a -> reshape(a, 2, 2));

julia> result = @call_message_update_rule(
           node = ContinuousTransition, target = :y, algorithm = algorithm,
           m = (x = MvNormalMeanCovariance([1.0, 2.0], [1.0 0.0; 0.0 1.0]),),
           q = (a = MvNormalMeanCovariance([1.0, 0.0, 0.0, 1.0], 1e-12 * Matrix(1.0I, 4, 4)), W = Wishart(3, [1.0 0.0; 0.0 1.0])),
       );

julia> m, V = mean_cov(getresult(result));

julia> m ≈ [1.0, 2.0] && V ≈ (1 + 1 / 3) * I
true

The covariance is A Σₓ Aᵀ plus the inverse of the mean of q(W), 3 I.

See also CTVMP, CTransition.

source