00001
00002
00003
00004 #include "GaussianPdf.hpp"
00005 #include "Random.hpp"
00006
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
00031 assert(mu.size() == sigma.size1());
00032
00033
00034 setExpectation(mu);
00035
00036
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
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
00115 assert(mu.size() == N);
00116
00117 this->mu = mu;
00118 dirty();
00119 }
00120
00121 void GaussianPdf::setCovariance(const symmetric_matrix& sigma) {
00122
00123 assert(sigma.size1() == N);
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
00139 for (i = 0; i < N; i++) {
00140 z(i) = Random::gaussian(0.0, 1.0);
00141 }
00142
00143
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
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
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
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
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
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
00273 isMuZero = true;
00274 for (i = 0; i < N && isMuZero; i++) {
00275 isMuZero = isMuZero && mu(i) == 0.0;
00276 }
00277
00278
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
00284
00285
00286 }
00287 }
00288 if (isSigmaIDiagonal) {
00289 noalias(sigmaIDiag) = diag(sigmaI);
00290 }
00291
00292 haveDensityOptimisations = true;
00293
00294
00295 assert (haveDensityOptimisations);
00296 }
00297