|
EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
|
The last layer (decisions.md [layered-headers]): estimates of past states from later data [smoothing]. More...
Classes | |
| class | Emrys::CategoricalHistory< Space_, Scalar_, Allocator_ > |
| The record of a HiddenMarkovFilter's forward pass that a smoother runs backward over: one CategoricalPredictedJoint per prediction, each with room for its smoothed distribution. More... | |
| struct | Emrys::ValueSequence< Space, Scalar, Allocator > |
| The most probable sequence of values over a CategoricalHistory, and its posterior probability, from mostProbableSequence(). More... | |
| class | Emrys::GaussianHistory< Space_, Scalar_, Allocator_ > |
| The record of a Kalman filter's forward pass that a smoother runs backward over: one PredictedJoint per prediction, each with room for its smoothed Gaussian. More... | |
| class | Emrys::GaussianMixtureHistory< Space_, Scalar_, Allocator_ > |
| The record of a Gaussian mixture filter's forward pass that its smoother runs backward over: one GaussianMixturePredictedJoint per prediction, each with room for its smoothed mixture. More... | |
| 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. More... | |
| struct | Emrys::Lag |
| The number of steps a fixed-lag history keeps. More... | |
| class | Emrys::ParticleHistory< Space_, Scalar_, Allocator_ > |
| The record of a particle filter's forward pass that ancestor tracing runs backward over: the particles at each step, how each descends from the step before, and room for the step's smoothed particles. More... | |
Functions | |
| template<typename Space, typename Scalar> | |
| CategoricalParams< Space, Scalar > | Emrys::smoothStep (CategoricalPredictedJoint< Space, Scalar > const &joint, CategoricalParams< Space, Scalar > const &smoothedNext) |
| template<typename Space, typename Scalar> | |
| CategoricalPredictedJoint< Space, Scalar >::MatrixType | Emrys::transitionPosterior (CategoricalPredictedJoint< Space, Scalar > const &joint, CategoricalParams< Space, Scalar > const &smoothedNext) |
| template<typename Space, typename Scalar, typename Allocator> | |
| ValueSequence< Space, Scalar, Allocator > | Emrys::mostProbableSequence (CategoricalHistory< Space, Scalar, Allocator > const &history, CategoricalParams< Space, Scalar > const &latest) |
| template<typename Space_, typename Scalar_> | |
| GaussianParams< Space_, Scalar_ > | Emrys::smoothStep (PredictedJoint< Space_, Scalar_ > const &joint, typename GaussianParams< Space_, Scalar_ >::MatrixType const &gain, GaussianParams< Space_, Scalar_ > const &smoothedNext) |
| template<typename Space_, typename Scalar_> | |
| GaussianParams< Space_, Scalar_ > | Emrys::smoothStep (PredictedJoint< Space_, Scalar_ > const &joint, GaussianParams< Space_, Scalar_ > const &smoothedNext) |
| smoothStep() with the gain computed here, from joint alone (internal::rtsGain()): the form for one step; a history keeps the gain with the step and applies it at every smooth(). | |
The last layer (decisions.md [layered-headers]): estimates of past states from later data [smoothing].
A filter is unchanged by smoothing but for predictJoint(), which fills the joint of a step's start and end, and the record of the forward pass is the caller's: a history, which the caller keeps outside the filter and pushes each step to, and whose smooth() takes the filter's latest state and fills every step's smoothed estimate, from the newest step back, over the whole run or the newest steps of a Lag. GaussianHistory.h, with smoothStep(), the Rauch-Tung-Striebel step, is the shape the others follow: HybridGaussianHistory.h smooths an ImmFilter by its modes, GaussianMixtureHistory.h a MixtureFilter of Gaussians by its components' lineage, ParticleHistory.h a ParticleFilter by its particles' ancestors, and CategoricalHistory.h a HiddenMarkovFilter, with its most probable sequence.
|
nodiscard |
The Viterbi algorithm of Viterbi (1967), in the max-product form of Rabiner (1989), §III-B: a forward pass of \( \delta_{k+1}(j) = \max_i [\delta_k(i) + \log K_{ij}] + \log L_{k+1}(j) \) with back-pointers, then a backtrack from the best final value. The departure is where the likelihoods come from: the history keeps no measurements, so \( L_{k+1}(j) \) is the ratio of the filtered to the predicted probability, \( f_{k+1}(j) / \hat p_{k+1}(j) \), which is the product of every update's likelihood between the two predictions divided by their evidence. The evidence is the same for every sequence, so the argmax is unchanged, and since \( \delta \) starts from the oldest step's filtered distribution, the maximum is the posterior log-probability of the sequence rather than a joint density. Over a fixed-lag window the sequence is the most probable one of the window's values, the earlier ones summed out. It is a free function and not a filter member because it is the one place the library takes a maximum instead of a sum.
Ties go to the lowest value. It allocates the back-pointers, one index per value per step, and the result.
|
nodiscard |
\[ s'_i = f_i \sum_j K_{ij} \, \frac{s_j}{\hat p_j}. \]
The Bayesian smoothing equation of Särkkä (2013), chapter 8, on a finite set, where its integral is a sum. On a hidden Markov model it gives the smoothed marginals \( \gamma \) of the forward-backward algorithm (Rabiner (1989), §III-A) in the form that needs only the filtered distributions and no likelihoods, as smoothStep() does for a Gaussian, rather than Rabiner's product of forward and backward variables. The two are the same, since \( s_j / \hat p_j \) is the backward variable, normalized. The ratio is taken in logs and scaled by its largest entry, so a value improbable beyond a double's range at either end still divides correctly. smoothedNext must have valid weights, and give no probability to a value the prediction rules out.
|
nodiscard |
\[ \hat x_{k \mid N} = \hat x_{k \mid k} \boxplus G_k \,(\hat x_{k+1 \mid N} \boxminus \hat x_{k+1 \mid k}), \qquad P_{k \mid N} = P_{k \mid k} + G_k \,(P_{k+1 \mid N} - P_{k+1 \mid k})\, G_k^T. \]
The smoother of Rauch, Tung, and Striebel (1965), in the form of the Gaussian smoother of Särkkä (2013), chapter 10, which writes the gain in terms of the cross-covariance so that the same step serves the affine, linearized, and unscented predictions. The departure is the manifold: the residual is \( \boxminus \) and the step \( \boxplus \), so a unit quaternion's smoothed mean stays unit, and each covariance is moved to the tangent space it is combined in (decisions.md [covariance-reset]). \( P_{k+1 \mid N} \), about the smoothed mean, is carried back to the tangent space at the predicted mean by the inverse of the reset Jacobian at the predicted mean and the correction, and the smoothed covariance, formed at the filtered mean, is carried to the smoothed mean by resetCovariance(). On a space with an identity reset both are nothing. \( P_{k+1 \mid k} \) is factorized by LDLT, which tolerates a semidefinite prediction.
|
nodiscard |
The pairwise posteriors \( \xi \) of Rabiner (1989), §III-C, there in terms of the forward and backward variables, here of the filtered and smoothed distributions as smoothStep() takes them. Summed over a history they are the expected transition counts that the Baum-Welch re-estimation of a chain consumes; the re-estimation itself is model identification, outside the library (decisions.md [analysis-project]). smoothedNext must meet smoothStep()'s conditions.