indii/ml/aux/GaussianPdf.cpp

00001 //#if defined(__GNUC__) && defined(GCC_PCH)
00002 //  #include "aux.hpp"
00003 //#else
00004   #include "GaussianPdf.hpp"
00005   #include "Random.hpp"
00006 //#endif
00007 
00008 #include "boost/numeric/bindings/traits/ublas_matrix.hpp"
00009 #include "boost/numeric/bindings/traits/ublas_vector.hpp"
00010 #include "boost/numeric/bindings/traits/ublas_symmetric.hpp"
00011 #include "boost/numeric/bindings/lapack/lapack.hpp"
00012 
00013 #include <assert.h>
00014 
00015 using namespace indii::ml::aux;
00016 
00017 namespace ublas = boost::numeric::ublas;
00018 namespace lapack = boost::numeric::bindings::lapack;
00019 
00020 GaussianPdf::GaussianPdf() : mu(0), sigma(0), L(0,0), sigmaI(0),
00021     sigmaIDiag(0), haveCholesky(false), haveInverse(false),
00022     haveDeterminant(false), haveZ(false), haveDensityOptimisations(false) {
00023   //
00024 }
00025 
00026 GaussianPdf::GaussianPdf(const vector& mu, const symmetric_matrix& sigma) :
00027     Pdf(mu.size()), mu(N), sigma(N), L(N,N), sigmaI(N), sigmaIDiag(N),
00028     haveCholesky(false), haveInverse(false), haveDeterminant(false),
00029     haveZ(false), haveDensityOptimisations(false) {
00030   /* pre-condition */
00031   assert(mu.size() == sigma.size1());
00032 
00033   /* expectation */ 
00034   setExpectation(mu);
00035 
00036   /* covariance */
00037   setCovariance(sigma);
00038 }
00039 
00040 GaussianPdf::GaussianPdf(unsigned int N) : Pdf(N), mu(N), sigma(N), L(N,N),
00041     sigmaI(N), sigmaIDiag(N), haveCholesky(false), haveInverse(false),
00042     haveDeterminant(false), haveZ(false), haveDensityOptimisations(false) {
00043   //
00044 }
00045 
00046 GaussianPdf& GaussianPdf::operator=(const GaussianPdf& o) {
00047   /* pre-condition */
00048   assert (o.N == N);
00049 
00050   haveCholesky = o.haveCholesky;
00051   haveInverse = o.haveInverse;
00052   haveDeterminant = o.haveDeterminant;
00053   haveZ = o.haveZ;
00054   haveDensityOptimisations = o.haveDensityOptimisations;
00055 
00056   mu = o.mu;
00057   sigma = o.sigma;
00058   if (haveCholesky) {
00059     L = o.L;
00060   }
00061   if (haveInverse) {
00062     sigmaI = o.sigmaI;
00063   }
00064   if (haveDeterminant) {
00065     sigmaDet = o.sigmaDet;
00066   }
00067   if (haveZ) {
00068     ZI = o.ZI;
00069   }
00070   if (haveDensityOptimisations) {
00071     isMuZero = o.isMuZero;
00072     isSigmaIDiagonal = o.isSigmaIDiagonal;
00073     if (isSigmaIDiagonal) {
00074       sigmaIDiag = o.sigmaIDiag;
00075     }
00076   }
00077 
00078   return *this;
00079 }
00080 
00081 GaussianPdf::~GaussianPdf() {
00082   //
00083 }
00084 
00085 void GaussianPdf::setDimensions(const unsigned int N, const bool preserve) {
00086   this->N = N;
00087 
00088   mu.resize(N, preserve);
00089   sigma.resize(N, preserve);
00090   L.resize(N, N, preserve);
00091   sigmaI.resize(N);
00092   sigmaIDiag.resize(N);
00093 
00094   dirty();
00095 }
00096 
00097 const vector& GaussianPdf::getExpectation() const {
00098   return mu;
00099 }
00100 
00101 const symmetric_matrix& GaussianPdf::getCovariance() const {
00102   return sigma;
00103 }
00104 
00105 const vector& GaussianPdf::getExpectation() {
00106   return mu;
00107 }  
00108 
00109 const symmetric_matrix& GaussianPdf::getCovariance() {
00110   return sigma;
00111 }
00112 
00113 void GaussianPdf::setExpectation(const vector& mu) {
00114   /* pre-condition */
00115   assert(mu.size() == N); // new same size as old
00116 
00117   this->mu = mu;
00118   dirty();
00119 }
00120 
00121 void GaussianPdf::setCovariance(const symmetric_matrix& sigma) {
00122   /* pre-condition */
00123   assert(sigma.size1() == N); // new same size as old
00124 
00125   this->sigma = sigma;
00126   dirty();
00127 }
00128 
00129 vector GaussianPdf::sample() {
00130   vector z(N);
00131   unsigned int i;
00132 
00133   if (!haveCholesky) {
00134     calculateCholesky();
00135     assert(haveCholesky);
00136   }
00137 
00138   /* \f$x\f$ sampled from \f$\mathcal{N}(\mathbf{0_N}, I_N)\f$ */
00139   for (i = 0; i < N; i++) {
00140     z(i) = Random::gaussian(0.0, 1.0);
00141   }
00142 
00143   /* \f$\mathbf{x} = L\mathbf{z} + \mu*/
00144   return mu + prod(L,z);
00145 }
00146 
00147 double GaussianPdf::densityAt(const vector& x) {
00148   if (!haveDensityOptimisations) {
00149     calculateDensityOptimisations();
00150     assert(haveDensityOptimisations);
00151   }
00152 
00153   double exponent;
00154   if (isMuZero && isSigmaIDiagonal) {
00155     exponent = inner_prod(x, element_prod(sigmaIDiag, x));
00156   } else {
00157     if (isMuZero) {
00158       exponent = inner_prod(x, prod(sigmaI, x));
00159     } else {
00160       aux::vector d(x - mu);
00161       if (isSigmaIDiagonal) {
00162         exponent = inner_prod(d, element_prod(sigmaIDiag, d));
00163       } else {
00164         exponent = inner_prod(d, prod(sigmaI, d));
00165       }
00166     }
00167   }
00168   
00169   double p = ZI * exp(-0.5 * exponent);
00170   if (isnan(p)) {
00171     p = 0.0;
00172   }
00173   
00174   /* post-condition */
00175   assert (p >= 0.0);
00176   
00177   return p;
00178 }
00179 
00180 void GaussianPdf::dirty() {
00181   haveCholesky = false;
00182   haveInverse = false;
00183   haveDeterminant = false;
00184   haveZ = false;
00185   haveDensityOptimisations = false;
00186 }
00187 
00188 double GaussianPdf::calculateDensity(const vector& x) {
00189   return densityAt(x);
00190 }
00191 
00192 void GaussianPdf::calculateCholesky() {
00193   symmetric_matrix tmp(sigma);
00194   int err;
00195 
00196   err = lapack::pptrf(tmp);
00197   assert (err == 0);
00198   noalias(L) = ublas::triangular_adaptor<symmetric_matrix, ublas::lower>(tmp);
00199   haveCholesky = true;
00200   
00201   /* post-condition */
00202   assert(haveCholesky);
00203 }
00204 
00205 void GaussianPdf::calculateInverse() {
00206   if (!haveCholesky) {
00207     calculateCholesky();
00208     assert(haveCholesky);
00209   }
00210 
00211   matrix X(L), I(N,N);
00212   I = identity_matrix(N);
00213 
00214   int err;
00215   err = lapack::potrs('L', X, I);
00216   assert (err == 0);
00217   noalias(sigmaI) = ublas::symmetric_adaptor<matrix, ublas::lower>(I);
00218   haveInverse = true;
00219 
00220   /* post-condition */
00221   assert(haveInverse);
00222 }
00223 
00224 void GaussianPdf::calculateDeterminant() {
00225   if (!haveCholesky) {
00226     calculateCholesky();
00227     assert(haveCholesky);
00228   }
00229 
00230   ublas::matrix_vector_range<lower_triangular_matrix> d = diag(L);
00231   assert(d.size() > 0);
00232   unsigned int i;
00233   double LDet = d(0);
00234   for (i = 1; i < d.size(); i++) {
00235     LDet *= d(i);
00236   }
00237   sigmaDet = LDet*LDet;
00238   haveDeterminant = true;
00239 
00240   /* post-condition */
00241   assert(haveDeterminant);
00242 }
00243 
00244 void GaussianPdf::calculateZ() {
00245   if (!haveDeterminant) {
00246     calculateDeterminant();
00247     assert(haveDeterminant);
00248   }
00249   ZI = 1.0 / sqrt(pow(2*M_PI,N) * sigmaDet);
00250   haveZ = true;
00251 
00252   /* post-condition */
00253   assert(haveZ);
00254 }
00255 
00256 void GaussianPdf::calculateDensityOptimisations() {
00257   unsigned int i, j;
00258 
00259   if (!haveDeterminant) {
00260     calculateDeterminant();
00261     assert(haveDeterminant);
00262   }
00263   if (!haveInverse) {
00264     calculateInverse();
00265     assert(haveInverse);
00266   }
00267   if (!haveZ) {
00268     calculateZ();
00269     assert(haveZ);
00270   }
00271 
00272   /* is expectation zero? */
00273   isMuZero = true;
00274   for (i = 0; i < N && isMuZero; i++) {
00275     isMuZero = isMuZero && mu(i) == 0.0;
00276   }
00277 
00278   /* is covariance diagonal? */
00279   isSigmaIDiagonal = true;
00280   for (j = 0; j < N && isSigmaIDiagonal; j++) {
00281     for (i = j+1; i < N && isSigmaIDiagonal; i++) {
00282       isSigmaIDiagonal = isSigmaIDiagonal && sigma(i,j) == 0.0;
00283       /* ^ better to check sigma rather than sigmaI elements here --
00284            probably supplied by user, making floating point equality
00285            comparisons reliable. */
00286     }
00287   }
00288   if (isSigmaIDiagonal) {
00289     noalias(sigmaIDiag) = diag(sigmaI);
00290   }
00291 
00292   haveDensityOptimisations = true;
00293 
00294   /* post-condition */
00295   assert (haveDensityOptimisations);
00296 }
00297 

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