EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
KernelDensity.h
Go to the documentation of this file.
1#pragma once
2
8
9#include <cstddef>
10#include <expected>
11#include <random>
12#include <span>
13#include <utility>
14
31
32namespace Emrys
33{
34
53template <Space Space_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
54class KernelDensity
55{
56public:
57
58 using SpaceType = Space_;
60 using allocator_type = Allocator_;
61
64
67
70
73
75 using KernelType =
77
79 using Scalar = typename ParamsType::Scalar;
80 using Index = typename ParamsType::Index;
85
87 [[nodiscard]] Index
88 size() const
89 {
90 return particles().size();
91 }
92
94 [[nodiscard]] Index
96 {
97 return particles().valueDimension();
98 }
99
101 [[nodiscard]] Index
103 {
105 }
106
108 [[nodiscard]] Space_ const &
109 space() const
110 {
111 return particles().space();
112 }
113
115 [[nodiscard]] ParamsType const &
116 particles() const
117 {
118 return m_particles.get();
119 }
120
122 [[nodiscard]] Scalar
124 {
125 return m_logTotalWeight;
126 }
127
129 [[nodiscard]] MatrixType const &
130 bandwidth() const
131 {
132 return m_kernel.covarianceLower();
133 }
134
136 [[nodiscard]] typename KernelType::DensityType
137 kernel() const
138 {
139 auto const density = m_kernel.toDensity();
140 return *density;
141 }
142
146 [[nodiscard]] MomentsType
147 moments() const
148 {
150 }
151
155 template <ExecutionPolicy P>
156 [[nodiscard]] MomentsType
157 moments(P && policy) const
158 {
159 MomentsType result = internal::weightedMomentsIn<MomentsType>(
160 std::forward<P>(policy),
161 space(),
162 particles().states(),
163 particles().logWeights(),
164 m_logTotalWeight
165 );
166 result.covarianceLower() += m_kernel.covarianceLower().template selfadjointView<Eigen::Lower>().toDenseMatrix();
167 return result;
168 }
169
178 template <typename Derived>
179 [[nodiscard]] Scalar
180 logDensity(Eigen::MatrixBase<Derived> const & x) const
181 {
183 }
184
188 template <ExecutionPolicy P, typename Derived>
189 [[nodiscard]] Scalar
190 logDensity(P && policy, Eigen::MatrixBase<Derived> const & x) const
191 {
192 static_assert(Derived::IsVectorAtCompileTime, "logDensity() expects a vector");
193 EMRYS_FILTER_ASSERT(x.size() == valueDimension());
194 auto const & llt = internal::FactorizedGaussian::llt(m_kernel);
195 auto const & set = particles();
196 LogSumExp<Scalar> const sum = internal::reduceRanges(
197 std::forward<P>(policy),
198 size(),
199 [&](Index begin, Index length) {
200 auto range = LogSumExp<Scalar>{};
201 for (Index i = begin; i < begin + length; ++i)
202 {
203 DifferenceVectorType residual = Emrys::minus(set.space(), set.state(i), x);
204 llt.matrixL().solveInPlace(residual);
205 range.add(set.logWeight(i) - Scalar(0.5) * residual.squaredNorm());
206 }
207 return range;
208 },
209 [](LogSumExp<Scalar> a, LogSumExp<Scalar> const & b) {
210 a.add(b);
211 return a;
212 }
213 );
214 return sum.value() - m_logTotalWeight + internal::FactorizedGaussian::logPeak(m_kernel);
215 }
216
223 template <typename Derived>
224 [[nodiscard]] Scalar
225 density(Eigen::MatrixBase<Derived> const & x) const
226 {
227 return Eigen::numext::exp(logDensity(x));
228 }
229
230private:
231
232 friend ParamsType;
233
238 template <MatrixExpression Bandwidth>
239 [[nodiscard]] static std::expected<KernelDensity, GaussianError>
240 create(ParamsType const & particles, Scalar logTotalWeight, Bandwidth && bandwidth)
241 {
242 auto kernel =
243 KernelType{DifferenceVectorType::Zero(particles.differenceDimension()), std::forward<Bandwidth>(bandwidth)};
244 if (auto result = kernel.factorize(); !result)
245 {
246 return std::unexpected{result.error()};
247 }
248 return KernelDensity{particles, logTotalWeight, std::move(kernel)};
249 }
250
255 : m_particles{particles}
256 , m_logTotalWeight{logTotalWeight}
257 , m_kernel{std::move(kernel)}
258 {}
259
260 internal::ValuesCheck<ParamsType> m_particles;
261 Scalar m_logTotalWeight;
262 KernelType m_kernel;
263};
264
277template <
278 typename Scalar,
279 typename Space,
280 typename Allocator,
281 std::uniform_random_bit_generator G,
282 typename R = resamplers::Multinomial>
284[[nodiscard]] SampleSet<Space, Scalar>
285drawSamples(KernelDensity<Space, Scalar, Allocator> const & kde, Eigen::Index n, G & rng, R const & resampler = {})
286{
287 EMRYS_FILTER_ASSERT(n >= 0);
288 using SampleSetType = SampleSet<Space, Scalar>;
289 auto const & particles = kde.particles();
290 auto weights = internal::bufferOf<Scalar>(particles.get_allocator());
291 weights.resize(std::size_t(particles.size()));
292 for (Eigen::Index i = 0; i < particles.size(); ++i)
293 {
294 weights[std::size_t(i)] = Eigen::numext::exp(particles.logWeight(i) - kde.logTotalWeight());
295 }
296 auto ancestors = internal::bufferOf<Eigen::Index>(particles.get_allocator());
297 ancestors.resize(std::size_t(n));
298 resampler(std::span<Scalar const>{weights}, std::span<Eigen::Index>{ancestors}, rng);
299
300 using Normals = SampleSet<DifferenceSpace<Space>, Scalar>;
301 Normals const offsets = internal::multiply(
302 internal::asTriangular<Eigen::Lower>(kde.kernel().llt().matrixLLT()),
303 internal::standardNormal<Normals>(kde.differenceDimension(), n, rng)
304 );
305 auto samples = SampleSetType{kde.valueDimension(), n};
306 for (Eigen::Index j = 0; j < n; ++j)
307 {
308 samples.col(j) = Emrys::plus(kde.space(), particles.state(ancestors[std::size_t(j)]), offsets.col(j));
309 }
310 return samples;
311}
312
319template <typename Scalar, typename Space, typename Allocator, std::uniform_random_bit_generator G>
320[[nodiscard]] typename Moments<Space, Scalar>::VectorType
322{
323 using Difference = DifferenceVector<Space, Scalar>;
324 auto const & particles = kde.particles();
325 Eigen::Index const picked = internal::drawIndex<Scalar>(
326 particles.size(),
327 [&](Eigen::Index k) {
328 return Eigen::numext::exp(Scalar(particles.logWeight(k) - kde.logTotalWeight()));
329 },
330 rng
331 );
332 Difference const z = internal::standardNormalVector<Difference>(kde.differenceDimension(), rng);
334 kde.space(),
335 particles.state(picked),
336 Difference{internal::multiply(internal::asTriangular<Eigen::Lower>(kde.kernel().llt().matrixLLT()), z)}
337 )};
338}
339
349template <
350 typename Scalar,
351 typename Space,
352 typename Allocator,
353 std::uniform_random_bit_generator G,
354 typename R = resamplers::Multinomial>
355 requires Resampler<R, Scalar, G>
356[[nodiscard]] SamplesWithLogDensity<Space, Scalar>
359 Eigen::Index n,
360 G & rng,
361 R const & resampler = {}
362)
363{
364 return drawSamplesWithLogDensity(execution::sequential, kde, n, rng, resampler);
365}
366
372template <
374 typename Scalar,
375 typename Space,
376 typename Allocator,
377 std::uniform_random_bit_generator G,
378 typename R = resamplers::Multinomial>
382 P && policy,
384 Eigen::Index n,
385 G & rng,
386 R const & resampler = {}
387)
388{
389 auto result = SamplesWithLogDensity<Space, Scalar>{drawSamples(kde, n, rng, resampler), {}};
390 result.logDensity.resize(n);
391 internal::forEachIndex(std::forward<P>(policy), n, [&](Eigen::Index j) {
392 result.logDensity(j) = kde.logDensity(result.samples.col(j));
393 });
394 return result;
395}
396
402template <typename Scalar, typename Space, typename Allocator, std::uniform_random_bit_generator G>
405{
406 auto result = SampleWithLogDensity<Space, Scalar>{drawSample(kde, rng), 0};
407 result.logDensity = kde.logDensity(result.sample);
408 return result;
409}
410
411} // namespace Emrys
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
How a function that stores a matrix argument takes it (decisions.md [forwarded-matrices]): the concep...
The Gaussian: its parameters GaussianParams, the Gaussian that evaluates the density,...
What marginals and conditionals are built from: resolving Eigen index sets against a space; the share...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
Products of fixed-size matrices by Eigen's fixed-size product at every size: multiply() is by Eigen'...
The particle set: ParticleSet, the weighted samples, with resampling, upkeep of weights and particles...
The resamplers: function objects that draw ancestors in proportion to weights, which ParticleSet::res...
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...
What parameters and their views share (decisions.md [view-stamp], [params-and-density]): Stamp,...
Weighted statistics over points, all in internal, which the containers of weighted elements (Gaussian...
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
Gaussian kernel density estimate over a ParticleSet.
Definition KernelDensity.h:55
Scalar density(Eigen::MatrixBase< Derived > const &x) const
Definition KernelDensity.h:225
Scalar logDensity(P &&policy, Eigen::MatrixBase< Derived > const &x) const
logDensity() under policy: a log-sum-exp over the particles, reduced over chunks of them (see Executi...
Definition KernelDensity.h:190
typename ParamsType::DifferenceVectorType DifferenceVectorType
a difference
Definition KernelDensity.h:82
Space_ SpaceType
the particles' space
Definition KernelDensity.h:58
Scalar logTotalWeight() const
Definition KernelDensity.h:123
Allocator_ allocator_type
the particles' allocator
Definition KernelDensity.h:60
MomentsType moments() const
Definition KernelDensity.h:147
static constexpr int ValueDimensionAtCompileTime
Definition KernelDensity.h:63
Index size() const
Definition KernelDensity.h:88
static constexpr int DifferenceDimensionAtCompileTime
Definition KernelDensity.h:69
Scalar logDensity(Eigen::MatrixBase< Derived > const &x) const
Definition KernelDensity.h:180
typename ParamsType::MomentsType MomentsType
a mean and covariance together
Definition KernelDensity.h:78
typename ParamsType::Scalar Scalar
the scalar type
Definition KernelDensity.h:79
GaussianParams< Linear< DifferenceDimensionAtCompileTime, MaxDifferenceDimensionAtCompileTime >, Scalar_ > KernelType
The zero-mean kernel, in the linear tangent space of Space_.
Definition KernelDensity.h:75
typename ParamsType::MatrixType MatrixType
a matrix over differences
Definition KernelDensity.h:83
typename ParamsType::VectorType VectorType
a value, such as a state
Definition KernelDensity.h:81
ParticleSet< Space_, Scalar_, Allocator_ > ParamsType
the particles it reads
Definition KernelDensity.h:59
MatrixType const & bandwidth() const
Definition KernelDensity.h:130
typename ParamsType::Index Index
Eigen's index type.
Definition KernelDensity.h:80
ParamsType const & particles() const
Definition KernelDensity.h:116
Index differenceDimension() const
Definition KernelDensity.h:102
KernelType::DensityType kernel() const
Definition KernelDensity.h:137
typename ParamsType::SampleSetType SampleSetType
states as columns
Definition KernelDensity.h:84
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition KernelDensity.h:72
Index valueDimension() const
Definition KernelDensity.h:95
static constexpr int MaxValueDimensionAtCompileTime
Definition KernelDensity.h:66
MomentsType moments(P &&policy) const
moments() under policy: the particles' moments are reductions over them (see Execution policies).
Definition KernelDensity.h:157
Space_ const & space() const
Definition KernelDensity.h:109
Streaming log-sum-exp: accumulates without overflow.
Definition Weights.h:46
void add(Scalar x)
Adds x to the sum, rescaling the running sum if x is the new maximum.
Definition Weights.h:51
Scalar value() const
Definition Weights.h:133
ValueVector< Space_, Scalar > VectorType
a value, such as the mean
Definition Moments.h:74
A set of weighted samples: the empirical distribution.
Definition ParticleSet.h:279
typename MomentsType::Index Index
Definition ParticleSet.h:284
static constexpr int MaxDifferenceDimensionAtCompileTime
Definition ParticleSet.h:317
Index size() const
Definition ParticleSet.h:606
static constexpr int ValueDimensionAtCompileTime
Definition ParticleSet.h:308
typename MomentsType::Scalar Scalar
Definition ParticleSet.h:283
static constexpr int MaxValueDimensionAtCompileTime
Definition ParticleSet.h:311
typename MomentsType::DifferenceVectorType DifferenceVectorType
Definition ParticleSet.h:286
Space_ const & space() const
Definition ParticleSet.h:652
Index differenceDimension() const
Definition ParticleSet.h:645
static constexpr int DifferenceDimensionAtCompileTime
Definition ParticleSet.h:314
typename MomentsType::VectorType VectorType
Definition ParticleSet.h:285
Moments< Space_, Scalar_ > MomentsType
Definition ParticleSet.h:282
Index valueDimension() const
Definition ParticleSet.h:638
typename MomentsType::MatrixType MatrixType
Definition ParticleSet.h:287
SampleSet< Space_, Scalar_ > SampleSetType
Definition ParticleSet.h:290
An execution policy: execution::sequential, a policy of the standard library such as std::execution::...
Definition Execution.h:459
Requirements on a resampler for scalar type Scalar and random generator G.
Definition Resampling.h:61
A type usable as the space of a distribution.
Definition Space.h:186
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
Eigen::Index drawSample(Categorical< Space, Scalar > const &distribution, G &rng)
Definition Categorical.h:1111
std::vector< Eigen::Index > drawSamples(Categorical< Space, Scalar > const &distribution, Eigen::Index n, G &rng)
Definition Categorical.h:1087
SamplesWithLogDensity< Space, Scalar > drawSamplesWithLogDensity(Gamma< Space, Scalar > const &gamma, Eigen::Index n, G &rng)
Definition Gamma.h:529
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
auto plus(S const &space, Eigen::MatrixBase< XDerived > const &x, Eigen::MatrixBase< DDerived > const &d)
Definition Space.h:675
auto minus(S const &space, Eigen::MatrixBase< ADerived > const &a, Eigen::MatrixBase< BDerived > const &b)
Definition Space.h:715
ValueVector< DifferenceSpace< S >, Scalar > DifferenceVector
A difference in S as an Eigen column vector of Scalar, the type of minus() and of the offset plus() t...
Definition Space.h:650
One sample in Space, in a vector sized by the space, together with the log of the density it was draw...
Definition Sampling.h:50
Scalar logDensity
the log-density at sample
Definition Sampling.h:52
Samples in Space together with the log of the density they were drawn from, evaluated at each.
Definition Sampling.h:36
Draws each ancestor independently.
Definition Resampling.h:219