|
EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
|
A mixture filter: a Mixture state, a process model, and the method that predicts through it, each component run by the recursion its type names, or by the one given. More...
#include <EmrysFilter/Filters/MixtureFilter.h>
Public Types | |
| using | Scalar = typename Params<Component_>::Scalar |
| the scalar type | |
| using | SpaceType = typename Params<Component_>::SpaceType |
| the state space | |
| using | ModelType = Model_ |
| the process model | |
| using | MethodType = Method_ |
| the prediction method | |
| using | StateType = MixtureParams<Component_, Allocator_> |
| the state the filter owns | |
| using | ComponentType = Params<Component_> |
| one component of the state | |
| using | allocator_type = Allocator_ |
| the state's allocator | |
| using | RecursionType = Recursion_ |
| the steps each component runs (see Recursion) | |
| using | JointType = GaussianMixturePredictedJoint<SpaceType, Scalar, Allocator_> |
| what predictJoint() returns, for Gaussian components | |
| template<Measurement M> | |
| using | ResultType |
| What update() with M fills when given a result: one component's update per component, each with the filter's allocator for what in it is sized by a run-time count. | |
| template<Measurement M> | |
| using | UpdateType = std::expected<ResultType<M>, NumericError> |
What update() with M returns: the ResultType in a std::expected (decisions.md [innovation-cholesky]). | |
Public Member Functions | |
| MixtureFilter (Model_ model, StateType state, MethodFor< Model_, Method_ > method={}, std::type_identity_t< Upkeep_ > upkeep={}) | |
| A filter with process model model, initial state state, prediction method method, which a fixed-step model carries instead, and upkeep upkeep, which maintain() runs. | |
| MixtureFilter (Model_ model, StateType state, std::type_identity_t< Upkeep_ > upkeep) | |
| A filter with process model model, initial state state, the default prediction method, and upkeep upkeep, which maintain() runs. | |
| MixtureFilter (Recursion_ recursion, StateType state, std::type_identity_t< Upkeep_ > upkeep={}) | |
| A filter that runs recursion, the library's or one of the user's own (see Recursion), on each component of the initial state state, which must have at least one, with upkeep upkeep, which maintain() runs. | |
| allocator_type | get_allocator () const |
| StateType const & | state () const |
| StateType & | state () |
| void | setState (StateType state) |
| Replaces the state estimate, as when reinitializing, with one that has at least one component and whose space the 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) |
| Moves each component forward by dt, any TimeStep (see Time steps), through the process model, with inputs passed to its blocks as in Emrys::predict(). | |
| template<ExecutionPolicy P, typename... Inputs> requires (!isFixedStep<Model_>) | |
| auto | predict (P &&policy, TimeStep< Scalar > auto const &dt, Inputs const &... inputs) |
| predict(dt, inputs...) under policy, a for-each over the components, so the result is Sequential's (see Execution policies). | |
| template<typename... Inputs> requires (isFixedStep<Model_> && !internal::leadsWithPolicy<Inputs...> && !internal::leadsWithSteps<Inputs...>) | |
| auto | predict (Inputs const &... inputs) |
| Moves each component forward by the fixed-step model's step, with inputs passed as in Emrys::predict(). | |
|
template<ExecutionPolicy P, typename... Inputs> requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>) | |
| auto | predict (P &&policy, Inputs const &... inputs) |
| predict(inputs...) under policy, as predict(policy, dt, inputs...) runs. | |
|
template<typename... Inputs> requires isFixedStep<Model_> | |
| auto | predict (Steps steps, Inputs const &... inputs) |
| Moves each component forward by steps of the fixed-step model, as that many calls of predict(inputs...) do, with inputs at every step, returning as predict(inputs...) does. | |
|
template<ExecutionPolicy P, typename... Inputs> requires isFixedStep<Model_> | |
| auto | predict (P &&policy, Steps steps, Inputs const &... inputs) |
| predict(steps, inputs...) under policy, as that many calls of predict(policy, inputs...) run, stopping at the first that reports an error. | |
| template<typename... Inputs> requires (!isFixedStep<Model_> && isGaussian<ComponentType>) | |
| JointType | predictJoint (TimeStep< Scalar > auto const &dt, Inputs const &... inputs) |
| Moves each Gaussian component forward as predict(dt, inputs...) does, and starts the state's lineage over (MixtureParams::startLineage()). | |
|
template<typename JointAllocator, typename... Inputs> requires (!isFixedStep<Model_> && isGaussian<ComponentType>) | |
| void | predictJoint (TimeStep< Scalar > auto const &dt, GaussianMixturePredictedJoint< SpaceType, 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 as large. | |
|
template<ExecutionPolicy P, typename JointAllocator, typename... Inputs> requires (!isFixedStep<Model_> && isGaussian<ComponentType>) | |
| void | predictJoint (P &&policy, TimeStep< Scalar > auto const &dt, GaussianMixturePredictedJoint< SpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs) |
| predictJoint(dt, joint, inputs...) under policy, a for-each over the components into the joint's slots, so the joint and the state are Sequential's (see Execution policies). | |
| template<typename... Inputs> requires ( isFixedStep<Model_> && isGaussian<ComponentType> && !internal::leadsWithPolicy<Inputs...> && !internal::leadsWithSteps <Inputs...> ) | |
| JointType | predictJoint (Inputs const &... inputs) |
| Moves each Gaussian component forward as predict(inputs...) does with a fixed-step model, and starts the state's lineage over. | |
|
template<typename JointAllocator, typename... Inputs> requires (isFixedStep<Model_> && isGaussian<ComponentType>) | |
| void | predictJoint (GaussianMixturePredictedJoint< SpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs) |
| As predictJoint(inputs...), but into joint, whose storage is reused. | |
|
template<ExecutionPolicy P, typename JointAllocator, typename... Inputs> requires (isFixedStep<Model_> && isGaussian<ComponentType>) | |
| void | predictJoint (P &&policy, GaussianMixturePredictedJoint< SpaceType, Scalar, JointAllocator > &joint, Inputs const &... inputs) |
| predictJoint(joint, inputs...) under policy, as predictJoint(policy, dt, joint, inputs...) runs. | |
| template<Measurement M, typename Policy = reductions::Collapse> requires ( !isGaussian<ComponentType> || MixtureReductionPolicy<Policy, internal::MeasuredUpdate<ComponentType, M>, Component_> ) | |
| UpdateType< M > | update (M const &measurement, Policy const &policy={}) |
| Conditions each component on measurement, by the measurement's own method, reduces its branches with policy, a MixtureReductionPolicy over the component (Collapse, Select, Keep, or a Gate), and multiplies the weight of what the policy keeps by \( p(z \mid i) \) times its share among the branches: for a Gate the passing branches' share, so that a branch the gate fails counts as impossible. | |
| template<ExecutionPolicy P, Measurement M, typename Policy = reductions::Collapse> requires ( !isGaussian<ComponentType> || MixtureReductionPolicy<Policy, internal::MeasuredUpdate<ComponentType, M>, Component_> ) | |
| UpdateType< M > | update (P &&executionPolicy, M const &measurement, Policy const &policy={}) |
| update(measurement, policy) under executionPolicy: each component's update is a for-each into slots made first, and the reweighting, the evidence, and the posterior then follow in order, so the result is Sequential's (see Execution policies). | |
| template<Measurement M, typename Policy, typename ResultAllocator> requires ( !isGaussian<ComponentType> || MixtureReductionPolicy<Policy, internal::MeasuredUpdate<ComponentType, M>, Component_> ) | |
| std::expected< void, NumericError > | update (M const &measurement, Policy const &policy, MixtureFilterUpdate< typename ResultType< M >::ComponentUpdateType, 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<ExecutionPolicy P, Measurement M, typename Policy, typename ResultAllocator> requires ( !isGaussian<ComponentType> || MixtureReductionPolicy<Policy, internal::MeasuredUpdate<ComponentType, M>, Component_> ) | |
| std::expected< void, NumericError > | update (P &&executionPolicy, M const &measurement, Policy const &policy, MixtureFilterUpdate< typename ResultType< M >::ComponentUpdateType, Scalar, ResultAllocator > &result) |
| update(measurement, policy, result) under executionPolicy, as update(executionPolicy, measurement, policy) runs. | |
A mixture filter: a Mixture state, a process model, and the method that predicts through it, each component run by the recursion its type names, or by the one given.
Each component is predicted and updated as a filter over one of it would be, and an update multiplies each component's weight by the observation's likelihood under it, \( p(z \mid i) \). With Gaussian components (GaussianMixtureFilter) each is a KalmanFilter's step, and a mixture can carry a multimodal state, such as an ambiguity that later measurements resolve, or many narrow components that follow a nonlinear model more closely than one wide one, as uniformComponents() gives over a box where only the box is known (UniformMixture.h). With hybrid components (a Mixture of HybridGaussian) each is an ImmFilter's step, mixed through its switching model and updated per mode, and reweighted by the IMM's likelihood \( \log \sum_m \mu_m \, p(z \mid m) \): a mixture of IMM filters (decisions.md [filters-and-recursions]).
The Gaussian sum filter of Alspach and Sorenson (1972), with the component reduction left to the caller (Upkeep); with hybrid components, its components are mode-matched as the Gaussian mixture filter for jump Markov systems of Pasha, Vo, Tuan, and Ma (2009) carries each target's.
The policy reduces each component's branches, one per measurement-noise component. Collapse (the default), Select, and Gate keep one component per component, so the mixture keeps its size; Keep, for Gaussian components, keeps every branch, so with mixture noise the number of components grows by that factor with each update. Reduce the mixture between steps through state(): pruneBelow(), pruneTail(), pruneToSize(), merge(), or fitMixture(). With Gaussian components, predictJoint() records each step for a GaussianMixtureHistory to smooth, and the state's lineage (MixtureParams::lineage()) follows the components through the updates and reductions between (decisions.md [smoothing]).
Keep gains over Collapse when the components disagree for longer than one update, as about a multimodal state or an outlier too near to tell from an inlier at once. When each fix is plainly one or the other, every component's branches agree on it and Collapse loses little. kullbackLeiblerCollapseBounds() of the state's density says whether its moments describe it (see Divergences), and of a component's branches whether collapsing them would lose much, so a policy of the user's own can keep the branches only where they disagree. A Gate commits: a component it drops is gone, where mixture noise without a gate would only lower its weight, so a bad fix that another component happens to explain removes the right one.
Which single estimate to read from the mixture depends on the question. The moments of the state's density, state().toDensity()->moments(), are the minimum mean-squared-error estimate, and can sit between the modes, where no component is. The most probable component, state()[state().mostProbable()].component, is the estimate a display shows of a multimodal state.
A component whose update the recursion rejects, as a Gate does when no branch passes, is dropped. When every component is rejected, the measurement is rejected and the state keeps its prior, so with one component the filter behaves as the filter over that component. A component whose update fails, as one whose innovation covariance is not positive definite does, fails the whole update, which returns the error. The components are updated in place, so those before the failed one stay updated, with the weights as they were; nothing is copied to undo a failure (decisions.md [innovation-cholesky]). A policy that keeps several branches builds the posterior apart, and its failure leaves the state as it was.
The recursion updates the state's components in place. A policy that keeps several branches builds the posterior in a second mixture from the state's allocator, with the state's capacity, exchanged with the state. An update allocates nothing once the mixture has been as large. update(measurement, policy, result) also reuses the result's storage (see Allocation). A recursion with scratch, the IMM's, runs the components in order, so a policy other than Sequential is a compile-time error with hybrid components.
A recursion of the user's own, one that meets Recursion over the component, runs on each component in place of the one the component names: MixtureFilter{recursion, mixture} deduces it as the last argument (decisions.md [user-extension-points]).
| Component_ | the components' type (see Component): a Gaussian over the state space (see Spaces), or a Hybrid of them |
| Model_ | the process model, a ProcessModel or a FixedStepModel, which partition the space, or a SwitchingModel for hybrid components; with a FixedStepModel, predict() takes no time step |
| Method_ | the prediction method; a FixedStepModel's own |
| Allocator_ | the state's allocator (see Allocation) |
| Recursion_ | the recursion each component's steps run (see Recursion): the one the component names unless given one |
| Upkeep_ | the upkeep maintain() runs on the state (see Upkeep), upkeep::None unless given one |
| using Emrys::MixtureFilter< Component_, Model_, Method_, Allocator_, Recursion_, Upkeep_ >::ResultType |
What update() with M fills when given a result: one component's update per component, each with the filter's allocator for what in it is sized by a run-time count.
|
inline |
A filter with process model model, initial state state, prediction method method, which a fixed-step model carries instead, and upkeep upkeep, which maintain() runs.
state must have at least one component.
|
inline |
A filter that runs recursion, the library's or one of the user's own (see Recursion), on each component of the initial state state, which must have at least one, with upkeep upkeep, which maintain() runs.
A recursion with scratch, as the IMM's, must have been given the components' allocator.
|
inlinenodiscard |
|
inlinenodiscard |
|
inlinenodiscard |
|
inlinenodiscard |
|
inlinenodiscard |
Moves each component forward by the fixed-step model's step, with inputs passed as in Emrys::predict().
The weights are unchanged.
|
inlinenodiscard |
predict(dt, inputs...) under policy, a for-each over the components, so the result is Sequential's (see Execution policies).
A mixture of a few components is best left Sequential, and a recursion with scratch, the IMM's, takes nothing else.
|
inlinenodiscard |
Moves each component forward by dt, any TimeStep (see Time steps), through the process model, with inputs passed to its blocks as in Emrys::predict().
The weights are unchanged.
std::expected that is empty when a component could not take the step, as its recursion's predict() reports, a component that could not take it keeps its prior while the others move (decisions.md [gaussian-forms])
|
inlinenodiscard |
Moves each Gaussian component forward as predict(inputs...) does with a fixed-step model, and starts the state's lineage over.
|
inlinenodiscard |
Moves each Gaussian component forward as predict(dt, inputs...) does, and starts the state's lineage over (MixtureParams::startLineage()).
|
inlinenodiscard |
|
inline |
Replaces the state estimate, as when reinitializing, with one that has at least one component and whose space the process model's writes partition, as the constructor's must.
The scratch follows the new state's space, allocator, and capacity. A recursion with scratch is given the new components' allocator through its resetScratch(), as the IMM's is; one without that member must have it already.
|
inlinenodiscard |
|
inlinenodiscard |
|
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.
|
inlinenodiscard |
Conditions each component on measurement, by the measurement's own method, reduces its branches with policy, a MixtureReductionPolicy over the component (Collapse, Select, Keep, or a Gate), and multiplies the weight of what the policy keeps by \( p(z \mid i) \) times its share among the branches: for a Gate the passing branches' share, so that a branch the gate fails counts as impossible.
The weights are then normalized, and the observation's log-likelihood counts every branch. Components the recursion rejects are dropped; if it rejects them all, the state is unchanged.
|
inlinenodiscard |
update(measurement, policy) under executionPolicy: each component's update is a for-each into slots made first, and the reweighting, the evidence, and the posterior then follow in order, so the result is Sequential's (see Execution policies).
The slots allocate.