EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
SqrtGaussian.h
Go to the documentation of this file.
1#pragma once
2
10
11#include <expected>
12#include <utility>
13
29
30namespace Emrys
31{
32
33template <typename C>
34struct ComponentTraits;
35
36namespace internal
37{
38
39template <typename Source, typename Selection, typename SubSpace>
40class SqrtComponentMarginal;
41
42} // namespace internal
43
79template <Space Space_, RealScalar Scalar_ = double>
81{
82public:
83
85 using Scalar = typename MomentsType::Scalar;
86 using Index = typename MomentsType::Index;
90 using SpaceType = Space_;
92
95
98
101
104
105 static constexpr bool IsParams = true;
106 static constexpr bool IsInfoForm = false;
107 static constexpr bool IsSqrtForm = true;
108
111 : SqrtGaussianParams{Space_{}}
112 {}
113
115 explicit SqrtGaussianParams(Space_ space)
116 : m_space{std::move(space)}
117 {
118 m_mean.setZero(Emrys::valueDimension(m_space));
119 m_root.setZero(differenceDimension(), differenceDimension());
120 }
121
125 template <MatrixExpression Mean, MatrixExpression Root>
126 requires internal::SizedByLength<Space_>
127 SqrtGaussianParams(Mean && mean, Root && root)
128 : m_mean{std::forward<Mean>(mean)}
129 , m_root{std::forward<Root>(root)}
130 {
131 fitSpace();
132 }
133
137 template <MatrixExpression Mean, MatrixExpression Root>
138 SqrtGaussianParams(Space_ space, Mean && mean, Root && root)
139 : m_mean{std::forward<Mean>(mean)}
140 , m_root{std::forward<Root>(root)}
141 , m_space{std::move(space)}
142 {
143 fitSpace();
144 }
145
147 [[nodiscard]] Index
149 {
150 return Emrys::valueDimension(m_space);
151 }
152
154 [[nodiscard]] Index
156 {
157 return Emrys::differenceDimension(m_space);
158 }
159
161 [[nodiscard]] Space_ const &
162 space() const
163 {
164 return m_space;
165 }
166
168 [[nodiscard]] VectorType const &
169 mean() const
170 {
171 return m_mean;
172 }
173
177 [[nodiscard]] MatrixType const &
178 root() const
179 {
180 return m_root;
181 }
182
184 [[nodiscard]] MomentsType
185 moments() const
186 {
187 return MomentsType{m_mean, *(covariance())};
188 }
189
193 template <MatrixExpression Mean>
194 void
195 setMean(Mean && mean)
196 {
197 m_stamp.touchValues();
198 EMRYS_FILTER_ASSERT(mean.size() == valueDimension());
199 m_mean = std::forward<Mean>(mean);
200 normalizeValues(m_space, m_mean);
201 }
202
206 template <MatrixExpression Root>
207 void
208 setRoot(Root && root)
209 {
210 m_stamp.touchValues();
211 EMRYS_FILTER_ASSERT(root.rows() == differenceDimension() && root.cols() == differenceDimension());
212 m_root = std::forward<Root>(root);
213 m_root.template triangularView<Eigen::StrictlyUpper>().setZero();
214 }
215
217 [[nodiscard]] std::expected<MatrixType, GaussianError>
219 {
220 MatrixType result = internal::multiply(internal::asTriangular<Eigen::Lower>(m_root), m_root.transpose());
221 return result;
222 }
223
227 [[nodiscard]] std::expected<MatrixType, GaussianError>
228 precision() const
229 {
230 auto inverse = precisionRoot();
231 if (!inverse)
232 {
233 return std::unexpected{inverse.error()};
234 }
235 MatrixType result = internal::multiply(inverse->transpose(), *inverse);
236 return result;
237 }
238
240 [[nodiscard]] std::expected<MatrixType, GaussianError>
242 {
243 return m_root;
244 }
245
249 [[nodiscard]] std::expected<MatrixType, GaussianError>
251 {
252 if (auto checked = checkRoot(); !checked)
253 {
254 return std::unexpected{checked.error()};
255 }
256 MatrixType result = MatrixType::Identity(differenceDimension(), differenceDimension());
257 internal::solveInPlace(m_root.template triangularView<Eigen::Lower>(), result);
258 return result;
259 }
260
268 template <typename Path>
269 [[nodiscard]] auto
270 marginal() const
271 {
272 return internal::marginalOverPath<Path>(m_space, [&](auto const & kept, auto const & keptSpace) {
273 return marginalAt(kept, keptSpace);
274 });
275 }
276
280 template <typename Path>
281 [[nodiscard]] auto
282 marginal(Index element) const
283 {
284 return internal::marginalOverElement<Path>(m_space, element, [&](auto const & kept, auto const & keptSpace) {
285 return marginalAt(kept, keptSpace);
286 });
287 }
288
296 [[nodiscard]] std::expected<void, GaussianError>
297 factorize() const
298 {
299 if (auto checked = checkRoot(); !checked)
300 {
301 return std::unexpected{checked.error()};
302 }
303 // Unchanged, it writes nothing, so factorizing what is factorized only reads.
304 Scalar const logDeterminant = Scalar(2) * m_root.diagonal().array().abs().log().sum();
305 if (m_logDeterminant != logDeterminant)
306 {
307 m_logDeterminant = logDeterminant;
308 }
309 return {};
310 }
311
315 template <typename R>
316 [[nodiscard]] std::expected<void, GaussianError>
317 factorize(R const & /*repair*/) const
318 {
319 static_assert(
320 sizeof(R) == 0,
321 "a square-root form's root is positive semidefinite by construction and has no repair; repairing a "
322 "covariance is the covariance form's (decisions.md [gaussian-forms])"
323 );
324 return factorize();
325 }
326
333 [[nodiscard]] std::expected<DensityType, GaussianError>
334 toDensity() const &
335 {
336 if (auto factorized = factorize(); !factorized)
337 {
338 return std::unexpected{factorized.error()};
339 }
340 return DensityType{*this};
341 }
342
344 void
345 toDensity() const && = delete;
346
347private:
348
349 friend DensityType;
350
352 friend struct ComponentTraits<SqrtGaussianParams>;
353
355 template <typename, typename, typename>
356 friend class internal::SqrtComponentMarginal;
357
359 template <typename>
360 friend class internal::ValuesCheck;
361
365 [[nodiscard]] std::expected<void, GaussianError>
366 checkRoot() const
367 {
368 if (!allFinite(m_root))
369 {
370 return std::unexpected{GaussianError::NonFinite};
371 }
372 if ((m_root.diagonal().array() == Scalar(0)).any())
373 {
374 return std::unexpected{GaussianError::NotPositiveDefinite};
375 }
376 return {};
377 }
378
383 template <typename Kept, typename KeptSpace>
385 marginalAt(Kept const & kept, KeptSpace const & keptSpace) const
386 {
388 using Rows = internal::MatrixOf<
389 Scalar,
390 Result::DifferenceDimensionAtCompileTime,
392 Result::MaxDifferenceDimensionAtCompileTime,
394 typename Result::VectorType mean = m_mean(kept.values);
395 Rows const rows = m_root(kept.differences, Eigen::placeholders::all);
396 return Result{keptSpace, std::move(mean), internal::lowerTriangularRoot(rows)};
397 }
398
400 template <typename Kept, typename KeptSpace>
402 densityMarginalAt(Kept const & kept, KeptSpace const & keptSpace) const
403 {
404 return marginalAt(kept, keptSpace);
405 }
406
408 [[nodiscard]] MomentsType
409 densityMoments() const
410 {
411 return moments();
412 }
413
415 [[nodiscard]] VectorType const &
416 densityMean() const
417 {
418 return m_mean;
419 }
420
422 void
423 whitenInPlace(DifferenceVectorType & residual) const
424 {
425 m_root.template triangularView<Eigen::Lower>().solveInPlace(residual);
426 }
427
429 void
430 colorInto(auto & colored, auto const & normals) const
431 {
432 colored.noalias() = internal::multiply(internal::asTriangular<Eigen::Lower>(m_root), normals);
433 }
434
438 void
439 fitSpace()
440 {
441 internal::fitValueDimension(m_space, m_mean.size());
442 EMRYS_FILTER_ASSERT(
443 m_root.rows() == differenceDimension() && m_root.cols() == differenceDimension()
444 && "the root must have a row and a column per difference coordinate of the space"
445 );
446 normalizeValues(m_space, m_mean);
447 m_root.template triangularView<Eigen::StrictlyUpper>().setZero();
448 }
449
450 VectorType m_mean;
451 MatrixType m_root;
452 EMRYS_FILTER_NO_UNIQUE_ADDRESS Space_ m_space;
453 mutable Scalar m_logDeterminant{};
454 internal::Stamp m_stamp;
455};
456
457namespace internal
458{
459
466template <typename Source, typename Selection, typename SubSpace>
467class SqrtComponentMarginal : public SqrtGaussianParams<SubSpace, typename Source::Scalar>
468{
469public:
470
472
474 SqrtComponentMarginal(Source const & source, Selection shared)
475 : ParamsType{source.marginalAt(
476 Coordinates<
477 decltype(shared.resolved->indices) const &,
478 decltype(shared.resolved->differenceIndices) const &>{
479 shared.resolved->indices,
480 shared.resolved->differenceIndices
481 },
482 shared.resolved->space
483 )}
484 {}
485
487 [[nodiscard]] ParamsType
488 copy() const
489 {
490 return *this;
491 }
492};
493
494} // namespace internal
495
504template <Space Space_, RealScalar Scalar_ = double>
506
520template <GaussianForm G>
521[[nodiscard]] std::expected<SqrtGaussianParams<typename G::SpaceType, typename G::Scalar>, GaussianError>
522toSqrt(G const & form)
523{
525 std::expected<typename G::VectorType, GaussianError> mean = form.mean();
526 if (!mean)
527 {
528 return std::unexpected{mean.error()};
529 }
530 auto root = form.covarianceRoot();
531 if (!root)
532 {
533 return std::unexpected{root.error()};
534 }
535 if constexpr (isInfoForm<G>)
536 {
537 typename Result::MatrixType lower = internal::lowerTriangularRoot(*root);
539 {
540 auto const step = Emrys::minus(form.space(), *mean, form.reference());
541 auto const g = resetJacobian(form.space(), form.reference(), step);
542 lower = internal::lowerTriangularRoot(typename Result::MatrixType{internal::multiply(g, lower)});
543 }
544 return Result{form.space(), std::move(*mean), std::move(lower)};
545 }
546 else
547 {
548 return Result{form.space(), std::move(*mean), std::move(*root)};
549 }
550}
551
552} // namespace Emrys
553
554#ifndef EMRYS_FILTER_NO_IO
555
556namespace Emrys
557{
558
559namespace internal
560{
561
563template <typename Space_, typename Scalar_>
564void
565describe(std::ostream & stream, SqrtGaussianParams<Space_, Scalar_> const & gaussian, Indent indent)
566{
567 stream << indent << "square-root Gaussian";
568 printDimension(stream, gaussian.valueDimension(), gaussian.differenceDimension());
569 printMoments(stream, indent.deeper(), gaussian.moments());
570}
571
572} // namespace internal
573
574} // namespace Emrys
575
576#endif // EMRYS_FILTER_NO_IO
Spaces built from other spaces (decisions.md [sizes-and-dimensions], [several-resizable-blocks]): Com...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
What the four forms of the Gaussian share (decisions.md [gaussian-forms], [form-densities]): the Gaus...
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,...
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'...
Printing: operator<< and std::formatter for every distribution, every filter (which prints its name a...
The covariance reset (decisions.md [covariance-reset]): hasIdentityReset<>, resetJacobian(),...
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...
What parameters and their views share (decisions.md [view-stamp], [params-and-density]): Stamp,...
Triangularization: the triangular factor of a stacked matrix by Householder reflections,...
The density of a Gaussian in a square-root or information form: a read-only view.
Definition GaussianForm.h:139
First and second moments (mean and covariance) of a distribution.
Definition Moments.h:55
Scalar_ Scalar
Definition Moments.h:58
Eigen::Index Index
Definition Moments.h:59
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
Definition Moments.h:76
DifferenceVector< Space_, Scalar > DifferenceVectorType
Definition Moments.h:75
ValueVector< Space_, Scalar > VectorType
Definition Moments.h:74
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition Moments.h:72
static constexpr int DifferenceDimensionAtCompileTime
Definition Moments.h:69
static constexpr int MaxValueDimensionAtCompileTime
Definition Moments.h:66
static constexpr int ValueDimensionAtCompileTime
Definition Moments.h:63
A Gaussian kept as a mean and a lower-triangular root of its covariance.
Definition SqrtGaussian.h:81
std::expected< MatrixType, GaussianError > precision() const
Definition SqrtGaussian.h:228
typename MomentsType::VectorType VectorType
Definition SqrtGaussian.h:87
static constexpr int MaxValueDimensionAtCompileTime
Definition SqrtGaussian.h:97
Index differenceDimension() const
Definition SqrtGaussian.h:155
std::expected< void, GaussianError > factorize(R const &) const
Refused: a root needs no repair, being positive semidefinite by construction, so a mixture of square-...
Definition SqrtGaussian.h:317
static constexpr bool IsInfoForm
Definition SqrtGaussian.h:106
MomentsType moments() const
Definition SqrtGaussian.h:185
typename MomentsType::Index Index
Definition SqrtGaussian.h:86
static constexpr int DifferenceDimensionAtCompileTime
Definition SqrtGaussian.h:100
static constexpr bool IsSqrtForm
Definition SqrtGaussian.h:107
std::expected< MatrixType, GaussianError > covarianceRoot() const
Definition SqrtGaussian.h:241
SqrtGaussianParams(Space_ space)
Constructs a SqrtGaussianParams in space, with a zero mean and root.
Definition SqrtGaussian.h:115
std::expected< MatrixType, GaussianError > covariance() const
Definition SqrtGaussian.h:218
Index valueDimension() const
Definition SqrtGaussian.h:148
std::expected< void, GaussianError > factorize() const
Checks the root and computes , writing it to a cache when it has changed, so that a container of comp...
Definition SqrtGaussian.h:297
Space_ const & space() const
Definition SqrtGaussian.h:162
std::expected< DensityType, GaussianError > toDensity() const &
factorize(), so that the cache it writes is not safe on one object from two threads at once while the...
Definition SqrtGaussian.h:334
Moments< Space_, Scalar_ > MomentsType
Definition SqrtGaussian.h:84
VectorType const & mean() const
Definition SqrtGaussian.h:169
auto marginal() const
Definition SqrtGaussian.h:270
static constexpr int ValueDimensionAtCompileTime
Definition SqrtGaussian.h:94
typename MomentsType::DifferenceVectorType DifferenceVectorType
Definition SqrtGaussian.h:88
FormGaussian< SqrtGaussianParams > DensityType
Definition SqrtGaussian.h:91
SqrtGaussianParams(Mean &&mean, Root &&root)
Constructs a SqrtGaussianParams from mean and the lower triangle of root, moving either one passed as...
Definition SqrtGaussian.h:127
Space_ SpaceType
Definition SqrtGaussian.h:90
typename MomentsType::Scalar Scalar
Definition SqrtGaussian.h:85
static constexpr bool IsParams
Definition SqrtGaussian.h:105
SqrtGaussianParams(Space_ space, Mean &&mean, Root &&root)
SqrtGaussianParams(mean, root) in space, which is resized to fit them if it has one resizable block.
Definition SqrtGaussian.h:138
auto marginal(Index element) const
marginal<Path>() of repetition element of the repeated block Path starts at (see GaussianParams::marg...
Definition SqrtGaussian.h:282
void setMean(Mean &&mean)
Sets the mean, normalized by the space, moving mean if it is an rvalue of the stored type.
Definition SqrtGaussian.h:195
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition SqrtGaussian.h:103
void toDensity() const &&=delete
A density of a temporary would outlive it.
typename MomentsType::MatrixType MatrixType
Definition SqrtGaussian.h:89
MatrixType const & root() const
Definition SqrtGaussian.h:178
SqrtGaussianParams()
Constructs a SqrtGaussianParams in a default space, with a zero mean and root.
Definition SqrtGaussian.h:110
std::expected< MatrixType, GaussianError > precisionRoot() const
Definition SqrtGaussian.h:250
void setRoot(Root &&root)
Sets the root to the lower triangle of root, moving it if it is an rvalue of the stored type.
Definition SqrtGaussian.h:208
bool allFinite(Eigen::DenseBase< Derived > const &x)
Definition Math.h:354
#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
@ NonFinite
a matrix, a factor, or a log-weight holds NaN or infinity (+inf, for a log-weight)
Definition NumericError.h:33
std::expected< SqrtGaussianParams< typename G::SpaceType, typename G::Scalar >, GaussianError > toSqrt(G const &form)
Definition SqrtGaussian.h:522
NumericError GaussianError
Reasons a Gaussian, in any of its forms (decisions.md [gaussian-forms]), can fail to give a density,...
Definition NumericError.h:45
constexpr bool isInfoForm
Whether the GaussianForm G holds the precision, as InfoGaussianParams and SqrtInfoGaussianParams do,...
Definition GaussianForm.h:69
FormGaussian< SqrtGaussianParams< Space_, Scalar_ > > SqrtGaussian
The Gaussian in square-root form, which evaluates its density: what SqrtGaussianParams::toDensity() r...
Definition SqrtGaussian.h:505
constexpr Shared shared()
Definition Batched.h:63
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
auto resetJacobian(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Reset.h:377
constexpr bool hasIdentityReset
Whether the reset Jacobian of S is the identity, so that a covariance needs no change when its mean m...
Definition Reset.h:100
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
void normalizeValues(S const &space, Eigen::MatrixBase< Derived > const &block)
Brings the values in block (rows are space's coordinates, columns samples) into canonical form,...
Definition Space.h:331
What a container does with a component beyond the component's own members (decisions....
Definition Component.h:104