The arithmetic nodes +, -, * and dot are deterministic nodes: their output is a function of their inputs, such as out = in[1] + in[2]. Base's and LinearAlgebra's functions are the nodes themselves, so an RxInfer model writes y ~ x + z or y ~ A * x, and rule_coverage(+) takes the function. The package re-exports LinearAlgebra's dot for models to use.
A deterministic node's clusters are always out and the joint over its inputs, whatever the factorisation. Every rule here is therefore belief propagation over messages. The message towards out is the pushforward of the input messages, the distribution of f(in...). The message towards an input inverts the function against the other messages. For normal messages, with one factor of * or dot known, these messages are exact and normal. Between two uncertain factors of * the result has no closed form, and the rules return log-densities or samples instead. None of the nodes has an average energy of its own.
One node sums any number of terms, so a + b + c, which Julia parses as one call of +, is one node, with no variable for an intermediate sum. That matters in a model with loops, where an intermediate sum needs initial messages of its own and changes the schedule.
MessagePassingRulesBase.nodespec(+)
NodeSpec: +
interface
aliases
out
in
a group of any number of members
default algorithm
MessagePassingRulesBase.DefaultAlgorithm()
static inputs
none
min group length
2
defined
nodes/addition.jl:3
MessagePassingRulesBase.rule_coverage(+)
Rule coverage for +
DefaultAlgorithm
→ out
✓×3
→ (in, k)
✓
q(in)
✓×2
The message towards out adds the means and the variances of the terms:
@call_message_update_rule(
node = +, target = :out,
m = (in = (NormalMeanVariance(1.0, 1.0), NormalMeanVariance(2.0, 3.0), PointMass(1.0)),),
)
RuleResult: message of + towards :outbelief propagation
m[:in...]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T member 3 is a BayesBase.PointMass{Float64}, not a Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T
Towards a term, the rule subtracts the others from out. With out observed at 5, the message towards the second of two terms is a normal around 5 - 1. The group's tuple holds nothing at the target's own position:
@call_message_update_rule(
node = +, target = (:in, 2),
m = (out = PointMass(5.0), in = (NormalMeanVariance(1.0, 1.0), nothing)),
)
RuleResult: message of + towards (:in, 2)belief propagation
The messages are normal whenever any term is normal, and a PointMass when every term is known. A normal shifted by a known value keeps its mean–precision or weighted-mean form. Any other result is in mean–variance or mean–covariance form. Towards out, Distributions' convolve sums any other distributions, and it throws a MethodError for a pair it does not know. Every rule has log scale zero.
The joint marginal of the terms, which the free energy takes the entropy of, depends on out's message:
with a normal out, the normal terms are one joint normal, their messages times out's message of their sum, and each known term is a block of its own;
with out known, the terms lie on the plane where they sum to it. Every normal term but the last is free, and the last is the rest of the sum: an StandardMessagePassingRules.InputsGivenSum.
@call_marginal_update_rule(
node = +, target = (:in,),
m = (out = PointMass(3.0), in = (NormalMeanVariance(2.0, 2.0), PointMass(2.0), NormalMeanVariance(0.0, 1.0))),
)
m[:out]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T got BayesBase.PointMass{Float64}
The joint of the inputs of + when their sum is known, the marginal rule's result for a PointMass on out: the inputs then lie on the plane where they sum to it. The point-mass inputs are known, and of the Gaussian ones every member but the last is free; the last is their difference from the sum. Its entropy is the free members' joint entropy and one point-mass entropy for each known input and for the sum, which the free energy needs.
Fields
free: the joint of the free Gaussian members, their messages times the last member's message of the difference, or nothing when at most one input is Gaussian;
Its rules are +'s, rearranged. Towards out, the message is the difference of the inputs. Towards in1, it is the sum of out and in2, and it takes any two distributions through convolve, as + does towards out. Towards in2, it is the difference of in1 and out.
m[:A]::BayesBase.PointMass{<:AbstractMatrix}, m[:in]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T
m[:in]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
multiplication/out.jl:24
✓
algorithm MultiplicationSampling
✗
m[:A]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got BayesBase.PointMass{Matrix{Float64}}
m[:in]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
multiplication/out.jl:38
✓
algorithm MultiplicationSampling
✗
m[:A]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T got BayesBase.PointMass{Matrix{Float64}}
m[:in]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
multiplication/out.jl:53
✓
algorithm MultiplicationSampling
✗
m[:A]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got BayesBase.PointMass{Matrix{Float64}}
✗
m[:in]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
Towards in, the message is a normal in weighted-mean form, with precision Aᵀ Σ⁻¹ A and weighted mean Aᵀ Σ⁻¹ m from the message N(m, Σ) on out. The card lists the service the rule reads, matrix_correction:
m[:out]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
m[:out]::Union{ExponentialFamily.NormalDistributionsFamily{T}, ExponentialFamily.GaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
m[:out]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got ExponentialFamily.MvNormalMeanCovariance{Float64, Vector{Float64}, Matrix{Float64}}
✗
m[:A]::Union{ExponentialFamily.UnivariateNormalDistributionsFamily{T}, ExponentialFamily.UnivariateGaussianDistributionsFamily{T}} where T got BayesBase.PointMass{Matrix{Float64}}
With one factor known, the rules are in closed form. Towards out, the message is a scaled normal or Gamma. Towards the unknown factor, it is a normal in weighted-mean form or a Gamma, with the log scale -d log |c| of a known scalar c. A known factor may be a scalar, a vector or a matrix. With a vector, the other factor is a scalar. With a matrix, the rules compute only A * in, never in * A, so they refuse a matrix in towards A. The precision matrices that the rules towards an input build may be singular. The rules correct them with the context's matrix_correction, ReplaceZeroDiagonalEntries(tiny) by default.
Between two univariate normals, the messages are log-densities, a ContinuousUnivariateLogPdf. Towards an input, the rule computes the exact integral. Towards out, it sums a Bessel series truncated at ten terms. Between two other univariate distributions, the rules draw samples from the context's rng, as many as the algorithm says. These messages are unnormalised and declare no log scale.
The algorithm of the node *, out = A * in, and its default, so a model need not name it. With a known factor the rules are closed-form. Between two univariate normals they return a ContinuousUnivariateLogPdf: towards an input the exact integral, towards out a Bessel series truncated at ten terms. Between two other univariate distributions, one rule towards each interface draws samples values of one factor from the rule context's rng and returns an unnormalised ContinuousUnivariateLogPdf.
Limitations. One input must be known. Two normal inputs have no closed form. The rules for that case exist only to throw an error that points to the SoftDot node, whose package handles it. The precision a w aᵀ towards an input has rank one, and the rule corrects it with the context's matrix_correction, ReplaceZeroDiagonalEntries(tiny) by default.
Settings
This document was generated with Documenter.jl version 1.19.0 on Thursday 15 October 2026. Using Julia version 1.13.2.