EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ > Class Template Reference

An interacting multiple model (IMM) filter: a hybrid state with one component per mode, and the ImmRecursion that mixes it through a switching process model and updates it per mode. More...

#include <EmrysFilter/Filters/ImmFilter.h>

Inheritance diagram for Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >:
Emrys::AmmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >

Public Types

using RecursionType = ImmRecursion<Component_, Model_, Method_, Allocator_>
 the steps it runs
using Scalar = typename RecursionType::Scalar
 the scalar type
using SpaceType = typename RecursionType::SpaceType
 the state space
using ModelType = Model_
 the process model
using MethodType = Method_
 the prediction method
using ModeSpaceType = typename RecursionType::ModeSpaceType
 the discrete mode space
using StateType = typename RecursionType::StateType
 the state the filter owns
using ComponentType = Params<Component_>
 one component of the state
using allocator_type = Allocator_
 the state's allocator
using JointType = typename RecursionType::JointType
 what predictJoint() returns

Public Member Functions

 ImmFilter (Model_ model, StateType state, MethodFor< Model_, Method_ > method={}, std::type_identity_t< Upkeep_ > upkeep={})
 A filter with switching model model, initial state state, prediction method method, and upkeep upkeep, which maintain() runs.
 ImmFilter (Model_ model, StateType state, std::type_identity_t< Upkeep_ > upkeep)
 A filter with switching model model, initial state state, the default prediction method, and upkeep upkeep, which maintain() runs.
allocator_type get_allocator () const
StateType const & state () const
StateType & state ()
void setState (StateType state)
 Replaces the state, as when reinitializing, with one that has the model's modes and whose space every process model's writes partition, as the constructor's must.
