root/library/bdm/estim/kalman.h @ 907

Revision 907, 14.1 kB (checked in by mido, 14 years ago)

LOG LEVEL improved and hopefully finished

  • Property svn:eol-style set to native
Line 
1/*!
2  \file
3  \brief Bayesian Filtering for linear Gaussian models (Kalman Filter) and extensions
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 KF_H
14#define KF_H
15
16
17#include "../math/functions.h"
18#include "../stat/exp_family.h"
19#include "../math/chmat.h"
20#include "../base/user_info.h"
21//#include <../applications/pmsm/simulator_zdenek/ekf_example/pmsm_mod.h>
22
23namespace bdm {
24
25/*!
26 * \brief Basic elements of linear state-space model
27
28Parameter evolution model:\f[ x_{t+1} = A x_{t} + B u_t + Q^{1/2} e_t \f]
29Observation model: \f[ y_t = C x_{t} + C u_t + R^{1/2} w_t. \f]
30Where $e_t$ and $w_t$ are mutually independent vectors of Normal(0,1)-distributed disturbances.
31 */
32template<class sq_T>
33class StateSpace {
34protected:
35        //! Matrix A
36        mat A;
37        //! Matrix B
38        mat B;
39        //! Matrix C
40        mat C;
41        //! Matrix D
42        mat D;
43        //! Matrix Q in square-root form
44        sq_T Q;
45        //! Matrix R in square-root form
46        sq_T R;
47public:
48        StateSpace() :  A(), B(), C(), D(), Q(), R() {}
49        //!copy constructor
50        StateSpace ( const StateSpace<sq_T> &S0 ) :  A ( S0.A ), B ( S0.B ), C ( S0.C ), D ( S0.D ), Q ( S0.Q ), R ( S0.R ) {}
51        //! set all matrix parameters
52        void set_parameters ( const mat &A0, const  mat &B0, const  mat &C0, const  mat &D0, const  sq_T &Q0, const sq_T &R0 );
53        //! validation
54        void validate();
55        //! not virtual in this case
56        void from_setting ( const Setting &set ) {
57                UI::get ( A, set, "A", UI::compulsory );
58                UI::get ( B, set, "B", UI::compulsory );
59                UI::get ( C, set, "C", UI::compulsory );
60                UI::get ( D, set, "D", UI::compulsory );
61                mat Qtm, Rtm; // full matrices
62                if ( !UI::get ( Qtm, set, "Q", UI::optional ) ) {
63                        vec dq;
64                        UI::get ( dq, set, "dQ", UI::compulsory );
65                        Qtm = diag ( dq );
66                }
67                if ( !UI::get ( Rtm, set, "R", UI::optional ) ) {
68                        vec dr;
69                        UI::get ( dr, set, "dQ", UI::compulsory );
70                        Rtm = diag ( dr );
71                }
72                R = Rtm; // automatic conversion to square-root form
73                Q = Qtm;
74
75                validate();
76        }
77        //! access function
78        const mat& _A() const {
79                return A;
80        }
81        //! access function
82        const mat& _B() const {
83                return B;
84        }
85        //! access function
86        const mat& _C() const {
87                return C;
88        }
89        //! access function
90        const mat& _D() const {
91                return D;
92        }
93        //! access function
94        const sq_T& _Q() const {
95                return Q;
96        }
97        //! access function
98        const sq_T& _R() const {
99                return R;
100        }
101};
102
103//! Common abstract base for Kalman filters
104template<class sq_T>
105class Kalman: public BM, public StateSpace<sq_T> {
106protected:
107        //! id of output
108        RV yrv;
109        //! Kalman gain
110        mat  _K;
111        //!posterior
112        enorm<sq_T> est;
113        //!marginal on data f(y|y)
114        enorm<sq_T>  fy;
115public:
116        Kalman<sq_T>() : BM(), StateSpace<sq_T>(), yrv(), _K(),  est() {}
117        //! Copy constructor
118        Kalman<sq_T> ( const Kalman<sq_T> &K0 ) : BM ( K0 ), StateSpace<sq_T> ( K0 ), yrv ( K0.yrv ), _K ( K0._K ),  est ( K0.est ), fy ( K0.fy ) {}
119        //!set statistics of the posterior
120        void set_statistics ( const vec &mu0, const mat &P0 ) {
121                est.set_parameters ( mu0, P0 );
122        };
123        //!set statistics of the posterior
124        void set_statistics ( const vec &mu0, const sq_T &P0 ) {
125                est.set_parameters ( mu0, P0 );
126        };
127        //! return correctly typed posterior (covariant return)
128        const enorm<sq_T>& posterior() const {
129                return est;
130        }
131        //! load basic elements of Kalman from structure
132        void from_setting ( const Setting &set ) {
133                StateSpace<sq_T>::from_setting ( set );
134
135                mat P0;
136                vec mu0;
137                UI::get ( mu0, set, "mu0", UI::optional );
138                UI::get ( P0, set,  "P0", UI::optional );
139                set_statistics ( mu0, P0 );
140                // Initial values
141                shared_ptr<RV> yrv_ptr = UI::build<RV>( set, "yrv", UI::optional );
142                if( !yrv_ptr ) yrv_ptr = new RV();
143                shared_ptr<RV> rvc_ptr = UI::build<RV>( set, "urv", UI::optional );
144                if( !rvc_ptr ) rvc_ptr = new RV();
145                set_yrv ( concat ( *yrv_ptr, *rvc_ptr ) );
146        }
147        //! validate object
148        void validate() {
149                StateSpace<sq_T>::validate();
150                dimy = this->C.rows();
151                dimc = this->B.cols();
152                set_dim ( this->A.rows() );
153
154                bdm_assert ( est.dimension(), "Statistics and model parameters mismatch" );
155        }
156
157};
158/*!
159* \brief Basic Kalman filter with full matrices
160*/
161
162class KalmanFull : public Kalman<fsqmat> {
163public:
164        //! For EKFfull;
165        KalmanFull() : Kalman<fsqmat>() {};
166        //! Here dt = [yt;ut] of appropriate dimensions
167        void bayes ( const vec &yt, const vec &cond = empty_vec );
168
169        virtual KalmanFull* _copy() const {
170                KalmanFull* K = new KalmanFull;
171                K->set_parameters ( A, B, C, D, Q, R );
172                K->set_statistics ( est._mu(), est._R() );
173                return K;
174        }
175};
176UIREGISTER ( KalmanFull );
177
178
179/*! \brief Kalman filter in square root form
180
181Trivial example:
182\include kalman_simple.cpp
183
184Complete constructor:
185*/
186class KalmanCh : public Kalman<chmat> {
187protected:
188        //! @{ \name Internal storage - needs initialize()
189        //! pre array (triangular matrix)
190        mat preA;
191        //! post array (triangular matrix)
192        mat postA;
193        //!@}
194public:
195        //! copy constructor
196        virtual KalmanCh* _copy() const {
197                KalmanCh* K = new KalmanCh;
198                K->set_parameters ( A, B, C, D, Q, R );
199                K->set_statistics ( est._mu(), est._R() );
200                K->validate();
201                return K;
202        }
203        //! set parameters for adapt from Kalman
204        void set_parameters ( const mat &A0, const mat &B0, const mat &C0, const mat &D0, const chmat &Q0, const chmat &R0 );
205        //! initialize internal parametetrs
206        void initialize();
207
208        /*!\brief  Here dt = [yt;ut] of appropriate dimensions
209
210        The following equality hold::\f[
211        \left[\begin{array}{cc}
212        R^{0.5}\\
213        P_{t|t-1}^{0.5}C' & P_{t|t-1}^{0.5}CA'\\
214        & Q^{0.5}\end{array}\right]<\mathrm{orth.oper.}>=\left[\begin{array}{cc}
215        R_{y}^{0.5} & KA'\\
216        & P_{t+1|t}^{0.5}\\
217        \\\end{array}\right]\f]
218
219        Thus this object evaluates only predictors! Not filtering densities.
220        */
221        void bayes ( const vec &yt, const vec &cond = empty_vec );
222
223        void from_setting ( const Setting &set ) {
224                Kalman<chmat>::from_setting ( set );
225                validate();
226        }
227        void validate() {
228                Kalman<chmat>::validate();
229                initialize();
230        }
231};
232UIREGISTER ( KalmanCh );
233
234/*!
235\brief Extended Kalman Filter in full matrices
236
237An approximation of the exact Bayesian filter with Gaussian noices and non-linear evolutions of their mean.
238*/
239class EKFfull : public KalmanFull {
240protected:
241        //! Internal Model f(x,u)
242        shared_ptr<diffbifn> pfxu;
243
244        //! Observation Model h(x,u)
245        shared_ptr<diffbifn> phxu;
246
247public:
248        //! Default constructor
249        EKFfull ();
250
251        //! Set nonlinear functions for mean values and covariance matrices.
252        void set_parameters ( const shared_ptr<diffbifn> &pfxu, const shared_ptr<diffbifn> &phxu, const mat Q0, const mat R0 );
253
254        //! Here dt = [yt;ut] of appropriate dimensions
255        void bayes ( const vec &yt, const vec &cond = empty_vec );
256        //! set estimates
257        void set_statistics ( const vec &mu0, const mat &P0 ) {
258                est.set_parameters ( mu0, P0 );
259        };
260        //! access function
261        const mat _R() {
262                return est._R().to_mat();
263        }
264        void from_setting ( const Setting &set ) {
265                BM::from_setting ( set );
266                shared_ptr<diffbifn> IM = UI::build<diffbifn> ( set, "IM", UI::compulsory );
267                shared_ptr<diffbifn> OM = UI::build<diffbifn> ( set, "OM", UI::compulsory );
268
269                //statistics
270                int dim = IM->dimension();
271                vec mu0;
272                if ( !UI::get ( mu0, set, "mu0" ) )
273                        mu0 = zeros ( dim );
274
275                mat P0;
276                vec dP0;
277                if ( UI::get ( dP0, set, "dP0" ) )
278                        P0 = diag ( dP0 );
279                else if ( !UI::get ( P0, set, "P0" ) )
280                        P0 = eye ( dim );
281
282                set_statistics ( mu0, P0 );
283
284                //parameters
285                vec dQ, dR;
286                UI::get ( dQ, set, "dQ", UI::compulsory );
287                UI::get ( dR, set, "dR", UI::compulsory );
288                set_parameters ( IM, OM, diag ( dQ ), diag ( dR ) );
289
290//                      pfxu = UI::build<diffbifn>(set, "IM", UI::compulsory);
291//                      phxu = UI::build<diffbifn>(set, "OM", UI::compulsory);
292//
293//                      mat R0;
294//                      UI::get(R0, set, "R",UI::compulsory);
295//                      mat Q0;
296//                      UI::get(Q0, set, "Q",UI::compulsory);
297//
298//
299//                      mat P0; vec mu0;
300//                      UI::get(mu0, set, "mu0", UI::optional);
301//                      UI::get(P0, set,  "P0", UI::optional);
302//                      set_statistics(mu0,P0);
303//                      // Initial values
304//                      UI::get (yrv, set, "yrv", UI::optional);
305//                      UI::get (urv, set, "urv", UI::optional);
306//                      set_drv(concat(yrv,urv));
307//
308//                      // setup StateSpace
309//                      pfxu->dfdu_cond(mu0, zeros(pfxu->_dimu()), A,true);
310//                      phxu->dfdu_cond(mu0, zeros(pfxu->_dimu()), C,true);
311//
312        }
313
314        void validate() {
315                KalmanFull::validate();
316
317                // check stats and IM and OM
318        }
319};
320UIREGISTER ( EKFfull );
321
322
323/*!
324\brief Extended Kalman Filter in Square root
325
326An approximation of the exact Bayesian filter with Gaussian noices and non-linear evolutions of their mean.
327*/
328
329class EKFCh : public KalmanCh {
330protected:
331        //! Internal Model f(x,u)
332        shared_ptr<diffbifn> pfxu;
333
334        //! Observation Model h(x,u)
335        shared_ptr<diffbifn> phxu;
336public:
337        //! copy constructor duplicated - calls different set_parameters
338        EKFCh* _copy() const {
339                return new EKFCh(*this);
340        }
341        //! Set nonlinear functions for mean values and covariance matrices.
342        void set_parameters ( const shared_ptr<diffbifn> &pfxu, const shared_ptr<diffbifn> &phxu, const chmat Q0, const chmat R0 );
343
344        //! Here dt = [yt;ut] of appropriate dimensions
345        void bayes ( const vec &yt, const vec &cond = empty_vec );
346
347        void from_setting ( const Setting &set );
348
349        void validate() {};
350        // TODO dodelat void to_setting( Setting &set ) const;
351
352};
353
354UIREGISTER ( EKFCh );
355SHAREDPTR ( EKFCh );
356
357
358//////// INstance
359
360/*! \brief (Switching) Multiple Model
361The model runs several models in parallel and evaluates thier weights (fittness).
362
363The statistics of the resulting density are merged using (geometric?) combination.
364
365The next step is performed with the new statistics for all models.
366*/
367class MultiModel: public BM {
368protected:
369        //! List of models between which we switch
370        Array<EKFCh*> Models;
371        //! vector of model weights
372        vec w;
373        //! cache of model lls
374        vec _lls;
375        //! type of switching policy [1=maximum,2=...]
376        int policy;
377        //! internal statistics
378        enorm<chmat> est;
379public:
380        //! set internal parameters
381        void set_parameters ( Array<EKFCh*> A, int pol0 = 1 ) {
382                Models = A;//TODO: test if evalll is set
383                w.set_length ( A.length() );
384                _lls.set_length ( A.length() );
385                policy = pol0;
386
387                est.set_rv ( RV ( "MM", A ( 0 )->posterior().dimension(), 0 ) );
388                est.set_parameters ( A ( 0 )->posterior().mean(), A ( 0 )->posterior()._R() );
389        }
390        void bayes ( const vec &yt, const vec &cond = empty_vec ) {
391                int n = Models.length();
392                int i;
393                for ( i = 0; i < n; i++ ) {
394                        Models ( i )->bayes ( yt );
395                        _lls ( i ) = Models ( i )->_ll();
396                }
397                double mlls = max ( _lls );
398                w = exp ( _lls - mlls );
399                w /= sum ( w ); //normalization
400                //set statistics
401                switch ( policy ) {
402                case 1: {
403                        int mi = max_index ( w );
404                        const enorm<chmat> &st = Models ( mi )->posterior() ;
405                        est.set_parameters ( st.mean(), st._R() );
406                }
407                break;
408                default:
409                        bdm_error ( "unknown policy" );
410                }
411                // copy result to all models
412                for ( i = 0; i < n; i++ ) {
413                        Models ( i )->set_statistics ( est.mean(), est._R() );
414                }
415        }
416        //! return correctly typed posterior (covariant return)
417        const enorm<chmat>& posterior() const {
418                return est;
419        }
420
421        void from_setting ( const Setting &set );
422
423};
424UIREGISTER ( MultiModel );
425SHAREDPTR ( MultiModel );
426
427//! conversion of outer ARX model (mlnorm) to state space model
428/*!
429The model is constructed as:
430\f[ x_{t+1} = Ax_t + B u_t + R^{1/2} e_t, y_t=Cx_t+Du_t + R^{1/2}w_t, \f]
431For example, for:
432Using Frobenius form, see [].
433
434For easier use in the future, indices theta_in_A and theta_in_C are set. TODO - explain
435*/
436//template<class sq_T>
437class StateCanonical: public StateSpace<fsqmat> {
438protected:
439        //! remember connection from theta ->A
440        datalink_part th2A;
441        //! remember connection from theta ->C
442        datalink_part th2C;
443        //! remember connection from theta ->D
444        datalink_part th2D;
445        //!cached first row of A
446        vec A1row;
447        //!cached first row of C
448        vec C1row;
449        //!cached first row of D
450        vec D1row;
451
452public:
453        //! set up this object to match given mlnorm
454        void connect_mlnorm ( const mlnorm<fsqmat> &ml );
455
456        //! fast function to update parameters from ml - not checked for compatibility!!
457        void update_from ( const mlnorm<fsqmat> &ml );
458};
459/*!
460State-Space representation of multivariate autoregressive model.
461The original model:
462\f[ y_t = \theta [\ldots y_{t-k}, \ldots u_{t-l}, \ldots z_{t-m}]' + \Sigma^{-1/2} e_t \f]
463where \f$ k,l,m \f$ are maximum delayes of corresponding variables in the regressor.
464
465The transformed state is:
466\f[ x_t = [y_{t} \ldots y_{t-k-1}, u_{t} \ldots u_{t-l-1}, z_{t} \ldots z_{t-m-1}]\f]
467
468The state accumulates all delayed values starting from time \f$ t \f$ .
469
470
471*/
472class StateFromARX: public StateSpace<chmat> {
473protected:
474        //! remember connection from theta ->A
475        datalink_part th2A;
476        //! remember connection from theta ->B
477        datalink_part th2B;
478        //!function adds n diagonal elements from given starting point r,c
479        void diagonal_part ( mat &A, int r, int c, int n ) {
480                for ( int i = 0; i < n; i++ ) {
481                        A ( r, c ) = 1.0;
482                        r++;
483                        c++;
484                }
485        };
486        //! similar to ARX.have_constant
487        bool have_constant;
488public:
489        //! set up this object to match given mlnorm
490        //! Note that state-space and common mpdf use different meaning of \f$ _t \f$ in \f$ u_t \f$.
491        //!While mlnorm typically assumes that \f$ u_t \rightarrow y_t \f$ in state space it is \f$ u_{t-1} \rightarrow y_t \f$
492        //! For consequences in notation of internal variable xt see arx2statespace_notes.lyx.
493        void connect_mlnorm ( const mlnorm<chmat> &ml, RV &xrv, RV &urv );
494
495        //! fast function to update parameters from ml - not checked for compatibility!!
496        void update_from ( const mlnorm<chmat> &ml );
497
498        //! access function
499        bool _have_constant() const {
500                return have_constant;
501        }
502};
503
504/////////// INSTANTIATION
505
506template<class sq_T>
507void StateSpace<sq_T>::set_parameters ( const mat &A0, const  mat &B0, const  mat &C0, const  mat &D0, const  sq_T &Q0, const sq_T &R0 ) {
508
509        A = A0;
510        B = B0;
511        C = C0;
512        D = D0;
513        R = R0;
514        Q = Q0;
515        validate();
516}
517
518template<class sq_T>
519void StateSpace<sq_T>::validate() {
520        bdm_assert ( A.cols() == A.rows(), "KalmanFull: A is not square" );
521        bdm_assert ( B.rows() == A.rows(), "KalmanFull: B is not compatible" );
522        bdm_assert ( C.cols() == A.rows(), "KalmanFull: C is not compatible" );
523        bdm_assert ( ( D.rows() == C.rows() ) && ( D.cols() == B.cols() ), "KalmanFull: D is not compatible" );
524        bdm_assert ( ( Q.cols() == A.rows() ) && ( Q.rows() == A.rows() ), "KalmanFull: Q is not compatible" );
525        bdm_assert ( ( R.cols() == C.rows() ) && ( R.rows() == C.rows() ), "KalmanFull: R is not compatible" );
526}
527
528}
529#endif // KF_H
530
Note: See TracBrowser for help on using the browser.