EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
Simulation.h
Go to the documentation of this file.
1#pragma once
2
10
11#include <random>
12
24
25namespace Emrys
26{
27
28namespace internal
29{
30
37template <typename Space_, typename Vector, typename Noise, typename G>
38[[nodiscard]] Vector
39drawProcessNoise(Space_ const & space, Vector const & moved, Eigen::MatrixBase<Noise> const & noise, G & rng)
40{
41 using Scalar = typename Vector::Scalar;
42 using Difference = DifferenceVector<Space_, Scalar>;
43 Difference z = Difference::Zero(differenceDimension(space));
44 fillStandardNormal(z, rng);
45 auto const factor = defaultRoot(noise);
46 return Emrys::plus(space, moved, Difference{multiply(factor, z)});
47}
48
53template <typename Distribution, typename G>
54[[nodiscard]] auto
55drawMeasurementNoise(Distribution const & distribution, G & rng)
56{
57 if constexpr (requires { distribution.toDensity(); })
58 {
59 auto const density = distribution.toDensity();
60 EMRYS_FILTER_ASSERT(density && "the measurement's noise must factorize");
61 return drawSample(*density, rng);
62 }
63 else
64 {
65 return drawSample(distribution, rng);
66 }
67}
68
73template <typename R, typename Derived, typename Scalar, typename G>
75drawStep(R const & resolved, Eigen::MatrixBase<Derived> const & x, Scalar step, G & rng)
76{
78 auto const draws = std::apply(
79 [&](auto const &... inputs) {
80 return std::tuple{drawMeasurementNoise(inputOf(inputs), rng)...};
81 },
82 resolved.inputs()
83 );
84 return std::apply(
85 [&](auto const &... drawn) {
86 return Vector{valueOf(resolved.propagate(x.derived(), step, drawn...))};
87 },
88 draws
89 );
90}
91
92} // namespace internal
93
128template <Space Space_, typename Derived, ProcessModel Model, std::uniform_random_bit_generator G, typename... Args>
131 Space_ const & space,
132 Eigen::MatrixBase<Derived> const & x,
133 Model const & model,
134 G & rng,
136 Args const &... args
137)
138{
139 using Scalar = typename Derived::Scalar;
140 EMRYS_FILTER_ASSERT(x.size() == valueDimension(space) && "the state does not fit the space");
141 internal::assertPartitions(model, space);
142 Scalar const step = internal::toScalarTime<Scalar>(dt);
143 auto const call = internal::modelWith(std::cref(model), args...);
144 auto const resolved = internal::resolveProcess(call, space);
145 return internal::drawProcessNoise(
146 space,
147 internal::drawStep(resolved, x, step, rng),
148 resolved.template noise<Scalar>(step),
149 rng
150 );
151}
152
158template <Space Space_, typename Derived, typename Model, std::uniform_random_bit_generator G, typename... Args>
159 requires isFixedStep<Model>
161simulate(Space_ const & space, Eigen::MatrixBase<Derived> const & x, Model const & model, G & rng, Args const &... args)
162{
163 using Scalar = typename Derived::Scalar;
164 EMRYS_FILTER_ASSERT(x.size() == valueDimension(space) && "the state does not fit the space");
165 internal::assertPartitions(model, space, "the model must fit the state space");
166 auto const call = internal::modelWith(std::cref(model.model()), args...);
167 auto const resolved = internal::resolveProcess(call, space);
168 return internal::drawProcessNoise(
169 space,
170 internal::drawStep(resolved, x, Scalar(model.timeStep()), rng),
171 model.noise().template cast<Scalar>(),
172 rng
173 );
174}
175
180template <Space Space_, typename Derived, typename Step, std::uniform_random_bit_generator G, typename... Args>
181 requires isUnboundStep<Step>
183simulate(Space_ const & space, Eigen::MatrixBase<Derived> const & x, Step const & step, G & rng, Args const &... args)
184{
185 return simulate(space, x, step.model(), rng, step.timeStep(), args...);
186}
187
209template <Space Space_, typename Derived, MeasurementModel M, std::uniform_random_bit_generator G, typename... Args>
210[[nodiscard]] auto
211simulate(Space_ const & space, Eigen::MatrixBase<Derived> const & x, M const & model, G & rng, Args const &... args)
212{
213 using Scalar = typename Derived::Scalar;
214 EMRYS_FILTER_ASSERT(x.size() == valueDimension(space) && "the state does not fit the space");
215 auto const observed = internal::modelWith(std::cref(model), args...);
216 auto const resolved = internal::resolveMeasurement(observed, space);
217 using R = std::remove_cvref_t<decltype(resolved)>;
218 static_assert(
219 R::HasFunction || R::HasSample,
220 "a measurement model known by its likelihood alone has no function to draw a measurement from; give it "
221 "sample(g), a draw of the observation at a state"
222 );
223 // Every input drawn first, in the model's order, then the model's own draw, or the function at
224 // the state and the draws and then the noise.
225 auto const draws = std::apply(
226 [&](auto const &... inputs) {
227 return std::tuple{internal::drawMeasurementNoise(inputs, rng)...};
228 },
229 resolved.inputs()
230 );
231 if constexpr (!R::HasFunction)
232 {
233 return std::apply(
234 [&](auto const &... drawn) {
235 return resolved.sampleAtState(x, rng, drawn...);
236 },
237 draws
238 );
239 }
240 else
241 {
242 auto const & measurementSpace = resolved.space();
243 using Vector = typename Moments<typename R::Out, Scalar>::VectorType;
244 Vector predicted = std::apply(
245 [&](auto const &... drawn) {
246 return internal::valueOf(resolved.evaluateAtState(x, drawn...)).eval();
247 },
248 draws
249 );
250 normalizeValues(measurementSpace, predicted);
251 if constexpr (R::HasAdditiveNoise)
252 {
253 auto const noise = internal::drawMeasurementNoise(resolved.noise().distribution(), rng);
254 return Vector{Emrys::plus(measurementSpace, predicted, noise.template cast<Scalar>())};
255 }
256 else
257 {
258 return predicted;
259 }
260 }
261}
262
267template <
268 FixedSizeSpace Space_,
269 typename Derived,
270 typename Model,
271 std::uniform_random_bit_generator G,
272 typename... Inputs>
273 requires(Space<Space_> && !isFixedStep<Model> && !isUnboundStep<Model>)
276 Eigen::MatrixBase<Derived> const & x,
277 Model const & model,
278 G & rng,
280 Inputs const &... inputs
281)
282{
283 return simulate(Space_{}, x, model, rng, dt, inputs...);
284}
285
291template <
292 FixedSizeSpace Space_,
293 typename Derived,
294 typename Model,
295 std::uniform_random_bit_generator G,
296 typename... Inputs>
297 requires(Space<Space_> && (isFixedStep<Model> || isUnboundStep<Model>))
299simulate(Eigen::MatrixBase<Derived> const & x, Model const & model, G & rng, Inputs const &... inputs)
300{
301 return simulate(Space_{}, x, model, rng, inputs...);
302}
303
308template <
309 FixedSizeSpace Space_,
310 typename Derived,
311 MeasurementModel M,
312 std::uniform_random_bit_generator G,
313 typename... Args>
314 requires Space<Space_>
315[[nodiscard]] auto
316simulate(Eigen::MatrixBase<Derived> const & x, M const & model, G & rng, Args const &... args)
317{
318 return simulate(Space_{}, x, model, rng, args...);
319}
320
333template <typename Space, typename Scalar, std::uniform_random_bit_generator G>
334[[nodiscard]] Eigen::Index
335simulate(Eigen::Index from, MarkovChain<Space, Scalar> const & chain, G & rng)
336{
337 EMRYS_FILTER_ASSERT(from >= 0 && from < chain.space().cardinality() && "the value is not in the chain's space");
338 return internal::drawIndex<Scalar>(
339 chain.space().cardinality(),
340 [&](Eigen::Index to) {
341 return chain.transition(from, to);
342 },
343 rng
344 );
345}
346
352template <typename Space, typename Scalar, std::uniform_random_bit_generator G>
353[[nodiscard]] Eigen::Index
355 Eigen::Index from,
357 G & rng,
358 TimeStep<Scalar> auto const & dt
359)
360{
361 return simulate(from, chain.transition(dt), rng);
362}
363
364} // namespace Emrys
The distribution over a discrete space (decisions.md [discrete-spaces], [params-and-density]): Catego...
The continuous-time chain on a discrete space: ContinuousTimeMarkovChain, built from a generator of r...
The macros every header includes first.
The update of a Gaussian by a measurement (decisions.md [templated-measurements], [weighted-branches]...
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'...
Process models as functions with reads and writes (decisions.md [model-policies], [continuous-time-no...
Drawing samples: SamplesWithLogDensity and SampleWithLogDensity, the Gaussian draws drawSamples(),...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
Time steps (decisions.md [time-steps]): TimeTraits, which converts a time type to a scalar,...
Log-weights: the WeightError a normalization can fail with (NumericError.h), and the streaming log-su...
A continuous-time Markov chain on a discrete space: the rate of moving from each value to each other ...
Definition ContinuousTimeMarkovChain.h:61
MarkovChain< Space_, Scalar_ > transition(TimeStep< Scalar_ > auto const &time) const
Definition ContinuousTimeMarkovChain.h:252
A Markov chain on a discrete space: the probability of moving from each value to each value in one st...
Definition Categorical.h:829
Space_ const & space() const
Definition Categorical.h:1000
ValueVector< Space_, Scalar > VectorType
a value, such as the mean
Definition Moments.h:74
A process model: what processModel() returns, a built-in model, or a composition of process models (d...
Definition ProcessModel.h:2010
A type usable as the space of a distribution.
Definition Space.h:186
A step of time that converts to Scalar: Scalar itself, or any type with a TimeTraits,...
Definition Time.h:128
Eigen::Index drawSample(Categorical< Space, Scalar > const &distribution, G &rng)
Definition Categorical.h:1111
constexpr bool isFixedStep
Whether Model is a FixedStepModel, whose predictions take no time step.
Definition ProcessModel.h:2776
Moments< Space_, typenameDerived::Scalar >::VectorType simulate(Space_ const &space, Eigen::MatrixBase< Derived > const &x, Model const &model, G &rng, TimeStep< typename Derived::Scalar > auto const &dt, Args const &... args)
Definition Simulation.h:130
constexpr bool isUnboundStep
Whether Model is a fixed step not yet bound to a state space: an UnboundFixedStep,...
Definition ProcessModel.h:2792
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
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
Eigen::Index differenceDimension(S const &space)
Definition Space.h:284
void normalizeValues(S const &space, Eigen::MatrixBase< Derived > const &block)
Brings the values in block (rows are space's coordinates, columns samples) into canonical form,...
Definition Space.h:331