RecursionType const & recursion () const
Model_ const & model () const
Model_ & model ()
Method_ const & method () const
template<typename... Inputs>
requires (!isFixedStep<Model_>)
auto predict (TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
 Mixes the modes' components through the chain, moves each by dt, any TimeStep (see Time steps), through its mode's process model, with inputs passed as in Emrys::predict(), and moves the mode probabilities one step of the chain.
template<typename... Inputs>
requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
auto predict (Inputs const &... inputs)
 Mixes the modes' components through the chain, moves each by its mode's fixed-step model's step, by that model's method, with inputs passed as in Emrys::predict(), and moves the mode probabilities one step of the chain, returning as predict(dt, inputs...) does.
template<typename... Inputs>
requires isFixedStep<Model_>
auto predict (Steps steps, Inputs const &... inputs)
 Moves the state forward by steps of the fixed-step models, as that many calls of predict(inputs...) do, with inputs at every step: the chain takes a step each time.
template<typename... Inputs>
requires (!isFixedStep<Model_>)
JointType predictJoint (TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
 Moves the state forward as predict(dt, inputs...) does.
template<typename JointAllocator, typename... Inputs>
requires (!isFixedStep<Model_>)
void predictJoint (TimeStep< Scalar > auto const &dt, HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs)
 As predictJoint(dt, inputs...), but into joint, whose storage is reused: with the same joint at every step, recording allocates nothing once it has been filled.
template<typename... Inputs>
requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
JointType predictJoint (Inputs const &... inputs)
 Moves the state forward as predict(inputs...) does with fixed-step models.
template<typename JointAllocator, typename... Inputs>
requires isFixedStep<Model_>
void predictJoint (HybridPredictedJoint< SpaceType, ModeSpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs)
 As predictJoint(inputs...), but into joint, whose storage is reused.
template<Measurement M, typename Policy = reductions::Collapse>
auto update (M const &measurement, Policy const &policy={})
 Conditions each mode's component on measurement, by the measurement's own method, reduces its branches with policy (Collapse, Select, or a Gate), and multiplies each mode's probability by \( p(z \mid m) \).
template<Measurement M, typename Policy, typename ModeUpdate, typename ResultAllocator>
std::expected< void, NumericError > update (M const &measurement, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
 As update(measurement, policy), but into result, whose storage is reused: with the same result at every step, an update allocates nothing once it has been as large.
template<typename P, Measurement... Ms, typename Policy = reductions::Collapse>
auto update (SwitchingMeasurement< P, Ms... > const &measurement, Policy const &policy={})
 As update(measurement, policy), but with a measurement per value of the mode axis that measurement names (see SwitchingMeasurement): each mode is updated by the measurement that its value in that axis selects, and its own likelihood \( p(z \mid m) \) reweights it.
template<typename P, Measurement... Ms, typename Policy, typename ModeUpdate, typename ResultAllocator>
std::expected< void, NumericError > update (SwitchingMeasurement< P, Ms... > const &measurement, Policy const &policy, ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > &result)
 As update(measurement, policy) with a SwitchingMeasurement, but into result, whose storage is reused.

Detailed Description

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
class Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >

An interacting multiple model (IMM) filter: a hybrid state with one component per mode, and the ImmRecursion that mixes it through a switching process model and updates it per mode.

predict() first mixes: the component that mode j starts from is the collapse of every mode's component, weighted by the probability \( \mu_{i \mid j} \propto \Pi_{ij} \mu_i \) that the target was in mode i given that it is now in j. Each mixed component is then predicted through its mode's process model, by the model's own method where it carries one (withMethod(), FixedStepModel) and by the filter's otherwise [switching-models], and the mode probabilities move one step of the chain. update() updates each mode's component as a KalmanFilter would and multiplies each mode's probability by \( p(z \mid m) \).

The interacting multiple model algorithm of Blom and Bar-Shalom (1988), as given in Hashimzade et al. (2024), ยง2.2.3, Algorithm 2 with one lag: mixing, mode-matched filtering, mode probability update, and the combined estimate as collapsed().

The mode space may be a DiscreteProduct, such as a maneuver regime and a sensor's inlier/outlier state. A measurement's noise components can then be the values of a mode axis, so that the outlier state is a Markov chain (bursts of outliers, a stuck sensor) rather than independent at each measurement: a measurement whose noise is additiveNoise(mixture).byMode<Sensor>() (see ModeNoise) uses, in each mode, only the noise component that the mode's Sensor value names. A measurement that depends on the mode in more than its noise, such as a sensor whose bias the mode sets, is one measurement per value of an axis: update(switchingMeasurement<Sensor>(nominal, biased)) updates each mode by the one that its Sensor value names (see SwitchingMeasurement).

auto filter = ImmFilter{switchingModel<Maneuver>(chain, cruise, turn),
filter.predict(dt);
auto const updated = filter.update(fix); // every mode, with the fix's own noise
auto const robust = filter.update(robustFix); // noise byMode<Sensor>(): inlier and outlier are its values
auto const biased = filter.update(switchingMeasurement<Sensor>(fix, biasedFix)); // a measurement per value
filter.state().modes().toDensity()->probability<Turn>();
filter.state().toDensity()->collapsed(); // the estimate as one GaussianParams
The parameters of a probability distribution over a discrete space: one weight per value.
Definition Categorical.h:257
The product of the discrete spaces Axes: a value has one value in each axis.
Definition Discrete.h:247
The density of a hybrid state, a checked view of its HybridParams (decisions.md [params-and-density],...
Definition Hybrid.h:528
An interacting multiple model (IMM) filter: a hybrid state with one component per mode,...
Definition ImmFilter.h:1382
auto predict(TimeStep< Scalar > auto const &dt, Inputs const &... inputs)
Mixes the modes' components through the chain, moves each by dt, any TimeStep (see Time steps),...
Definition ImmFilter.h:1517
A Markov chain on a discrete space: the probability of moving from each value to each value in one st...
Definition Categorical.h:829
static MarkovChain persistent(Scalar stay, Space_ space={})
Definition Categorical.h:934
SwitchingMeasurement< P, Measurements... > switchingMeasurement(Measurements... measurements)
Definition SwitchingModel.h:618
SwitchingModel< ModeSpace, Scalar, P, Models... > switchingModel(MarkovChain< ModeSpace, Scalar > chain, Models... models)
Definition SwitchingModel.h:510

A mode whose update the policy rejects keeps its prior component and gets probability zero, from which the chain can bring it back at the next prediction. When every mode rejects the measurement, it is rejected and the state is unchanged. With one mode the filter is a KalmanFilter. With the identity chain, MarkovChain<Mode>{}, it is the autonomous multiple model estimator, which AmmFilter spells: each mode's mixing weights put everything on its own component, so predict() skips the mixing and leaves the mode probabilities as they are.

The predicted and posterior components are built in scratch from the state's allocator and exchanged with the state's, so on a bounded space its steps allocate nothing; update(measurement, policy, result) also reuses the result's storage (see Allocation).

Template Parameters
Component_the component given one mode (see Component), a Gaussian over the state space (see Spaces)
Model_a SwitchingModel over the mode space, of process models whose blocks partition the state space; with fixed-step models, predict() takes no time step
Method_the prediction method of every mode whose model carries none of its own (see withMethod()); ModelsMethod with fixed-step models, which all carry one
Allocator_the state's allocator (see Allocation)
Upkeep_the upkeep maintain() runs on the state (see Upkeep), upkeep::None unless given one, such as FloorModes or RepairCovariance
See also
Hybrid, SwitchingModel, ImmRecursion, Filters

Constructor & Destructor Documentation

◆ ImmFilter()

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::ImmFilter ( Model_ model,
StateType state,
MethodFor< Model_, Method_ > method = {},
std::type_identity_t< Upkeep_ > upkeep = {} )
inline

A filter with switching model model, initial state state, prediction method method, and upkeep upkeep, which maintain() runs.

Fixed-step models carry their own method, so there is none to give; each mode predicts by its own model's.

Member Function Documentation

◆ get_allocator()

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
allocator_type Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::get_allocator ( ) const
inlinenodiscard
Returns
the allocator of the state and scratch

◆ method()

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
Method_ const & Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::method ( ) const
inlinenodiscard
Returns
the prediction method of the modes whose models carry none of their own; ModelsMethod with fixed-step models

◆ model() [1/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
Model_ & Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::model ( )
inlinenodiscard
Returns
the switching model, to change its chain or its models' parameters

◆ model() [2/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
Model_ const & Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::model ( ) const
inlinenodiscard
Returns
the switching model

◆ predict() [1/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires isFixedStep<Model_>
auto Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::predict ( Steps steps,
Inputs const &... inputs )
inlinenodiscard

Moves the state forward by steps of the fixed-step models, as that many calls of predict(inputs...) do, with inputs at every step: the chain takes a step each time.

For a square-root form it stops at the first step a mode could not take, returning its error.

◆ predict() [2/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires (!isFixedStep<Model_>)
auto Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::predict ( TimeStep< Scalar > auto const & dt,
Inputs const &... inputs )
inlinenodiscard

Mixes the modes' components through the chain, moves each by dt, any TimeStep (see Time steps), through its mode's process model, with inputs passed as in Emrys::predict(), and moves the mode probabilities one step of the chain.

Returns
nothing for Gaussians; for a square-root form, a std::expected that is empty when a mode could not take the step (see ImmRecursion::predict())

◆ predictJoint() [1/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>)
JointType Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::predictJoint ( Inputs const &... inputs)
inlinenodiscard

Moves the state forward as predict(inputs...) does with fixed-step models.

Returns
the step as a HybridPredictedJoint, as predictJoint(dt, inputs...) does

◆ predictJoint() [2/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<typename... Inputs>
requires (!isFixedStep<Model_>)
JointType Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::predictJoint ( TimeStep< Scalar > auto const & dt,
Inputs const &... inputs )
inlinenodiscard

Moves the state forward as predict(dt, inputs...) does.

Returns
the step as a HybridPredictedJoint, from the state's allocator, for a HybridGaussianHistory to record

◆ recursion()

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
RecursionType const & Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::recursion ( ) const
inlinenodiscard
Returns
the recursion: the model, method, and scratch the steps run with

◆ setState()

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
void Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::setState ( StateType state)
inline

Replaces the state, as when reinitializing, with one that has the model's modes and whose space every process model's writes partition, as the constructor's must.

The scratch follows the new state's allocator.

◆ state() [1/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
StateType & Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::state ( )
inlinenodiscard
Returns
the current state, to change where it is between steps, as upkeep such as FloorModes and RepairCovariance does. It must keep its space, mode space, and allocator; setState() replaces it with another, and checks it.

◆ state() [2/2]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
StateType const & Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::state ( ) const
inlinenodiscard
Returns
the current state: the mode probabilities and each mode's component

◆ update() [1/3]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<Measurement M, typename Policy, typename ModeUpdate, typename ResultAllocator>
std::expected< void, NumericError > Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::update ( M const & measurement,
Policy const & policy,
ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > & result )
inlinenodiscard

As update(measurement, policy), but into result, whose storage is reused: with the same result at every step, an update allocates nothing once it has been as large.

Returns
nothing, or the error update(measurement, policy) would return

◆ update() [2/3]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<Measurement M, typename Policy = reductions::Collapse>
auto Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::update ( M const & measurement,
Policy const & policy = {} )
inlinenodiscard

Conditions each mode's component on measurement, by the measurement's own method, reduces its branches with policy (Collapse, Select, or a Gate), and multiplies each mode's probability by \( p(z \mid m) \).

Returns
each mode's update before reduction, the observation's log-likelihood, and whether it was accepted (see ImmFilterUpdate), with the modes' updates from the state's allocator, in a std::expected that is empty, with the state unchanged, when a mode's update failed (see ImmRecursion::update())

◆ update() [3/3]

template<Component Component_, typename Model_, typename Method_ = DefaultMethodFor<Model_>, typename Allocator_ = DefaultAllocator, typename Upkeep_ = upkeep::None>
template<typename P, Measurement... Ms, typename Policy, typename ModeUpdate, typename ResultAllocator>
std::expected< void, NumericError > Emrys::ImmFilter< Component_, Model_, Method_, Allocator_, Upkeep_ >::update ( SwitchingMeasurement< P, Ms... > const & measurement,
Policy const & policy,
ImmFilterUpdate< ModeUpdate, Scalar, ResultAllocator > & result )
inlinenodiscard

As update(measurement, policy) with a SwitchingMeasurement, but into result, whose storage is reused.

Returns
nothing, or the error update(measurement, policy) would return

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