EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ > Class Template Reference

The record of an IMM filter's forward pass that its smoother runs backward over: one HybridPredictedJoint per prediction, each with room for its smoothed HybridGaussian. More...

#include <EmrysFilter/Smoothers/HybridGaussianHistory.h>

Classes

class  Step
 One recorded prediction and its smoothed hybrid state, built from the history's allocator. More...

Public Types

using Scalar = Scalar_
 the scalar type
using SpaceType = Space_
 the continuous state space
using ModeSpaceType = ModeSpace_
 the discrete mode space
using JointType = HybridPredictedJoint<Space_, ModeSpace_, Scalar_, Allocator_>
 what each step records
using SmoothedType = HybridGaussianParams<Space_, ModeSpace_, Scalar_, Allocator_>
 the smoothed state's type
using allocator_type = Allocator_
 the allocator the steps come from
using Index = Eigen::Index
 Eigen's index type.
using const_iterator = typename ListType::const_iterator
 iterates the steps, oldest first

Public Member Functions

 HybridGaussianHistory ()=default
 An empty history that keeps every step.
 HybridGaussianHistory (Allocator_ const &allocator)
 An empty history that keeps every step, with storage from allocator.
 HybridGaussianHistory (Lag lag, Allocator_ const &allocator={})
 An empty history that keeps the newest lag steps, with storage from allocator.
 HybridGaussianHistory (HybridGaussianHistory const &other)
 Copies the steps into nodes from other's allocator (see Allocation).
 HybridGaussianHistory (HybridGaussianHistory const &other, Allocator_ const &allocator)
 Copies other's steps into storage from allocator.
 HybridGaussianHistory (HybridGaussianHistory &&other)=default
 Takes other's steps.
 HybridGaussianHistory (HybridGaussianHistory &&other, Allocator_ const &allocator)
 Moves other's steps into storage from allocator, taking its storage when the allocators are equal.
HybridGaussianHistory & operator= (HybridGaussianHistory const &other)=default
 Copies other's steps and lag.
HybridGaussianHistory & operator= (HybridGaussianHistory &&other)=default
 Takes other's steps and lag, as std::list's move assignment does.
allocator_type get_allocator () const
std::optional< Index > lag () const
Index size () const
bool empty () const
Step const & front () const
Step const & back () const
const_iterator begin () const
const_iterator end () const
void push (JointType const &joint)
 Records a copy of joint as the newest step.
template<typename LatestAllocator>
void smooth (HybridGaussianParams< Space_, ModeSpace_, Scalar_, LatestAllocator > const &latest)
 Fills every step's smoothed hybrid state by the backward IMM step above, from the newest back, given latest, the filter's state after the newest step: its posterior \( p(x_N, r_N \mid z_{1:N}) \), which is its own smoothed state.
void clear ()
 Drops every step, keeping the lag.

Detailed Description

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
class Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >

The record of an IMM filter's forward pass that its smoother runs backward over: one HybridPredictedJoint per prediction, each with room for its smoothed HybridGaussian.

It is to an ImmFilter what GaussianHistory is to a KalmanFilter (decisions.md [smoothing]): push() records the joint the filter's predictJoint() fills, and smooth() takes the filter's current state and fills every step's smoothed hybrid state, from the newest step back.

for (auto const & z : measurements)
{
filter.predictJoint(dt, joint);
history.push(joint);
if (!filter.update(fix(z))) { break; }
}
history.smooth(filter.state());
for (auto const & step : history) { use(step.smoothed.modes(), step.smoothed.toDensity()->collapsed()); }
HybridGaussianHistory()=default
An empty history that keeps every step.
void push(JointType const &joint)
Records a copy of joint as the newest step.
Definition HybridGaussianHistory.h:477
void smooth(HybridGaussianParams< Space_, ModeSpace_, Scalar_, LatestAllocator > const &latest)
Fills every step's smoothed hybrid state by the backward IMM step above, from the newest back,...
Definition HybridGaussianHistory.h:503
One IMM prediction as its smoother needs it: the mode probabilities the step started from,...
Definition ImmFilter.h:138

Each backward step runs the IMM in reverse. With \( \mu_{t \mid t} \) the filtered mode probabilities, \( \Pi \) the chain, and the smoothed hybrid state at \( t + 1 \) given:

  • the backward transition \( b_{ij} = p_{ji} \mu^j_{t \mid t} / e_i \), the probability of mode j at t given mode i at t + 1, with \( e_i = \sum_l p_{li} \mu^l_{t \mid t} \);
  • the backward mixing \( \mu^{i \mid j} \propto b_{ij} \mu^i_{t+1 \mid N} \), the probability of mode i at t + 1 given mode j at t, and for each j the moment-matched mixture of the smoothed Gaussians at t + 1 under it;
  • for each j, smoothStep() from mode j's filtered Gaussian, through the joint of its prediction by mode j's own model, to that mixture;
  • the smoothed mode probabilities \( \mu^j_{t \mid N} = \sum_i \mu^i_{t+1 \mid N}\, p(r_t = j \mid r_{t+1} = i, z_{1:N}) \), with \( p(r_t = j \mid r_{t+1} = i, z_{1:N}) \propto b_{ij}\, \mathcal{N}(\hat x^i_{t+1 \mid N}; \hat x^{j \to i}_{t+1 \mid t}, P^{j \to i}_{t+1 \mid t}) \) normalized over j, where \( \hat x^{j \to i}_{t+1 \mid t}, P^{j \to i}_{t+1 \mid t} \) is mode j's filtered Gaussian moved through mode i's model (HybridPredictedJoint::predictedThrough()): the expectation correction of Barber (2006), (12) and (13), under its mean approximation, which evaluates the average over the smoothed Gaussian at its mean.

