DeltaMessagePassingRules

The Delta node, out = f(in₁, …, inₙ) for any deterministic function f, and the rules that pass messages through it. Use it for a nonlinear function in a model, z := f(x, y) in RxInfer's syntax, when no node with exact rules exists for f. The rules approximate the pushforward through f, the distribution of f(x, y), with the method the model names. The unscented transform and linearisation handle normal messages. Projection onto an exponential family handles messages of other families.

DeltaMessagePassingRules — Module
DeltaMessagePassingRules

The Delta node, out = f(in₁, …, inₙ) for a deterministic function f, and its rules, written with MessagePassingRulesBase. The function is arbitrary, so the rules approximate: they push the messages through f by an approximation method, which the node's algorithm names.

  • DeltaFn: the node, with interfaces out and the group in;
  • DeltaApproximation: its algorithm, which a model must give, holding the method and an optional known inverse of f;
  • the Gaussian methods Unscented and Linearization, from MessagePassingRulesApproximations, for normal messages;
  • CVIProjection, sampling and projection onto an exponential family, for messages of other families. Its rules are in a package extension, loaded with using ExponentialFamilyProjection.

The node's function and its static inputs belong to the engine's node object: a rule reaches the function as getnodefn(ctx.node, Target(:out)), with any input connected to a constant or to data already folded in. A known inverse belongs to the algorithm, so a rule towards an input with one needs no node.

Examples

The message towards the input of out = 2in + 1 through its known inverse, by linearization:

julia> algorithm = DeltaApproximation(method = Linearization(), inverse = y -> (y - 1) / 2);

julia> result = @call_message_update_rule(
           node = DeltaFn, target = (:in, 1), algorithm = algorithm,
           m = (out = NormalMeanVariance(3.0, 2.0), in = (nothing,)),
       );

julia> all(mean_var(getresult(result)) .≈ (1.0, 0.5))
true
source
Where these rules run

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

A first example

The message towards out of out = 2in + 1, by the unscented transform, which is exact for an affine function. A rule reaches the node's function through its node object, which an engine provides. Here a small stand-in holds it, and a RuleContext hands it to the rule.

julia> struct WithFunction{F}
           f::F
       end

julia> MessagePassingRulesBase.getnodefn(node::WithFunction, ::MessagePassingRulesBase.Target{:out}) = node.f

julia> ctx = MessagePassingRulesBase.RuleContext(node = WithFunction(x -> 2x + 1));

julia> result = @call_message_update_rule(
           node = DeltaFn, target = :out, algorithm = DeltaApproximation(method = Unscented()),
           m = (in = (NormalMeanVariance(1.0, 0.5),),), ctx = ctx,
       );

julia> all(mean_var(getresult(result)) .≈ (3.0, 2.0))
true

The site

  • The Delta node: the node, its algorithm, its rules and their limitations;
  • Projection: CVIProjection, the method for messages that are not normal, and its sampling strategies;
  • Internals: the helpers the Gaussian rules share, for contributors.