Upkeep keeps a filter's representation in bounds without meaning to change the distribution much: resampling particles whose weights have degenerated, roughening them after, pruning the improbable particles, components, or cells, or repairing a covariance that rounding has left not positive definite.
More...
|
template<std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
requires (!std::integral<R>) |
| constexpr Resample< G, R > | Emrys::upkeep::resample (G &generator, R resampler={}) |
template<std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
requires (!std::integral<R>) |
| constexpr Resample< G, R > | Emrys::upkeep::resample (G &generator, Eigen::Index size, R resampler={}) |
template<ExecutionPolicy P, std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
requires (!std::integral<R>) |
| constexpr Resample< G, R, std::remove_cvref_t< P > > | Emrys::upkeep::resample (P &&policy, G &generator, R resampler={}) |
template<ExecutionPolicy P, std::uniform_random_bit_generator G, typename R = resamplers::Systematic>
requires (!std::integral<R>) |
| constexpr Resample< G, R, std::remove_cvref_t< P > > | Emrys::upkeep::resample (P &&policy, G &generator, Eigen::Index size, R resampler={}) |
template<std::uniform_random_bit_generator G, typename Value = double>
requires std::is_arithmetic_v<Value> |
| constexpr Roughen< G, internal::DeducedScalar< Value > > | Emrys::upkeep::roughen (G &generator, Value scale=1) |
template<ExecutionPolicy P, std::uniform_random_bit_generator G, typename Value = double>
requires std::is_arithmetic_v<Value> |
| constexpr Roughen< G, internal::DeducedScalar< Value >, std::remove_cvref_t< P > > | Emrys::upkeep::roughen (P &&policy, G &generator, Value scale=1) |
| template<typename Predicate, typename... Actions> |
| When< Predicate, Actions... > | Emrys::upkeep::when (Predicate predicate, Actions... actions) |
template<typename Compare, typename Value, typename... Actions>
requires (!ExecutionPolicy<Compare>) && std::is_arithmetic_v<Value> && std::predicate<Compare const &, internal::DeducedScalar
<Value>, internal::DeducedScalar<Value>> |
| WhenEss< Compare, internal::DeducedScalar< Value >, Actions... > | Emrys::upkeep::whenEss (Compare compare, Value fraction, Actions... actions) |
template<ExecutionPolicy P, typename Compare, typename Value, typename... Actions>
requires std::is_arithmetic_v<Value> && std::predicate<Compare const &, internal::DeducedScalar<Value>, internal::DeducedScalar
<Value>> |
| When< internal::EssTest< Compare, internal::DeducedScalar< Value >, std::remove_cvref_t< P > >, Actions... > | Emrys::upkeep::whenEss (P &&policy, Compare compare, Value fraction, Actions... actions) |
template<typename Compare, typename... Actions>
requires std::predicate<Compare const &, std::ptrdiff_t, std::ptrdiff_t> |
| WhenSize< Compare, Actions... > | Emrys::upkeep::whenSize (Compare compare, std::ptrdiff_t limit, Actions... actions) |
| template<typename... Actions> |
| Sequence< Actions... > | Emrys::upkeep::sequence (Actions... actions) |
| constexpr None | Emrys::upkeep::none () |
template<typename Value>
requires std::is_arithmetic_v<Value> |
| constexpr PruneBelow< internal::DeducedScalar< Value > > | Emrys::upkeep::pruneBelow (Value probability) |
template<typename Value>
requires std::is_arithmetic_v<Value> |
| constexpr PruneTail< internal::DeducedScalar< Value > > | Emrys::upkeep::pruneTail (Value fraction) |
| constexpr PruneToSize | Emrys::upkeep::pruneToSize (std::ptrdiff_t maxSize) |
| constexpr Subdivide | Emrys::upkeep::subdivide (std::ptrdiff_t factor=2) |
template<typename Value>
requires std::is_arithmetic_v<Value> |
| constexpr FloorModes< internal::DeducedScalar< Value > > | Emrys::upkeep::floorModes (Value probability) |
| template<Repair R = roots::Ridge<>> |
| constexpr RepairCovariance< R > | Emrys::upkeep::repairCovariance (R repair={}) |
Upkeep keeps a filter's representation in bounds without meaning to change the distribution much: resampling particles whose weights have degenerated, roughening them after, pruning the improbable particles, components, or cells, or repairing a covariance that rounding has left not positive definite.
The filters never do it on their own; the caller applies it to the filter's state() where it fits the problem, such as once after the last update before a prediction. Upkeep is not a reduction policy, which acts inside an update on the branches of one measurement (decisions.md [reduction-policies]); upkeep acts between steps on the whole state.
Every action leaves a state of the type it found, since the filter fixed that type for its cost (a shared covariance, a regular grid) and state() hands out a reference to it (decisions.md [rao-blackwellized-core]). Within the type it may change the size: the particles, components, or cells a prune leaves, the particles a Resample draws, a grid's cells after Subdivide. What changes the type, such as drawSamples() from a mixture, fitMixture() from particles, or collapsing a mixture into one Gaussian, is a conversion the caller makes outside the filter.
Each operation applies to the states with the members it calls, and UpkeepOperation answers whether it does (Prune is all three prunes):
WhenSize needs only size(), and WhenEss ess(), which the particle states have. A grid isn't resampled, since two copies of a cell would evolve identically. Of the Gaussian forms only the covariance form repairs: the square-root form refuses a repair at compile time, and the information forms have none.
No action merges or splits mixture components (decisions.md [upkeep]). How best to search for the merges to make depends on the use and its hardware, a software engineering choice and not a mathematical one, so the caller writes that action from MixtureParams::merged(), merge(), and the divergences (see Divergences); examples/gaussian_mixture.cpp merges by Runnalls' cost. The literature has no rule for splitting that the library takes up (decisions.md [splitting-hooks]).
Each upkeep object is a function object called with the state alone. It returns whether it changed the state, or a NumericError: a WeightError if the weights are invalid, or a GaussianError if a covariance can't be repaired. Actions do the work unconditionally, and each the library names leaves the state unchanged when it returns an error. Triggers decide whether to run the actions they hold, in order, on the same state; a trigger stops at the first error and returns it, and the actions that ran before it keep their effect (decisions.md [upkeep]), so a caller who needs all or nothing copies the state before the call. An action returns true when it did its work and false when the work would have been nothing: a prune that removed nothing, Subdivide by one, Roughen by zero, FloorModes with no mode below the floor, RepairCovariance on covariances that factorize. Resample returns true even on equal weights, since it redraws the particles. What else an action needs is given to its factory: Resample and Roughen hold a reference to the random generator they draw from, which must outlive them, and a caller's action captures what it needs.
An execution policy, when wanted, is given to the factory too, first, as it comes before the range in a standard algorithm (see Execution policies): Resample and Roughen run their loops under it, and whenEss(policy, ...) takes the effective sample size under it. The other actions run sequentially and take none.
Upkeep runs on the filter's state, which allocates through its allocator (decisions.md [allocation]). Under execution::sequential, Resample to a size within the set's capacity, Roughen on a ParticleSet, the prunes once the state's capacity is set, and FloorModes allocate nothing. Subdivide allocates the larger grid, Roughen on a Rao-Blackwellized state a copy of its particle set, and RepairCovariance a copy of each covariance it repairs. Under another policy the standard algorithms allocate, as [execution-policies] allows.
resample(particleFilter.state());
auto const inParallel =
upkeep::whenEss(std::execution::par, std::less{}, 0.5,
inParallel(particleFilter.state());
thin(pointMassFilter.state());
prune(mixtureFilter.state());
bound(mixtureFilter.state());
repair(kalmanFilter.state());
constexpr RepairCovariance< R > repairCovariance(R repair={})
Definition Upkeep.h:1340
constexpr Resample< G, R > resample(G &generator, R resampler={})
Definition Upkeep.h:494
constexpr PruneBelow< internal::DeducedScalar< Value > > pruneBelow(Value probability)
Definition Upkeep.h:954
constexpr PruneTail< internal::DeducedScalar< Value > > pruneTail(Value fraction)
Definition Upkeep.h:999
WhenSize< Compare, Actions... > whenSize(Compare compare, std::ptrdiff_t limit, Actions... actions)
Definition Upkeep.h:852
constexpr PruneToSize pruneToSize(std::ptrdiff_t maxSize)
Definition Upkeep.h:1032
WhenEss< Compare, internal::DeducedScalar< Value >, Actions... > whenEss(Compare compare, Value fraction, Actions... actions)
Definition Upkeep.h:805
constexpr Roughen< G, internal::DeducedScalar< Value > > roughen(G &generator, Value scale=1)
Definition Upkeep.h:694
Actions: Resample, Roughen, PruneBelow, PruneTail, PruneToSize, Subdivide, FloorModes, RepairCovariance. Triggers: When, which runs its actions when a predicate of the state holds, and WhenEss and WhenSize, the two Whens the library names, each testing what it is named for with a comparator given first, a standard one such as std::less or the caller's own. A trigger runs a caller's action too: any function object of the state that returns std::expected<bool, WeightError>. A Sequence runs its actions every time, so that a filter's whole upkeep is one value.
Every filter holds one upkeep, given last to its constructor and None unless given, and runs it on its state when the caller calls maintain():
(void)filter.update(fix);
(void)filter.maintain();
Resampling when the effective sample size falls is the sequential importance resampling of Arulampalam et al. (2002), §V-A, Algorithm 3; roughening after it is the regularized particle filter of Musso, Oudjane, and Le Gland (2001), as given in Arulampalam et al. (2002), §V-B3, Algorithm 6, with a Gaussian kernel; and the floor on mode probabilities is Athans et al. (1977).