66concept CovarianceForm = std::is_same_v<C, SharedCovariance> || std::is_same_v<C, PerPointCovariance>;
72inline constexpr bool isGaussianHybrid =
false;
75template <
typename Component_,
typename ModeSpace_,
typename Allocator_>
76inline constexpr bool isGaussianHybrid<HybridParams<Component_, ModeSpace_, Allocator_>> = std::is_same_v<
81template <
typename Space,
typename P>
85 using type =
decltype(complementSubspace<P>(std::declval<Space const &>()));
89template <
typename Space,
typename... Tags>
90struct ComplementOf<Space, TagPath<Tags...>>
93 using type =
decltype(complementSubspace<Tags...>(std::declval<Space const &>()));
97template <
typename P,
typename Space>
99complementOf(Space
const & space)
101 return withPathTags<P>([&]<
typename... Tags>() {
102 return complementSubspace<Tags...>(space);
109template <
typename Space_,
typename Path_>
115 using PathType = Path_;
118 using PinnedSpace = PathTag<Path_>;
121 using RestSpace =
typename ComplementOf<Space_, Path_>::type;
127 using RestDifferenceIndices = IndexVector<
132 explicit BlockSplit(Space_
const & space)
133 : m_pinned{pathBlock<Path_>(space)}
134 , m_rest{complementOf<Path_>(space)}
135 , m_rows{blockRows<Path_>(space)}
136 , m_differenceRows{differenceBlockRows<Path_>(space)}
138 , m_restIndices{outside<RestIndices>(m_rows, m_size)}
139 , m_restDifferenceIndices{outside<RestDifferenceIndices>(m_differenceRows,
differenceDimension(space))}
143 [[nodiscard]] PinnedSpace
const &
150 [[nodiscard]] RestSpace
const &
157 [[nodiscard]] BlockRows
164 [[nodiscard]] RestIndices
const &
167 return m_restIndices;
171 [[nodiscard]] BlockRows
172 pinnedDifferenceRows()
const
174 return m_differenceRows;
178 [[nodiscard]] RestDifferenceIndices
const &
179 restDifferenceIndices()
const
181 return m_restDifferenceIndices;
187 template <
typename X,
typename P>
189 assemble(X
const & rest, P
const & pinned)
const
193 rest.size() == m_restIndices.size() && pinned.size() == m_rows.size &&
"the blocks do not fit the space"
204 full(m_restIndices) = rest;
205 middleRowsOf<valueDimensionAtCompileTime<PinnedSpace>>(full, m_rows.start, m_rows.size) =
206 pinned.template cast<T>();
213 template <
typename Indices>
214 [[nodiscard]]
static Indices
215 outside(BlockRows rows, Eigen::Index n)
217 auto result = Indices{};
218 result.resize(n - rows.size);
220 for (Eigen::Index i = 0; i < n; ++i)
222 if (i < rows.start || i >= rows.start + rows.size)
230 PinnedSpace m_pinned;
233 BlockRows m_differenceRows;
235 RestIndices m_restIndices;
236 RestDifferenceIndices m_restDifferenceIndices;
249 typename Element = GaussianParams<Space, Scalar>>
253template <
typename Space,
typename Scalar_,
typename Allocator>
254class Conditionals<Space, SharedCovariance, Scalar_, Allocator, GaussianParams<Space, Scalar_>>
259 using Scalar = Scalar_;
262 using ConditionalType = GaussianParams<Space, Scalar>;
265 using MatrixType =
typename ConditionalType::MatrixType;
270 static constexpr bool Shared =
true;
273 Conditionals(ConditionalType
const & conditional, Eigen::Index count, Allocator
const & allocator = {})
274 : m_space{conditional.space()}
277 , m_means(ScalarAllocator(allocator))
278 , m_scratch(ScalarAllocator(allocator))
279 , m_cov{conditional.covarianceLower()}
281 m_means.resize(std::size_t(m_rows * m_count));
282 means().colwise() = conditional.mean();
286 Conditionals(Conditionals
const & other, Allocator
const & allocator)
287 : m_space{other.m_space}
288 , m_rows{other.m_rows}
289 , m_count{other.m_count}
290 , m_means(other.m_means, ScalarAllocator(allocator))
291 , m_scratch(ScalarAllocator(allocator))
296 Conditionals(Conditionals
const & other)
297 : Conditionals{other, other.m_means.get_allocator()}
300 Conditionals(Conditionals &&) noexcept = default;
304 operator=(Conditionals const & other)
310 m_space = other.m_space;
311 m_rows = other.m_rows;
312 m_count = other.m_count;
313 m_means.assign(other.m_means.begin(), other.m_means.end());
320 operator=(Conditionals &&) noexcept = default;
322 ~Conditionals() = default;
329 resizeLike(Conditionals const & other)
331 m_space = other.m_space;
332 m_rows = other.m_rows;
333 m_count = other.m_count;
334 m_means.resize(std::size_t(m_rows * m_count));
339 swap(Conditionals & other)
noexcept
341 std::swap(m_space, other.m_space);
342 std::swap(m_rows, other.m_rows);
343 std::swap(m_count, other.m_count);
344 m_means.swap(other.m_means);
345 m_scratch.swap(other.m_scratch);
346 std::swap(m_cov, other.m_cov);
350 [[nodiscard]] Eigen::Index
357 [[nodiscard]] Space
const &
365 mean(Eigen::Index i)
const
367 return means().col(i);
371 [[nodiscard]] MatrixType
const &
372 covarianceLower(Eigen::Index )
const
378 [[nodiscard]] ConditionalType
379 conditional(Eigen::Index i)
const
381 return ConditionalType{m_space, mean(i), m_cov};
389 [[nodiscard]] std::expected<void, NumericError>
392 if constexpr (MatrixType::RowsAtCompileTime == 0)
398 ConditionalType
const first = conditional(0);
399 return first.factorize();
407 return {m_means.data(), m_rows, m_count};
414 return {m_means.data(), m_rows, m_count};
418 [[nodiscard]] MatrixType
const &
419 sharedCovariance()
const
425 [[nodiscard]] MatrixType &
433 reserve(Eigen::Index count)
435 m_means.reserve(std::size_t(m_rows * count));
436 m_scratch.reserve(std::size_t(m_rows * count));
441 select(std::span<Eigen::Index const> from)
447 template <ExecutionPolicy P>
449 select(P && policy, std::span<Eigen::Index const> from)
451 Eigen::Index
const count = Eigen::Index(from.size());
452 m_scratch.resize(std::size_t(m_rows * count));
454 m_means.swap(m_scratch);
460 keep(std::span<Eigen::Index const> kept)
463 for (std::size_t k = 0; k < kept.size(); ++k)
465 if (kept[k] != Eigen::Index(k))
467 all.col(Eigen::Index(k)) = all.col(kept[k]);
470 m_count = Eigen::Index(kept.size());
471 m_means.resize(std::size_t(m_rows * m_count));
477 using ScalarAllocator = ReBound<Allocator, Scalar>;
481 Eigen::Index m_count;
482 Buffer<Scalar, Allocator> m_means;
483 Buffer<Scalar, Allocator> m_scratch;
488template <
typename Space,
typename Scalar_,
typename Allocator,
typename Element>
489class Conditionals<Space, PerPointCovariance, Scalar_, Allocator, Element>
492 static constexpr bool IsGaussian = std::is_same_v<Element, GaussianParams<Space, Scalar_>>;
497 using Scalar = Scalar_;
500 using ConditionalType = Element;
505 static constexpr bool Shared =
false;
508 Conditionals(ConditionalType
const & conditional, Eigen::Index count, Allocator
const & allocator = {})
509 : m_conditionals(std::size_t(count), conditional, GaussianAllocator(allocator))
510 , m_scratch(GaussianAllocator(allocator))
514 Conditionals(Conditionals
const & other, Allocator
const & allocator)
515 : m_conditionals(other.m_conditionals, GaussianAllocator(allocator))
516 , m_scratch(GaussianAllocator(allocator))
520 Conditionals(Conditionals
const & other)
521 : Conditionals{other, other.m_conditionals.get_allocator()}
524 Conditionals(Conditionals &&) noexcept = default;
528 operator=(Conditionals const & other)
532 m_conditionals.assign(other.m_conditionals.begin(), other.m_conditionals.end());
539 operator=(Conditionals &&) noexcept = default;
541 ~Conditionals() = default;
547 resizeLike(Conditionals const & other)
550 m_conditionals.resize(other.m_conditionals.size());
555 swap(Conditionals & other)
noexcept
557 m_conditionals.swap(other.m_conditionals);
558 m_scratch.swap(other.m_scratch);
562 [[nodiscard]] Eigen::Index
565 return Eigen::Index(m_conditionals.size());
569 [[nodiscard]] Space
const &
572 return m_conditionals.front().space();
576 [[nodiscard]]
decltype(
auto)
577 mean(Eigen::Index i)
const
580 return m_conditionals[std::size_t(i)].mean();
584 [[nodiscard]] MatrixType
const &
585 covarianceLower(Eigen::Index i)
const
588 return m_conditionals[std::size_t(i)].covarianceLower();
592 [[nodiscard]] ConditionalType
const &
593 conditional(Eigen::Index i)
const
595 return m_conditionals[std::size_t(i)];
599 [[nodiscard]] ConditionalType &
600 conditional(Eigen::Index i)
602 return m_conditionals[std::size_t(i)];
610 [[nodiscard]] std::expected<void, NumericError>
613 if constexpr (MatrixType::RowsAtCompileTime != 0)
615 for (
auto const & conditional : m_conditionals)
617 if (
auto result = conditional.factorize(); !result)
619 return std::unexpected{result.error()};
628 reserve(Eigen::Index count)
630 m_conditionals.reserve(std::size_t(count));
631 m_scratch.reserve(std::size_t(count));
636 select(std::span<Eigen::Index const> from)
644 template <ExecutionPolicy P>
646 select(P && policy, std::span<Eigen::Index const> from)
648 if (from.size() < m_scratch.size())
650 m_scratch.erase(m_scratch.begin() + std::ptrdiff_t(from.size()), m_scratch.end());
652 while (m_scratch.size() < from.size())
654 m_scratch.push_back(m_conditionals.front());
656 if constexpr (isSequential<P>)
658 for (std::size_t j = 0; j < from.size(); ++j)
660 m_scratch[j] = m_conditionals[std::size_t(from[j])];
665 forEachIndex(std::forward<P>(policy), Eigen::Index(from.size()), [&](Eigen::Index j) {
666 m_scratch[std::size_t(j)] = m_conditionals[std::size_t(from[std::size_t(j)])];
669 m_conditionals.swap(m_scratch);
674 keep(std::span<Eigen::Index const> kept)
676 for (std::size_t k = 0; k < kept.size(); ++k)
678 if (kept[k] != Eigen::Index(k))
680 m_conditionals[k] = m_conditionals[std::size_t(kept[k])];
683 m_conditionals.erase(m_conditionals.begin() + std::ptrdiff_t(kept.size()), m_conditionals.end());
689 using GaussianAllocator = ReBound<Allocator, ConditionalType>;
691 Buffer<ConditionalType, Allocator> m_conditionals;
692 Buffer<ConditionalType, Allocator> m_scratch;
706template <
typename Po
ints,
typename F>
708withPoint(Points
const & points, Eigen::Index i, F && f)
710 using Scalar =
typename Points::Scalar;
712 PinnedVector center = points.center(i);
713 if constexpr (Points::HasKernels)
715 if (
auto const * kernel = points.kernel(i))
718 GaussianParams<typename Points::PinnedSpace, Scalar>{points.pinnedSpace(), std::move(center), *kernel}
733template <
typename Po
ints,
typename Store,
typename WeightsDerived>
734[[nodiscard]] GaussianParams<typename Points::SpaceType, typename Points::Scalar>
736 Points
const & points,
737 Store
const & conditionals,
738 Eigen::ArrayBase<WeightsDerived>
const & logWeights
741 using Scalar =
typename Points::Scalar;
742 using SpaceType =
typename Points::SpaceType;
743 using MomentsType = Moments<SpaceType, Scalar>;
745 logWeights.size() == points.size() && conditionals.size() == points.size()
746 &&
"give one weight and one Gaussian per point"
748 BlockRows
const rows = points.pinnedRows();
749 auto const & rest = points.restIndices();
750 BlockRows
const differenceRows = points.pinnedDifferenceRows();
751 auto const & restDifferences = points.restDifferenceIndices();
752 auto const logTotal = logTotalWeight<Scalar>(logWeights);
753 EMRYS_FILTER_ASSERT(logTotal &&
"the weights must be finite and not all zero");
757 auto const meanAt = [&](Eigen::Index i) {
758 auto mean =
typename MomentsType::VectorType{};
760 middleRowsOf<PinnedValues>(mean, rows.start, rows.size) = points.center(i);
761 mean(rest) = conditionals.mean(i);
764 MomentsType moments = pointMomentsIn<MomentsType>(
768 [&](Eigen::Index i) {
769 return logWeights.derived()(i);
773 auto & cov = moments.covarianceLower();
774 auto pinned = blockOf<PinnedDifferences, PinnedDifferences>(
776 differenceRows.start,
777 differenceRows.start,
781 for (Eigen::Index i = 0; i < points.size(); ++i)
783 Scalar
const logWeight = logWeights.derived()(i);
784 if (logWeight == -Eigen::NumTraits<Scalar>::infinity())
788 Scalar
const weight = Eigen::numext::exp(logWeight - *logTotal);
789 if constexpr (Points::HasKernels)
791 if (
auto const * kernel = points.kernel(i))
793 pinned += weight * *kernel;
796 if constexpr (!Store::Shared)
798 cov(restDifferences, restDifferences) += weight * conditionals.covarianceLower(i);
801 if constexpr (Store::Shared)
803 cov(restDifferences, restDifferences) += conditionals.sharedCovariance();
806 return GaussianParams<SpaceType, Scalar>{points.space(), std::move(moments)};
813template <
typename Po
ints,
typename Store,
typename WeightsDerived,
typename Allocator>
814[[nodiscard]] GaussianParams<typename Points::SpaceType, typename Points::Scalar>
816 Points
const & points,
817 Store
const & hybrids,
818 Eigen::ArrayBase<WeightsDerived>
const & logWeights,
819 Allocator
const & allocator
822 using Scalar =
typename Points::Scalar;
823 using Traits = ComponentTraits<typename Store::ConditionalType>;
824 using Gaussians = Conditionals<typename Points::RestSpace, PerPointCovariance, Scalar, Allocator>;
825 auto gaussians = Gaussians{Traits::collapseModes(hybrids.conditional(0)), hybrids.size(), allocator};
826 for (Eigen::Index i = 1; i < hybrids.size(); ++i)
828 gaussians.conditional(i) = Traits::collapseModes(hybrids.conditional(i));
830 return collapseGivenPoints(points, gaussians, logWeights);
838template <
typename Store,
typename WeightsDerived>
840summedModes(Store
const & hybrids, Eigen::ArrayBase<WeightsDerived>
const & logWeights)
842 using ModesType =
typename Store::ConditionalType::ModesType;
843 using Scalar =
typename ModesType::Scalar;
844 auto const & modeSpace = hybrids.conditional(0).modeSpace();
845 Eigen::Index
const count = modeSpace.cardinality();
846 auto logP =
typename ModesType::ArrayType{};
848 for (Eigen::Index m = 0; m < count; ++m)
850 auto total = LogSumExp<Scalar>{};
851 for (Eigen::Index i = 0; i < hybrids.size(); ++i)
853 auto const & modes = hybrids.conditional(i).modes();
855 total.add(Scalar(logWeights.derived()(i)) + modes.logWeight(m) - modes.logTotalWeight().value());
857 logP(m) = total.value();
859 auto modes = ModesType{modeSpace, logP};
860 [[maybe_unused]]
auto const normalized = modes.normalize();
861 EMRYS_FILTER_ASSERT(normalized &&
"the points' weights must define a distribution");
870template <
typename Factor,
typename Gain,
typename Cov>
871struct PinnedConditioning
887template <
typename Split,
typename CovDerived>
889pinnedConditioning(Split
const & split, Eigen::MatrixBase<CovDerived>
const & cov)
891 using Scalar =
typename CovDerived::Scalar;
892 using PinnedMoments = Moments<typename Split::PinnedSpace, Scalar>;
893 using RestMoments = Moments<typename Split::RestSpace, Scalar>;
894 using PinnedMatrix =
typename PinnedMoments::MatrixType;
895 using RestMatrix =
typename RestMoments::MatrixType;
897 using PinnedRestMatrix = MatrixOf<
899 PinnedMoments::DifferenceDimensionAtCompileTime,
900 RestMoments::DifferenceDimensionAtCompileTime,
901 PinnedMoments::MaxDifferenceDimensionAtCompileTime,
902 RestMoments::MaxDifferenceDimensionAtCompileTime>;
903 using GainMatrix = MatrixOf<
905 RestMoments::DifferenceDimensionAtCompileTime,
906 PinnedMoments::DifferenceDimensionAtCompileTime,
907 RestMoments::MaxDifferenceDimensionAtCompileTime,
908 PinnedMoments::MaxDifferenceDimensionAtCompileTime>;
909 BlockRows
const rows = split.pinnedDifferenceRows();
910 auto const & rest = split.restDifferenceIndices();
911 constexpr int PinnedRows = PinnedMoments::DifferenceDimensionAtCompileTime;
912 PinnedMatrix
const pinned = blockOf<PinnedRows, PinnedRows>(cov, rows.start, rows.start, rows.size, rows.size);
913 PinnedRestMatrix
const pinnedRest = cov(Eigen::seqN(rows.start, rows.size), rest);
914 auto const ldlt = Eigen::LDLT<PinnedMatrix>{pinned};
915 auto gain = transposedSolve<GainMatrix>(ldlt, pinnedRest);
916 RestMatrix restCov = cov(rest, rest);
917 restCov -= multiply(gain, pinnedRest);
918 restCov = (Scalar(0.5) * (restCov + restCov.transpose())).eval();
919 return PinnedConditioning{defaultRoot(pinned), std::move(gain), std::move(restCov)};
932 typename Conditioning,
933 typename MeanDerived,
940 Conditioning
const & conditioning,
941 Eigen::MatrixBase<MeanDerived>
const & mean,
942 Eigen::MatrixBase<ZDerived>
const & z,
947 BlockRows
const rows = split.pinnedRows();
948 auto const offset = multiply(conditioning.factor, z).eval();
950 pinned =
Emrys::plus(split.pinnedSpace(), middleRowsOf<PinnedValues>(mean, rows.start, rows.size), offset);
951 restMean =
Emrys::plus(split.restSpace(), mean(split.restIndices()), multiply(conditioning.gain, offset));
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
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.
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'...
Params and Density: the traits that pair a distribution's parameters with its density,...
SampleSet: a matrix whose columns are samples in a space, which the samplers return and the unscented...
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...
Weighted statistics over points, all in internal, which the containers of weighted elements (Gaussian...
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...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
A Gaussian distribution: a mean and covariance in a space.
Definition Gaussian.h:259
DifferenceMatrix< Space_, Space_, Scalar > MatrixType
a matrix over differences, such as the covariance
Definition Moments.h:76
ValueVector< Space_, Scalar > VectorType
a value, such as the mean
Definition Moments.h:74
std::allocator< std::byte > DefaultAllocator
The allocator a type with a run-time count uses unless given one.
Definition Allocation.h:47
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
Eigen::Map< SampleSet< Space, Scalar > > SampleSetMap
A writable view of samples in Space, one per column, over memory it does not own, laid out as a Sampl...
Definition SampleSet.h:50
typename std::remove_cvref_t< X >::Scalar ScalarOf
The scalar type of the Eigen object or distribution X, for writing functions generic in it.
Definition Moments.h:26
Eigen::Map< SampleSet< Space, Scalar > const > ConstSampleSetMap
A read-only view of samples in Space, one per column, over memory it does not own,...
Definition SampleSet.h:60
typename ParamsOf< D >::type Params
The parameters of the density D: GaussianParams for a Gaussian, and D itself for a distribution whose...
Definition Params.h:42
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
constexpr Select select()
Definition Reduction.h:334
constexpr Keep keep()
Definition Reduction.h:371
Eigen::Index valueDimension(S const &space)
Definition Space.h:272
auto plus(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Space.h:675
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
A covariance per point.
Definition Conditionals.h:59
One covariance for every point.
Definition Conditionals.h:49