These nodes model positive quantities (the Gamma and inverse-Gamma nodes), probabilities (Beta, Dirichlet) and discrete outcomes (Bernoulli, Categorical, Poisson). Several of them form conjugate pairs: a Beta prior on a Bernoulli's probability, a Dirichlet on a Categorical's and a Gamma on a Poisson's rate.
In a conjugate pair, belief propagation is exact and in closed form. An observed outcome, a point mass , sends its parameter a message in the prior's family, such as a Beta towards a Bernoulli's p. The product of that message and the prior is the posterior. The rules towards a Gamma's shape and rate, and towards a Categorical's p, take marginals and are variational : they use expectations such as E[log out] under those marginals, so an uncertain out works as well as an observed one. Many nodes on this page serve as priors only. They have the message towards out and no rule towards their parameters, which a model then gives as constants. The coverage tables show which.
The nodes are the types of ExponentialFamily and Distributions, declared as nodes here, and all run under DefaultAlgorithm .
An observed coin flip sends its bias a Beta likelihood. The message's log scale is log(1/2):
julia> using StandardMessagePassingRules, MessagePassingRulesBase, ExponentialFamily, BayesBase
julia> result = @call_message_update_rule(node = Bernoulli, target = :p, m = (out = PointMass(1.0),));
julia> getresult(result) isa Beta && all(params(getresult(result)) .≈ (2.0, 1.0))
true
julia> getlogscale(result) ≈ log(1 / 2)
true\[p(\mathrm{out} \mid α, β) = \frac{β^α}{Γ(α)} \mathrm{out}^{α - 1} e^{-β \mathrm{out}}, \quad \mathrm{out} > 0\]
MessagePassingRulesBase.nodespec(GammaShapeRate)NodeSpec: GammaShapeRate
α (a, shape) β (b, rate) out GammaShapeRate stochastic interface aliases outαa, shapeβb, rate
default algorithm MessagePassingRulesBase.DefaultAlgorithm()static inputs nonedefined nodes/gamma_shape_rate.jl:1
interface aliases meaning messages and marginals its rules take outthe variable any marginal with E[out], E[log out] αa, shapethe shape a PointMass message, or any marginal with E[α] βb, ratethe rate a PointMass message, or a Gamma marginal
MessagePassingRulesBase.rule_coverage(GammaShapeRate)Rule coverage for ExponentialFamily.GammaShapeRate DefaultAlgorithm → out ✓×2 → α ✓ → β ✓ average energy ✓×2 q(out, α, β) ✓
With known parameters, the message towards out is the node's own distribution:
@call_message_update_rule(node = GammaShapeRate, target = :out, m = (α = PointMass(2.0), β = PointMass(3.0)))RuleResult: message of GammaShapeRate towards :outbelief propagation
α β out GammaShapeRate Result value ExponentialFamily.GammaShapeRate{Float64}(a=2.0, b=3.0)type ExponentialFamily.GammaShapeRate{Float64}log scale 0 (declared)
Inputs edge value α m PointMass{Float64}(2.0)β m PointMass{Float64}(3.0)
Rule declared inputs m[:α]::BayesBase.PointMass, m[:β]::BayesBase.PointMassalgorithm DefaultAlgorithm()log scale 0 defined gamma_shape_rate/out.jl:1body args->GammaShapeRate(mean(args.m[:α]), mean(args.m[:β]))
Other rules for this target (1) gamma_shape_rate/out.jl:8✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:α]::Any not provided✗ q[:β]::Any not provided✗ m[:α]::BayesBase.PointMass{Float64} provided but not consumed✗ m[:β]::BayesBase.PointMass{Float64} provided but not consumed
The variational message towards the rate takes the marginals of out and α. It is a GammaShapeRate with shape E[α] + 1 and rate E[out]:
@call_message_update_rule(node = GammaShapeRate, target = :β, q = (out = GammaShapeRate(4.0, 2.0), α = PointMass(2.0)))RuleResult: message of GammaShapeRate towards :βvariational
out α β GammaShapeRate Result value ExponentialFamily.GammaShapeRate{Float64}(a=3.0, b=2.0)type ExponentialFamily.GammaShapeRate{Float64}log scale undefined: the message rule for GammaShapeRate towards :β under MessagePassingRulesBase.DefaultAlgorithm declares no `logscale`
Inputs edge value out q ExponentialFamily.GammaShapeRate{Float64}(a=4.0, b=2.0)α q PointMass{Float64}(2.0)
Rule declared inputs q[:out]::Any, q[:α]::Anyalgorithm DefaultAlgorithm()log scale none defined gamma_shape_rate/b.jl:1body args->GammaShapeRate(1 + mean(args.q[:α]), mean(args.q[:out]))
The rules towards α and β are variational only. Towards β the message is a GammaShapeRate. Towards α it is a GammaShapeLikelihood , and the rule needs a Gamma marginal of β. The average energy takes a PointMass or a Gamma marginal of α. Gamma and GammaShapeRate are different nodes: this one is parametrised by the rate.
\[p(\mathrm{out} \mid α, θ) = \frac{\mathrm{out}^{α - 1} e^{-\mathrm{out} / θ}}{Γ(α) θ^α}, \quad \mathrm{out} > 0\]
interface aliases meaning messages and marginals its rules take outthe variable any marginal (average energy) αshapethe shape a PointMass message, or any marginal θscalethe scale a PointMass message, or any marginal
MessagePassingRulesBase.rule_coverage(Gamma)Rule coverage for Distributions.Gamma DefaultAlgorithm → out ✓×2 → α → θ average energy ✓×2 q(out, α, θ) ✓
Limitations. No rules towards α or θ. The variational message towards out is Gamma(E[α], E[θ]), with the mean of the scale.
GammaInverse is ExponentialFamily's name for Distributions' InverseGamma: the two names refer to one type, and a message of that type prints as InverseGamma.
\[p(\mathrm{out} \mid α, θ) = \frac{θ^α}{Γ(α)} \mathrm{out}^{-α - 1} e^{-θ / \mathrm{out}}, \quad \mathrm{out} > 0\]
interface aliases meaning messages and marginals its rules take outthe variable a GammaInverse marginal (average energy) αshapethe shape a PointMass message, or any marginal θscalethe scale a PointMass message, or any marginal
MessagePassingRulesBase.rule_coverage(GammaInverse)Rule coverage for Distributions.InverseGamma DefaultAlgorithm → out ✓×2 → α → θ average energy ✓ q(out, α, θ) ✓
Limitations. No rules towards α or θ. The average energy needs both known.
\[p(\mathrm{out} \mid a, b) = \frac{\mathrm{out}^{a - 1} (1 - \mathrm{out})^{b - 1}}{B(a, b)}, \quad 0 < \mathrm{out} < 1\]
MessagePassingRulesBase.nodespec(Beta)NodeSpec: Beta
a (α) b (β) out Beta stochastic default algorithm MessagePassingRulesBase.DefaultAlgorithm()static inputs nonedefined nodes/beta.jl:1
interface aliases meaning messages and marginals its rules take outthe variable a Beta message (marginal rule), any marginal (average energy) aαthe first shape a PointMass bβthe second shape a PointMass
MessagePassingRulesBase.rule_coverage(Beta)Rule coverage for Distributions.Beta DefaultAlgorithm → out ✓×2 → a → b average energy ✓ q(out, a, b) ✓
As a prior with known shapes, Beta sends its own distribution towards out, with log scale zero:
@call_message_update_rule(node = Beta, target = :out, m = (a = PointMass(2.0), b = PointMass(3.0)))RuleResult: message of Beta towards :outbelief propagation
a b out Beta Result value Distributions.Beta{Float64}(α=2.0, β=3.0)type Distributions.Beta{Float64}log scale 0 (declared)
Inputs edge value a m PointMass{Float64}(2.0)b m PointMass{Float64}(3.0)
Rule declared inputs m[:a]::BayesBase.PointMass, m[:b]::BayesBase.PointMassalgorithm DefaultAlgorithm()log scale 0 defined beta/out.jl:1body args->Beta(mean(args.m[:a]), mean(args.m[:b]))
Other rules for this target (1) beta/out.jl:8✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:a]::BayesBase.PointMass not provided✗ q[:b]::BayesBase.PointMass not provided✗ m[:a]::BayesBase.PointMass{Float64} provided but not consumed✗ m[:b]::BayesBase.PointMass{Float64} provided but not consumed
Limitations. No rules towards a or b. The average energy accepts any marginals of a and b but uses only their means, which is exact for known shapes.
\[p(\mathrm{out} \mid p) = p^{\mathrm{out}} (1 - p)^{1 - \mathrm{out}}, \quad \mathrm{out} \in \{0, 1\}\]
MessagePassingRulesBase.nodespec(Bernoulli)NodeSpec: Bernoulli
p (θ) out Bernoulli stochastic default algorithm MessagePassingRulesBase.DefaultAlgorithm()static inputs nonedefined nodes/bernoulli.jl:1
interface aliases meaning messages and marginals its rules take outthe outcome a PointMass message; a PointMass, Bernoulli or two-category Categorical marginal pθthe probability of 1 a Beta or PointMass message; any marginal with E[log p], E[log(1 - p)]
MessagePassingRulesBase.rule_coverage(Bernoulli)Rule coverage for Distributions.Bernoulli DefaultAlgorithm → out ✓×4 → p ✓×4 average energy ✓ q(out, p) ✓×2
Towards p, the message is the likelihood Beta(1 + r, 2 - r) of an observed outcome r. Its log scale, log(1/2), normalises the likelihood:
@call_message_update_rule(node = Bernoulli, target = :p, m = (out = PointMass(1.0),))RuleResult: message of Bernoulli towards :pbelief propagation
out p Bernoulli Result value Distributions.Beta{Float64}(α=2.0, β=1.0)type Distributions.Beta{Float64}log scale loghalf = -0.693147180559... (declared)
Inputs edge value out m PointMass{Float64}(1.0)
Rule declared inputs m[:out]::BayesBase.PointMassalgorithm DefaultAlgorithm()log scale loghalf defined bernoulli/p.jl:5body args->bernoulli_likelihood(mean(args.m[:out]))
Other rules for this target (3) bernoulli/p.jl:12✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:out]::BayesBase.PointMass not provided✗ m[:out]::BayesBase.PointMass{Float64} provided but not consumed
bernoulli/p.jl:19✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:out]::Distributions.Bernoulli not provided✗ m[:out]::BayesBase.PointMass{Float64} provided but not consumed
bernoulli/p.jl:25✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:out]::Distributions.Categorical{P} where P<:Real not provided✗ m[:out]::BayesBase.PointMass{Float64} provided but not consumed
Towards out, the message is a Bernoulli. By belief propagation from a Beta(2, 3) message, its probability is the mean 2/5. The variational rule from a Beta(2, 3) marginal uses exp E[log p] instead, normalised against exp E[log(1 - p)], which gives a smaller value:
@call_message_update_rule(node = Bernoulli, target = :out, q = (p = Beta(2.0, 3.0),))RuleResult: message of Bernoulli towards :outvariational
p out Bernoulli Result value Distributions.Bernoulli{Float64}(p=0.377541)type Distributions.Bernoulli{Float64}log scale undefined: the message rule for Bernoulli towards :out under MessagePassingRulesBase.DefaultAlgorithm declares no `logscale`
Inputs edge value p q Distributions.Beta{Float64}(α=2.0, β=3.0)
Rule declared inputs q[:p]::Anyalgorithm DefaultAlgorithm()log scale none defined bernoulli/out.jl:25body args->begin
(rho_1, rho_2) = (mean(log, args.q[:p]), mean(mirrorlog, args.q[:p]))
m = max(rho_1, rho_2)
tmp = exp(rho_1 - m)
Bernoulli(clamp(tmp / (tmp + exp(rho_2 - m)), tiny, one(m)))
end
Other rules for this target (3) bernoulli/out.jl:1✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ m[:p]::Distributions.Beta not provided✗ q[:p]::Distributions.Beta{Float64} provided but not consumed
bernoulli/out.jl:10✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ m[:p]::BayesBase.PointMass not provided✗ q[:p]::Distributions.Beta{Float64} provided but not consumed
bernoulli/out.jl:17✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:p]::BayesBase.PointMass got Distributions.Beta{Float64}
A Categorical marginal of out with other than two categories is a RuleInputError .
\[p(\mathrm{out} \mid p) = \prod_{k=1}^K p_k^{\mathrm{out}_k}, \quad \mathrm{out} \text{ one-hot}\]
MessagePassingRulesBase.nodespec(Categorical)NodeSpec: Categorical
p out Categorical stochastic default algorithm MessagePassingRulesBase.DefaultAlgorithm()static inputs nonedefined nodes/categorical.jl:1
interface aliases meaning messages and marginals its rules take outthe one-hot outcome a Categorical or one-hot vector PointMass marginal pthe probability vector a Dirichlet or PointMass, as message or marginal
MessagePassingRulesBase.rule_coverage(Categorical)Rule coverage for Distributions.Categorical{P} where P<:Real DefaultAlgorithm → out ✓×4 → p ✓×3 average energy ✓ q(out, p) ✓×2
An observed one-hot outcome sends p the likelihood Dirichlet(1 .+ out), with one count on the observed category:
@call_message_update_rule(node = Categorical, target = :p, q = (out = PointMass([0.0, 1.0, 0.0]),))RuleResult: message of Categorical towards :pvariational
out p Categorical Result value Distributions.Dirichlet{Float64, Vector{Float64}, Float64}(alpha=[1.0, 2.0, 1.0])type Distributions.Dirichlet{Float64, Vector{Float64}, Float64}log scale -1.791759469228055 (computed from the inputs)
Inputs edge value out q PointMass{Vector{Float64}}([0.0, 1.0, 0.0])
Rule declared inputs q[:out]::BayesBase.PointMass{<:AbstractVector{<:Real}}algorithm DefaultAlgorithm()log scale a function of the inputs checks isonehot(mean(args.q[:out])) || lazy"`q(out)` must be one-hot; got $(mean(args.q[:out]))"defined categorical/p.jl:19body args->begin
probs = mean(args.q[:out])
Dirichlet(probs .+ one(eltype(probs)))
end
Other rules for this target (2) categorical/p.jl:9✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:out]::Distributions.Categorical{P} where P<:Real got BayesBase.PointMass{Vector{Float64}}
categorical/p.jl:30✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✓ q[:out]::Any got BayesBase.PointMass{Vector{Float64}}
The variational message towards out from a Dirichlet marginal of p normalises exp E[log pₖ]:
@call_message_update_rule(node = Categorical, target = :out, q = (p = Dirichlet([2.0, 3.0, 5.0]),))RuleResult: message of Categorical towards :outvariational
p out Categorical Result value Distributions.Categorical{Float64, Vector{Float64}}(support=Base.OneTo(3), p=[0.178468, 0.294245, 0.527287])type Distributions.Categorical{Float64, Vector{Float64}}log scale undefined: the message rule for Categorical towards :out under MessagePassingRulesBase.DefaultAlgorithm declares no `logscale`
Inputs edge value p q Distributions.Dirichlet{Float64, Vector{Float64}, Float64}(alpha=[2.0, 3.0, 5.0])
Rule declared inputs q[:p]::Distributions.Dirichletalgorithm DefaultAlgorithm()log scale none defined categorical/out.jl:10body args->begin
rho = max.(exp.(mean(BroadcastFunction(log), args.q[:p])), tiny)
Categorical(rho ./ sum(rho))
end
Other rules for this target (3) categorical/out.jl:1✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ m[:p]::Distributions.Dirichlet not provided✗ q[:p]::Distributions.Dirichlet{Float64, Vector{Float64}, Float64} provided but not consumed
categorical/out.jl:22✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ m[:p]::BayesBase.PointMass not provided✗ q[:p]::Distributions.Dirichlet{Float64, Vector{Float64}, Float64} provided but not consumed
categorical/out.jl:29✓ algorithm MessagePassingRulesBase.DefaultAlgorithm✗ q[:p]::BayesBase.PointMass got Distributions.Dirichlet{Float64, Vector{Float64}, Float64}
Limitations. The rules towards p take the marginal of out only, a Categorical or a one-hot PointMass, and give a Dirichlet. Any other marginal is an ArgumentError, and a PointMass that is not one-hot a RuleInputError . There is no rule towards p from a message on out. The variational message towards out keeps every probability above tiny.
\[p(\mathrm{out} \mid a) = \frac{Γ(\sum_k a_k)}{\prod_k Γ(a_k)} \prod_{k=1}^K \mathrm{out}_k^{a_k - 1}\]
interface aliases meaning messages and marginals its rules take outthe probability vector a Dirichlet athe concentrations a vector PointMass
MessagePassingRulesBase.rule_coverage(Dirichlet)Rule coverage for Distributions.Dirichlet DefaultAlgorithm → out ✓×2 → a average energy ✓ q(out, a) ✓
Limitations. No rule towards a, which must be known.
A collection of independent Dirichlets, one for each slice along the first dimension of out and of a:
\[p(\mathrm{out} \mid a) = \prod_{j} \mathrm{Dir}(\mathrm{out}_{:, j} \mid a_{:, j})\]
interface aliases meaning messages and marginals its rules take outthe array of probability vectors a DirichletCollection athe array of concentrations a PointMass
MessagePassingRulesBase.rule_coverage(DirichletCollection)Rule coverage for ExponentialFamily.DirichletCollection DefaultAlgorithm → out ✓×2 → a average energy ✓ q(out, a) ✓
Limitations. No rule towards a, which must be known.
\[p(\mathrm{out} \mid l) = \frac{l^{\mathrm{out}} e^{-l}}{\mathrm{out}!}, \quad \mathrm{out} \in \{0, 1, 2, \dots\}\]
interface aliases meaning messages and marginals its rules take outthe count a PointMass message, or any marginal with E[out] lλthe rate a PointMass or a Gamma message, or a Gamma marginal
MessagePassingRulesBase.rule_coverage(Poisson)Rule coverage for Distributions.Poisson DefaultAlgorithm → out ✓×2 → l ✓×2 average energy ✓×2 q(out, l) ✓
Towards l, the message is the likelihood Gamma(out + 1, 1) of an observed count. Towards out, it is a Poisson.
Limitations. For an out that is not a point mass, the average energy approximates E[log out!] by a series in E[out]. Beyond E[out] = 110 it is an error.
\[p(\mathrm{out} \mid a, b) = \frac{1}{b - a}, \quad a \le \mathrm{out} \le b\]
interface aliases meaning messages and marginals its rules take outthe variable a Beta marginal (average energy) aα, leftthe lower bound a PointMass, as message or marginal bβ, rightthe upper bound a PointMass, as message or marginal
MessagePassingRulesBase.rule_coverage(Uniform)Rule coverage for Distributions.Uniform DefaultAlgorithm → out ✓×4 → a → b average energy ✓
Limitations. No rules towards a or b, and no marginal rule. The average energy is defined only for Uniform(0, 1) with a Beta marginal of out, and is a RuleInputError for other bounds. The package defines the product of Uniform(0, 1) and a Beta, two types of other packages, as the Beta. For any other bounds the product is an ArgumentError.