root/library/bdm/estim/particles.h @ 1068

Revision 1068, 21.3 kB (checked in by mido, 14 years ago)

patch of documentation - all conditional pdfs revised

  • Property svn:eol-style set to native
Line 
1/*!
2  \file
3  \brief Bayesian Filtering using stochastic sampling (Particle Filters)
4  \author Vaclav Smidl.
5
6  -----------------------------------
7  BDM++ - C++ library for Bayesian Decision Making under Uncertainty
8
9  Using IT++ for numerical operations
10  -----------------------------------
11*/
12
13#ifndef PARTICLES_H
14#define PARTICLES_H
15
16
17#include "../estim/arx_ext.h"
18#include "../stat/emix.h"
19
20namespace bdm {
21
22//! \brief Internal class used in PF
23class MarginalizedParticleBase : public BM {
24protected:
25    //! discrte particle
26    dirac est_emp;
27    //! internal Bayes Model
28    shared_ptr<BM> bm;
29
30    //! \brief Internal class for custom posterior - product of empirical and exact part
31    class eprod_2:public eprod_base {
32    protected:
33        MarginalizedParticleBase &mp;
34    public:
35        eprod_2(MarginalizedParticleBase &m):mp(m) {}
36        const epdf* factor(int i) const {
37            return (i==0) ? &mp.bm->posterior() : &mp.est_emp;
38        }
39        const int no_factors() const {
40            return 2;
41        }
42    } est;
43
44public:
45    MarginalizedParticleBase():est(*this) {};
46    MarginalizedParticleBase(const MarginalizedParticleBase &m2):BM(m2),est(*this) {
47        bm = m2.bm->_copy();
48        est_emp = m2.est_emp;
49        est.validate();
50        validate();
51    };
52    void bayes(const vec &dt, const vec &cond) NOT_IMPLEMENTED_VOID;
53
54    const eprod_2& posterior() const {
55        return est;
56    }
57
58    void set_prior(const epdf *pdf0) {
59        const eprod *ep=dynamic_cast<const eprod*>(pdf0);
60        if (ep) { // full prior
61            bdm_assert(ep->no_factors()==2,"Incompatible prod");
62            bm->set_prior(ep->factor(0));
63            est_emp.set_point(ep->factor(1)->sample());
64        } else {
65            // assume prior is only for emp;
66            est_emp.set_point(pdf0->sample());
67        }
68    }
69    void from_setting(const Setting &set) {
70        BM::from_setting ( set );
71        bm = UI::build<BM> ( set, "bm", UI::compulsory );
72    }
73    void validate() {
74        BM::validate();
75        //est.validate(); --pdfs not known
76        bdm_assert(bm,"Internal BM is not given");
77    }
78};
79
80class MarginalizedParticle : public MarginalizedParticleBase {
81protected:
82    //! pdf with for transitional par
83    shared_ptr<pdf> par; // pdf for non-linear part
84    //! link from this to bm
85    shared_ptr<datalink_part> cond2bm;
86    //! link from cond to par
87    shared_ptr<datalink_part> cond2par;
88    //! link from emp 2 par
89    shared_ptr<datalink_part> emp2bm;
90    //! link from emp 2 par
91    shared_ptr<datalink_part> emp2par;
92
93public:
94    BM* _copy() const {
95        return new MarginalizedParticle(*this);
96    };
97    void bayes(const vec &dt, const vec &cond) {
98        vec par_cond(par->dimensionc());
99        cond2par->filldown(cond,par_cond); // copy ut
100        emp2par->filldown(est_emp._point(),par_cond); // copy xt-1
101
102        //sample new particle
103        est_emp.set_point(par->samplecond(par_cond));
104        //if (evalll)
105        vec bm_cond(bm->dimensionc());
106        cond2bm->filldown(cond, bm_cond);// set e.g. ut
107        emp2bm->filldown(est_emp._point(), bm_cond);// set e.g. ut
108        bm->bayes(dt, bm_cond);
109        ll=bm->_ll();
110    }
111
112    /*! parse structure
113    \code
114    class = "MarginalizedParticle";
115    parameter_pdf = {class = 'epdf_offspring', ...};
116    bm = {class = 'bm_offspring',...};
117    \endcode
118    If rvs are set, then it checks for compatibility.
119    */
120    void from_setting(const Setting &set) {
121        MarginalizedParticleBase::from_setting ( set );
122        par = UI::build<pdf> ( set, "parameter_pdf", UI::compulsory );
123    }
124
125    void to_setting(Setting &set) {
126        MarginalizedParticleBase::to_setting(set);
127        UI::save(par,set,"parameter_pdf");
128    }
129    void validate() {
130        MarginalizedParticleBase::validate();
131        est_emp.set_rv(par->_rv());
132        if (est_emp.point.length()!=par->dimension())
133            est_emp.set_point(zeros(par->dimension()));
134        est.validate();
135
136        yrv = bm->_yrv();
137        dimy = bm->dimensiony();
138        set_rv( concat(bm->_rv(), par->_rv()));
139        set_dim( par->dimension()+bm->dimension());
140
141        rvc = par->_rvc();
142        rvc.add(bm->_rvc());
143        rvc=rvc.subt(par->_rv());
144        rvc=rvc.subt(par->_rv().copy_t(-1));
145        rvc=rvc.subt(bm->_rv().copy_t(-1)); //
146
147        cond2bm=new datalink_part;
148        cond2par=new datalink_part;
149        emp2bm  =new datalink_part;
150        emp2par =new datalink_part;
151        cond2bm->set_connection(bm->_rvc(), rvc);
152        cond2par->set_connection(par->_rvc(), rvc);
153        emp2bm->set_connection(bm->_rvc(), par->_rv());
154        emp2par->set_connection(par->_rvc(), par->_rv().copy_t(-1));
155
156        dimc = rvc._dsize();
157    };
158};
159UIREGISTER(MarginalizedParticle);
160
161//! class used in PF
162class BootstrapParticle : public BM {
163    dirac est;
164    shared_ptr<pdf> par;
165    shared_ptr<pdf> obs;
166    shared_ptr<datalink_part> cond2par;
167    shared_ptr<datalink_part> cond2obs;
168    shared_ptr<datalink_part> xt2obs;
169    shared_ptr<datalink_part> xtm2par;
170public:
171    BM* _copy() const {
172        return new BootstrapParticle(*this);
173    };
174    void bayes(const vec &dt, const vec &cond) {
175        vec par_cond(par->dimensionc());
176        cond2par->filldown(cond,par_cond); // copy ut
177        xtm2par->filldown(est._point(),par_cond); // copy xt-1
178
179        //sample new particle
180        est.set_point(par->samplecond(par_cond));
181        //if (evalll)
182        vec obs_cond(obs->dimensionc());
183        cond2obs->filldown(cond, obs_cond);// set e.g. ut
184        xt2obs->filldown(est._point(), obs_cond);// set e.g. ut
185        ll=obs->evallogcond(dt,obs_cond);
186    }
187    const dirac& posterior() const {
188        return est;
189    }
190
191    void set_prior(const epdf *pdf0) {
192        est.set_point(pdf0->sample());
193    }
194
195    /*! parse structure
196    \code
197    class = "BootstrapParticle";
198    parameter_pdf = {class = 'epdf_offspring', ...};
199    observation_pdf = {class = 'epdf_offspring',...};
200    \endcode
201    If rvs are set, then it checks for compatibility.
202    */
203    void from_setting(const Setting &set) {
204        BM::from_setting ( set );
205        par = UI::build<pdf> ( set, "parameter_pdf", UI::compulsory );
206        obs = UI::build<pdf> ( set, "observation_pdf", UI::compulsory );
207    }
208    void validate() {
209        yrv = obs->_rv();
210        dimy = obs->dimension();
211        set_rv( par->_rv());
212        set_dim( par->dimension());
213
214        rvc = par->_rvc().subt(par->_rv().copy_t(-1));
215        rvc.add(obs->_rvc()); //
216
217        cond2obs=new datalink_part;
218        cond2par=new datalink_part;
219        xt2obs  =new datalink_part;
220        xtm2par =new datalink_part;
221        cond2obs->set_connection(obs->_rvc(), rvc);
222        cond2par->set_connection(par->_rvc(), rvc);
223        xt2obs->set_connection(obs->_rvc(), _rv());
224        xtm2par->set_connection(par->_rvc(), _rv().copy_t(-1));
225
226        dimc = rvc._dsize();
227    };
228};
229UIREGISTER(BootstrapParticle);
230
231
232/*!
233* \brief Trivial particle filter with proposal density equal to parameter evolution model.
234
235Posterior density is represented by a weighted empirical density (\c eEmp ).
236*/
237
238class PF : public BM {
239    //! \var log_level_enums logweights
240    //! all weightes will be logged
241
242    //! \var log_level_enums logmeans
243    //! means of particles will be logged
244    LOG_LEVEL(PF,logweights,logmeans,logvars);
245
246    class pf_mix: public emix_base {
247        Array<BM*> &bms;
248    public:
249        pf_mix(vec &w0, Array<BM*> &bms0):emix_base(w0),bms(bms0) {}
250        const epdf* component(const int &i)const {
251            return &(bms(i)->posterior());
252        }
253        int no_coms() const {
254            return bms.length();
255        }
256    };
257protected:
258    //!number of particles;
259    int n;
260    //!posterior density
261    pf_mix est;
262    //! weights;
263    vec w;
264    //! particles
265    Array<BM*> particles;
266    //! internal structure storing loglikelihood of predictions
267    vec lls;
268
269    //! which resampling method will be used
270    RESAMPLING_METHOD resmethod;
271    //! resampling threshold; in this case its meaning is minimum ratio of active particles
272    //! For example, for 0.5 resampling is performed when the numebr of active aprticles drops belo 50%.
273    double res_threshold;
274
275    //! \name Options
276    //!@{
277    //!@}
278
279public:
280    //! \name Constructors
281    //!@{
282    PF ( ) : est(w,particles) { };
283
284    void set_parameters ( int n0, double res_th0 = 0.5, RESAMPLING_METHOD rm = SYSTEMATIC ) {
285        n = n0;
286        res_threshold = res_th0;
287        resmethod = rm;
288    };
289    void set_model ( const BM *particle0, const epdf *prior) {
290        if (n>0) {
291            particles.set_length(n);
292            for (int i=0; i<n; i++) {
293                particles(i) = particle0->_copy();
294                particles(i)->set_prior(prior);
295            }
296        }
297        // set values for posterior
298        est.set_rv ( particle0->posterior()._rv() );
299    };
300    void set_statistics ( const vec w0, const epdf &epdf0 ) {
301        //est.set_statistics ( w0, epdf0 );
302    };
303    /*    void set_statistics ( const eEmp &epdf0 ) {
304            bdm_assert_debug ( epdf0._rv().equal ( par->_rv() ), "Incompatible input" );
305            est = epdf0;
306        };*/
307    //!@}
308
309    //! bayes compute weights of the
310    virtual void bayes_weights();
311    //! important part of particle filtering - decide if it is time to perform resampling
312    virtual bool do_resampling() {
313        double eff = 1.0 / ( w * w );
314        return eff < ( res_threshold*n );
315    }
316    void bayes ( const vec &yt, const vec &cond );
317    //!access function
318    vec& _lls() {
319        return lls;
320    }
321    //!access function
322    RESAMPLING_METHOD _resmethod() const {
323        return resmethod;
324    }
325    //! return correctly typed posterior (covariant return)
326    const pf_mix& posterior() const {
327        return est;
328    }
329
330    /*! configuration structure for basic PF
331    \code
332    parameter_pdf   = pdf_class;         // parameter evolution pdf
333    observation_pdf = pdf_class;         // observation pdf
334    prior           = epdf_class;         // prior probability density
335    --- optional ---
336    n               = 10;                 // number of particles
337    resmethod       = 'systematic', or 'multinomial', or 'stratified'
338                                          // resampling method
339    res_threshold   = 0.5;                // resample when active particles drop below 50%
340    \endcode
341    */
342    void from_setting ( const Setting &set ) {
343        BM::from_setting ( set );
344        UI::get ( log_level, set, "log_level", UI::optional );
345
346        shared_ptr<BM> bm0 = UI::build<BM>(set, "particle",UI::compulsory);
347
348        n =0;
349        UI::get(n,set,"n",UI::optional);;
350        if (n>0) {
351            particles.set_length(n);
352            for(int i=0; i<n; i++) {
353                particles(i)=bm0->_copy();
354            }
355            w = ones(n)/n;
356        }
357        shared_ptr<epdf> pri = UI::build<epdf>(set,"prior");
358        set_prior(pri.get());
359        // set resampling method
360        resmethod_from_set ( set );
361        //set drv
362
363        rvc = bm0->_rvc();
364        dimc = bm0->dimensionc();
365        BM::set_rv(bm0->_rv());
366        yrv=bm0->_yrv();
367        dimy = bm0->dimensiony();
368    }
369
370    void log_register ( bdm::logger& L, const string& prefix ) {
371        BM::log_register(L,prefix);
372        if (log_level[logweights]) {
373            L.add_vector( log_level, logweights, RV ( particles.length()), prefix);
374        }
375        if (log_level[logmeans]) {
376            for (int i=0; i<particles.length(); i++) {
377                L.add_vector( log_level, logmeans, RV ( particles(i)->dimension() ), prefix , i);
378            }
379        }
380        if (log_level[logvars]) {
381            for (int i=0; i<particles.length(); i++) {
382                L.add_vector( log_level, logvars, RV ( particles(i)->dimension() ), prefix , i);
383            }
384        }
385    };
386    void log_write ( ) const {
387        BM::log_write();
388        if (log_level[logweights]) {
389            log_level.store( logweights, w);
390        }
391        if (log_level[logmeans]) {
392            for (int i=0; i<particles.length(); i++) {
393                log_level.store( logmeans, particles(i)->posterior().mean(), i);
394            }
395        }
396        if (log_level[logvars]) {
397            for (int i=0; i<particles.length(); i++) {
398                log_level.store( logvars, particles(i)->posterior().variance(), i);
399            }
400        }
401
402    }
403
404    void set_prior(const epdf *pri) {
405        const emix_base *emi=dynamic_cast<const emix_base*>(pri);
406        if (emi) {
407            bdm_assert(particles.length()>0, "initial particle is not assigned");
408            n = emi->_w().length();
409            int old_n = particles.length();
410            if (n!=old_n) {
411                particles.set_length(n,true);
412            }
413            for(int i=old_n; i<n; i++) {
414                particles(i)=particles(0)->_copy();
415            }
416
417            for (int i =0; i<n; i++) {
418                particles(i)->set_prior(emi->_com(i));
419            }
420        } else {
421            // try to find "n"
422            bdm_assert(n>0, "Field 'n' must be filled when prior is not of type emix");
423            for (int i =0; i<n; i++) {
424                particles(i)->set_prior(pri);
425            }
426
427        }
428    }
429    //! auxiliary function reading parameter 'resmethod' from configuration file
430    void resmethod_from_set ( const Setting &set ) {
431        string resmeth;
432        if ( UI::get ( resmeth, set, "resmethod", UI::optional ) ) {
433            if ( resmeth == "systematic" ) {
434                resmethod = SYSTEMATIC;
435            } else  {
436                if ( resmeth == "multinomial" ) {
437                    resmethod = MULTINOMIAL;
438                } else {
439                    if ( resmeth == "stratified" ) {
440                        resmethod = STRATIFIED;
441                    } else {
442                        bdm_error ( "Unknown resampling method" );
443                    }
444                }
445            }
446        } else {
447            resmethod = SYSTEMATIC;
448        };
449        if ( !UI::get ( res_threshold, set, "res_threshold", UI::optional ) ) {
450            res_threshold = 0.9;
451        }
452        //validate();
453    }
454
455    void validate() {
456        BM::validate();
457        est.validate();
458        bdm_assert ( n>0, "empty particle pool" );
459        n = w.length();
460        lls = zeros ( n );
461
462        if ( particles(0)->_rv()._dsize() > 0 ) {
463            bdm_assert (  particles(0)->_rv()._dsize() == est.dimension(), "MPF:: Mismatch of RV " +particles(0)->_rv().to_string() +
464                          " of size (" +num2str(particles(0)->_rv()._dsize())+") and dimension of posterior ("+num2str(est.dimension()) + ")" );
465        }
466    }
467    //! resample posterior density (from outside - see MPF)
468    void resample ( ) {
469        ivec ind = zeros_i ( n );
470        bdm::resample(w,ind,resmethod);
471        // copy the internals according to ind
472        for (int i = 0; i < n; i++ ) {
473            if ( ind ( i ) != i ) {
474                delete particles(i);
475                particles( i ) = particles( ind ( i ) )->_copy();
476            }
477            w ( i ) = 1.0 / n;
478        }
479    }
480    //! access function
481    Array<BM*>& _particles() {
482        return particles;
483    }
484    ~PF() {
485        for (int i=0; i<particles.length(); i++) {
486            delete particles(i);
487        }
488    }
489
490};
491UIREGISTER ( PF );
492
493/*! Marginalized particle for state-space models with unknown parameters of distribuution of residues on \f$v_t\f$.
494
495\f{eqnarray*}{
496    x_t &=& g(x_{t-1}) + v_t,\\
497    y_t &\sim &fy(x_t),
498    \f}
499
500    This particle is a only a shell creating the residues calling internal estimator of their parameters. The internal estimator can be of any compatible type, e.g. ARX for Gaussian residues with unknown mean and variance.
501
502    */
503class NoiseParticleX : public MarginalizedParticleBase {
504protected:
505    //! function transforming xt, ut -> x_t+1
506    shared_ptr<fnc> g; // pdf for non-linear part
507    //! function transforming xt,ut -> yt
508    shared_ptr<pdf> fy; // pdf for non-linear part
509
510    RV rvx;
511    RV rvxc;
512    RV rvyc;
513
514    //!link from condition to f
515    datalink_part cond2g;
516    //!link from condition to h
517    datalink_part cond2fy;
518    //!link from xt to f
519    datalink_part x2g;
520    //!link from xt to h
521    datalink_part x2fy;
522
523public:
524    BM* _copy() const {
525        return new NoiseParticleX(*this);
526    };
527    void bayes(const vec &dt, const vec &cond) {
528        shared_ptr<epdf> pred_v=bm->epredictor();
529
530        vec vt=pred_v->sample();
531
532        //new sample
533        vec &xtm=est_emp.point;
534        vec g_args(g->dimensionc());
535        x2g.filldown(xtm,g_args);
536        cond2g.filldown(cond,g_args);
537        vec xt = g->eval(g_args) + vt;
538        est_emp.point=xt;
539
540        // the vector [v_t] updates bm,
541        bm->bayes(vt);
542
543        // residue of observation
544        vec fy_args(fy->dimensionc());
545        x2fy.filldown(xt,fy_args);
546        cond2fy.filldown(cond,fy_args);
547
548        ll=bm->_ll() + fy->evallogcond(dt,fy_args);
549    }
550    void from_setting(const Setting &set) {
551        MarginalizedParticleBase::from_setting(set); //reads bm, yrv,rvc, bm_rv, etc...
552
553        g=UI::build<fnc>(set,"g",UI::compulsory);
554        fy=UI::build<pdf>(set,"fy",UI::compulsory);
555        UI::get(rvx,set,"rvx",UI::compulsory);
556        est_emp.set_rv(rvx);
557
558        UI::get(rvxc,set,"rvxc",UI::compulsory);
559        UI::get(rvyc,set,"rvyc",UI::compulsory);
560
561    }
562    void validate() {
563        MarginalizedParticleBase::validate();
564
565        dimy = fy->dimension();
566        bm->set_yrv(rvx);
567
568        est_emp.set_rv(rvx);
569        est_emp.set_dim(rvx._dsize());
570        est.validate();
571        //
572        //check dimensions
573        rvc = rvxc.subt(rvx.copy_t(-1));
574        rvc.add( rvyc);
575        rvc=rvc.subt(rvx);
576
577        bdm_assert(g->dimension()==rvx._dsize(),"rvx is not described");
578        bdm_assert(g->dimensionc()==rvxc._dsize(),"rvxc is not described");
579        bdm_assert(fy->dimensionc()==rvyc._dsize(),"rvyc is not described");
580
581        bdm_assert(bm->dimensiony()==g->dimension(),
582                   "Incompatible noise estimator of dimension " +
583                   num2str(bm->dimensiony()) + " does not match dimension of g , " +
584                   num2str(g->dimension()));
585
586        dimc = rvc._dsize();
587
588        //establish datalinks
589        x2g.set_connection(rvxc, rvx.copy_t(-1));
590        cond2g.set_connection(rvxc, rvc);
591
592        x2fy.set_connection(rvyc, rvx);
593        cond2fy.set_connection(rvyc, rvc);
594    }
595};
596UIREGISTER(NoiseParticleX);
597
598/*! Marginalized particle for state-space models with unknown parameters of residues distribution
599
600\f{eqnarray*}{
601    x_t &=& g(x_{t-1}) + v_t,\\
602    z_t &= &h(x_{t-1}) + w_t,
603    \f}
604
605    This particle is a only a shell creating the residues calling internal estimator of their parameters. The internal estimator can be of any compatible type, e.g. ARX for Gaussian residues with unknown mean and variance.
606
607    */
608class NoiseParticle : public MarginalizedParticleBase {
609protected:
610    //! function transforming xt, ut -> x_t+1
611    shared_ptr<fnc> g; // pdf for non-linear part
612    //! function transforming xt,ut -> yt
613    shared_ptr<fnc> h; // pdf for non-linear part
614
615    RV rvx;
616    RV rvxc;
617    RV rvyc;
618
619    //!link from condition to f
620    datalink_part cond2g;
621    //!link from condition to h
622    datalink_part cond2h;
623    //!link from xt to f
624    datalink_part x2g;
625    //!link from xt to h
626    datalink_part x2h;
627
628public:
629    BM* _copy() const {
630        return new NoiseParticle(*this);
631    };
632    void bayes(const vec &dt, const vec &cond) {
633        shared_ptr<epdf> pred_vw=bm->epredictor();
634        shared_ptr<epdf> pred_v = pred_vw->marginal(rvx);
635
636        vec vt=pred_v->sample();
637
638        //new sample
639        vec &xtm=est_emp.point;
640        vec g_args(g->dimensionc());
641        x2g.filldown(xtm,g_args);
642        cond2g.filldown(cond,g_args);
643        vec xt = g->eval(g_args) + vt;
644        est_emp.point=xt;
645
646        // residue of observation
647        vec h_args(h->dimensionc());
648        x2h.filldown(xt,h_args);
649        cond2h.filldown(cond,h_args);
650        vec wt = dt-h->eval(h_args);
651        // the vector [v_t,w_t] is now complete
652        bm->bayes(concat(vt,wt));
653        ll=bm->_ll();
654    }
655    void from_setting(const Setting &set) {
656        MarginalizedParticleBase::from_setting(set); //reads bm, yrv,rvc, bm_rv, etc...
657
658        UI::get(g,set,"g",UI::compulsory);
659        UI::get(h,set,"h",UI::compulsory);
660        UI::get(rvx,set,"rvx",UI::compulsory);
661        est_emp.set_rv(rvx);
662
663        UI::get(rvxc,set,"rvxc",UI::compulsory);
664        UI::get(rvyc,set,"rvyc",UI::compulsory);
665
666    }
667    void validate() {
668        MarginalizedParticleBase::validate();
669
670        dimy = h->dimension();
671        bm->set_yrv(concat(rvx,yrv));
672
673        est_emp.set_rv(rvx);
674        est_emp.set_dim(rvx._dsize());
675        est.validate();
676        //
677        //check dimensions
678        rvc = rvxc.subt(rvx.copy_t(-1));
679        rvc.add( rvyc);
680        rvc=rvc.subt(rvx);
681
682        bdm_assert(g->dimension()==rvx._dsize(),"rvx is not described");
683        bdm_assert(g->dimensionc()==rvxc._dsize(),"rvxc is not described");
684        bdm_assert(h->dimension()==rvyc._dsize(),"rvyc is not described");
685
686        bdm_assert(bm->dimensiony()==g->dimension()+h->dimension(),
687                   "Incompatible noise estimator of dimension " +
688                   num2str(bm->dimensiony()) + " does not match dimension of g and h, " +
689                   num2str(g->dimension())+" and "+ num2str(h->dimension()) );
690
691        dimc = rvc._dsize();
692
693        //establish datalinks
694        x2g.set_connection(rvxc, rvx.copy_t(-1));
695        cond2g.set_connection(rvxc, rvc);
696
697        x2h.set_connection(rvyc, rvx);
698        cond2h.set_connection(rvyc, rvc);
699    }
700};
701UIREGISTER(NoiseParticle);
702
703
704}
705#endif // KF_H
706
Note: See TracBrowser for help on using the browser.