19#include <Eigen/Cholesky>
115template <
typename Scalar =
double>
129template <
typename Indices>
130[[nodiscard]] Eigen::Index
131indexCount(Indices
const & indices)
133 if constexpr (std::is_integral_v<Indices>)
139 return Eigen::Index(indices.size());
144template <
typename Indices>
145[[nodiscard]] Eigen::Index
146indexAt(Indices
const & indices, Eigen::Index k)
148 if constexpr (std::is_integral_v<Indices>)
150 return Eigen::Index(indices);
154 return Eigen::Index(indices[k]);
189template <Space Space_, RealScalar Scalar_ =
double>
202 , m_space{std::move(
space)}
209 template <
typename Path>
214 for (
Index k = 0; k < internal::indexCount(rows); ++k)
216 m_matrix.row(internal::indexAt(rows, k)) *= factor;
225 template <
typename Into,
typename From>
232 internal::indexCount(into) == internal::indexCount(from)
233 &&
"combined blocks must have the same difference dimension"
236 for (
Index k = 0; k < internal::indexCount(into); ++k)
238 m_matrix.row(internal::indexAt(into, k)) += factor * before.row(internal::indexAt(from, k));
250 [[nodiscard]] Space_
const &
267inline constexpr bool alwaysFalse =
false;
277 template <
typename T>
283assertSameDimensions(
auto const & p,
auto const & q)
286 p.valueDimension() == q.valueDimension() && p.differenceDimension() == q.differenceDimension()
287 &&
"the distributions must have the same dimensions"
296template <
typename Scalar>
305 if (x == Eigen::NumTraits<Scalar>::infinity())
314 Scalar
const delta = x - m_mean;
315 m_mean += delta / Scalar(m_count);
316 m_sumOfSquares += delta * (x - m_mean);
330 return m_infinite ? Eigen::NumTraits<Scalar>::infinity() : m_mean;
335 standardError()
const
337 return m_infinite ? Eigen::NumTraits<Scalar>::infinity()
338 : Eigen::numext::sqrt(m_sumOfSquares / Scalar(m_count - 1) / Scalar(m_count));
343 Eigen::Index m_count = 0;
345 Scalar m_sumOfSquares = 0;
346 bool m_infinite =
false;
350template <
typename Scalar>
354 return Eigen::NumTraits<Scalar>::infinity();
358template <
typename Scalar>
360logEvenMixture(Scalar a, Scalar b)
362 auto sum = LogSumExp<Scalar>{};
365 return sum.value() - Eigen::numext::log(Scalar(2));
369template <
typename Scalar>
371hellingerFromBhattacharyya(Scalar distance)
373 return Eigen::numext::sqrt(Eigen::numext::maxi(-Eigen::numext::expm1(-distance), Scalar(0)));
383template <
typename Scalar,
typename DifferenceDerived,
typename FirstDerived,
typename SecondDerived>
386 Eigen::MatrixBase<DifferenceDerived>
const & difference,
387 Eigen::MatrixBase<FirstDerived>
const & first,
388 Eigen::MatrixBase<SecondDerived>
const & second
391 using Cross = MatrixOf<
393 FirstDerived::ColsAtCompileTime,
394 SecondDerived::ColsAtCompileTime,
395 FirstDerived::MaxColsAtCompileTime,
396 SecondDerived::MaxColsAtCompileTime>;
397 Cross
const cross = multiply(first.transpose(), second);
398 Scalar
const nuclearNorm = Eigen::JacobiSVD<Cross>{cross}.singularValues().sum();
399 Scalar
const squared =
400 difference.squaredNorm() + first.squaredNorm() + second.squaredNorm() - Scalar(2) * nuclearNorm;
401 return Eigen::numext::sqrt(Eigen::numext::maxi(squared, Scalar(0)));
408inline constexpr bool isGaussianDensity =
false;
411template <
typename Space,
typename Scalar>
412inline constexpr bool isGaussianDensity<Gaussian<Space, Scalar>> =
true;
415template <
typename Params>
416inline constexpr bool isGaussianDensity<FormGaussian<Params>> =
true;
421template <
typename P,
typename Q>
422concept GaussianDensitiesOfOneSpace =
423 isGaussianDensity<P> && isGaussianDensity<Q> && std::same_as<typename P::SpaceType, typename Q::SpaceType>
424 && std::same_as<typename P::Scalar, typename Q::Scalar>;
430inline constexpr bool isCovarianceDensity = std::same_as<D, Gaussian<typename D::SpaceType, typename D::Scalar>>;
436inline constexpr int covarianceRootTriangle = D::ParamsType::IsInfoForm ? Eigen::Upper : Eigen::Lower;
445[[nodiscard]]
typename D::MatrixType
446covarianceRootOf(D
const & d)
448 using MatrixType =
typename D::MatrixType;
449 if constexpr (isCovarianceDensity<D>)
451 return MatrixType{d.llt().matrixL()};
453 else if constexpr (D::ParamsType::IsInfoForm)
455 Eigen::Index
const n = d.differenceDimension();
456 return d.colorNormals(MatrixType::Identity(n, n));
468template <
typename D,
typename Derived>
470whitenInPlace(D
const & d, Eigen::MatrixBase<Derived> & matrix)
472 if constexpr (isCovarianceDensity<D>)
474 solveInPlace(d.llt().matrixL(), matrix);
476 else if constexpr (D::ParamsType::IsInfoForm)
478 auto const root = covarianceRootOf(d);
479 solveInPlace(root.template triangularView<Eigen::Upper>(), matrix);
483 solveInPlace(d.root().template triangularView<Eigen::Lower>(), matrix);
491template <
typename P,
typename Q>
492[[nodiscard]]
typename P::MatrixType
493averageFactor(P
const & p, Q
const & q)
495 using Scalar =
typename P::Scalar;
496 using MatrixType =
typename P::MatrixType;
497 constexpr int Rows = MatrixType::RowsAtCompileTime;
498 constexpr int MaxRows = MatrixType::MaxRowsAtCompileTime;
499 using Stack = MatrixOf<
502 Rows == Eigen::Dynamic ? Eigen::Dynamic : 2 * Rows,
504 MaxRows == Eigen::Dynamic ? Eigen::Dynamic : 2 * MaxRows>;
505 Eigen::Index
const n = p.differenceDimension();
506 auto stack = Stack{n, 2 * n};
507 Scalar
const half = Eigen::numext::sqrt(Scalar(0.5));
508 stack.leftCols(n) = half * covarianceRootOf(p);
509 stack.rightCols(n) = half * covarianceRootOf(q);
510 return lowerTriangularRoot(stack);
514template <
typename P,
typename G>
518template <
typename P,
typename Q,
typename G>
519concept MonteCarloComparable =
520 SampleWithLogDensityFrom<P, G> && HasLogDensity<Q> && std::same_as<typename P::SpaceType, typename Q::SpaceType>
521 && std::same_as<typename P::Scalar, typename Q::Scalar>;
532template <
typename Scalar,
typename Policy,
typename P,
typename G,
typename TermOf>
533[[nodiscard]] RunningMean<Scalar>
534meanOverDraws(Policy && policy, P
const & p, Eigen::Index count, G & rng, TermOf
const & termOf)
536 EMRYS_FILTER_ASSERT(count >= 2 &&
"a Monte Carlo estimate needs at least two draws");
537 auto terms = RunningMean<Scalar>{};
538 if constexpr (isSequential<Policy>)
540 for (Eigen::Index j = 0; j < count && !terms.infinite(); ++j)
543 terms.add(termOf(draw.sample, draw.logDensity));
549 auto logP = Eigen::Array<Scalar, Eigen::Dynamic, 1>{count};
550 for (Eigen::Index j = 0; j < count; ++j)
553 samples.col(j) = draw.sample;
554 logP(j) = draw.logDensity;
556 auto values = Eigen::Array<Scalar, Eigen::Dynamic, 1>{count};
557 forEachIndex(std::forward<Policy>(policy), count, [&](Eigen::Index j) {
558 values(j) = termOf(samples.col(j), logP(j));
560 for (Eigen::Index j = 0; j < count && !terms.infinite(); ++j)
562 terms.add(values(j));
571template <
typename Scalar,
typename Policy,
typename P,
typename Q,
typename G>
572[[nodiscard]] RunningMean<Scalar>
573bhattacharyyaCoefficientTerms(Policy && policy, MonteCarlo
const & method, P
const & p, Q
const & q, G & rng)
575 return meanOverDraws<Scalar>(
576 std::forward<Policy>(policy),
580 [&](
auto const & sample, Scalar logP) {
581 return Eigen::numext::exp(Scalar(0.5) * (q.logDensity(sample) - logP));
590template <
typename Scalar,
typename Policy,
typename P,
typename Q,
typename G>
591[[nodiscard]] RunningMean<Scalar>
592mixtureDivergenceTerms(Policy && policy, MonteCarlo
const & method, P
const & p, Q
const & q, G & rng)
594 return meanOverDraws<Scalar>(
595 std::forward<Policy>(policy),
599 [&](
auto const & sample, Scalar logP) {
600 return Scalar(logP - logEvenMixture(logP, Scalar(q.logDensity(sample))));
606template <
typename Space,
typename MomentsType,
typename TruthDerived>
607[[nodiscard]]
typename MomentsType::Scalar
608expectedSquaredErrorOf(Space
const & space, MomentsType
const & moments, Eigen::MatrixBase<TruthDerived>
const & truth)
610 EMRYS_FILTER_ASSERT(truth.size() == moments.valueDimension() &&
"the truth must be a value of the space");
611 typename MomentsType::DifferenceVectorType
const error =
Emrys::minus(space, moments.mean(), truth);
612 return error.squaredNorm() + moments.covarianceLower().trace();
616template <
typename Space,
typename MomentsType,
typename TruthDerived,
typename WeightsDerived>
617[[nodiscard]]
typename MomentsType::Scalar
618expectedSquaredErrorOf(
620 MomentsType
const & moments,
621 Eigen::MatrixBase<TruthDerived>
const & truth,
622 Eigen::MatrixBase<WeightsDerived>
const & weights
625 EMRYS_FILTER_ASSERT(truth.size() == moments.valueDimension() &&
"the truth must be a value of the space");
627 weights.cols() == moments.differenceDimension() &&
"the weights must have a column per difference coordinate"
629 typename MomentsType::DifferenceVectorType
const error =
Emrys::minus(space, moments.mean(), truth);
631 return multiply(weights, error).squaredNorm()
632 + multiply(weights, moments.covarianceLower()).cwiseProduct(weights).sum();
638template <
typename Distribution,
typename Compute>
640fromMoments(Distribution
const & distribution, Compute compute)
642 using Result = std::remove_cvref_t<
decltype(distribution.moments())>;
643 if constexpr (isExpected<Result>)
645 using Value =
decltype(compute(std::declval<typename Result::value_type const &>()));
646 using Expected = std::expected<Value, typename Result::error_type>;
647 Result
const moments = distribution.moments();
650 return Expected{std::unexpect, moments.error()};
652 return Expected{compute(*moments)};
656 return compute(distribution.moments());
675template <
typename P,
typename Q>
676 requires internal::GaussianDensitiesOfOneSpace<P, Q>
677[[nodiscard]]
typename P::Scalar
680 using Scalar =
typename P::Scalar;
681 internal::assertSameDimensions(p, q);
682 typename P::MatrixType whitened = internal::covarianceRootOf(p);
683 internal::whitenInPlace(q, whitened);
684 Scalar
const divergence = Scalar(0.5)
685 * (whitened.squaredNorm() + q.squaredMahalanobisDistance(p.mean())
686 - Scalar(p.differenceDimension()) + q.logDeterminant() - p.logDeterminant());
687 return Eigen::numext::maxi(divergence, Scalar(0));
695template <DiscreteSpace Space,
typename Scalar>
702 Scalar divergence = 0;
705 if (logP(i) == -internal::infinity<Scalar>())
709 if (logQ(i) == -internal::infinity<Scalar>())
711 return internal::infinity<Scalar>();
713 divergence += Eigen::numext::exp(logP(i)) * (logP(i) - logQ(i));
715 return Eigen::numext::maxi(divergence, Scalar(0));
726template <
typename P,
typename Q, std::uniform_random_bit_generator G>
727 requires internal::MonteCarloComparable<P, Q, G>
728[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
740template <ExecutionPolicy Policy,
typename P,
typename Q, std::uniform_random_bit_generator G>
741 requires internal::MonteCarloComparable<P, Q, G>
742[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
745 using Scalar =
typename P::Scalar;
746 auto const terms = internal::meanOverDraws<Scalar>(
747 std::forward<Policy>(policy),
751 [&](
auto const &
sample, Scalar logP) {
752 Scalar const logQ = q.logDensity(sample);
753 return logQ == -internal::infinity<Scalar>() ? internal::infinity<Scalar>() : Scalar(logP - logQ);
756 return {terms.mean(), terms.standardError(), method.
count};
762template <
typename P,
typename Q>
763[[nodiscard]] internal::NoClosedForm
767 internal::alwaysFalse<P>,
768 "kullbackLeibler(p, q) has a closed form only between two Gaussian densities of one space, in any of the four "
769 "forms (call toDensity() on the parameters, and copy() a GaussianMarginal first), or two Categorical "
770 "views of one space; "
771 "estimate it otherwise with kullbackLeibler(MonteCarlo{count}, p, q, rng)"
780template <
typename Scalar =
double>
796template <
typename Scalar,
typename CovarianceDerived,
typename ForEach>
798collapseBounds(Eigen::MatrixBase<CovarianceDerived>
const & covarianceLower, ForEach forEach)
800 using MatrixType =
typename CovarianceDerived::PlainObject;
801 auto const llt = Eigen::LLT<MatrixType>{covarianceLower};
802 EMRYS_FILTER_ASSERT(llt.info() == Eigen::Success &&
"the mixture's covariance must be positive definite");
803 Scalar componentLogDeterminants = 0;
804 Scalar weightEntropy = 0;
805 forEach([&](Scalar
const logWeight,
auto const & gaussian) {
806 if (logWeight > -Eigen::NumTraits<Scalar>::infinity())
808 Scalar
const weight = Eigen::numext::exp(logWeight);
809 componentLogDeterminants += weight * FactorizedGaussian::logDeterminant(gaussian);
810 weightEntropy -= weight * logWeight;
813 Scalar
const logDeterminant = Scalar(2) * llt.matrixLLT().diagonal().array().log().sum();
814 Scalar
const upper = Eigen::numext::maxi(Scalar(0.5) * (logDeterminant - componentLogDeterminants), Scalar(0));
865template <
typename Space,
typename Scalar,
typename Allocator>
869 auto const moments = p.
moments();
870 return internal::collapseBounds<Scalar>(moments.covarianceLower(), [&](
auto const visit) {
871 for (auto const & entry : p)
873 visit(entry.logWeight - p.logTotalWeight(), entry.component);
885template <
typename Space,
typename ModeSpace,
typename Scalar,
typename Allocator>
886[[nodiscard]] CollapseBounds<Scalar>
889 auto const moments = p.
moments();
890 return internal::collapseBounds<Scalar>(moments.covarianceLower(), [&](
auto const visit) {
891 auto const logProbabilities = internal::logProbabilitiesOf(p.modes());
892 for (Eigen::Index m = 0; m < p.size(); ++m)
894 visit(Scalar(logProbabilities(m)), p.conditional(m));
907template <
typename Space,
typename ModeSpace,
typename Scalar,
typename ComponentAllocator,
typename Allocator>
908[[nodiscard]] CollapseBounds<Scalar>
913 auto const moments = p.moments();
914 return internal::collapseBounds<Scalar>(moments.covarianceLower(), [&](
auto const visit) {
915 for (auto const & entry : p)
917 Scalar const logWeight = entry.logWeight - p.logTotalWeight();
918 auto const logProbabilities = internal::logProbabilitiesOf(entry.component.modes());
919 for (Eigen::Index m = 0; m < entry.component.size(); ++m)
921 visit(Scalar(logWeight + logProbabilities(m)), entry.component.conditional(m));
941template <
typename P,
typename Q>
942 requires internal::GaussianDensitiesOfOneSpace<P, Q>
943[[nodiscard]]
typename P::Scalar
946 using Scalar =
typename P::Scalar;
947 using MatrixType =
typename P::MatrixType;
948 internal::assertSameDimensions(p, q);
949 typename P::DifferenceVectorType difference =
Emrys::minus(p.space(), p.mean(), q.mean());
950 Scalar logDeterminant{};
951 if constexpr (internal::isCovarianceDensity<P> && internal::isCovarianceDensity<Q>)
953 MatrixType
const average = Scalar(0.5) * (p.covarianceLower() + q.covarianceLower());
954 auto const llt = Eigen::LLT<MatrixType>{average};
955 EMRYS_FILTER_ASSERT(llt.info() == Eigen::Success &&
"the average covariance must be positive definite");
956 llt.matrixL().solveInPlace(difference);
957 logDeterminant = Scalar(2) * llt.matrixLLT().diagonal().array().log().sum();
961 MatrixType
const average = internal::averageFactor(p, q);
963 (average.diagonal().array() != Scalar(0)).all() &&
"the average covariance must be positive definite"
965 average.template triangularView<Eigen::Lower>().solveInPlace(difference);
966 logDeterminant = Scalar(2) * average.diagonal().array().abs().log().sum();
968 Scalar
const distance = Scalar(0.125) * difference.squaredNorm()
969 + Scalar(0.5) * (logDeterminant - Scalar(0.5) * (p.logDeterminant() + q.logDeterminant()));
970 return Eigen::numext::maxi(distance, Scalar(0));
977template <DiscreteSpace Space,
typename Scalar>
987 return Eigen::numext::maxi(-logCoefficient.value(), Scalar(0));
995template <
typename P,
typename Q, std::uniform_random_bit_generator G>
996 requires internal::MonteCarloComparable<P, Q, G>
997[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
1007template <ExecutionPolicy Policy,
typename P,
typename Q, std::uniform_random_bit_generator G>
1008 requires internal::MonteCarloComparable<P, Q, G>
1009[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
1012 using Scalar =
typename P::Scalar;
1013 auto const terms = internal::bhattacharyyaCoefficientTerms<Scalar>(std::forward<Policy>(policy), method, p, q, rng);
1014 return {-Eigen::numext::log(terms.mean()), terms.standardError() / terms.mean(), method.
count};
1020template <
typename P,
typename Q>
1021[[nodiscard]] internal::NoClosedForm
1025 internal::alwaysFalse<P>,
1026 "bhattacharyya(p, q) has a closed form only between two Gaussian densities of one space, in any of the four "
1027 "forms (call toDensity() on the parameters, and copy() a GaussianMarginal first), or two Categorical "
1028 "views of one space; "
1029 "estimate it otherwise with bhattacharyya(MonteCarlo{count}, p, q, rng)"
1039template <
typename P,
typename Q>
1040 requires internal::GaussianDensitiesOfOneSpace<P, Q>
1041[[nodiscard]]
typename P::Scalar
1044 return internal::hellingerFromBhattacharyya(
bhattacharyya(p, q));
1051template <DiscreteSpace Space,
typename Scalar>
1055 return internal::hellingerFromBhattacharyya(
bhattacharyya(p, q));
1062template <
typename P,
typename Q, std::uniform_random_bit_generator G>
1063 requires internal::MonteCarloComparable<P, Q, G>
1064[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
1073template <ExecutionPolicy Policy,
typename P,
typename Q, std::uniform_random_bit_generator G>
1074 requires internal::MonteCarloComparable<P, Q, G>
1075[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
1078 using Scalar =
typename P::Scalar;
1079 auto const terms = internal::bhattacharyyaCoefficientTerms<Scalar>(std::forward<Policy>(policy), method, p, q, rng);
1080 Scalar
const distance = Eigen::numext::sqrt(Eigen::numext::maxi(Scalar(1) - terms.mean(), Scalar(0)));
1083 Scalar
const standardError = distance > Scalar(0) ? terms.standardError() / (Scalar(2) * distance)
1084 : Eigen::numext::sqrt(terms.standardError());
1085 return {distance, standardError, method.
count};
1091template <
typename P,
typename Q>
1092[[nodiscard]] internal::NoClosedForm
1096 internal::alwaysFalse<P>,
1097 "hellinger(p, q) has a closed form only between two Gaussian densities of one space, in any of the four "
1098 "forms (call toDensity() on the parameters, and copy() a GaussianMarginal first), or two Categorical "
1099 "views of one space; "
1100 "estimate it otherwise with hellinger(MonteCarlo{count}, p, q, rng)"
1111template <DiscreteSpace Space,
typename Scalar>
1115 EMRYS_FILTER_ASSERT(p.
cardinality() == q.
cardinality() &&
"the distributions must have the same values");
1116 Scalar divergence = 0;
1117 for (Eigen::Index i = 0; i < p.
cardinality(); ++i)
1121 Scalar
const logMixture = internal::logEvenMixture(logP, logQ);
1122 if (logP != -internal::infinity<Scalar>())
1124 divergence += Eigen::numext::exp(logP) * (logP - logMixture);
1126 if (logQ != -internal::infinity<Scalar>())
1128 divergence += Eigen::numext::exp(logQ) * (logQ - logMixture);
1131 Scalar
const bound = Eigen::numext::log(Scalar(2));
1132 return Eigen::numext::mini(Eigen::numext::maxi(Scalar(0.5) * divergence, Scalar(0)), bound);
1141template <
typename P,
typename Q, std::uniform_random_bit_generator G>
1142 requires internal::MonteCarloComparable<P, Q, G> && internal::MonteCarloComparable<Q, P, G>
1143[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
1153template <ExecutionPolicy Policy,
typename P,
typename Q, std::uniform_random_bit_generator G>
1154 requires internal::MonteCarloComparable<P, Q, G> && internal::MonteCarloComparable<Q, P, G>
1155[[nodiscard]] MonteCarloEstimate<typename P::Scalar>
1158 using Scalar =
typename P::Scalar;
1159 auto const fromP = internal::mixtureDivergenceTerms<Scalar>(policy, method, p, q, rng);
1160 auto const fromQ = internal::mixtureDivergenceTerms<Scalar>(policy, method, q, p, rng);
1161 Scalar
const standardError =
1163 * Eigen::numext::sqrt(
1164 fromP.standardError() * fromP.standardError() + fromQ.standardError() * fromQ.standardError()
1166 return {Scalar(0.5) * (fromP.mean() + fromQ.mean()), standardError, method.
count};
1172template <
typename P,
typename Q>
1173[[nodiscard]] internal::NoClosedForm
1177 internal::alwaysFalse<P>,
1178 "jensenShannon(p, q) has a closed form only between two Categorical views of one space; between Gaussians "
1179 "or anything else, estimate it with jensenShannon(MonteCarlo{count}, p, q, rng)"
1195template <
typename P,
typename Q>
1196 requires internal::GaussianDensitiesOfOneSpace<P, Q>
1197[[nodiscard]]
typename P::Scalar
1200 using MatrixType =
typename P::MatrixType;
1201 internal::assertSameDimensions(p, q);
1202 typename P::DifferenceVectorType
const difference =
Emrys::minus(p.space(), p.mean(), q.mean());
1203 MatrixType
const first = internal::covarianceRootOf(p);
1204 MatrixType
const second = internal::covarianceRootOf(q);
1205 return internal::buresWasserstein<typename P::Scalar>(difference, first, second);
1215template <
typename P,
typename Q,
typename WeightsDerived>
1216 requires internal::GaussianDensitiesOfOneSpace<P, Q>
1217[[nodiscard]]
typename P::Scalar
1218wasserstein(P
const & p, Q
const & q, Eigen::MatrixBase<WeightsDerived>
const & weights)
1220 using Scalar =
typename P::Scalar;
1221 using DensityType = P;
1223 internal::MatrixOf<Scalar, WeightsDerived::RowsAtCompileTime, 1, WeightsDerived::MaxRowsAtCompileTime, 1>;
1224 using Factor = internal::MatrixOf<
1226 WeightsDerived::RowsAtCompileTime,
1227 DensityType::DifferenceDimensionAtCompileTime,
1228 WeightsDerived::MaxRowsAtCompileTime,
1229 DensityType::MaxDifferenceDimensionAtCompileTime>;
1230 internal::assertSameDimensions(p, q);
1231 EMRYS_FILTER_ASSERT(
1232 weights.cols() == p.differenceDimension() &&
"the weights must have a column per difference coordinate"
1234 typename DensityType::DifferenceVectorType
const difference =
Emrys::minus(p.space(), p.mean(), q.mean());
1235 Measured
const measured = internal::multiply(weights, difference);
1236 Factor
const first = internal::multiply(
1238 internal::asTriangular<internal::covarianceRootTriangle<P>>(internal::covarianceRootOf(p))
1240 Factor
const second = internal::multiply(
1242 internal::asTriangular<internal::covarianceRootTriangle<Q>>(internal::covarianceRootOf(q))
1244 return internal::buresWasserstein<Scalar>(measured, first, second);
1250template <
typename P,
typename Q>
1251 requires internal::GaussianDensitiesOfOneSpace<P, Q>
1252[[nodiscard]]
typename P::Scalar
1261template <
typename P,
typename Q>
1262[[nodiscard]] internal::NoClosedForm
1266 internal::alwaysFalse<P>,
1267 "wasserstein(p, q) has a closed form only between two Gaussian densities of one space, in any of the "
1268 "four forms (call toDensity() on the parameters, and copy() a GaussianMarginal first); between "
1269 "mixtures or particle sets it needs an optimal-transport solver, "
1270 "which the library does not have. expectedSquaredError(p, truth) gives the distance to a point"
1286template <
internal::HasMoments Distribution,
typename TruthDerived>
1290 return internal::fromMoments(distribution, [&](
auto const & moments) {
1291 return internal::expectedSquaredErrorOf(distribution.space(), moments, truth);
1300template <
internal::HasMoments Distribution,
typename TruthDerived,
typename WeightsDerived>
1303 Distribution
const & distribution,
1304 Eigen::MatrixBase<TruthDerived>
const & truth,
1305 Eigen::MatrixBase<WeightsDerived>
const & weights
1308 return internal::fromMoments(distribution, [&](
auto const & moments) {
1309 return internal::expectedSquaredErrorOf(distribution.space(), moments, truth, weights);
1316template <
internal::HasMoments Distribution,
typename TruthDerived>
1319 Distribution
const & distribution,
1320 Eigen::MatrixBase<TruthDerived>
const & truth,
The distribution over a discrete space (decisions.md [discrete-spaces], [params-and-density]): Catego...
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
The hybrid state of a discrete mode and a component per mode (decisions.md [imm], [component-first]):...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
The mixture: the parameters MixtureParams of weighted components of one type (decisions....
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'...
Product: the product of independent densities, each on its own block of a space, built with on<Path>(...
Drawing samples: SamplesWithLogDensity and SampleWithLogDensity, the Gaussian draws drawSamples(),...
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...
The square-root form of the Gaussian (decisions.md [gaussian-forms]): its parameters SqrtGaussianPara...
Tag paths and the index utilities (decisions.md [tag-addressing], [index-names]): TagPath and TagPath...
Triangularization: the triangular factor of a stacked matrix by Householder reflections,...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
The probabilities of a CategoricalParams whose weights define a distribution: a read-only view.
Definition Categorical.h:601
auto logProbabilities() const
Definition Categorical.h:632
Eigen::Index cardinality() const
Definition Categorical.h:623
Scalar logProbability(Eigen::Index value) const
Definition Categorical.h:648
A weighting of a space's differences, built by tag path: the ground metric of wasserstein().
Definition Divergence.h:191
void combine(Scalar factor)
Adds factor times the measured coordinates of the block named by From to those of Into,...
Definition Divergence.h:227
void scale(Scalar factor)
Multiplies the measured coordinates of the block named by Path (a tag or a TagPath) by factor.
Definition Divergence.h:211
typename Moments< Space_, Scalar_ >::MatrixType MatrixType
's type, square over differences
Definition Divergence.h:197
Space_ SpaceType
the space whose differences are measured
Definition Divergence.h:194
Eigen::Index Index
Eigen's index type.
Definition Divergence.h:196
MatrixType const & matrix() const
Definition Divergence.h:244
Scalar_ Scalar
the scalar type
Definition Divergence.h:195
GroundMetric(Space_ space)
The identity on space: each difference measured by its Euclidean norm.
Definition Divergence.h:200
Space_ const & space() const
Definition Divergence.h:251
MomentsType moments() const
Definition Hybrid.h:632
Streaming log-sum-exp: accumulates without overflow.
Definition Weights.h:46
The density of a Mixture whose components have been factorized: a read-only view.
Definition Mixture.h:2090
MomentsType moments() const
Definition Mixture.h:2232
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
a matrix over differences, such as the covariance
Definition Moments.h:76
#define EMRYS_FILTER_NO_UNIQUE_ADDRESS
Assertion used for programmer errors (misuse of the API).
Definition Macros.h:38
Mixture< Gaussian< Space_, Scalar_ >, Allocator_ > GaussianMixture
The Mixture of a GaussianMixtureParams, the spelling for the common case (decisions....
Definition Mixture.h:2330
SampleWithLogDensity< Space, Scalar > drawSampleWithLogDensity(Gamma< Space, Scalar > const &gamma, G &rng)
Definition Gamma.h:546
Eigen::Matrix< Scalar, valueDimensionAtCompileTime< Space >, Eigen::Dynamic,(maxValueDimensionAtCompileTime< Space >==1 ? Eigen::RowMajor :Eigen::ColMajor), maxValueDimensionAtCompileTime< Space >, Eigen::Dynamic > SampleSet
A matrix whose columns are samples in Space (see Spaces), with the space's size and storage bound.
Definition SampleSet.h:33
Hybrid< Gaussian< Space_, Scalar_ >, ModeSpace_, Allocator_ > HybridGaussian
The Hybrid of Gaussians in covariance form, the spelling for the common case (decisions....
Definition Hybrid.h:742
auto expectedSquaredError(Distribution const &distribution, Eigen::MatrixBase< TruthDerived > const &truth)
Definition Divergence.h:1288
Scalar jensenShannon(Categorical< Space, Scalar > const &p, Categorical< Space, Scalar > const &q)
Definition Divergence.h:1113
P::Scalar bhattacharyya(P const &p, Q const &q)
Definition Divergence.h:944
P::Scalar kullbackLeibler(P const &p, Q const &q)
Definition Divergence.h:678
P::Scalar hellinger(P const &p, Q const &q)
Definition Divergence.h:1042
P::Scalar wasserstein(P const &p, Q const &q)
Definition Divergence.h:1198
CollapseBounds< Scalar > kullbackLeiblerCollapseBounds(GaussianMixture< Space, Scalar, Allocator > const &p)
Definition Divergence.h:867
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
Sample< std::remove_cvref_t< G > > sample(G &&function)
Definition Policies.h:665
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
The bracket on the divergence of a distribution from its collapse, which kullbackLeiblerCollapseBoun...
Definition Divergence.h:782
Scalar upper
: the divergence is at most this
Definition Divergence.h:784
Scalar lower
: the divergence is at least this
Definition Divergence.h:783
An estimate from MonteCarlo draws, with its standard error.
Definition Divergence.h:117
Scalar standardError
its standard error, by the delta method for a function of a mean
Definition Divergence.h:119
Scalar value
the estimate
Definition Divergence.h:118
Eigen::Index count
draws taken from each distribution sampled
Definition Divergence.h:120
Method that estimates a divergence from count draws.
Definition Divergence.h:108
Eigen::Index count
draws from each distribution sampled, at least two
Definition Divergence.h:109