The continuous part is the backward IMM smoother of Nadarajah (2009), ยง8.2.1, equations 8.11 to 8.17, published as Nadarajah, Tharmarasa,McDonald, and Kirubarajan (2012). It needs no backward-time dynamics, unlike the two-filter IMM smoother, and runs one RTS step per mode, not one per pair of modes. The mode probabilities are not its (8.18, 8.19), which weight each mode by \( \Lambda_j \), the density of the smoothed means at t + 1 under its own prediction, without the smoothed mode probabilities at t + 1: that left an autonomous multiple model filter's, which must stay at the final filtered ones, drifting from 0.27 to 0.003 where all were 0.034, and made a maneuver certain where enumeration gives 0.21 to 0.74. The expectation correction keeps the first exactly, since \( b_{ij} = \delta_{ij} \) for \( \Pi = I \), and the test ImmSmootherModeProbabilitiesTrackEnumeration holds it to the second; it is not the smoother of Kim (1994), \( \sum_i b_{ij} \mu^i_{t+1 \mid N} \), which is exact for \( \Pi = I \) too but loses what the later measurements say about a maneuver through the continuous state. It costs a prediction per pair of modes in predictJoint(). Three departures in the continuous part. The thesis calls \( \hat x^j_{t+1 \mid t}, P^j_{t+1 \mid t} \) the predicted mean and covariance of mode j; its derivation (8.2) divides by \( p(x_{t+1} \mid r_t = j, Z^t) \), which is mode j's filtered Gaussian moved through its own model, and its gain (8.17), \( P^j_{t \mid t} F_j^T (P^j_{t+1 \mid t})^{-1} \), is consistent only with that, so that is what ImmFilter::predictJoint() records, rather than the IMM's prediction from the mixed Gaussian. The smoothed covariance (8.16) is printed with a minus sign; this is the RTS update, with a plus, which is what the derivation gives. And the step is written with the cross-covariance and in the tangent space, as smoothStep() is, so that it serves every prediction method and manifold states. The weights are kept as logarithms; a mode no smoothed mode at t + 1 can have come from keeps its filtered Gaussian.

As with GaussianHistory, a Lag keeps the newest steps and recycles the oldest step's storage once full. A full history's push() copies the joint into that storage, so with one joint kept and refilled by predictJoint(dt, joint), fixed-lag smoothing allocates nothing once the window is full. Each step's joints and smoothed Gaussians come from the history's allocator, by uses-allocator construction.

Template Parameters
Space_the continuous state space
ModeSpace_the discrete mode space
Scalar_scalar type, double by default
Allocator_allocator for the steps and what they hold (see Allocation)
See also
HybridPredictedJoint, ImmFilter::predictJoint(), GaussianHistory, Smoothers

Member Function Documentation

◆ back()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
Step const & Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::back ( ) const
inlinenodiscard
Returns
the newest step; the history must not be empty

◆ begin()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
const_iterator Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::begin ( ) const
inlinenodiscard
Returns
an iterator to the oldest step

◆ empty()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
bool Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::empty ( ) const
inlinenodiscard
Returns
whether no step is recorded

◆ end()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
const_iterator Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::end ( ) const
inlinenodiscard
Returns
an iterator past the newest step

◆ front()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
Step const & Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::front ( ) const
inlinenodiscard
Returns
the oldest step kept; the history must not be empty

◆ get_allocator()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
allocator_type Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::get_allocator ( ) const
inlinenodiscard
Returns
the allocator the steps come from

◆ lag()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
std::optional< Index > Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::lag ( ) const
inlinenodiscard
Returns
the most steps a fixed-lag history keeps, or nothing if it keeps every step

◆ push()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
void Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::push ( JointType const & joint)
inline

Records a copy of joint as the newest step.

A full fixed-lag history drops its oldest step and copies joint into its storage.

◆ size()

template<Space Space_, DiscreteSpace ModeSpace_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
Index Emrys::HybridGaussianHistory< Space_, ModeSpace_, Scalar_, Allocator_ >::size ( ) const
inlinenodiscard
Returns
the number of steps recorded, at most lag()

The documentation for this class was generated from the following file: