EmrysFilter 0.1.0
State estimation utilities in C++23 using Eigen
Loading...
Searching...
No Matches
ParticleHistory.h
Go to the documentation of this file.
1#pragma once
2
8
9#include <cstddef>
10#include <list>
11#include <optional>
12#include <span>
13#include <utility>
14
22
23namespace Emrys
24{
25
81template <Space Space_, RealScalar Scalar_ = double, typename Allocator_ = DefaultAllocator>
83{
84public:
85
86 using Scalar = Scalar_;
87 using SpaceType = Space_;
89 using allocator_type = Allocator_;
90 using Index = Eigen::Index;
91
92private:
93
94 using IndexBuffer = internal::Buffer<Index, Allocator_>;
95 using IndexAllocator = typename IndexBuffer::allocator_type;
96
97public:
98
102 class Step
103 {
104 public:
105
106 using allocator_type = Allocator_;
107
111 explicit Step(ParticleSetType const & recorded, Allocator_ const & allocator = {})
112 : particles{recorded, allocator}
113 , smoothed{recorded, allocator}
114 , parents(IndexAllocator(allocator))
115 {
116 parents.reserve(std::size_t(recorded.size()));
117 }
118
120 Step(Step const & other, Allocator_ const & allocator)
121 : particles{other.particles, allocator}
122 , smoothed{other.smoothed, allocator}
123 , parents(other.parents, IndexAllocator(allocator))
124 , resampled{other.resampled}
125 {}
126
130 Step(Step && other, Allocator_ const & allocator)
131 : particles{std::move(other.particles), allocator}
132 , smoothed{std::move(other.smoothed), allocator}
133 , parents(std::move(other.parents), IndexAllocator(allocator))
134 , resampled{other.resampled}
135 {}
136
137 Step(Step const &) = default;
138 Step(Step &&) = default;
139
141 Step &
142 operator=(Step const &) = default;
143
145 Step &
146 operator=(Step &&) = default;
147
148 ~Step() = default;
149
152
157
161 IndexBuffer parents;
162
163 bool resampled = false;
164 };
165
166private:
167
168 using StepAllocator = internal::ReBound<Allocator_, Step>;
169 using ListType = std::list<Step, StepAllocator>;
170
171public:
172
173 using const_iterator = typename ListType::const_iterator;
174
176 ParticleHistory() = default;
177
179 explicit ParticleHistory(Allocator_ const & allocator)
180 : m_steps(StepAllocator(allocator))
181 , m_lineage(IndexAllocator(allocator))
182 , m_traced(internal::bufferOf<Scalar>(allocator))
183 {}
184
186 explicit ParticleHistory(Lag lag, Allocator_ const & allocator = {})
187 : m_steps(StepAllocator(allocator))
188 , m_lineage(IndexAllocator(allocator))
189 , m_traced(internal::bufferOf<Scalar>(allocator))
190 , m_lag{lag.steps}
191 {
192 EMRYS_FILTER_ASSERT(lag.steps > 0 && "a fixed-lag history keeps at least one step");
193 }
194
197 : ParticleHistory{other, other.get_allocator()}
198 {}
199
201 ParticleHistory(ParticleHistory const & other, Allocator_ const & allocator)
202 : m_steps(other.m_steps, StepAllocator(allocator))
203 , m_lineage(IndexAllocator(allocator))
204 , m_traced(internal::bufferOf<Scalar>(allocator))
205 , m_lag{other.m_lag}
206 , m_resampleCount{other.m_resampleCount}
207 {}
208
210 ParticleHistory(ParticleHistory && other) = default;
211
215 ParticleHistory(ParticleHistory && other, Allocator_ const & allocator)
216 : m_steps(std::move(other.m_steps), StepAllocator(allocator))
217 , m_lineage(IndexAllocator(allocator))
218 , m_traced(internal::bufferOf<Scalar>(allocator))
219 , m_lag{other.m_lag}
220 , m_resampleCount{other.m_resampleCount}
221 {}
222
225 operator=(ParticleHistory const & other) = default;
226
229 operator=(ParticleHistory && other) = default;
230
231 ~ParticleHistory() = default;
232
234 [[nodiscard]] allocator_type
236 {
237 return allocator_type(m_steps.get_allocator());
238 }
239
241 [[nodiscard]] std::optional<Index>
242 lag() const
243 {
244 return m_lag > 0 ? std::optional<Index>{m_lag} : std::nullopt;
245 }
246
248 [[nodiscard]] Index
249 size() const
250 {
251 return Index(m_steps.size());
252 }
253
255 [[nodiscard]] bool
256 empty() const
257 {
258 return m_steps.empty();
259 }
260
262 [[nodiscard]] Step const &
263 front() const
264 {
265 EMRYS_FILTER_ASSERT(!empty() && "the history is empty");
266 return m_steps.front();
267 }
268
270 [[nodiscard]] Step const &
271 back() const
272 {
273 EMRYS_FILTER_ASSERT(!empty() && "the history is empty");
274 return m_steps.back();
275 }
276
278 [[nodiscard]] const_iterator
279 begin() const
280 {
281 return m_steps.begin();
282 }
283
285 [[nodiscard]] const_iterator
286 end() const
287 {
288 return m_steps.end();
289 }
290
295 void
296 push(ParticleSetType const & particles)
297 {
298 bool const resampled = !empty() && newResample(particles);
299 EMRYS_FILTER_ASSERT(
300 (empty() || resampled || particles.size() == back().particles.size())
301 && "without a resample the particles must be those of the step before"
302 );
303 if (m_lag > 0 && size() == m_lag)
304 {
305 m_steps.splice(m_steps.end(), m_steps, m_steps.begin());
306 Step & step = m_steps.back();
307 step.particles = particles;
308 step.smoothed = particles;
309 }
310 else
311 {
312 m_steps.emplace_back(particles);
313 }
314 Step & step = m_steps.back();
315 step.resampled = resampled;
316 if (resampled)
317 {
318 std::span<Index const> const ancestors = particles.ancestors();
319 step.parents.assign(ancestors.begin(), ancestors.end());
320 }
321 m_resampleCount = particles.resampleCount();
322 }
323
328 void
329 smooth(ParticleSetType const & latest)
330 {
332 }
333
337 template <ExecutionPolicy P>
338 void
339 smooth(P && policy, ParticleSetType const & latest)
340 {
341 if (empty())
342 {
343 return;
344 }
345 bool const resampled = newResample(latest);
346 EMRYS_FILTER_ASSERT(
347 (resampled || latest.size() == back().particles.size())
348 && "without a resample the particles must be those of the newest step"
349 );
350 Index const count = latest.size();
351 m_lineage.resize(std::size_t(count));
352 std::span<Index const> const ancestors = latest.ancestors();
353 internal::forEachIndex(policy, count, [&](Index j) {
354 m_lineage[std::size_t(j)] = resampled ? ancestors[std::size_t(j)] : j;
355 });
356 Step const * next = nullptr;
357 for (auto step = m_steps.rbegin(); step != m_steps.rend(); ++step)
358 {
359 if (next != nullptr && next->resampled)
360 {
361 internal::forEachIndex(policy, count, [&](Index j) {
362 Index const here = m_lineage[std::size_t(j)];
363 EMRYS_FILTER_ASSERT(here < Index(next->parents.size()) && "a traced index is out of range");
364 m_lineage[std::size_t(j)] = next->parents[std::size_t(here)];
365 });
366 }
367 auto const states = step->particles.states();
368 m_traced.resize(std::size_t(states.rows() * count));
369 auto traced = SampleSetMap<Space_, Scalar_>{m_traced.data(), states.rows(), count};
370 internal::gather(policy, std::span<Index const>{m_lineage}, states, traced);
371 step->smoothed.assign(traced, latest.logWeights());
372 step->smoothed.clearBandwidth();
373 next = &*step;
374 }
375 }
376
378 void
380 {
381 m_steps.clear();
382 }
383
384private:
385
389 [[nodiscard]] bool
390 newResample(ParticleSetType const & particles) const
391 {
392 Index const resamples = particles.resampleCount() - m_resampleCount;
393 EMRYS_FILTER_ASSERT(
394 resamples >= 0 && resamples <= 1 && "at most one resample may fall between two records of the particles"
395 );
396 return resamples == 1;
397 }
398
399 ListType m_steps;
400 IndexBuffer m_lineage;
401 internal::Buffer<Scalar, Allocator_> m_traced;
402 Index m_lag = 0;
403 Index m_resampleCount = 0;
404};
405
406} // namespace Emrys
The allocator conventions of decisions.md [allocation]: DefaultAllocator, Capacity,...
Execution policies (decisions.md [execution-policies]): execution::sequential, the ExecutionPolicy co...
Lag, the number of steps a fixed-lag history keeps, which every history of the Smoothers layer takes ...
The macros every header includes first.
Mathematical constants and scalar functions that work in every scalar type the library accepts,...
The particle set: ParticleSet, the weighted samples, with resampling, upkeep of weights and particles...
The Space concept and what every continuous space shares (decisions.md [sizes-and-dimensions]): the t...
One recorded set of particles, its descent from the step before, and its smoothed set,...
Definition ParticleHistory.h:103
ParticleSetType particles
The particles as push() recorded them, before the step's prediction.
Definition ParticleHistory.h:151
Step(ParticleSetType const &recorded, Allocator_ const &allocator={})
A copy of recorded in storage from allocator, with the recorded particles as the smoothed ones until ...
Definition ParticleHistory.h:111
Step(Step const &other, Allocator_ const &allocator)
Copies other into storage from allocator.
Definition ParticleHistory.h:120
Step & operator=(Step &&)=default
Takes the other step's storage, as the members' do.
Step(Step &&other, Allocator_ const &allocator)
Moves other into storage from allocator, taking its storage when the allocators are equal.
Definition ParticleHistory.h:130
Allocator_ allocator_type
the allocator its sets and indices come from
Definition ParticleHistory.h:106
Step(Step &&)=default
moves the sets and the parents
IndexBuffer parents
If resampled, particle i here descends from particle parents[i] of the step before; otherwise from pa...
Definition ParticleHistory.h:161
bool resampled
whether a resample fell between the step before and this one
Definition ParticleHistory.h:163
ParticleSetType smoothed
for the step's , as of the last smooth(): the traced states with the latest weights; a copy of partic...
Definition ParticleHistory.h:156
Step & operator=(Step const &)=default
Copies into this step's storage.
Step(Step const &)=default
copies the sets and the parents
The record of a particle filter's forward pass that ancestor tracing runs backward over: the particle...
Definition ParticleHistory.h:83
ParticleHistory()=default
An empty history that keeps every step.
ParticleHistory(Allocator_ const &allocator)
An empty history that keeps every step, with storage from allocator.
Definition ParticleHistory.h:179
ParticleHistory & operator=(ParticleHistory &&other)=default
Takes other's steps and lag, as std::list's move assignment does.
ParticleHistory & operator=(ParticleHistory const &other)=default
Copies other's steps and lag.
ParticleSet< Space_, Scalar_, Allocator_ > ParticleSetType
what each step records
Definition ParticleHistory.h:88
Step const & back() const
Definition ParticleHistory.h:271
ParticleHistory(ParticleHistory const &other, Allocator_ const &allocator)
Copies other's steps into storage from allocator.
Definition ParticleHistory.h:201
std::optional< Index > lag() const
Definition ParticleHistory.h:242
bool empty() const
Definition ParticleHistory.h:256
const_iterator end() const
Definition ParticleHistory.h:286
Space_ SpaceType
the state space
Definition ParticleHistory.h:87
ParticleHistory(ParticleHistory &&other)=default
Takes other's steps.
typename ListType::const_iterator const_iterator
iterates the steps, oldest first
Definition ParticleHistory.h:173
void push(ParticleSetType const &particles)
Records particles, the filter's state before its next prediction, as the newest step,...
Definition ParticleHistory.h:296
void clear()
Drops every step, keeping the lag.
Definition ParticleHistory.h:379
void smooth(P &&policy, ParticleSetType const &latest)
smooth() under policy: each step's tracing and gather are for-eaches over the latest particles (see E...
Definition ParticleHistory.h:339
void smooth(ParticleSetType const &latest)
Fills every step's smoothed set by ancestor tracing, from the newest back, given latest,...
Definition ParticleHistory.h:329
ParticleHistory(ParticleHistory const &other)
Copies the steps into nodes from other's allocator (see Allocation).
Definition ParticleHistory.h:196
Allocator_ allocator_type
the allocator the steps come from
Definition ParticleHistory.h:89
ParticleHistory(Lag lag, Allocator_ const &allocator={})
An empty history that keeps the newest lag steps, with storage from allocator.
Definition ParticleHistory.h:186
const_iterator begin() const
Definition ParticleHistory.h:279
ParticleHistory(ParticleHistory &&other, Allocator_ const &allocator)
Moves other's steps into storage from allocator, taking its storage when the allocators are equal.
Definition ParticleHistory.h:215
Eigen::Index Index
Eigen's index type.
Definition ParticleHistory.h:90
Index size() const
Definition ParticleHistory.h:249
allocator_type get_allocator() const
Definition ParticleHistory.h:235
Scalar_ Scalar
the scalar type
Definition ParticleHistory.h:86
Step const & front() const
Definition ParticleHistory.h:263
A set of weighted samples: the empirical distribution.
Definition ParticleSet.h:279
Index resampleCount() const
Definition ParticleSet.h:1331
Index size() const
Definition ParticleSet.h:606
LogWeightsType logWeights() const
Definition ParticleSet.h:666
std::span< Index const > ancestors() const
Definition ParticleSet.h:1321
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
constexpr Sequential sequential
The default execution policy: plain loops on the calling thread, in index order (Sequential).
Definition Execution.h:128
The number of steps a fixed-lag history keeps.
Definition Lag.h:17