EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Gaussian.h
Go to the documentation of this file.
1#pragma once
2
12
13#include <expected>
14#include <optional>
15#include <type_traits>
16#include <utility>
17
18#include <Eigen/Cholesky>
19
34
35namespace Emrys
36{
37
38template <Space Space_, RealScalar Scalar_>
39class Gaussian;
40
41template <Space Space_, RealScalar Scalar_>
42class GaussianParams;
43
44template <typename Source, typename Selection, typename SubSpace>
46
54template <Space Space_, RealScalar Scalar_ = double>
60
61namespace internal
62{
63
67struct GaussianAccess
68{
72 template <typename G>
73 [[nodiscard]] static typename G::MomentsType &
74 moments(G & gaussian)
75 {
76 gaussian.m_stamp.touchValues();
77 gaussian.m_factorValid = false;
78 return gaussian.m_moments;
79 }
80};
81
88struct FactorizedGaussian
89{
91 template <typename Space, typename Scalar>
92 [[nodiscard]] static Scalar
93 logDeterminant(GaussianParams<Space, Scalar> const & gaussian)
94 {
95 EMRYS_FILTER_ASSERT(gaussian.m_factorValid && "the Gaussian must be factorized");
96 return gaussian.m_logDeterminant;
97 }
98
100 template <typename Space, typename Scalar>
101 [[nodiscard]] static Scalar
102 logPeak(GaussianParams<Space, Scalar> const & gaussian)
103 {
104 EMRYS_FILTER_ASSERT(gaussian.m_factorValid && "the Gaussian must be factorized");
105 Scalar const log2Pi = Emrys::log2Pi<Scalar>();
106 return Scalar(-0.5)
107 * (internal::scalarFromCount<Scalar>(gaussian.differenceDimension()) * log2Pi + gaussian.m_logDeterminant);
108 }
109
111 template <typename Space, typename Scalar, typename Derived>
112 [[nodiscard]] static Scalar
113 squaredMahalanobisDistance(GaussianParams<Space, Scalar> const & gaussian, Eigen::MatrixBase<Derived> const & x)
114 {
115 EMRYS_FILTER_ASSERT(gaussian.m_factorValid && "the Gaussian must be factorized");
117 Emrys::minus(gaussian.space(), x, gaussian.mean());
118 gaussian.m_llt->matrixL().solveInPlace(residual);
119 return residual.squaredNorm();
120 }
121
123 template <typename Space, typename Scalar, typename Derived>
124 [[nodiscard]] static Scalar
125 logDensity(GaussianParams<Space, Scalar> const & gaussian, Eigen::MatrixBase<Derived> const & x)
126 {
127 return logPeak(gaussian) - Scalar(0.5) * squaredMahalanobisDistance(gaussian, x);
128 }
129
131 template <typename Space, typename Scalar>
132 [[nodiscard]] static Eigen::LLT<typename GaussianParams<Space, Scalar>::MatrixType> const &
133 llt(GaussianParams<Space, Scalar> const & gaussian)
134 {
135 EMRYS_FILTER_ASSERT(gaussian.m_factorValid && "the Gaussian must be factorized");
136 auto const & llt = gaussian.m_llt;
137 return *llt;
138 }
139};
140
141template <template <typename, typename, typename> typename Marginal, typename Path, typename Source>
142[[nodiscard]] auto
143tagMarginal(Source const & source);
144
145template <template <typename, typename, typename> typename Marginal, typename Path, typename Source>
146[[nodiscard]] auto
147elementMarginal(Source const & source, Eigen::Index element);
148
153template <
154 bool WithEvidence,
155 typename Source,
156 typename Target,
157 typename Given,
158 typename Derived,
159 typename TargetSpace,
160 typename GivenSpace>
161[[nodiscard]] auto
162conditionalGaussianIn(
163 Source const & source,
164 Target const & target,
165 Given const & given,
166 Eigen::MatrixBase<Derived> const & values,
167 TargetSpace const & targetSpace,
168 GivenSpace const & givenSpace
169)
170{
171 using Scalar = typename Source::Scalar;
172 EMRYS_FILTER_ASSERT(distinctIndices(target.values, given.values) && "target and given indices must be distinct");
173 if constexpr (WithEvidence)
174 {
175 using Result = std::expected<GaussianConditional<TargetSpace, Scalar>, GaussianError>;
176 auto result = conditionMomentsWithEvidence(
177 source.mean(),
178 source.covarianceLower(),
179 target,
180 given,
181 values,
182 givenSpace,
183 targetSpace
184 );
185 if (!result)
186 {
187 return Result{std::unexpect, GaussianError::NotPositiveDefinite};
188 }
189 return Result{GaussianConditional<TargetSpace, Scalar>{
190 {targetSpace, std::move(result->moments.mean), std::move(result->moments.cov)},
191 result->logLikelihood
192 }};
193 }
194 else
195 {
196 auto result =
197 conditionMoments(source.mean(), source.covarianceLower(), target, given, values, givenSpace, targetSpace);
198 return GaussianParams<TargetSpace, Scalar>{targetSpace, std::move(result.mean), std::move(result.cov)};
199 }
200}
201
205template <bool WithEvidence, typename GivenPath, typename Source, typename Derived>
206[[nodiscard]] auto
207tagConditionalGaussian(Source const & source, Eigen::MatrixBase<Derived> const & values)
208{
209 return tagConditional<GivenPath>(
210 source.space(),
211 [&](auto const & target, auto const & given, auto const & targetSpace, auto const & givenSpace) {
212 return conditionalGaussianIn<WithEvidence>(source, target, given, values, targetSpace, givenSpace);
213 }
214 );
215}
216
217} // namespace internal
218
257template <Space Space_, RealScalar Scalar_ = double>
259{
260public:
261
263 using Scalar = typename MomentsType::Scalar;
264 using Index = typename MomentsType::Index;
268 using SpaceType = Space_;
270
273
276
279
282
283 static constexpr bool IsParams = true;
284 static constexpr bool IsInfoForm = false;
285 static constexpr bool IsSqrtForm = false;
286
291 : GaussianParams{Space_{}}
292 {}
293
299 requires internal::SizedByLength<Space_>
300 {
301 fitValueDimension(n);
302 zeroMoments();
303 }
304
307 requires internal::SizedByLength<Space_>
308 : m_moments{std::move(moments)}
309 {
310 fitSpace();
311 }
312
317 template <MatrixExpression Mean, CovarianceExpression Cov>
318 requires internal::SizedByLength<Space_>
319 GaussianParams(Mean && mean, Cov && cov)
320 : m_moments{std::forward<Mean>(mean), std::forward<Cov>(cov)}
321 {
322 fitSpace();
323 }
324
326 explicit GaussianParams(Space_ space)
327 : m_moments{space}
328 , m_space{std::move(space)}
329 {
330 zeroMoments();
331 fitSpace();
332 }
333
339 : m_space{std::move(space)}
340 {
341 fitValueDimension(n);
342 zeroMoments();
343 }
344
349 : m_moments{std::move(moments)}
350 , m_space{std::move(space)}
351 {
352 fitSpace();
353 }
354
356 template <MatrixExpression Mean, CovarianceExpression Cov>
357 GaussianParams(Space_ space, Mean && mean, Cov && cov)
358 : m_moments{std::forward<Mean>(mean), std::forward<Cov>(cov)}
359 , m_space{std::move(space)}
360 {
361 fitSpace();
362 }
363
366 : m_moments{other.m_moments}
367 , m_space{other.m_space}
368 , m_llt{other.m_factorValid ? other.m_llt : std::nullopt}
369 , m_factorValid{other.m_factorValid}
370 {
371 if (m_factorValid)
372 {
373 m_logDeterminant = other.m_logDeterminant;
374 }
375 }
376
379 : m_moments{std::move(other.m_moments)}
380 , m_space{std::move(other.m_space)}
381 , m_llt{other.m_factorValid ? std::move(other.m_llt) : std::nullopt}
382 , m_factorValid{other.m_factorValid}
383 {
384 if (m_factorValid)
385 {
386 m_logDeterminant = other.m_logDeterminant;
387 }
388 other.m_stamp.touchShape();
389 other.m_factorValid = false;
390 }
391
397 {
398 m_stamp.touchShape();
399 m_moments = other.m_moments;
400 m_space = other.m_space;
401 if (other.m_factorValid)
402 {
403 m_llt = other.m_llt;
404 m_logDeterminant = other.m_logDeterminant;
405 }
406 m_factorValid = other.m_factorValid;
407 return *this;
408 }
409
414 operator=(GaussianParams && other) noexcept
415 {
416 m_stamp.touchShape();
417 other.m_stamp.touchShape();
418 m_moments = std::move(other.m_moments);
419 m_space = std::move(other.m_space);
420 if (other.m_factorValid)
421 {
422 m_llt = std::move(other.m_llt);
423 m_logDeterminant = other.m_logDeterminant;
424 }
425 m_factorValid = other.m_factorValid;
426 other.m_factorValid = false;
427 return *this;
428 }
429
431 [[nodiscard]] Index
433 {
434 return Emrys::valueDimension(m_space);
435 }
436
438 [[nodiscard]] Index
440 {
441 return Emrys::differenceDimension(m_space);
442 }
443
449 void
451 requires internal::SizedByLength<Space_>
452 {
453 m_stamp.touchShape();
454 fitValueDimension(n);
455 m_factorValid = false;
456 }
457
466 template <typename Path, typename MeanDerived, typename CovDerived, typename CrossDerived>
467 void
469 Index element,
470 Eigen::MatrixBase<MeanDerived> const & mean,
471 Eigen::MatrixBase<CovDerived> const & cov,
472 Eigen::MatrixBase<CrossDerived> const & cross
473 )
474 {
475 m_stamp.touchShape();
476 EMRYS_FILTER_ASSERT(cross.cols() == differenceDimension());
477 auto const rows = internal::growBlock<Path>(m_space, element, mean.size());
478 EMRYS_FILTER_ASSERT(
479 cov.rows() == rows.differenceCount && cov.cols() == rows.differenceCount
480 && cross.rows() == rows.differenceCount
481 );
482 internal::insertRows(m_moments.mean(), rows.start, mean);
483 internal::insertSymmetric(m_moments.covarianceLower(), rows.differenceStart, cov, cross);
484 normalizeValues(m_space, m_moments.mean());
485 m_factorValid = false;
486 }
487
489 template <typename Path, typename MeanDerived, typename CovDerived>
490 void
491 insert(Index element, Eigen::MatrixBase<MeanDerived> const & mean, Eigen::MatrixBase<CovDerived> const & cov)
492 {
493 using Cross = internal::MatrixOf<
494 Scalar,
495 Eigen::Dynamic,
496 Eigen::Dynamic,
497 CovDerived::MaxRowsAtCompileTime,
498 MomentsType::MatrixType::MaxColsAtCompileTime>;
499 insert<Path>(element, mean, cov, Cross::Zero(cov.rows(), differenceDimension()));
500 }
501
506 template <typename Path>
507 void
508 erase(Index element, Index count = 1)
509 {
510 m_stamp.touchShape();
511 auto const rows = internal::shrinkBlock<Path>(m_space, element, count);
512 internal::eraseRows(m_moments.mean(), rows.start, rows.count);
513 internal::eraseSymmetric(m_moments.covarianceLower(), rows.differenceStart, rows.differenceCount);
514 m_factorValid = false;
515 }
516
518 [[nodiscard]] Space_ const &
519 space() const
520 {
521 return m_space;
522 }
523
525 [[nodiscard]] MomentsType const &
526 moments() const
527 {
528 return m_moments;
529 }
530
532 [[nodiscard]] VectorType const &
533 mean() const
534 {
535 return m_moments.mean();
536 }
537
542 [[nodiscard]] MatrixType const &
544 {
545 return m_moments.covarianceLower();
546 }
547
552 template <typename Path>
553 [[nodiscard]] auto
554 mean() const
555 {
556 return mean()(idx<Path>(m_space));
557 }
558
562 template <typename Path>
563 [[nodiscard]] auto
564 mean(Index element) const
565 {
566 return mean()(idx<Path>(m_space, element));
567 }
568
573 template <typename Path>
574 [[nodiscard]] auto
576 {
577 return internal::lowerSymmetricSelection<DifferenceDimensionAtCompileTime, MaxDifferenceDimensionAtCompileTime>(
579 diffIdx<Path>(m_space)
580 );
581 }
582
584 template <typename Path>
585 [[nodiscard]] auto
586 covariance(Index element) const
587 {
588 return internal::lowerSymmetricSelection<DifferenceDimensionAtCompileTime, MaxDifferenceDimensionAtCompileTime>(
590 diffIdx<Path>(m_space, element)
591 );
592 }
593
599 template <typename RowPath, typename ColPath>
600 [[nodiscard]] auto
602 {
603 return internal::lowerSymmetricCross<DifferenceDimensionAtCompileTime, MaxDifferenceDimensionAtCompileTime>(
605 diffIdx<RowPath>(m_space),
606 diffIdx<ColPath>(m_space)
607 );
608 }
609
613 template <MatrixExpression Mean>
614 void
615 setMean(Mean && mean)
616 {
617 m_stamp.touchValues();
618 EMRYS_FILTER_ASSERT(mean.size() == valueDimension());
619 m_moments.mean() = std::forward<Mean>(mean);
620 normalizeValues(m_space, m_moments.mean());
621 }
622
628 template <CovarianceExpression Cov>
629 void
630 setCovariance(Cov && cov)
631 {
632 m_stamp.touchValues();
633 EMRYS_FILTER_ASSERT(cov.rows() == differenceDimension() && cov.cols() == differenceDimension());
634 m_moments.covarianceLower() = std::forward<Cov>(cov);
635 m_factorValid = false;
636 }
637
652 void
654 {
655 m_stamp.touchValues();
656 EMRYS_FILTER_ASSERT(probability > 0 && probability < 1 && "the probability must be in (0, 1)");
657 if (differenceDimension() == 0)
658 {
659 return;
660 }
661 m_moments.covarianceLower() *=
662 chiSquaredQuantile<Scalar>(probability, internal::scalarFromCount<Scalar>(differenceDimension()));
663 m_factorValid = false;
664 }
665
673 [[nodiscard]] std::expected<MatrixType, GaussianError>
675 {
676 MatrixType result = covarianceLower().template selfadjointView<Eigen::Lower>();
677 return result;
678 }
679
683 [[nodiscard]] std::expected<MatrixType, GaussianError>
684 precision() const
685 {
686 if (auto factorized = factorize(); !factorized)
687 {
688 return std::unexpected{factorized.error()};
689 }
690 MatrixType result = MatrixType::Identity(differenceDimension(), differenceDimension());
691 internal::solveInPlace(*m_llt, result);
692 return result;
693 }
694
698 [[nodiscard]] std::expected<MatrixType, GaussianError>
700 {
701 if (auto factorized = factorize(); !factorized)
702 {
703 return std::unexpected{factorized.error()};
704 }
705 MatrixType result = m_llt->matrixL();
706 return result;
707 }
708
714 [[nodiscard]] std::expected<MatrixType, GaussianError>
716 {
717 if (auto factorized = factorize(); !factorized)
718 {
719 return std::unexpected{factorized.error()};
720 }
721 MatrixType result = MatrixType::Identity(differenceDimension(), differenceDimension());
722 internal::solveInPlace(m_llt->matrixL(), result);
723 return result;
724 }
725
731 template <typename Path>
732 [[nodiscard]] auto
733 marginal() const &
734 {
735 return internal::tagMarginal<GaussianMarginal, Path>(*this);
736 }
737
739 template <typename Path>
740 void
741 marginal() const && = delete;
742
748 template <typename Path>
749 [[nodiscard]] auto
750 marginal(Index element) const &
751 {
752 return internal::elementMarginal<GaussianMarginal, Path>(*this, element);
753 }
754
756 template <typename Path>
757 void
758 marginal(Index element) const && = delete;
759
772 template <typename GivenPath, typename Derived>
773 [[nodiscard]] auto
774 conditional(Eigen::MatrixBase<Derived> const & values) const
775 {
776 return internal::tagConditionalGaussian<false, GivenPath>(*this, values);
777 }
778
791 template <typename GivenPath, typename Derived>
792 [[nodiscard]] auto
793 conditionalWithEvidence(Eigen::MatrixBase<Derived> const & values) const
794 {
795 return internal::tagConditionalGaussian<true, GivenPath>(*this, values);
796 }
797
814 [[nodiscard]] std::expected<void, GaussianError>
815 factorize() const
816 {
817 if (m_factorValid)
818 {
819 return {};
820 }
821 return factorizeCovariance(m_moments.covarianceLower());
822 }
823
842 template <typename R>
843 [[nodiscard]] std::expected<void, GaussianError>
844 factorize(R const & repair)
845 {
846 static_assert(
847 Repair<R>,
848 "a density takes a repair, such as roots::ridge(), not axes: its factor is the Cholesky factor "
849 "(decisions.md [covariance-repair])"
850 );
851 auto result = std::as_const(*this).factorize();
852 if (result || result.error() != GaussianError::NotPositiveDefinite)
853 {
854 return result;
855 }
856 auto repaired = repair.repaired(m_moments.covarianceLower());
857 if (!repaired)
858 {
859 return std::unexpected{repaired.error()};
860 }
861 result = factorizeCovariance(*repaired);
862 if (result)
863 {
864 m_stamp.touchValues();
865 m_moments.covarianceLower() = std::move(*repaired);
866 }
867 return result;
868 }
869
878 [[nodiscard]] std::expected<DensityType, GaussianError>
879 toDensity() const &
880 {
881 if (auto result = factorize(); !result)
882 {
883 return std::unexpected{result.error()};
884 }
885 return DensityType{*this};
886 }
887
889 void
890 toDensity() const && = delete;
891
897 template <typename R>
898 [[nodiscard]] std::expected<DensityType, GaussianError>
899 toDensity(R const & repair) &
900 {
901 if (auto result = factorize(repair); !result)
902 {
903 return std::unexpected{result.error()};
904 }
905 return DensityType{*this};
906 }
907
909 template <typename R>
910 void
911 toDensity(R const &) && = delete;
912
913private:
914
915 friend DensityType;
916 friend struct internal::FactorizedGaussian;
917
919 template <typename>
920 friend class internal::ValuesCheck;
921
923 template <typename>
924 friend class internal::ShapeCheck;
925
926 friend struct internal::GaussianAccess;
927
929 [[nodiscard]] std::expected<void, GaussianError>
930 factorizeCovariance(MatrixType const & cov) const
931 {
932 if (!m_llt)
933 {
934 m_llt.emplace(cov.rows());
935 }
936 m_llt->compute(cov);
937 if (m_llt->info() != Eigen::Success)
938 {
939 return std::unexpected{GaussianError::NotPositiveDefinite};
940 }
941 // NaN slips through LLT's positivity test but always reaches the diagonal of
942 // L, so checking the log-determinant catches any non-finite lower-triangle input.
943 Scalar const logDet = Scalar(2) * m_llt->matrixLLT().diagonal().array().log().sum();
944 if (!isFinite(logDet))
945 {
946 return std::unexpected{GaussianError::NonFinite};
947 }
948 m_logDeterminant = logDet;
949 m_factorValid = true;
950 return {};
951 }
952
956 void
957 fitSpace()
958 {
959 internal::fitValueDimension(m_space, m_moments.valueDimension());
960 EMRYS_FILTER_ASSERT(
961 m_moments.differenceDimension() == differenceDimension()
962 && "the covariance must have a row per difference coordinate of the space"
963 );
964 normalizeValues(m_space, m_moments.mean());
965 }
966
968 void
969 fitValueDimension(Index n)
970 {
971 internal::fitValueDimension(m_space, n);
972 m_moments.resize(m_space);
973 }
974
976 void
977 zeroMoments()
978 {
979 m_moments.mean().setZero();
980 m_moments.covarianceLower().setZero();
981 }
982
983 MomentsType m_moments;
984 EMRYS_FILTER_NO_UNIQUE_ADDRESS Space_ m_space;
985
986 // Factorization cache, filled lazily by the const factorize(), and so written through const
987 // members (see factorize()).
990 mutable std::optional<Eigen::LLT<MatrixType>> m_llt;
991
992 mutable Scalar m_logDeterminant{};
993 mutable bool m_factorValid{false};
994 internal::Stamp m_stamp;
995};
996
1009template <Space Space_, RealScalar Scalar_ = double>
1010class Gaussian
1011{
1012public:
1013
1015 using SpaceType = Space_;
1017 using Scalar = typename ParamsType::Scalar;
1018 using Index = typename ParamsType::Index;
1022 using LltType = Eigen::LLT<MatrixType>;
1023
1026
1029
1032
1035
1036 static constexpr bool IsParams = false;
1037
1039 [[nodiscard]] Index
1041 {
1042 return source().valueDimension();
1043 }
1044
1046 [[nodiscard]] Index
1048 {
1049 return source().differenceDimension();
1050 }
1051
1053 [[nodiscard]] Space_ const &
1054 space() const
1055 {
1056 return source().space();
1057 }
1058
1060 [[nodiscard]] MomentsType const &
1061 moments() const
1062 {
1063 return source().moments();
1064 }
1065
1067 [[nodiscard]] VectorType const &
1068 mean() const
1069 {
1070 return source().mean();
1071 }
1072
1074 [[nodiscard]] MatrixType const &
1076 {
1077 return source().covarianceLower();
1078 }
1079
1081 template <typename Path>
1082 [[nodiscard]] auto
1083 mean() const
1084 {
1085 return source().template mean<Path>();
1086 }
1087
1089 template <typename Path>
1090 [[nodiscard]] auto
1091 mean(Index element) const
1092 {
1093 return source().template mean<Path>(element);
1094 }
1095
1097 template <typename Path>
1098 [[nodiscard]] auto
1100 {
1101 return source().template covariance<Path>();
1102 }
1103
1105 template <typename Path>
1106 [[nodiscard]] auto
1107 covariance(Index element) const
1108 {
1109 return source().template covariance<Path>(element);
1110 }
1111
1113 template <typename RowPath, typename ColPath>
1114 [[nodiscard]] auto
1116 {
1117 return source().template covariance<RowPath, ColPath>();
1118 }
1119
1121 [[nodiscard]] LltType const &
1122 llt() const
1123 {
1124 auto const & llt = source().m_llt;
1125 return *llt;
1126 }
1127
1129 [[nodiscard]] Scalar
1131 {
1132 return source().m_logDeterminant;
1133 }
1134
1140 [[nodiscard]] Scalar
1141 logPeak() const
1142 {
1143 return internal::FactorizedGaussian::logPeak(source());
1144 }
1145
1154 template <typename Derived>
1155 [[nodiscard]] Scalar
1156 squaredMahalanobisDistance(Eigen::MatrixBase<Derived> const & x) const
1157 {
1158 static_assert(Derived::IsVectorAtCompileTime, "squaredMahalanobisDistance() expects a vector");
1159 EMRYS_FILTER_ASSERT(x.size() == valueDimension());
1160 return internal::FactorizedGaussian::squaredMahalanobisDistance(source(), x);
1161 }
1162
1174 template <typename Derived>
1175 [[nodiscard]] Scalar
1176 confidence(Eigen::MatrixBase<Derived> const & x) const
1177 {
1178 if (differenceDimension() == 0)
1179 {
1180 return Scalar(0);
1181 }
1182 return chiSquaredCdf<Scalar>(
1184 internal::scalarFromCount<Scalar>(differenceDimension())
1185 );
1186 }
1187
1201 template <typename Derived>
1202 [[nodiscard]] bool
1203 contains(Eigen::MatrixBase<Derived> const & x, Scalar probability) const
1204 {
1205 EMRYS_FILTER_ASSERT(probability > 0 && probability < 1 && "the probability must be in (0, 1)");
1206 if (differenceDimension() == 0)
1207 {
1208 return true;
1209 }
1211 <= chiSquaredQuantile<Scalar>(probability, internal::scalarFromCount<Scalar>(differenceDimension()));
1212 }
1213
1221 template <typename Derived>
1222 [[nodiscard]] Scalar
1223 logDensity(Eigen::MatrixBase<Derived> const & x) const
1224 {
1225 return logPeak() - Scalar(0.5) * squaredMahalanobisDistance(x);
1226 }
1227
1234 template <typename Derived>
1235 [[nodiscard]] Scalar
1236 density(Eigen::MatrixBase<Derived> const & x) const
1237 {
1238 return Eigen::numext::exp(logDensity(x));
1239 }
1240
1244 template <typename Path>
1245 [[nodiscard]] auto
1246 marginal() const
1247 {
1248 return internal::tagMarginal<GaussianMarginal, Path>(*this);
1249 }
1250
1252 template <typename Path>
1253 [[nodiscard]] auto
1254 marginal(Index element) const
1255 {
1256 return internal::elementMarginal<GaussianMarginal, Path>(*this, element);
1257 }
1258
1262 template <typename GivenPath, typename Derived>
1263 [[nodiscard]] auto
1264 conditional(Eigen::MatrixBase<Derived> const & values) const
1265 {
1266 return internal::tagConditionalGaussian<false, GivenPath>(*this, values);
1267 }
1268
1272 template <typename GivenPath, typename Derived>
1273 [[nodiscard]] auto
1274 conditionalWithEvidence(Eigen::MatrixBase<Derived> const & values) const
1275 {
1276 return internal::tagConditionalGaussian<true, GivenPath>(*this, values);
1277 }
1278
1279private:
1280
1281 friend ParamsType;
1282
1286 explicit Gaussian(ParamsType const & source)
1287 : m_check{source}
1288 {}
1289
1291 [[nodiscard]] ParamsType const &
1292 source() const
1293 {
1294 return m_check.get();
1295 }
1296
1297 internal::ValuesCheck<ParamsType> m_check;
1298};
1299
1322template <typename Source, typename Selection, typename SubSpace>
1324{
1325public:
1326
1327 using Scalar = typename Source::Scalar;
1328 using Index = Eigen::Index;
1329 using SpaceType = SubSpace;
1330
1333
1336
1339
1343
1348
1349 static constexpr bool IsInfoForm = false;
1350 static constexpr bool IsSqrtForm = false;
1351
1353 using ResolvedType = typename internal::SelectionHolder<Source, Selection, SubSpace>::ResolvedType;
1354
1356 using IndexVectorType = decltype(ResolvedType::indices);
1357
1359 using DifferenceIndexVectorType = decltype(ResolvedType::differenceIndices);
1360
1362 GaussianMarginal(Source const & source, Selection const & selection, SubSpace space)
1363 requires(!internal::isSharedSelection<Selection>)
1364 : m_holder{source}
1365 , m_selection{source, selection, std::move(space)}
1366 {}
1367
1369 GaussianMarginal(Source const & source, Selection shared)
1370 requires(internal::isSharedSelection<Selection>)
1371 : m_holder{source}
1372 , m_selection{shared}
1373 {}
1374
1376 [[nodiscard]] Index
1378 {
1379 return indices().size();
1380 }
1381
1383 [[nodiscard]] Index
1385 {
1386 return differenceIndices().size();
1387 }
1388
1390 [[nodiscard]] SubSpace const &
1391 space() const
1392 {
1393 return m_selection.get().space;
1394 }
1395
1397 [[nodiscard]] IndexVectorType const &
1398 indices() const
1399 {
1400 return m_selection.get().indices;
1401 }
1402
1404 [[nodiscard]] DifferenceIndexVectorType const &
1406 {
1407 return m_selection.get().differenceIndices;
1408 }
1409
1411 [[nodiscard]] auto
1412 mean() const
1413 {
1414 return source().mean()(indices());
1415 }
1416
1420 [[nodiscard]] auto
1422 {
1423 return internal::lowerSymmetricBlock(source().covarianceLower(), differenceIndices(), differenceIndices());
1424 }
1425
1427 [[nodiscard]] MomentsType
1428 moments() const
1429 {
1430 return MomentsType{mean(), covarianceLower()};
1431 }
1432
1434 [[nodiscard]] ParamsType
1435 copy() const
1436 {
1437 return ParamsType{space(), moments()};
1438 }
1439
1444 [[nodiscard]] std::expected<MatrixType, GaussianError>
1446 {
1447 MatrixType result = covarianceLower();
1448 return result;
1449 }
1450
1455 [[nodiscard]] std::expected<MatrixType, GaussianError>
1457 {
1458 auto const llt = factorization();
1459 if (!llt)
1460 {
1461 return std::unexpected{llt.error()};
1462 }
1463 MatrixType result = MatrixType::Identity(differenceDimension(), differenceDimension());
1464 internal::solveInPlace(*llt, result);
1465 return result;
1466 }
1467
1471 [[nodiscard]] std::expected<MatrixType, GaussianError>
1473 {
1474 auto const llt = factorization();
1475 if (!llt)
1476 {
1477 return std::unexpected{llt.error()};
1478 }
1479 MatrixType result = llt->matrixL();
1480 return result;
1481 }
1482
1486 [[nodiscard]] std::expected<MatrixType, GaussianError>
1488 {
1489 auto const llt = factorization();
1490 if (!llt)
1491 {
1492 return std::unexpected{llt.error()};
1493 }
1494 MatrixType result = MatrixType::Identity(differenceDimension(), differenceDimension());
1495 internal::solveInPlace(llt->matrixL(), result);
1496 return result;
1497 }
1498
1499private:
1500
1502 [[nodiscard]] Source const &
1503 source() const
1504 {
1505 return m_holder.get();
1506 }
1507
1512 [[nodiscard]] std::expected<Eigen::LLT<MatrixType>, GaussianError>
1513 factorization() const
1514 {
1515 auto llt = Eigen::LLT<MatrixType>{MatrixType{covarianceLower()}};
1516 if (llt.info() != Eigen::Success)
1517 {
1518 return std::unexpected{GaussianError::NotPositiveDefinite};
1519 }
1520 // As in GaussianParams::factorize(): NaN passes LLT's test but reaches the diagonal.
1521 if (!allFinite(llt.matrixLLT().diagonal()))
1522 {
1523 return std::unexpected{GaussianError::NonFinite};
1524 }
1525 return llt;
1526 }
1527
1528 internal::HolderFor<Source> m_holder;
1529 internal::SelectionHolder<Source, Selection, SubSpace> m_selection;
1530};
1531
1532namespace internal
1533{
1534
1538template <template <typename, typename, typename> typename Marginal, typename Path, typename Source>
1539[[nodiscard]] auto
1540tagMarginal(Source const & source)
1541{
1542 auto const selection =
1543 Coordinates{asIndexSet(idx<Path>(source.space())), asIndexSet(diffIdx<Path>(source.space()))};
1544 auto space = pathSubspace<Path>(source.space());
1545 return Marginal<Source, std::remove_cvref_t<decltype(selection)>, decltype(space)>{
1546 source,
1547 selection,
1548 std::move(space)
1549 };
1550}
1551
1556template <template <typename, typename, typename> typename Marginal, typename Path, typename Source>
1557[[nodiscard]] auto
1558elementMarginal(Source const & source, Eigen::Index element)
1559{
1560 auto const selection =
1561 Coordinates{asIndexSet(idx<Path>(source.space(), element)), asIndexSet(diffIdx<Path>(source.space(), element))};
1562 auto space = pathElementSubspace<Path>(source.space());
1563 return Marginal<Source, std::remove_cvref_t<decltype(selection)>, decltype(space)>{
1564 source,
1565 selection,
1566 std::move(space)
1567 };
1568}
1569
1570} // namespace internal
1571
1572} // namespace Emrys
1573
1574#ifndef EMRYS_FILTER_NO_IO
1575
1576namespace Emrys
1577{
1578
1579namespace internal
1580{
1581
1583template <typename Space_, typename Scalar_>
1584void
1585describe(std::ostream & stream, GaussianParams<Space_, Scalar_> const & gaussian, Indent indent)
1586{
1587 stream << indent << "Gaussian";
1588 printDimension(stream, gaussian.valueDimension(), gaussian.differenceDimension());
1589 printMoments(stream, indent.deeper(), gaussian.moments());
1590}
1591
1592} // namespace internal
1593
1594} // namespace Emrys
1595
1596#endif // EMRYS_FILTER_NO_IO
The chi-squared distribution's CDF and quantile, with no dependency outside Eigen,...
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 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...
Printing: operator<< and std::formatter for every distribution, every filter (which prints its name a...
Square roots of a covariance (decisions.md [sigma-point-axes]): the Axes and Repair concepts that a r...
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,...
Subspaces, and growing and shrinking blocks (decisions.md [several-resizable-blocks]).
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
Read-only view of a subset of the entries of a GaussianParams or Gaussian.
Definition Gaussian.h:1324
SubSpace SpaceType
Definition Gaussian.h:1329
typename ParamsType::VectorType VectorType
Definition Gaussian.h:1346
static constexpr int MaxValueDimensionAtCompileTime
Definition Gaussian.h:1335
static constexpr bool IsInfoForm
Definition Gaussian.h:1349
ParamsType copy() const
Definition Gaussian.h:1435
Index differenceDimension() const
Definition Gaussian.h:1384
static constexpr bool IsSqrtForm
Definition Gaussian.h:1350
std::expected< MatrixType, GaussianError > covariance() const
Definition Gaussian.h:1445
GaussianParams< SubSpace, Scalar > ParamsType
Definition Gaussian.h:1344
Eigen::Index Index
Definition Gaussian.h:1328
std::expected< MatrixType, GaussianError > precisionRoot() const
Definition Gaussian.h:1487
std::expected< MatrixType, GaussianError > precision() const
Definition Gaussian.h:1456
decltype(ResolvedType::differenceIndices) DifferenceIndexVectorType
Definition Gaussian.h:1359
static constexpr int ValueDimensionAtCompileTime
Definition Gaussian.h:1332
typename ParamsType::MomentsType MomentsType
Definition Gaussian.h:1345
static constexpr int DifferenceDimensionAtCompileTime
Definition Gaussian.h:1338
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition Gaussian.h:1341
IndexVectorType const & indices() const
Definition Gaussian.h:1398
auto mean() const
Definition Gaussian.h:1412
Index valueDimension() const
Definition Gaussian.h:1377
typename Type::Scalar Scalar
Definition Gaussian.h:1327
auto covarianceLower() const
Definition Gaussian.h:1421
GaussianMarginal(Source const &source, Selection shared)
Selects the coordinates of source that shared resolved, borrowing them.
Definition Gaussian.h:1369
DifferenceIndexVectorType const & differenceIndices() const
Definition Gaussian.h:1405
typename ParamsType::MatrixType MatrixType
Definition Gaussian.h:1347
MomentsType moments() const
Definition Gaussian.h:1428
decltype(ResolvedType::indices) IndexVectorType
Definition Gaussian.h:1356
typename internal::SelectionHolder< Type, Selection, SubSpace >::ResolvedType ResolvedType
Definition Gaussian.h:1353
std::expected< MatrixType, GaussianError > covarianceRoot() const
Definition Gaussian.h:1472
SubSpace const & space() const
Definition Gaussian.h:1391
GaussianMarginal(Source const &source, Selection const &selection, SubSpace space)
Selects selection's coordinates, which form space.
Definition Gaussian.h:1362
A Gaussian distribution: a mean and covariance in a space.
Definition Gaussian.h:259
typename MomentsType::Index Index
Definition Gaussian.h:264
auto mean() const
Definition Gaussian.h:554
void setMean(Mean &&mean)
Sets the mean, normalized by the space, moving mean if it is an rvalue of the stored type.
Definition Gaussian.h:615
Moments< Space_, Scalar_ > MomentsType
Definition Gaussian.h:262
GaussianParams(Space_ space, MomentsType moments)
Constructs a Gaussian from moments in space, which is resized to fit them if it has one resizable blo...
Definition Gaussian.h:348
std::expected< MatrixType, GaussianError > covarianceRoot() const
Definition Gaussian.h:699
GaussianParams(MomentsType moments)
Constructs a Gaussian from moments, in a default space resized to fit them.
Definition Gaussian.h:306
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition Gaussian.h:281
static constexpr bool IsParams
Definition Gaussian.h:283
static constexpr int DifferenceDimensionAtCompileTime
Definition Gaussian.h:278
static constexpr bool IsInfoForm
Definition Gaussian.h:284
void toDensity(R const &) &&=delete
A density of a temporary would outlive it.
GaussianParams(Space_ space, Mean &&mean, Cov &&cov)
GaussianParams(mean, cov) in space.
Definition Gaussian.h:357
typename MomentsType::MatrixType MatrixType
Definition Gaussian.h:267
GaussianParams(Space_ space, Index n)
Constructs a Gaussian with n value coordinates in space, with a zero mean and covariance.
Definition Gaussian.h:338
GaussianParams(Mean &&mean, Cov &&cov)
Constructs a Gaussian from mean and cov, moving either one passed as an rvalue of the stored type.
Definition Gaussian.h:319
void setCovariance(Cov &&cov)
Sets the covariance, moving cov if it is an rvalue of the stored type, and invalidates the factorizat...
Definition Gaussian.h:630
void resize(Index n)
Resizes mean, covariance, and factorization, and the space's resizable block, to n value coordinates.
Definition Gaussian.h:450
typename MomentsType::VectorType VectorType
Definition Gaussian.h:265
static constexpr int ValueDimensionAtCompileTime
Definition Gaussian.h:272
void insert(Index element, Eigen::MatrixBase< MeanDerived > const &mean, Eigen::MatrixBase< CovDerived > const &cov)
insert() of entries independent of the existing ones.
Definition Gaussian.h:491
Gaussian< Space_, Scalar_ > DensityType
Definition Gaussian.h:269
auto covariance() const
Definition Gaussian.h:601
GaussianParams & operator=(GaussianParams &&other) noexcept
Moves the data and any valid factorization; a view of either object may not be read afterward.
Definition Gaussian.h:414
void erase(Index element, Index count=1)
Removes count elements of the resizable block named by the tag path Path, starting at its element ele...
Definition Gaussian.h:508
typename MomentsType::DifferenceVectorType DifferenceVectorType
a difference, such as a residual
Definition Gaussian.h:266
std::expected< void, GaussianError > factorize() const
Definition Gaussian.h:815
std::expected< MatrixType, GaussianError > precision() const
Definition Gaussian.h:684
GaussianParams(GaussianParams &&other) noexcept
Moves the data and any valid factorization; a view of other may not be read afterward.
Definition Gaussian.h:378
auto mean(Index element) const
mean<Path>() of repetition element of the repeated block Path starts at (see marginal<Path>(element))...
Definition Gaussian.h:564
static constexpr bool IsSqrtForm
Definition Gaussian.h:285
std::expected< MatrixType, GaussianError > precisionRoot() const
Definition Gaussian.h:715
MatrixType const & covarianceLower() const
Definition Gaussian.h:543
auto covariance(Index element) const
covariance<Path>() of repetition element of the repeated block Path starts at.
Definition Gaussian.h:586
auto conditional(Eigen::MatrixBase< Derived > const &values) const
Definition Gaussian.h:774
GaussianParams()
Constructs a Gaussian in a default space, with a zero mean and covariance, which factorize() rejects ...
Definition Gaussian.h:290
GaussianParams(Space_ space)
Constructs a Gaussian in space, with a zero mean and covariance.
Definition Gaussian.h:326
GaussianParams & operator=(GaussianParams const &other)
Copies the data and any valid factorization; a view of this object may not be read afterward.
Definition Gaussian.h:396
auto covariance() const
Definition Gaussian.h:575
typename MomentsType::Scalar Scalar
Definition Gaussian.h:263
std::expected< void, GaussianError > factorize(R const &repair)
factorize(), repairing a covariance that is not positive definite by repair, a Repair: g....
Definition Gaussian.h:844
Index differenceDimension() const
Definition Gaussian.h:439
Space_ SpaceType
Definition Gaussian.h:268
void marginal() const &&=delete
A marginal of a temporary would outlive it; copy the marginal of a named Gaussian instead.
void insert(Index element, Eigen::MatrixBase< MeanDerived > const &mean, Eigen::MatrixBase< CovDerived > const &cov, Eigen::MatrixBase< CrossDerived > const &cross)
Inserts the entries mean into the resizable block named by the tag path Path, before its element elem...
Definition Gaussian.h:468
std::expected< DensityType, GaussianError > toDensity() const &
Factorizes the covariance if needed (see factorize()), which writes the cache and so is not safe on o...
Definition Gaussian.h:879
MomentsType const & moments() const
Definition Gaussian.h:526
Index valueDimension() const
Definition Gaussian.h:432
VectorType const & mean() const
Definition Gaussian.h:533
Space_ const & space() const
Definition Gaussian.h:519
void marginal(Index element) const &&=delete
A marginal of a temporary would outlive it.
static constexpr int MaxValueDimensionAtCompileTime
Definition Gaussian.h:275
auto marginal() const &
Definition Gaussian.h:733
auto conditionalWithEvidence(Eigen::MatrixBase< Derived > const &values) const
Definition Gaussian.h:793
void scaleToConfidence(Scalar probability)
Scales the covariance so that its one-standard-deviation ellipsoid is the probability confidence regi...
Definition Gaussian.h:653
GaussianParams(Index n)
Constructs a Gaussian with n value coordinates, with a zero mean and covariance.
Definition Gaussian.h:298
void toDensity() const &&=delete
A density of a temporary would outlive it.
GaussianParams(GaussianParams const &other)
Copies the data and any valid factorization.
Definition Gaussian.h:365
std::expected< MatrixType, GaussianError > covariance() const
Definition Gaussian.h:674
The density of a Gaussian whose covariance has been factorized: a read-only view.
Definition Gaussian.h:1011
Index valueDimension() const
Definition Gaussian.h:1040
typename ParamsType::Index Index
Definition Gaussian.h:1018
Index differenceDimension() const
Definition Gaussian.h:1047
typename ParamsType::Scalar Scalar
Definition Gaussian.h:1017
auto covariance() const
Definition Gaussian.h:1099
Scalar density(Eigen::MatrixBase< Derived > const &x) const
Definition Gaussian.h:1236
typename ParamsType::MomentsType MomentsType
Definition Gaussian.h:1016
VectorType const & mean() const
Definition Gaussian.h:1068
static constexpr int DifferenceDimensionAtCompileTime
Definition Gaussian.h:1031
static constexpr bool IsParams
Definition Gaussian.h:1036
LltType const & llt() const
Definition Gaussian.h:1122
Space_ const & space() const
Definition Gaussian.h:1054
Eigen::LLT< MatrixType > LltType
Definition Gaussian.h:1022
GaussianParams< Space_, Scalar_ > ParamsType
Definition Gaussian.h:1014
static constexpr int ValueDimensionAtCompileTime
Definition Gaussian.h:1025
auto conditional(Eigen::MatrixBase< Derived > const &values) const
Definition Gaussian.h:1264
Space_ SpaceType
Definition Gaussian.h:1015
Scalar confidence(Eigen::MatrixBase< Derived > const &x) const
Definition Gaussian.h:1176
auto marginal() const
marginal() of the subspace named by the path Path; see GaussianParams::marginal().
Definition Gaussian.h:1246
MatrixType const & covarianceLower() const
Definition Gaussian.h:1075
Scalar squaredMahalanobisDistance(Eigen::MatrixBase< Derived > const &x) const
Definition Gaussian.h:1156
typename ParamsType::VectorType VectorType
Definition Gaussian.h:1019
auto mean(Index element) const
mean<Path>() of one repetition; see GaussianParams::mean<Path>(element).
Definition Gaussian.h:1091
auto mean() const
Definition Gaussian.h:1083
Scalar logDensity(Eigen::MatrixBase< Derived > const &x) const
Definition Gaussian.h:1223
auto covariance(Index element) const
covariance<Path>() of one repetition; see GaussianParams::covariance<Path>(element).
Definition Gaussian.h:1107
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition Gaussian.h:1034
auto marginal(Index element) const
marginal() of one repetition of a repeated block; see GaussianParams::marginal(element).
Definition Gaussian.h:1254
auto conditionalWithEvidence(Eigen::MatrixBase< Derived > const &values) const
Definition Gaussian.h:1274
auto covariance() const
Definition Gaussian.h:1115
typename ParamsType::DifferenceVectorType DifferenceVectorType
Definition Gaussian.h:1020
typename ParamsType::MatrixType MatrixType
Definition Gaussian.h:1021
Scalar logPeak() const
Definition Gaussian.h:1141
bool contains(Eigen::MatrixBase< Derived > const &x, Scalar probability) const
Definition Gaussian.h:1203
MomentsType const & moments() const
Definition Gaussian.h:1061
static constexpr int MaxValueDimensionAtCompileTime
Definition Gaussian.h:1028
Scalar logDeterminant() const
Definition Gaussian.h:1130
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 repair: what turns a covariance whose root failed into one to retry (decisions.md [sigma-point-axes...
Definition Roots.h:321
Scalar chiSquaredQuantile(std::type_identity_t< Scalar > const &probability, std::type_identity_t< Scalar > const &dof)
Definition ChiSquared.h:148
Scalar chiSquaredCdf(std::type_identity_t< Scalar > const &x, std::type_identity_t< Scalar > const &dof)
Definition ChiSquared.h:123
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
bool isFinite(T const &x)
Definition Math.h:305
Scalar log2Pi()
Definition Math.h:177
@ 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
NumericError GaussianError
Reasons a Gaussian, in any of its forms (decisions.md [gaussian-forms]), can fail to give a density,...
Definition NumericError.h:45
GivenBlock< Space, P, Value > given(Space const &space, Value const &value)
Definition Measurement.h:917
constexpr Residual< Inner > residual(Inner inner={})
Definition Resampling.h:544
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
constexpr auto diffIdx()
idx<S, Path>() of the differences of the subspace named by Path: its rows of a covariance.
Definition TagPath.h:669
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
constexpr int valueDimensionAtCompileTime
The value dimension of Space at compile time, or Eigen::Dynamic if it is resizable.
Definition Space.h:251
constexpr int maxValueDimensionAtCompileTime
The largest valid value dimension of Space, or Eigen::Dynamic if it is unbounded.
Definition Space.h:258
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
constexpr auto idx()
Definition TagPath.h:658
The result of GaussianParams::conditionalWithEvidence(): the conditional distribution and the log-evi...
Definition Gaussian.h:56
Scalar_ logEvidence
, the given values' log-density under their marginal
Definition Gaussian.h:58
GaussianParams< Space_, Scalar_ > conditional
the distribution of the other blocks given the values
Definition Gaussian.h:57