14#include <Eigen/Cholesky>
41template <
typename Matrix>
50template <
typename Matrix>
51[[nodiscard]] InfoSums<Matrix>
52addInfoSums(InfoSums<Matrix> a, InfoSums<Matrix>
const & b)
56 a.valid = a.valid && b.valid;
117template <Space Space_, RealScalar Scalar_ =
double>
130 template <MatrixExpression Info>
132 : m_space{std::move(
space)}
133 , m_info{std::forward<Info>(
info)}
137 &&
"the information matrix must have a row and a column per difference coordinate"
145 [[nodiscard]]
static std::expected<PosteriorCramerRaoBound, GaussianError>
148 auto const llt = Eigen::LLT<MatrixType>{internal::symmetricFromLower<MatrixType>(prior.
covarianceLower())};
149 if (llt.info() != Eigen::Success)
158 [[nodiscard]] Space_
const &
174 [[nodiscard]] std::expected<MatrixType, GaussianError>
177 auto const llt = Eigen::LLT<MatrixType>{m_info};
178 if (llt.info() != Eigen::Success)
182 MatrixType result = internal::inverseFrom<MatrixType>(llt);
183 return symmetric(std::move(result));
196 template <
ProcessModel Model,
typename Truths,
typename... Inputs>
198 [[nodiscard]] std::expected<void, GaussianError>
201 Eigen::MatrixBase<Truths>
const & truths,
203 Inputs
const &... inputs
215 [[nodiscard]] std::expected<void, GaussianError>
219 Eigen::MatrixBase<Truths>
const & truths,
221 Inputs
const &... inputs
226 "the bound takes a model's true inputs, as simulate() does; an uncertain input adds noise that the "
227 "transition density of the bound does not model (decisions.md [cramer-rao-bound])"
229 internal::assertPartitions(model, m_space);
230 Scalar_
const step = internal::toScalarTime<Scalar_>(dt);
231 auto const call = internal::modelWith(std::cref(model), inputs...);
232 auto const resolved = internal::resolveProcess(call, m_space);
233 using R = std::remove_cvref_t<
decltype(resolved)>;
234 auto const noise = internal::symmetricFromLower<MatrixType>(resolved.template noise<Scalar_>(step));
235 if constexpr (R::Affine && R::ReturnsJacobian && R::InputCount == 0)
237 MatrixType const transition = resolved.template transition<Scalar_>(step);
238 return predictFrom(noise, [&](
auto const & solveNoise) {
239 internal::InfoSums<MatrixType> sums{transition,
MatrixType{},
true};
240 MatrixType const whitened = solveNoise(transition);
241 sums.second.noalias() = internal::multiply(whitened.transpose(), whitened);
247 assertTruths(truths);
248 auto const f = [&](
auto const & x) {
249 return resolved.propagate(x, step);
251 using Input = internal::PointInput<Space_, Scalar_>;
252 using Problem = internal::Transform<
decltype(f)
const, Space_, Input>;
253 Eigen::Index
const count = truths.cols();
254 return predictFrom(noise, [&](
auto const & solveNoise) {
256 std::forward<P>(policy),
258 [&](Eigen::Index i, internal::InfoSums<MatrixType> & sums) {
260 auto const point = Input{m_space,
VectorType{truths.col(i)}};
261 auto const problem = Problem{f, m_space, point};
264 MatrixType const whitened = solveNoise(jacobian);
265 sums.first += jacobian;
266 sums.second.noalias() += internal::multiply(whitened.transpose(), whitened);
276 template <
typename Model,
typename Truths,
typename... Inputs>
279 &&
requires(Model
const & m) { m.model(); }
281 [[nodiscard]] std::expected<void, GaussianError>
282 predict(Model
const & model, Eigen::MatrixBase<Truths>
const & truths, Inputs
const &... inputs)
288 template <
ExecutionPolicy P,
typename Model,
typename Truths,
typename... Inputs>
290 [[nodiscard]] std::expected<void, GaussianError>
291 predict(P && policy, Model
const & model, Eigen::MatrixBase<Truths>
const & truths, Inputs
const &... inputs)
293 return predict(std::forward<P>(policy), model.model(), truths, model.timeStep(), inputs...);
307 [[nodiscard]] std::expected<void, GaussianError>
308 update(Eigen::MatrixBase<Truths>
const & truths, M
const & model, Args
const &... args)
315 [[nodiscard]] std::expected<void, GaussianError>
316 update(P && policy, Eigen::MatrixBase<Truths>
const & truths, M
const & model, Args
const &... args)
320 "the bound takes a model's true inputs, as simulate() does; an uncertain input adds noise that the "
321 "measurement density of the bound does not model (decisions.md [cramer-rao-bound])"
323 assertTruths(truths);
324 auto const observed = internal::modelWith(std::cref(model), args...);
325 auto const resolved = internal::resolveMeasurement(observed, m_space);
326 using R = std::remove_cvref_t<
decltype(resolved)>;
328 R::HasAdditiveNoise && !R::MixtureNoise,
329 "the bound takes a measurement model with additive Gaussian noise, whose likelihood is the Gaussian of "
330 "Tichavsky et al. (1998), eq. (33) (decisions.md [cramer-rao-bound])"
332 auto const sums = averageOver(
333 std::forward<P>(policy),
335 [&](Eigen::Index i, internal::InfoSums<MatrixType> & sums) {
336 auto const linearization = internal::linearizeMeasurementAtPoint<Scalar_>(
338 VectorType{truths.col(i)},
342 using NoiseMatrix =
typename std::remove_cvref_t<
decltype(linearization)>::NoiseMatrix;
343 auto const llt = Eigen::LLT<NoiseMatrix>{linearization.noise};
344 if (llt.info() != Eigen::Success)
349 using JacobianMatrix =
typename std::remove_cvref_t<
decltype(linearization)>::JacobianMatrix;
350 JacobianMatrix whitened = linearization.jacobian;
351 internal::solveInPlace(llt.matrixL(), whitened);
352 sums.second.noalias() += internal::multiply(whitened.transpose(), whitened);
359 m_info += sums.second;
360 m_info = symmetric(std::move(m_info));
367 [[nodiscard]]
static MatrixType
370 MatrixType result = Scalar_(0.5) * (matrix + matrix.transpose());
375 template <
typename Truths>
377 assertTruths(Eigen::MatrixBase<Truths>
const & truths)
const
379 EMRYS_FILTER_ASSERT(truths.cols() > 0 &&
"the bound takes at least one true state");
380 EMRYS_FILTER_ASSERT(truths.rows() ==
valueDimension(m_space) &&
"a true state does not fit the space");
386 template <ExecutionPolicy P,
typename AddTerms>
387 [[nodiscard]] internal::InfoSums<MatrixType>
388 averageOver(P && policy, Eigen::Index count, AddTerms
const & addTerms)
const
391 auto sums = internal::reduceRanges(
392 std::forward<P>(policy),
394 [&](Eigen::Index begin, Eigen::Index length) {
395 auto range = internal::InfoSums<MatrixType>{MatrixType::Zero(n, n), MatrixType::Zero(n, n),
true};
396 for (Eigen::Index i = begin; i < begin + length; ++i)
402 internal::addInfoSums<MatrixType>
404 sums.first /= Scalar_(count);
405 sums.second /= Scalar_(count);
414 template <
typename Average>
415 [[nodiscard]] std::expected<void, GaussianError>
416 predictFrom(MatrixType
const & noise, Average
const & average)
418 auto const noiseLlt = Eigen::LLT<MatrixType>{noise};
419 if (noiseLlt.info() != Eigen::Success)
423 auto const solveNoise = [&](MatrixType
const & a) {
424 MatrixType result = a;
425 internal::solveInPlace(noiseLlt.matrixL(), result);
428 internal::InfoSums<MatrixType>
const sums = average(solveNoise);
429 auto const combinedLlt = Eigen::LLT<MatrixType>{MatrixType{m_info + sums.second}};
430 if (combinedLlt.info() != Eigen::Success)
434 MatrixType
const noiseInverse = internal::inverseFrom<MatrixType>(noiseLlt);
435 MatrixType gain = sums.first;
436 internal::solveInPlace(noiseLlt, gain);
437 MatrixType whitened = gain.transpose();
438 internal::solveInPlace(combinedLlt.matrixL(), whitened);
439 MatrixType info = noiseInverse;
440 info.noalias() -= internal::multiply(whitened.transpose(), whitened);
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
What marginals and conditionals are built from: resolving Eigen index sets against a space; the share...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
The update of a Gaussian by a measurement (decisions.md [templated-measurements], [weighted-branches]...
Moments: the mean and covariance that a Gaussian carries and that a mixture or a sample set is moment...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
Process models as functions with reads and writes (decisions.md [model-policies], [continuous-time-no...
Solving by a factorization of a small matrix: transposedSolve() gives , the shape of a Kalman gain an...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Time steps (decisions.md [time-steps]): TimeTraits, which converts a time type to a scalar,...
A Gaussian distribution: a mean and covariance in a space.
Definition Gaussian.h:259
MatrixType const & covarianceLower() const
Definition Gaussian.h:543
Space_ const & space() const
Definition Gaussian.h:519
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
a matrix over differences, such as the covariance
Definition Moments.h:76
ValueVector< Space_, Scalar > VectorType
a value, such as the mean
Definition Moments.h:74
std::expected< void, GaussianError > update(Eigen::MatrixBase< Truths > const &truths, M const &model, Args const &... args)
Adds the information that a measurement at the true states truths, a column each, carries: ,...
Definition CramerRao.h:308
typename Moments< Space_, Scalar_ >::VectorType VectorType
a state value
Definition CramerRao.h:124
std::expected< void, GaussianError > predict(Model const &model, Eigen::MatrixBase< Truths > const &truths, TimeStep< Scalar_ > auto const &dt, Inputs const &... inputs)
Moves the bound forward by dt, any TimeStep (see Time steps), through the process model model with th...
Definition CramerRao.h:199
Space_ SpaceType
the state space
Definition CramerRao.h:123
std::expected< void, GaussianError > predict(P &&policy, Model const &model, Eigen::MatrixBase< Truths > const &truths, Inputs const &... inputs)
predict(model, truths, inputs...) over a fixed-step model's step under policy.
Definition CramerRao.h:291
static std::expected< PosteriorCramerRaoBound, GaussianError > create(GaussianParams< Space_, Scalar_ > const &prior)
Definition CramerRao.h:146
PosteriorCramerRaoBound(Space_ space, Info &&info)
The bound in space with the information matrix information, symmetric and positive semidefinite over ...
Definition CramerRao.h:131
std::expected< void, GaussianError > predict(Model const &model, Eigen::MatrixBase< Truths > const &truths, Inputs const &... inputs)
predict() over the step of the fixed-step model model, or of fixedStep()'s result,...
Definition CramerRao.h:282
Scalar_ Scalar
the scalar type
Definition CramerRao.h:122
typename Moments< Space_, Scalar_ >::MatrixType MatrixType
, over differences
Definition CramerRao.h:125
Space_ const & space() const
Definition CramerRao.h:159
std::expected< void, GaussianError > update(P &&policy, Eigen::MatrixBase< Truths > const &truths, M const &model, Args const &... args)
update(truths, model, args...) under policy: a reduction over the columns, as in predict().
Definition CramerRao.h:316
MatrixType const & info() const
Definition CramerRao.h:166
std::expected< MatrixType, GaussianError > covariance() const
Definition CramerRao.h:175
std::expected< void, GaussianError > predict(P &&policy, Model const &model, Eigen::MatrixBase< Truths > const &truths, TimeStep< Scalar_ > auto const &dt, Inputs const &... inputs)
predict(model, truths, dt, inputs...) under policy: the Jacobians at the columns are taken as a reduc...
Definition CramerRao.h:216
An execution policy: execution::sequential, a policy of the standard library such as std::execution::...
Definition Execution.h:459
A measurement model: what measurementModel() returns, or a composition of such models (decisions....
Definition MeasurementModel.h:231
A process model: what processModel() returns, a built-in model, or a composition of process models (d...
Definition ProcessModel.h:2010
A step of time that converts to Scalar: Scalar itself, or any type with a TimeTraits,...
Definition Time.h:128
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
@ NotPositiveDefinite
the covariance is not positive definite, so it has no Cholesky factor or inverse
Definition NumericError.h:31
@ Singular
the precision is not positive definite, so the mean and covariance are undefined
Definition NumericError.h:32
constexpr auto symmetric(Fallbacks... fallbacks)
Definition Roots.h:1204
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
constexpr bool isFixedStep
Whether Model is a FixedStepModel, whose predictions take no time step.
Definition ProcessModel.h:2776
constexpr bool isUnboundStep
Whether Model is a fixed step not yet bound to a state space: an UnboundFixedStep,...
Definition ProcessModel.h:2792
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284