indii/ml/filter/KernelForwardBackwardSmoother.hpp

00001 #ifndef INDII_ML_FILTER_KERNELFORWARDBACKWARDSMOOTHER_HPP
00002 #define INDII_ML_FILTER_KERNELFORWARDBACKWARDSMOOTHER_HPP
00003 
00004 #include "Smoother.hpp"
00005 #include "ParticleResampler.hpp"
00006 #include "KernelForwardBackwardSmootherModel.hpp"
00007 #include "flags.hpp"
00008 #include "../aux/Almost2Norm.hpp"
00009 #include "../aux/AlmostGaussianKernel.hpp"
00010 #include "../aux/MedianPartitioner.hpp"
00011 
00012 namespace indii {
00013   namespace ml {
00014     namespace filter {
00015 /**
00016  * Kernel forward-backward smoother.
00017  *
00018  * @author Lawrence Murray <lawrence@indii.org>
00019  * @version $Rev: 584 $
00020  * @date $Date: 2008-12-15 17:26:36 +0000 (Mon, 15 Dec 2008) $
00021  *
00022  * @param T The type of time.
00023  * @param NT Norm type.
00024  * @param KT Kernel type.
00025  * @param ST Partitioner type.
00026  *
00027  * KernelForwardBackwardSmoother is suitable for continuous time systems with
00028  * nonlinear transition and measurement functions, approximating state and
00029  * noise with indii::ml::aux::DiracMixturePdf distributions. It is
00030  * particularly suitable in situations where the transition density is
00031  * intractable, such as for transition functions defined using Stochastic
00032  * Differential Equations (SDEs).
00033  *
00034  * For ease of use, KernelForwardBackwardSmoother handles proposal
00035  * distribution sampling and sample propagations internally.
00036  * 
00037  * A number of significant optimisations may be triggered using Flags. The
00038  * use of flags is entirely optional, and considered an advanced feature.
00039  * Not using flags will trigger the most generally applicable algorithms,
00040  * suitable in all situations. Using the right flags in the right situation
00041  * will give significant performance improvements. Using flags in the wrong
00042  * situation will give erronous results. Be sure to understand the 
00043  * assumptions implied by a flag, and be certain that those assumptions are
00044  * suitable, before putting it to use.
00045  * 
00046  * @see indii::ml::filter for general usage guidelines.
00047  *
00048  * @section KernelForwardBackwardSmoother_references References
00049  *
00050  * @anchor Murray2009
00051  * Murray, L. Bayesian Learning of Continuous Time Dynamical Systems (with
00052  * applications in Functional Magnetic Resonance Imaging). PhD thesis.
00053  * Online at http://www.indii.org/research/.
00054  */
00055 template <class T = unsigned int,
00056     class NT = Almost2Norm,
00057     class KT = AlmostGaussianKernel,
00058     class ST = MedianPartitioner>
00059 class KernelForwardBackwardSmoother :
00060     public Smoother<T,indii::ml::aux::DiracMixturePdf> {
00061 public:  
00062   /**
00063    * Constructor.
00064    * 
00065    * @param model Model to estimate.
00066    * @param N The kernel density norm.
00067    * @param K The kernel density kernel.
00068    * @param tT \f$t_T\f$; starting time.
00069    * @param p_xT \f$p(\mathbf{x}_T)\f$; prior over the state at time
00070    * \f$t_T\f$.
00071    */
00072   KernelForwardBackwardSmoother(KernelForwardBackwardSmootherModel<T>* model,
00073       const NT& N, const KT& K, const T tT,
00074       const indii::ml::aux::DiracMixturePdf& p_xT);
00075 
00076   /**
00077    * Destructor.
00078    */
00079   virtual ~KernelForwardBackwardSmoother();
00080 
00081   /**
00082    * Get the model being estimated.
00083    *
00084    * @return The model being estimated.
00085    */
00086   virtual KernelForwardBackwardSmootherModel<T>* getModel();
00087 
00088   /**
00089    * Step back in time and smooth.
00090    *
00091    * @param tn \f$t_n\f$; the time to which to rewind the
00092    * system. This must be less than the current time \f$t_{n+1}\f$.
00093    * @param p_xtn_ytn \f$p(\mathbf{x}_n\,|\,\mathbf{y}_{1:n})\f$;
00094    * filter density at time \f$t_n\f$. May be modified.
00095    * @param p_xtnp1_ytn \f$p(\mathbf{x}_{n+1}\,|\,\mathbf{y}_{1:n})\f$;
00096    * uncorrected filter density at time \f$t_{n+1}\f$. May be modified.
00097    * @param q_xtn \f$q(\mathbf{x}_n)\f$; proposal distribution.
00098    * @param flags Optimisation flags.
00099    *
00100    * @see Flags for optimisation flags.
00101    */
00102   void smooth(const T tn,
00103       indii::ml::aux::DiracMixturePdf& p_xtn_ytn,
00104       indii::ml::aux::DiracMixturePdf& p_xtnp1_ytn,
00105       indii::ml::aux::Pdf& q_xtn,
00106       const unsigned int flags = 0);
00107 
00108   /**
00109    * Step back in time and smooth, using %filter density as proposal
00110    * distribution.
00111    *
00112    * @param tn \f$t_n\f$; the time to which to rewind the
00113    * system. This must be less than the current time \f$t_{n+1}\f$.
00114    * @param p_xtn_ytn \f$p(\mathbf{x}_n\,|\,\mathbf{y}_{1:n})\f$;
00115    * filter density at time \f$t_n\f$. May be modified.
00116    * @param p_xtnp1_ytn \f$p(\mathbf{x}_{n+1}\,|\,\mathbf{y}_{1:n})\f$;
00117    * uncorrected filter density at time \f$t_{n+1}\f$. May be modified.
00118    * @param flags Optimisation flags.
00119    *
00120    * @see Flags for optimisation flags.
00121    */
00122   void smooth(const T tn,
00123       indii::ml::aux::DiracMixturePdf& p_xtn_ytn,
00124       indii::ml::aux::DiracMixturePdf& p_xtnp1_ytn,
00125       const unsigned int flags = 0);
00126 
00127   virtual indii::ml::aux::DiracMixturePdf smoothedMeasure();
00128 
00129   /**
00130    * Get last set of proposal samples.
00131    *
00132    * @return Set of proposal samples from last call to smooth().
00133    */
00134   indii::ml::aux::DiracMixturePdf& getProposals();
00135   
00136   /**
00137    * Get last set of proposal sample propagations.
00138    *
00139    * @return Set of proposal sample propagations from last call to smooth().
00140    */
00141   indii::ml::aux::DiracMixturePdf& getPropagations();
00142 
00143   /**
00144    * Resample the smoothed state.
00145    *
00146    * @see ParticleFilter::resample()
00147    */
00148   void smoothedResample(ParticleResampler* resampler);
00149 
00150 private:
00151   /**
00152    * Model.
00153    */
00154   KernelForwardBackwardSmootherModel<T>* model;
00155 
00156   /**
00157    * \f$\|\mathbf{x}\|_p\f$; the norm.
00158    */
00159   NT N;
00160   
00161   /**
00162    * \f$K(\|\mathbf{x}\|_p) \f$; the density kernel.
00163    */
00164   KT K;
00165 
00166   /**
00167    * Number of samples to use.
00168    */
00169   unsigned int P;
00170   
00171   /**
00172    * Proposal distribution samples.
00173    */
00174   indii::ml::aux::DiracMixturePdf q_xtns;
00175   
00176   /**
00177    * Proposal distribution sample densities.
00178    */
00179   indii::ml::aux::vector q_ptns;
00180   
00181   /**
00182    * Proposal distribution sample propagations.
00183    */
00184   indii::ml::aux::DiracMixturePdf q_xtnp1s;
00185   
00186   /**
00187    * Time difference for last proposal sample propagations.
00188    */
00189   T q_delta;
00190   
00191 };
00192 
00193     }
00194   }
00195 }
00196 
00197 #include "StratifiedParticleResampler.hpp"
00198 #include "../aux/kde.hpp"
00199 
00200 #include <assert.h>
00201 
00202 template <class T, class NT, class KT, class ST>
00203 indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::KernelForwardBackwardSmoother(
00204     KernelForwardBackwardSmootherModel<T>* model, const NT& N, const KT& K,
00205     const T tT, const indii::ml::aux::DiracMixturePdf& p_xT) :
00206     Smoother<T,indii::ml::aux::DiracMixturePdf>(tT, p_xT),
00207     model(model),
00208     N(N),
00209     K(K),
00210     q_xtns(model->getStateSize()),
00211     q_xtnp1s(model->getStateSize()) {
00212   P = p_xT.getDistributedSize(); 
00213 }
00214 
00215 template <class T, class NT, class KT, class ST>
00216 indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::~KernelForwardBackwardSmoother() {
00217   //
00218 }
00219 
00220 template <class T, class NT, class KT, class ST>
00221 indii::ml::filter::KernelForwardBackwardSmootherModel<T>*
00222     indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::getModel() {
00223   return model;
00224 }
00225 
00226 template <class T, class NT, class KT, class ST>
00227 void indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::smooth(
00228     const T tn,
00229     indii::ml::aux::DiracMixturePdf& p_xtn_ytn,
00230     indii::ml::aux::DiracMixturePdf& p_xtnp1_ytn,
00231     indii::ml::aux::Pdf& q_xtn,
00232     const unsigned int flags) {
00233   const unsigned int D = model->getStateSize();
00234     
00235   /* pre-conditions */
00236   assert (p_xtn_ytn.getDimensions() == D);
00237   assert (p_xtnp1_ytn.getDimensions() == D);
00238   assert (q_xtn.getDimensions() == D);
00239 
00240   vector x(D);
00241   const T del = this->tn - tn;
00242   DiracMixturePdf& p_xtnp1_ytT = this->p_xtn_ytT;
00243   unsigned int P_local = aux::shareOf(P);
00244   unsigned int i;
00245 
00246   /* pick apart flags */
00247   bool sameProposal = flags & SAME_PROPOSAL;
00248   bool samePropagations = flags & SAME_PROPAGATIONS;
00249   bool noResampling = flags & NO_RESAMPLING;
00250   bool noStandardisation = flags & NO_STANDARDISATION;
00251 
00252   /* sample particles from proposal */
00253   if (!sameProposal || q_xtns.getSize() == 0) {
00254     q_xtns.clear();
00255     q_ptns.resize(P_local);
00256     for (i = 0; i < P_local; i++) {
00257       noalias(x) = q_xtn.sample();
00258       q_xtns.add(x);
00259       q_ptns(i) = q_xtn.densityAt(x);
00260     }
00261   }
00262 
00263   /* propagate sample particles */
00264   if (!samePropagations || !sameProposal || this->q_delta != del ||
00265       q_xtns.getSize() != q_xtnp1s.getSize()) {
00266     this->q_delta = del;
00267     q_xtnp1s.clear();
00268     for (i = 0; i < q_xtns.getSize(); i++) {
00269       noalias(x) = model->transition(q_xtns.get(i), tn, del);
00270       q_xtnp1s.add(x, q_xtns.getWeight(i));
00271     }
00272   }
00273 
00274   aux::vector pi(P_local), delta(P_local), psi(P_local);
00275     
00276   if (noStandardisation) {
00277     /* filter density evaluation */
00278     {
00279       //p_xtn_ytn.redistributeBySpace();
00280       aux::KDTree<ST> queryTree(&q_xtns);
00281       aux::KDTree<ST> targetTree(&p_xtn_ytn);    
00282       noalias(pi) = aux::distributedDualTreeDensity(queryTree,
00283           targetTree, p_xtn_ytn.getWeights(), N, K);
00284     }
00285     
00286     /* uncorrected and smooth densities have same support, evaluate
00287      * together */
00288     {
00289       assert (p_xtnp1_ytn.getSize() == p_xtnp1_ytT.getSize());
00290 
00291       aux::KDTree<ST> queryTree(&q_xtnp1s);
00292       aux::KDTree<ST> targetTree(&p_xtnp1_ytT);
00293 
00294       aux::matrix ws(2,p_xtnp1_ytn.getSize());
00295       row(ws,0) = p_xtnp1_ytT.getWeights();
00296       row(ws,1) = p_xtnp1_ytn.getWeights();
00297       
00298       aux::matrix result(2,q_xtnp1s.getSize());
00299       noalias(result) = aux::distributedDualTreeDensity(queryTree,
00300           targetTree, ws, N, K);
00301           
00302       noalias(psi) = row(result,0);
00303       noalias(delta) = row(result,1);
00304     }
00305   } else {
00306     /* filter density evaluation */
00307     {
00308       aux::vector mu(D);
00309       aux::lower_triangular_matrix L(D,D);
00310       noalias(mu) = p_xtn_ytn.getDistributedExpectation();
00311       noalias(L) = p_xtn_ytn.getDistributedStandardDeviation();
00312   
00313       DiracMixturePdf q(q_xtns);
00314       q.standardise(mu, L);
00315       p_xtn_ytn.standardise(mu, L);
00316 
00317       //p_xtn_ytn.redistributeBySpace();
00318       aux::KDTree<ST> queryTree(&q);
00319       aux::KDTree<ST> targetTree(&p_xtn_ytn);    
00320       noalias(pi) = aux::distributedDualTreeDensity(queryTree,
00321           targetTree, p_xtn_ytn.getWeights(), N, K);
00322     }
00323     
00324     /* smooth density evaluation */
00325     {
00326       aux::vector mu(D);
00327       aux::lower_triangular_matrix L(D,D);
00328       noalias(mu) = p_xtnp1_ytT.getDistributedExpectation();
00329       noalias(L) = p_xtnp1_ytT.getDistributedStandardDeviation();
00330   
00331       DiracMixturePdf q(q_xtnp1s);
00332       q.standardise(mu, L);
00333       p_xtnp1_ytT.standardise(mu, L);
00334 
00335       //p_xtnp1_ytT.redistributeBySpace();
00336       aux::KDTree<ST> queryTree(&q);
00337       aux::KDTree<ST> targetTree(&p_xtnp1_ytT);
00338       noalias(psi) = aux::distributedDualTreeDensity(queryTree,
00339           targetTree, p_xtnp1_ytT.getWeights(), N, K);
00340     }
00341     
00342     /* uncorrected filter density evaluation */
00343     {
00344       aux::vector mu(D);
00345       aux::lower_triangular_matrix L(D,D);
00346       noalias(mu) = p_xtnp1_ytn.getDistributedExpectation();
00347       noalias(L) = p_xtnp1_ytn.getDistributedStandardDeviation();
00348   
00349       DiracMixturePdf q(q_xtnp1s);
00350       q.standardise(mu, L);
00351       p_xtnp1_ytn.standardise(mu, L);
00352 
00353       //p_xtnp1_ytn.redistributeBySpace();
00354       aux::KDTree<ST> queryTree(&q);
00355       aux::KDTree<ST> targetTree(&p_xtnp1_ytn);
00356       noalias(delta) = aux::distributedDualTreeDensity(queryTree,
00357           targetTree, p_xtnp1_ytn.getWeights(), N, K);
00358     }
00359   }
00360 
00361   /* build smoothed distribution */
00362   psi = element_div(element_prod(pi,psi), element_prod(delta,q_ptns));
00363   this->p_xtn_ytT = q_xtns;
00364   this->p_xtn_ytT.setWeights(psi);
00365   
00366   /* for at least one system, double well, normalisation has proved
00367    * necessary to prevent degeneracy, and so we include it here. */
00368   this->p_xtn_ytT.distributedNormalise();
00369   
00370   /* update state */
00371   this->tn = tn;
00372 }
00373 
00374 template <class T, class NT, class KT, class ST>
00375 void indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::smooth(
00376     const T tn,
00377     indii::ml::aux::DiracMixturePdf& p_xtn_ytn,
00378     indii::ml::aux::DiracMixturePdf& p_xtnp1_ytn,
00379     const unsigned int flags) {
00380   const unsigned int D = model->getStateSize();
00381     
00382   /* pre-conditions */
00383   assert (p_xtn_ytn.getDimensions() == D);
00384   assert (p_xtnp1_ytn.getDimensions() == D);
00385 
00386   vector x(D);
00387   const T del = this->tn - tn;
00388   DiracMixturePdf& p_xtnp1_ytT = this->p_xtn_ytT;
00389   unsigned int P_local = p_xtn_ytn.getSize();
00390   unsigned int i;
00391 
00392   /* pick apart flags */
00393   bool noResampling = flags & NO_RESAMPLING;
00394   bool noStandardisation = flags & NO_STANDARDISATION;
00395   bool filterSmoother = flags & FILTER_SMOOTHER;
00396 
00397   q_xtns = p_xtn_ytn;
00398   if (noResampling && noStandardisation && filterSmoother) {
00399     /* use filter-smoother */
00400     q_xtnp1s = p_xtnp1_ytT;
00401     assert (p_xtn_ytn.getSize() == p_xtnp1_ytT.getSize());  
00402 
00403     aux::vector psi(p_xtnp1_ytT.getWeights());
00404     this->p_xtn_ytT = p_xtn_ytn;
00405     this->p_xtn_ytT.setWeights(psi);
00406 
00407     /* update state */
00408     this->tn = tn;
00409 
00410     return;
00411   } else if (noResampling) {
00412     /* use p_xtnp1_ytn particles as propagations */
00413     q_xtnp1s = p_xtnp1_ytT;
00414     assert (p_xtn_ytn.getSize() == p_xtnp1_ytT.getSize());  
00415   } else {
00416     /* propagate filter particles */
00417     this->q_delta = del;
00418     q_xtnp1s.clear();
00419     for (i = 0; i < p_xtn_ytn.getSize(); i++) {
00420       noalias(x) = model->transition(p_xtn_ytn.get(i), tn, del);
00421       q_xtnp1s.add(x, p_xtn_ytn.getWeight(i));
00422     }
00423   }
00424 
00425   const aux::vector& pi = p_xtn_ytn.getWeights();
00426   aux::vector delta(P_local), psi(P_local);
00427   
00428   if (noStandardisation) {
00429     /* uncorrected and smooth densities have same support, evaluate
00430      * together */
00431     assert (p_xtnp1_ytn.getSize() == p_xtnp1_ytT.getSize());
00432     
00433     if (noResampling) {
00434       /* query tree also has same support, perform self-tree evaluation */
00435       aux::KDTree<ST> tree(&p_xtnp1_ytT);
00436 
00437       aux::matrix ws(2,p_xtnp1_ytn.getSize());
00438       row(ws,0) = p_xtnp1_ytT.getWeights();
00439       row(ws,1) = p_xtnp1_ytn.getWeights();
00440 
00441       aux::matrix result(2,p_xtnp1_ytn.getSize());
00442       noalias(result) = aux::distributedSelfTreeDensity(tree, ws, N, K,
00443           false);
00444 
00445       noalias(psi) = row(result,0);
00446       noalias(delta) = row(result,1);
00447     } else {
00448       /* uncorrected and smooth densities have same support, evaluate
00449        * together */
00450       assert (p_xtnp1_ytn.getSize() == p_xtnp1_ytT.getSize());
00451 
00452       //p_xtnp1_ytT.redistributeBySpace();
00453       //p_xtnp1_ytn.redistributeBySpace();
00454       aux::KDTree<ST> queryTree(&q_xtnp1s);
00455       aux::KDTree<ST> targetTree(&p_xtnp1_ytT);
00456 
00457       aux::matrix ws(2,p_xtnp1_ytn.getSize());
00458       row(ws,0) = p_xtnp1_ytT.getWeights();
00459       row(ws,1) = p_xtnp1_ytn.getWeights();
00460       
00461       aux::matrix result(2,q_xtnp1s.getSize());
00462       noalias(result) = aux::distributedDualTreeDensity(queryTree,
00463           targetTree, ws, N, K);
00464 
00465       noalias(psi) = row(result,0);
00466       noalias(delta) = row(result,1);
00467     }
00468   } else {
00469     /* smooth density evaluation */
00470     {
00471       aux::vector mu(D);
00472       aux::lower_triangular_matrix L(D,D);
00473       noalias(mu) = p_xtnp1_ytT.getDistributedExpectation();
00474       noalias(L) = p_xtnp1_ytT.getDistributedStandardDeviation();
00475       p_xtnp1_ytT.standardise(mu, L);
00476       //p_xtnp1_ytT.redistributeBySpace();
00477 
00478       if (noResampling) {
00479         /* query tree has same support, perform self-tree evaluation */
00480         aux::KDTree<ST> tree(&p_xtnp1_ytT);
00481         noalias(delta) = aux::distributedSelfTreeDensity(tree,
00482             p_xtnp1_ytT.getWeights(), N, K);
00483       } else {
00484         DiracMixturePdf q(q_xtnp1s);
00485         q.standardise(mu, L);
00486 
00487         aux::KDTree<ST> queryTree(&q);
00488         aux::KDTree<ST> targetTree(&p_xtnp1_ytT);
00489         noalias(delta) = aux::distributedDualTreeDensity(queryTree,
00490             targetTree, p_xtnp1_ytT.getWeights(), N, K);
00491       }
00492     }
00493   
00494     /* uncorrected filter density evaluation */
00495     {
00496       aux::vector mu(D);
00497       aux::lower_triangular_matrix L(D,D);
00498       noalias(mu) = p_xtnp1_ytn.getDistributedExpectation();
00499       noalias(L) = p_xtnp1_ytn.getDistributedStandardDeviation();
00500       p_xtnp1_ytn.standardise(mu, L);
00501       //p_xtnp1_ytn.redistributeBySpace();
00502   
00503       if (noResampling) {
00504         /* query tree has same support, perform self-tree evaluation */
00505         aux::KDTree<ST> tree(&p_xtnp1_ytn);
00506         noalias(delta) = aux::distributedSelfTreeDensity(tree,
00507             p_xtnp1_ytn.getWeights(), N, K);
00508       } else {
00509         DiracMixturePdf q(q_xtnp1s);
00510         q.standardise(mu, L);
00511 
00512         aux::KDTree<ST> queryTree(&q);
00513         aux::KDTree<ST> targetTree(&p_xtnp1_ytn);
00514         noalias(delta) = aux::distributedDualTreeDensity(queryTree,
00515             targetTree, p_xtnp1_ytn.getWeights(), N, K);
00516       }
00517     }
00518   }
00519   
00520   /* build smoothed distribution */
00521   psi = element_div(element_prod(pi,psi), delta);
00522   this->p_xtn_ytT = p_xtn_ytn;
00523   this->p_xtn_ytT.setWeights(psi);
00524 
00525   /* for at least one system, double well, normalisation has proved
00526    * necessary to prevent degeneracy, and so we include it here. */
00527   this->p_xtn_ytT.distributedNormalise();
00528   
00529   /* update state */
00530   this->tn = tn;
00531 }
00532 
00533 template <class T, class NT, class KT, class ST>
00534 inline indii::ml::aux::DiracMixturePdf&
00535     indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::getProposals() {
00536   return q_xtns;
00537 }
00538 
00539 template <class T, class NT, class KT, class ST>
00540 inline indii::ml::aux::DiracMixturePdf&
00541     indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::getPropagations() {
00542   return q_xtnp1s;
00543 }
00544 
00545 template <class T, class NT, class KT, class ST>
00546 indii::ml::aux::DiracMixturePdf
00547     indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::smoothedMeasure() {
00548   namespace aux = indii::ml::aux;
00549     
00550   unsigned int i;
00551   StratifiedParticleResampler resampler;
00552   aux::DiracMixturePdf resampled(resampler.resample(
00553       this->getSmoothedState()));
00554   indii::ml::aux::DiracMixturePdf p_ytn_xtT(model->getMeasurementSize());
00555   
00556   for (i = 0; i < resampled.getSize(); i++) {
00557     p_ytn_xtT.add(model->measure(resampled.get(i)));
00558   }
00559 
00560   return p_ytn_xtT;
00561 }
00562 
00563 template <class T, class NT, class KT, class ST>
00564 void indii::ml::filter::KernelForwardBackwardSmoother<T,NT,KT,ST>::smoothedResample(
00565     ParticleResampler* resampler) {
00566   indii::ml::aux::DiracMixturePdf resampled(resampler->resample(
00567       this->getSmoothedState()));
00568   this->setSmoothedState(resampled);
00569 }
00570 
00571 #endif

Generated on Wed Dec 17 15:11:57 2008 for dysii Dynamical Systems Library by  doxygen 1.5.3