The unscented transform

The unscented transform estimates the mean and covariance of f(x) for a normal x without differentiating f. It places 2d + 1 sigma points around the mean of a d-dimensional normal N(m, V), passes each through f, and forms weighted sums of the results.

The method

With a scaling $λ = α^2 (d + κ) - d$ and $L$ a square root of $(d + λ) V$, so that $L L^\top = (d + λ) V$, the sigma points are the mean and a pair along each column $L_i$:

\[χ_0 = m, \qquad χ_i = m + L_i, \qquad χ_{d + i} = m - L_i, \qquad i = 1, \ldots, d.\]

The output's moments are weighted sums over the transformed points $y_i = f(χ_i)$:

\[\tilde{m} = \sum_i W^{(m)}_i y_i, \qquad \tilde{V} = \sum_i W^{(c)}_i (y_i - \tilde{m})(y_i - \tilde{m})^\top,\]

with $W^{(m)}_0 = λ / (d + λ)$, $W^{(c)}_0 = W^{(m)}_0 + 1 - α^2 + β$ and $W^{(m)}_i = W^{(c)}_i = 1 / (2(d + λ))$ for the other points. The keywords alpha, beta and kappa of Unscented are $α$, $β$ and $κ$. The estimates are exact when f is affine, and match the true moments to second order in general.

sigma_points_weights returns the points and both sets of weights. For a scalar normal with mean 1 and variance 0.5, and alpha = 1, the three points lie at the mean and one standard deviation times $\sqrt{1 + λ}$ on either side:

using MessagePassingRulesApproximations

points, weights_mean, weights_cov = sigma_points_weights(Unscented(alpha = 1.0), 1.0, 0.5)
((1.0, 1.7071067811865475, 0.2928932188134524), (0.0, 0.5, 0.5), (2.0, 0.5, 0.5))

Moments through a function

approximate returns the output's mean and covariance. The classic example converts a position from polar to Cartesian coordinates, with a radius known to within 0.1 and an angle known to within about 0.55 radians:

polar_to_cartesian(x) = [x[1] * cos(x[2]), x[1] * sin(x[2])]

m, V = [1.0, 0.0], [0.01 0.0; 0.0 0.3]
approximate(Unscented(), polar_to_cartesian, (m,), (V,))
([0.8500000075437129, 0.0], [0.055000017975544324 0.0; 0.0 0.29999994000000485])

The exact mean of the first coordinate is $\mathrm{E}[r] \, \mathrm{E}[\cos θ] = \exp(-0.3 / 2) ≈ 0.861$. The unscented estimate, 0.85, follows the curvature of the circle towards the origin; a linearisation at the mean gives 1.0 (Linearisation).

Several inputs are passed as several entries of the tuples, and are treated as one normal whose covariance is block diagonal:

approximate(Unscented(), (x, y) -> x * y, (1.0, 2.0), (0.1, 0.2))
(1.9999999998835847, 0.6000000000000328)

unscented_statistics also returns the cross-covariance between the inputs and the output, which smoothRTS needs:

unscented_statistics(Unscented(), x -> 2x + 1, (1.0,), (0.5,))
(3.0000000004656613, 1.9999999999997828, 0.9999999999999699)

API

MessagePassingRulesApproximations.Unscented — Type
Unscented(; alpha = 1e-3, beta = 2.0, kappa = 0.0)
Unscented(dim::Integer; alpha = 1e-3, beta = 2.0, kappa = 0.0)

The unscented transform: the mean and covariance of f(x) for a normal x, estimated from 2d + 1 deterministically placed sigma points, d being the dimension of x. It needs no derivatives and captures the mean and covariance of f(x) to second order. UT and UnscentedTransform are aliases.

Keywords

  • alpha: the spread of the sigma points around the mean, a small positive number. Default 1e-3;
  • beta: prior knowledge of the input's distribution, 2 being optimal for a normal. Default 2.0;
  • kappa: the secondary scaling. Default 0.0.

With dim, the method is for inputs of that dimension, and sigma_point_parameters returns the weights it stores; an input of another dimension, or several inputs whose joint has another dimension, is a DimensionMismatch. Without it, the method takes inputs of any dimension. The sigma points and their weights are computed on each call either way.

It is used through approximate, for the output's moments, and unscented_statistics, which adds the cross-covariance smoothRTS needs. Linearization is the alternative that expands the function instead.

Examples

julia> m, V = approximate(Unscented(), sin, (0.0,), (1e-4,));

julia> isapprox(m, 0.0; atol = 1e-12) && isapprox(V, 1e-4; rtol = 1e-3)
true
source
MessagePassingRulesApproximations.approximate — Method
approximate(method::Unscented, f, means::Tuple, covs::Tuple) -> (m, V)

The mean and covariance of f(x₁, x₂, …) for independent normal inputs, by the unscented transform.

Arguments

  • f: a function of one argument per input, returning a number or a vector;
  • means, covs: one entry per input, a number and a variance, or a vector and a covariance matrix. Several inputs are treated jointly, as one normal whose covariance is block diagonal.

Returns

(m, V): the output's mean and variance, or mean vector and covariance matrix.

Throws

A DomainError when a covariance is infinite. A zero covariance is not an error: the input is a point, and the result is f at it with zero covariance, after a warning.

source
MessagePassingRulesApproximations.unscented_statistics — Function
unscented_statistics(method::Unscented, g, means::Tuple, covs::Tuple) -> (m, V, C)

The output's mean m and covariance V, as approximate(::Unscented, …) computes them, and the cross-covariance C between the inputs, concatenated into one vector, and the output: the forward statistics smoothRTS takes. Arguments and errors are those of approximate; for a point input, C is zero.

source
MessagePassingRulesApproximations.sigma_points_weights — Function
sigma_points_weights(method::Unscented, m::Real, V::Real) -> (points, weights_m, weights_c)
sigma_points_weights(method::Unscented, m::AbstractVector, V::AbstractMatrix) -> (points, weights_m, weights_c)

The 2d + 1 sigma points of N(m, V) under method's parameters, and their weights for the mean and for the covariance, in the same order: tuples for a scalar normal, vectors otherwise. The first point is the mean. It warns when the parameters make d + λ negative, which gives unreliable estimates.

source
MessagePassingRulesApproximations.sigma_point_parameters — Function
sigma_point_parameters(method::Unscented, dim::Integer) -> (; λ, Wm, Wc)

The scaling λ and the weights of the mean, Wm, and of the covariance, Wc, of the 2 dim + 1 sigma points method places for an input of dimension dim: those method carries when it was built for dim, Unscented(dim; …), and computed from its alpha, beta and kappa when it was built without a dimension, Unscented(; …). For a caller that places the sigma points itself.

Throws

  • DimensionMismatch when method was built for a dimension other than dim.

Examples

julia> p = MessagePassingRulesApproximations.sigma_point_parameters(Unscented(; alpha = 1.0), 2);

julia> p.λ, length(p.Wm), sum(p.Wm) ≈ 1
(0.0, 5, true)
source