The first layer (decisions.md [layered-headers]): what the rest of the library builds on and what depends on nothing but Eigen.
More...
|
| concept | Emrys::MatrixExpression |
| | A dense matrix or matrix expression, possibly cv- or reference-qualified: what a const Eigen::MatrixBase<Derived>& parameter accepts.
|
| concept | Emrys::DiagonalExpression |
| | A diagonal matrix or diagonal expression, possibly cv- or reference-qualified, such as v.asDiagonal() or an Eigen::DiagonalMatrix: what a const Eigen::DiagonalBase<Derived>& parameter accepts.
|
| concept | Emrys::CovarianceExpression |
| | A covariance as a function that stores one takes it: a MatrixExpression, or a DiagonalExpression, whose diagonal holds the variances and whose other entries are zero, so that GaussianParams<State>{mean, variances.asDiagonal()} needs no toDenseMatrix().
|
| concept | Emrys::RealScalar |
| | A type usable as the scalar of a distribution: a real number type such as float, double, or an automatic-differentiation scalar.
|
|
| template<typename Scalar> |
| Scalar | Emrys::chiSquaredCdf (std::type_identity_t< Scalar > const &x, std::type_identity_t< Scalar > const &dof) |
| template<typename Scalar> |
| Scalar | Emrys::chiSquaredQuantile (std::type_identity_t< Scalar > const &probability, std::type_identity_t< Scalar > const &dof) |
| template<typename Scalar> |
| Scalar | Emrys::pi () |
| template<typename Scalar> |
| Scalar | Emrys::log2Pi () |
| template<typename Scalar> |
| Scalar | Emrys::logGamma (std::type_identity_t< Scalar > const &x) |
| template<typename Scalar> |
| Scalar | Emrys::logOnePlus (std::type_identity_t< Scalar > const &x) |
| template<typename Scalar> |
| Scalar | Emrys::floorOf (std::type_identity_t< Scalar > const &x) |
| template<typename T> |
| bool | Emrys::isFinite (T const &x) |
| template<typename T> |
| bool | Emrys::isNaN (T const &x) |
| template<typename Derived> |
| bool | Emrys::allFinite (Eigen::DenseBase< Derived > const &x) |
| template<internal::Printable T> |
| std::ostream & | Emrys::operator<< (std::ostream &stream, T const &value) |
| | Prints value, any type the library describes (internal::Printable): a distribution, a filter, by name with its state nested under it, or an error code.
|
template<typename M, typename Derived = std::remove_cvref_t<M>>
requires std::derived_from<Derived, Eigen::MatrixBase<Derived>> && (!std::is_const_v<std::remove_reference_t<M>>) &&
((Derived::Flags & Eigen::LvalueBit) != 0) |
| void | Emrys::triangularize (M &&m) |
| | Reduces m to upper-triangular form in place by Householder reflections from the left, with a nonnegative diagonal.
|
| template<typename Scalar, ExecutionPolicy P, typename LogAt> |
| Scalar | Emrys::logSumExp (P &&policy, Eigen::Index count, LogAt const &logAt) |
|
template<typename Scalar, typename LogAt> |
| Scalar | Emrys::logSumExp (Eigen::Index count, LogAt const &logAt) |
| | logSumExp(policy, count, logAt) under execution::sequential.
|
| template<ExecutionPolicy P, typename Derived> |
| Derived::Scalar | Emrys::logSumExp (P &&policy, Eigen::ArrayBase< Derived > const &logs) |
|
template<typename Derived> |
| Derived::Scalar | Emrys::logSumExp (Eigen::ArrayBase< Derived > const &logs) |
| | logSumExp(policy, logs) under execution::sequential.
|
The first layer (decisions.md [layered-headers]): what the rest of the library builds on and what depends on nothing but Eigen.
It holds the version (Version.h), the macros (Macros.h), the RealScalar concept and constants and scalar functions in any such scalar (Math.h), the concept that lets a function move a matrix argument it stores (Forwarding.h, [forwarded-matrices]), the allocator conventions and buffers (Allocation.h, [allocation]), the execution policies and the primitives that run loops over a count under them (Execution.h, [execution-policies]), the time steps a prediction takes and their conversion to a scalar (Time.h, [time-steps]), log-weights and their normalization (Weights.h), the resamplers (Resampling.h), the chi-squared distribution's CDF and quantile (ChiSquared.h), the triangularization that the square-root forms of the Gaussian are built on (Triangularization.h, [gaussian-forms]), the products that keep a fixed size on Eigen's fixed-size path (Multiply.h), the solve that gives a gain from a factorization of a small matrix one column at a time (Solve.h), the modification stamp that parameters keep and the checks by which their views read it (Stamp.h, [view-stamp]), and the operator<< and std::formatter that print every distribution and filter (Print.h), left out under EMRYS_FILTER_NO_IO. Read Version.h first, and Spaces next.
| #define EMRYS_FILTER_NO_UNIQUE_ADDRESS |
Assertion used for programmer errors (misuse of the API).
Defaults to eigen_assert, so it is controlled by the same switches as Eigen's own checks (NDEBUG, EIGEN_NO_DEBUG). Define it before including any EmrysFilter header to override, as an expression like assert() or as a statement such as do { ... } while (false): the library uses it only as a statement of its own.
Lets an empty member, such as the default Linear space, take no storage.
Reasons a computation from parameters the caller gave can fail: a matrix that does not factorize or a set of log-weights that does not normalize.
A function returns the ones its documentation names, through either name below; a step that can fail both ways, such as a mixture filter's update, may return any of them (decisions.md [update-outcomes]).
A Gaussian's errors name the matrix that failed: a covariance that is singular or indefinite, as a square-root factor with a zero on its diagonal gives, is NotPositiveDefinite; a precision that is, as a prior with no information has, is Singular.
- See also
- GaussianError, WeightError
| Enumerator |
|---|
| NotPositiveDefinite | the covariance is not positive definite, so it has no Cholesky factor or inverse
|
| Singular | the precision is not positive definite, so the mean and covariance are undefined
|
| NonFinite | a matrix, a factor, or a log-weight holds NaN or infinity (+inf, for a log-weight)
|
| Empty | there are no weights
|
| ZeroSum | every weight is zero (every log-weight is -inf)
|
template<typename Scalar>
| Scalar Emrys::chiSquaredCdf |
( |
std::type_identity_t< Scalar > const & | x, |
|
|
std::type_identity_t< Scalar > const & | dof ) |
|
nodiscard |
- Returns
- the chi-squared cumulative distribution function with dof degrees of freedom at x, \( P(\tfrac{k}{2}, \tfrac{x}{2}) \)
The chi-squared distribution is that of a sum of dof squared standard normals, which is what a normalized innovation squared is for a consistent filter (Bar-Shalom, Li, and Kirubarajan(2001); Chen et al. (2023), §III-A).
Scalar is not deduced, as for logGamma(): a call names it, as in chiSquaredCdf<Scalar>(squaredDistance, k), and computes in it throughout. In an autodiff scalar the derivative in x is the chi-squared density, to the precision of the series or the continued fraction. It is 0 at zero and below, 1 at infinity, and NaN at NaN.
template<typename Scalar>
| Scalar Emrys::chiSquaredQuantile |
( |
std::type_identity_t< Scalar > const & | probability, |
|
|
std::type_identity_t< Scalar > const & | dof ) |
|
nodiscard |
- Returns
- the probability quantile of the chi-squared distribution with dof degrees of freedom: the NIS that a consistent filter's innovations stay under with that probability.
Solved by safeguarded Newton iteration on chiSquaredCdf(), to within four epsilons of Scalar: the root is bracketed by doubling, each Newton step uses the chi-squared density as the derivative, and a step that leaves the bracket, or shrinks less than half as fast as the step before last, is replaced by bisection (Press et al. (2007), rtsafe). A quantile far below one, as a small probability with a degree of freedom or less gives, is bracketed from below by squaring a ratio and bisected geometrically, so that it takes as few steps as one near one; a quantile below the smallest normal Scalar comes out at most that. Scalar is not deduced, as for chiSquaredCdf().
A probability outside (0, 1) is a programmer error. Where asserts are off it still returns, as the limits do: zero for a probability of zero or less, infinity for one or more, and NaN for a NaN probability or degrees of freedom that are not positive.
template<typename Scalar>
| Scalar Emrys::floorOf |
( |
std::type_identity_t< Scalar > const & | x | ) |
|
|
nodiscard |
- Returns
- \( \lfloor x \rfloor \), the largest integer not above x, as a Scalar.
In place of Eigen::numext::floor, which fails for AutoDiffScalar for want of its floor. A floating-point Scalar takes std::floor. An Eigen::AutoDiffScalar takes its value's, with zero derivatives: the floor is constant between integers, and a wrap such as Periodic's subtracts a whole number of periods, so its derivative is the identity's. Any other scalar goes through Eigen::numext::floor.
Scalar is not deduced, as for logGamma(): a call names it, as in floorOf<Scalar>(x).
template<typename Scalar>
- Returns
- \( \log 2\pi \) as a Scalar, the constant in every Gaussian normalizer.
A floating-point Scalar takes a literal, correctly rounded for float, double, and an 80-bit long double, so it is a constant on every compiler; std::log is not constexpr before C++26, and std::log(2 * pi) would be one ulp low in double, since \( \pi \) is rounded before the log. A scalar whose Eigen::NumTraits<Scalar>::Literal is floating point, such as Eigen's AutoDiffScalar, converts that literal type's, so its derivatives are zero. Any other scalar takes the log of twice pi() in its own precision, which a multiprecision type may set at run time, so nothing is cached; the product is held in a Scalar before the log because an expression type is one numext::log cannot take.
template<typename Scalar>
| Scalar Emrys::logGamma |
( |
std::type_identity_t< Scalar > const & | x | ) |
|
|
nodiscard |
- Returns
- \( \log \Gamma(x) \), the log of the gamma function, as a Scalar.
In place of Eigen::numext::lgamma, which supports only float and double, and std::lgamma, which AutoDiffScalar does not overload. A floating-point Scalar takes std::lgamma, long double included. An Eigen::AutoDiffScalar takes its value's and carries the derivatives by \( \tfrac{d}{dx} \log \Gamma(x) = \psi(x) \), with the digamma function \( \psi \) from Eigen::numext::digamma, so its value type must be float or double. Any other scalar calls its own lgamma, found by argument-dependent lookup. The Gamma and Student-t normalizers use it; their derivative with respect to a shape or the degrees of freedom runs through the digamma term.
Scalar is not deduced: std::type_identity_t keeps the argument out of deduction, so a call names it, as in logGamma<Scalar>(a * b). An argument that is an expression, as AutoDiffScalar arithmetic yields, then converts to a plain Scalar rather than becoming the return type.
template<typename Scalar>
| Scalar Emrys::logOnePlus |
( |
std::type_identity_t< Scalar > const & | x | ) |
|
|
nodiscard |
- Returns
- \( \log(1 + x) \) as a Scalar, accurate for small x.
In place of Eigen::numext::log1p, which fails for AutoDiffScalar for want of its log1p. A floating-point Scalar takes std::log1p. An Eigen::AutoDiffScalar takes its value's and carries the derivatives by \( \tfrac{d}{dx} \log(1 + x) = 1 / (1 + x) \), keeping the small-x accuracy that log(1 + x) would lose. Any other scalar goes through Eigen::numext::log1p.
Scalar is not deduced, as for logGamma(): a call names it, as in logOnePlus<Scalar>(x).
template<internal::Printable T>
| std::ostream & Emrys::operator<< |
( |
std::ostream & | stream, |
|
|
T const & | value ) |
Prints value, any type the library describes (internal::Printable): a distribution, a filter, by name with its state nested under it, or an error code.
See Print.h for what a description holds.
std::cout << filter << '\n';
template<typename Scalar>
- Returns
- \( \pi \) as a Scalar.
In place of std::numbers::pi_v<Scalar>, which is ill-formed for a scalar that is not floating point, and EIGEN_PI, which rounds through long double. A floating-point Scalar takes std::numbers::pi_v. A scalar whose Eigen::NumTraits<Scalar>::Literal is floating point, such as Eigen's AutoDiffScalar, converts pi_v of that literal type, so its derivatives are zero. Any other scalar, such as a multiprecision type whose literal is itself, computes \( 4 \arctan 1 \) in its own precision through Eigen::numext::atan.
template<typename M, typename Derived = std::remove_cvref_t<M>>
requires std::derived_from<Derived, Eigen::MatrixBase<Derived>> && (!std::is_const_v<std::remove_reference_t<M>>) &&
((Derived::Flags & Eigen::LvalueBit) != 0)
| void Emrys::triangularize |
( |
M && | m | ) |
|
Reduces m to upper-triangular form in place by Householder reflections from the left, with a nonnegative diagonal.
On return m holds \( Q^T M \) for the input \( M \) and an orthogonal \( Q \) that is never formed, with zeros below the diagonal. Since \( Q \) is orthogonal, \( (Q^T M)^T (Q^T M) = M^T M \): the leading square block \( R \) of the result is an upper-triangular factor of \( M^T M \), which is its Cholesky factor when the diagonal is positive, without \( M^T M \) ever being formed. Columns appended to \( M \) are carried along: triangularizing \( [\,A \;\; b\,] \) gives \( [\,R \;\; Q^T b\,] \), which is how the square-root information form keeps its vector beside its factor. A rank-deficient \( M \) gives zeros on the diagonal rather than failing.
This is Householder QR (Golub and Van Loan (2013), as given in QR decomposition), with the reflections applied as they are made and then discarded, so the work is in m and a row of scratch the width of m, sized by its compile-time bound: a matrix with a bounded size allocates nothing [allocation]. It departs from the algorithm only in flipping the sign of each row whose diagonal element is negative, which keeps \( R^T R \) and makes the factor of a full-rank \( M \) unique.
m may be any writable Eigen matrix or expression, such as a block of a larger buffer passed as a temporary; a const one is rejected rather than written through.