259 lines
9.6 KiB
C++
259 lines
9.6 KiB
C++
/*
|
|
* CondensationBackwardFilter.h
|
|
*
|
|
* Created on: Jun 23, 2015
|
|
* Author: Toni Fetzer
|
|
*/
|
|
|
|
#ifndef BACKWARDSIMULATION_H_
|
|
#define BACKWARDSIMULATION_H_
|
|
|
|
|
|
#include <vector>
|
|
#include <memory>
|
|
#include <algorithm>
|
|
|
|
#include "BackwardFilterTransition.h"
|
|
#include "BackwardFilter.h"
|
|
|
|
#include "../Particle.h"
|
|
|
|
#include "../filtering/resampling/ParticleFilterResampling.h"
|
|
#include "../filtering/estimation/ParticleFilterEstimation.h"
|
|
#include "../filtering/ParticleFilterEvaluation.h"
|
|
#include "../filtering/ParticleFilterInitializer.h"
|
|
|
|
#include "../sampling/ParticleTrajectorieSampler.h"
|
|
|
|
#include "../../Assertions.h"
|
|
|
|
namespace SMC {
|
|
|
|
/**
|
|
* the main-class for the Backward Simulation Filter
|
|
* running "backwards" in time, generates multiple backwards trajectories
|
|
* (Realizations) by repeating the backward simulation M time.
|
|
* it can be started at a random time T of any forward particle filter
|
|
* [Monte Carlo smoothing for non-linear time series Godsill et al. '03]
|
|
* @param State the (user-defined) state for each particle
|
|
* @param numRealizations is the number of backward trajectories starting
|
|
*/
|
|
template <typename State, typename Control, typename Observation>
|
|
class BackwardSimulation : public BackwardFilter<State, Control, Observation>{
|
|
|
|
private:
|
|
|
|
/** all smoothed particles T -> 1*/
|
|
std::vector<std::vector<Particle<State>>> backwardParticles;
|
|
|
|
/** container for particles */
|
|
std::vector<Particle<State>> smoothedParticles;
|
|
|
|
/** the estimation function to use */
|
|
std::unique_ptr<ParticleFilterEstimation<State>> estimation;
|
|
|
|
/** the transition function to use */
|
|
std::unique_ptr<BackwardFilterTransition<State, Control>> transition;
|
|
|
|
/** the resampler to use */
|
|
std::unique_ptr<ParticleFilterResampling<State>> resampler;
|
|
|
|
/** the sampler for drawing trajectories */
|
|
std::unique_ptr<ParticleTrajectorieSampler<State>> sampler;
|
|
|
|
/** the percentage-of-efficient-particles-threshold for resampling */
|
|
double nEffThresholdPercent = 0.25;
|
|
|
|
/** number of realizations to be calculated */
|
|
int numRealizations;
|
|
|
|
/** is update called the first time? */
|
|
bool firstFunctionCall;
|
|
|
|
|
|
public:
|
|
|
|
/** ctor */
|
|
BackwardSimulation(int numRealizations) {
|
|
this->numRealizations = numRealizations;
|
|
backwardParticles.reserve(numRealizations);
|
|
smoothedParticles.reserve(numRealizations);
|
|
firstFunctionCall = true;
|
|
}
|
|
|
|
/** dtor */
|
|
~BackwardSimulation() {
|
|
;
|
|
}
|
|
|
|
/** reset **/
|
|
void reset(){
|
|
this->numRealizations = numRealizations;
|
|
|
|
backwardParticles.clear();
|
|
backwardParticles.reserve(numRealizations);
|
|
|
|
smoothedParticles.clear();
|
|
smoothedParticles.reserve(numRealizations);
|
|
|
|
firstFunctionCall = true;
|
|
}
|
|
|
|
/** access to all backward / smoothed particles */
|
|
const std::vector<std::vector<Particle<State>>>& getbackwardParticles() {
|
|
return backwardParticles;
|
|
}
|
|
|
|
/** set the estimation method to use */
|
|
void setEstimation(std::unique_ptr<ParticleFilterEstimation<State>> estimation) {
|
|
Assert::isNotNull(estimation, "setEstimation() MUST not be called with a nullptr!");
|
|
this->estimation = std::move(estimation);
|
|
}
|
|
|
|
/** set the transition method to use */
|
|
void setTransition(std::unique_ptr<BackwardFilterTransition<State, Control>> transition) {
|
|
Assert::isNotNull(transition, "setTransition() MUST not be called with a nullptr!");
|
|
this->transition = std::move(transition);
|
|
}
|
|
|
|
/** set the resampling method to use */
|
|
void setResampling(std::unique_ptr<ParticleFilterResampling<State>> resampler) {
|
|
Assert::isNotNull(resampler, "setResampling() MUST not be called with a nullptr!");
|
|
this->resampler = std::move(resampler);
|
|
}
|
|
|
|
/** set the sampler method to use */
|
|
void setSampler(std::unique_ptr<ParticleTrajectorieSampler<State>> sampler){
|
|
Assert::isNotNull(sampler, "setSampler() MUST not be called with a nullptr!");
|
|
this->sampler = std::move(sampler);
|
|
}
|
|
|
|
|
|
/** set the resampling threshold as the percentage of efficient particles */
|
|
void setNEffThreshold(const double thresholdPercent) {
|
|
this->nEffThresholdPercent = thresholdPercent;
|
|
}
|
|
|
|
/** get the used transition method */
|
|
BackwardFilterTransition<State, Control>* getTransition() {
|
|
return this->transition.get();
|
|
}
|
|
|
|
/**
|
|
* perform update: transition -> correction -> approximation
|
|
* gets the weighted sample set of a standard condensation
|
|
* particle filter in REVERSED order!
|
|
*/
|
|
State update(std::vector<Particle<State>> const& forwardParticles) {
|
|
|
|
// sanity checks (if enabled)
|
|
Assert::isNotNull(transition, "transition MUST not be null! call setTransition() first!");
|
|
Assert::isNotNull(estimation, "estimation MUST not be null! call setEstimation() first!");
|
|
|
|
//storage for single trajectories / smoothed particles
|
|
smoothedParticles.clear();
|
|
|
|
// Choose \tilde x_T = x^(i)_T with probability w^(i)_T
|
|
// Therefore sample independently from the categorical distribution of weights.
|
|
if(firstFunctionCall){
|
|
|
|
smoothedParticles = sampler->drawTrajectorie(forwardParticles, numRealizations);
|
|
|
|
firstFunctionCall = false;
|
|
backwardParticles.push_back(smoothedParticles);
|
|
|
|
const State es = estimation->estimate(smoothedParticles);
|
|
return es;
|
|
}
|
|
|
|
// compute weights using the transition model
|
|
// transitionWeigths[numRealizations][numParticles]
|
|
std::vector<std::vector<double>> transitionWeights = transition->transition(forwardParticles, backwardParticles.back());
|
|
|
|
//get the next trajectorie for a realisation
|
|
for(int j = 0; j < numRealizations; ++j){
|
|
|
|
//vector for the current smoothedWeights at time t
|
|
std::vector<Particle<State>> smoothedWeights;
|
|
smoothedWeights.resize(forwardParticles.size());
|
|
smoothedWeights = forwardParticles;
|
|
|
|
//check if all transitionWeights are zero
|
|
double weightSumTransition = std::accumulate(transitionWeights[j].begin(), transitionWeights[j].end(), 0.0);
|
|
Assert::isNot0(weightSumTransition, "all transition weights for smoothing are zero");
|
|
|
|
int i = 0;
|
|
for (auto& w : transitionWeights.at(j)) {
|
|
|
|
// multiply the weight of the particles at time t and normalize
|
|
smoothedWeights.at(i).weight = (smoothedWeights.at(i).weight * w);
|
|
if(smoothedWeights.at(i).weight != smoothedWeights.at(i).weight) {throw "detected NaN";}
|
|
|
|
// iter
|
|
++i;
|
|
}
|
|
|
|
//get the sum of all weights
|
|
auto lambda = [](double current, const Particle<State>& a){return current + a.weight; };
|
|
double weightSumSmoothed = std::accumulate(smoothedWeights.begin(), smoothedWeights.end(), 0.0, lambda);
|
|
|
|
//normalize the weights
|
|
if(weightSumSmoothed != 0.0){
|
|
for (int i = 0; i < smoothedWeights.size(); ++i){
|
|
smoothedWeights.at(i).weight /= weightSumSmoothed;
|
|
}
|
|
|
|
//check if normalization worked
|
|
double normWeightSum = std::accumulate(smoothedWeights.begin(), smoothedWeights.end(), 0.0, lambda);
|
|
Assert::isNear(normWeightSum, 1.0, 0.001, "Smoothed weights do not sum to 1");
|
|
}
|
|
|
|
|
|
//draw the next trajectorie at time t for a realization and save them
|
|
smoothedParticles.push_back(sampler->drawSingleParticle(smoothedWeights));
|
|
|
|
//throw if weight of smoothedParticle is zero
|
|
//in practice this is possible, if a particle is completely separated from the rest and is therefore
|
|
//weighted zero or very very low.
|
|
Assert::isNot0(smoothedParticles.back().weight, "smoothed particle has zero weight");
|
|
}
|
|
|
|
|
|
if(resampler)
|
|
{
|
|
|
|
//TODO - does this even make sense?
|
|
std::cout << "Warning - Resampling is not yet implemented!" << std::endl;
|
|
// //resampling if necessery
|
|
// double sum = 0.0;
|
|
// double weightSum = std::accumulate(smoothedParticles.begin().weight, smoothedParticles.end().weight, 0.0);
|
|
// for (auto& p : smoothedParticles) {
|
|
// p.weight /= weightSum;
|
|
// sum += (p.weight * p.weight);
|
|
// }
|
|
|
|
// const double neff = 1.0/sum;
|
|
// if (neff != neff) {throw "detected NaN";}
|
|
|
|
// // if the number of efficient particles is too low, perform resampling
|
|
// if (neff < smoothedParticles.size() * nEffThresholdPercent) { resampler->resample(smoothedParticles); }
|
|
}
|
|
|
|
// push_back the smoothedParticles
|
|
backwardParticles.push_back(smoothedParticles);
|
|
|
|
// estimate the current state
|
|
const State est = estimation->estimate(smoothedParticles);
|
|
|
|
// done
|
|
return est;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
#endif /* BACKWARDSIMULATION_H_ */
|