|
EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
|
A particle filter: a weighted set of state points, moved by sampling the process model and weighted by the measurement likelihood. More...
#include <EmrysFilter/Filters/ParticleFilter.h>
Public Types | |
| using | Scalar = Scalar_ |
| the scalar type | |
| using | SpaceType = Space_ |
| the state space | |
| using | ModelType = Model_ |
| the process model | |
| using | StateType = ParticleSet<Space_, Scalar_, Allocator_> |
| the state the filter owns | |
| using | allocator_type = Allocator_ |
| the state's allocator | |
Public Member Functions | |
| ParticleFilter (std::type_identity_t< Model_ > model, StateType state, std::type_identity_t< Upkeep_ > upkeep={}) | |
| A filter with process model model, initial particles state, which must not be empty, and upkeep upkeep, which maintain() runs. | |
| allocator_type | get_allocator () const |
| StateType const & | state () const |
| StateType & | state () |
| void | setState (StateType state) |
| Replaces the particles, as when reinitializing, with a set that has at least one and whose space the process model's writes partition, as the constructor's must. | |
| Model_ const & | model () const |
| Model_ & | model () |
| template<std::uniform_random_bit_generator G, typename... Inputs> requires (!isFixedStep<Model_>) | |
| void | predict (G &rng, TimeStep< Scalar_ > auto const &dt, Inputs const &... inputs) |
| Moves each particle forward by dt, any TimeStep (see Time steps), through the process model and adds an independent draw of the process noise \( Q \), drawn with rng. | |
| template<ExecutionPolicy P, std::uniform_random_bit_generator G, typename... Inputs> requires (!isFixedStep<Model_>) | |
| void | predict (P &&policy, G &rng, TimeStep< Scalar_ > auto const &dt, Inputs const &... inputs) |
| predict(rng, dt, inputs...) under policy: the draws are taken first, as the sequential prediction takes them, and the particles then move as a for-each, so the result is Sequential's (see Execution policies). | |
|
template<std::uniform_random_bit_generator G, typename... Inputs> requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>) | |
| void | predict (G &rng, Inputs const &... inputs) |
| Moves each particle forward by the fixed-step model's step, as predict(rng, dt, inputs...) does, with the model's cached factor of \( Q \) (FixedStepModel::noiseRoot()). | |
|
template<ExecutionPolicy P, std::uniform_random_bit_generator G, typename... Inputs> requires (isFixedStep<Model_> && !internal::leadsWithSteps<Inputs...>) | |
| void | predict (P &&policy, G &rng, Inputs const &... inputs) |
| predict(rng, inputs...) with a fixed-step model, under policy, as predict(policy, rng, dt, inputs...) runs. | |
|
template<std::uniform_random_bit_generator G, typename... Inputs> requires isFixedStep<Model_> | |
| void | predict (G &rng, Steps steps, Inputs const &... inputs) |
| Moves each particle forward by steps of the fixed-step model, as that many calls of predict(rng, inputs...) do, with inputs at every step and the draws in the same order. | |
|
template<ExecutionPolicy P, std::uniform_random_bit_generator G, typename... Inputs> requires isFixedStep<Model_> | |
| void | predict (P &&policy, G &rng, Steps steps, Inputs const &... inputs) |
| predict(rng, steps, inputs...) under policy, as that many calls of predict(policy, rng, inputs...) run. | |
| template<internal::WeighsPoints M> | |
| std::expected< ParticleFilterUpdate< Scalar_ >, WeightError > | update (M const &measurement) |
| Multiplies each particle's weight by the observation's likelihood there, \( p(z \mid x_i) \), and normalizes the weights. | |
| template<ExecutionPolicy P, internal::WeighsPoints M> | |
| std::expected< ParticleFilterUpdate< Scalar_ >, WeightError > | update (P &&policy, M const &measurement) |
| update() under policy: the likelihoods are a for-each over the particles and the evidence and the total weight reductions, which match Sequential's to rounding (see Execution policies). | |
A particle filter: a weighted set of state points, moved by sampling the process model and weighted by the measurement likelihood.
predict() moves each particle through the process model and adds a draw of its process noise, so it needs no transform method and the model may be as nonlinear as it likes. update() multiplies each particle's weight by \( p(z \mid x_i) \) from logLikelihoods(), which any measurement with additive noise provides, including mixture noise, and a model with its own logLikelihood(g), such as one known only by its likelihood, supplies itself (decisions.md [likelihood-measurements]).
The filter never resamples on its own. Upkeep, such as resampling when the effective sample size drops, is applied by the caller to state(), when it fits the problem: once after the last update before a prediction, not after each of several updates at one time.
With Resample applied by the caller this is the bootstrap filter of Gordon, Salmond, and Smith (1993), the sequential importance resampling filter of Arulampalam et al. (2002), §IV.B, with the transition prior as the proposal.
Both steps also take an execution policy first (see Execution policies). A prediction draws every particle's standard normals before it moves any, in the order a sequential prediction draws them, so the particles then move as a for-each and match Sequential's from the same generator. The normals come from std::normal_distribution, so a seed fixes the run within one standard library only (decisions.md [random-draws]). An update's likelihoods are a for-each and its evidence a reduction.
Under Sequential a prediction draws one particle's normals at a time, in a vector sized by the noise and the uncertain inputs, so it takes no scratch, and an update's likelihoods go into scratch from the state's allocator, reused across updates. On a bounded space its steps therefore allocate nothing under Sequential once it has run one at a steady number of particles (see Allocation). Under another policy the draws go into scratch from the state's allocator too, reserved for the noise and reused, which the first prediction with an uncertain input grows.
The filter can hold its upkeep, given last to the constructor, and run it where the caller calls maintain():
| Space_ | the state space (see Spaces) |
| Model_ | the process model, a ProcessModel or a FixedStepModel, which partition the space; with a FixedStepModel, predict() takes no time step |
| Scalar_ | scalar type, double by default |
| Allocator_ | the particles' allocator (see Allocation) |
| Upkeep_ | the upkeep maintain() runs on the particles (see Upkeep), upkeep::None unless given one |
|
inline |
A filter with process model model, initial particles state, which must not be empty, and upkeep upkeep, which maintain() runs.
Its scratch comes from the state's allocator, with room for the state's capacity if it has one. The model is not deduced from here, so that the deduction guide below, which binds fixedStep()'s result to the state's space, deduces the filter's arguments.
|
inlinenodiscard |
|
inlinenodiscard |
|
inlinenodiscard |
|
inline |
Moves each particle forward by dt, any TimeStep (see Time steps), through the process model and adds an independent draw of the process noise \( Q \), drawn with rng.
inputs reach the model as in Emrys::predict(), except that each uncertain input is drawn once per particle rather than carried as a covariance. The weights are unchanged.
\( Q \) may be singular, as for a Constant model, whose coordinates then move only as the dynamics move them. Each particle's noise is drawn before its uncertain inputs, which are drawn in order.
|
inline |
predict(rng, dt, inputs...) under policy: the draws are taken first, as the sequential prediction takes them, and the particles then move as a for-each, so the result is Sequential's (see Execution policies).
The process model's propagate() then runs for several particles at once.
|
inline |
Replaces the particles, as when reinitializing, with a set that has at least one and whose space the process model's writes partition, as the constructor's must.
The scratch follows the new set's allocator and capacity.
|
inlinenodiscard |
|
inlinenodiscard |
|
inlinenodiscard |
Multiplies each particle's weight by the observation's likelihood there, \( p(z \mid x_i) \), and normalizes the weights.
If no particle can explain the observation, the weights are unchanged and the measurement is rejected, with log-likelihood \( -\infty \) (decisions.md [update-outcomes]).
|
inlinenodiscard |
update() under policy: the likelihoods are a for-each over the particles and the evidence and the total weight reductions, which match Sequential's to rounding (see Execution policies).
The measurement's measure() then runs for several particles at once.