00001 #ifndef INDII_ML_AUX_GAUSSIANPDF_HPP 00002 #define INDII_ML_AUX_GAUSSIANPDF_HPP 00003 00004 #include "Pdf.hpp" 00005 00006 #include "boost/serialization/split_member.hpp" 00007 00008 namespace indii { 00009 namespace ml { 00010 namespace aux { 00011 /** 00012 * Multivariate Gaussian probability distribution. 00013 * 00014 * @author Lawrence Murray <lawrence@indii.org> 00015 * @version $Rev: 538 $ 00016 * @date $Date: 2008-08-31 14:41:10 +0100 (Sun, 31 Aug 2008) $ 00017 * 00018 * @section Serialization 00019 * 00020 * This class supports serialization through the Boost.Serialization 00021 * library. Two issues are worth noting. 00022 * 00023 * Firstly, Boost.uBLAS has limited serialization support in its CVS 00024 * snapshot at time of writing (3 Sep 2007). It appears limited to the 00025 * serialization of basic vectors and matrices: symmetric matrices do 00026 * not appear to be supported, for example. 00027 * 00028 * Secondly, it's arguable whether serializing pre-calculations is 00029 * useful or not. It makes for larger messages in a parallel 00030 * environment, the cost of sending which may exceed the savings of 00031 * the precalculations in the first place. 00032 * 00033 * The implementation here takes the path of least resistance in both 00034 * cases. We choose to use the limited Boost.uBLAS serialization 00035 * features anyway for ease of implementation, presuming that 00036 * eventually full serialization support will be provided. We also 00037 * choose to serialize just the bare essentials, excluding 00038 * pre-calculation results. 00039 */ 00040 class GaussianPdf : public Pdf { 00041 public: 00042 /** 00043 * Default constructor. 00044 * 00045 * Initialises the Gaussian with zero dimensions. This should 00046 * generally only be used when the object is to be restored from a 00047 * serialization. 00048 */ 00049 GaussianPdf(); 00050 00051 /** 00052 * Construct Gaussian given mean and covariance. 00053 * 00054 * @param mu \f$\mathbf{\mu}\f$; mean of the Gaussian. 00055 * @param sigma \f$\Sigma\f$; covariance of the Gaussian. 00056 */ 00057 GaussianPdf(const vector& mu, const symmetric_matrix& sigma); 00058 00059 /** 00060 * Construct Gaussian given dimensionality. The mean and covariance 00061 * remain uninitialised, and should be set with setExpectation() and 00062 * setCovariance(). 00063 * 00064 * @param N \f$N\f$; number of dimensions of the Gaussian. 00065 */ 00066 GaussianPdf(unsigned int N); 00067 00068 /** 00069 * Assignment operator. Both sides must have the same dimensionality. 00070 */ 00071 GaussianPdf& operator=(const GaussianPdf& o); 00072 00073 /** 00074 * Destructor. 00075 */ 00076 virtual ~GaussianPdf(); 00077 00078 virtual void setDimensions(const unsigned int N, 00079 const bool preserve = false); 00080 00081 /** 00082 * Get the expected value of the distribution. 00083 * 00084 * @return \f$\mathbf{\mu}\f$; expected value of the distribution. 00085 */ 00086 virtual const vector& getExpectation() const; 00087 00088 /** 00089 * Get the covariance of the distribution. 00090 * 00091 * @return \f$\Sigma\f$; covariance of the distribution. 00092 */ 00093 virtual const symmetric_matrix& getCovariance() const; 00094 00095 virtual const vector& getExpectation(); 00096 00097 virtual const symmetric_matrix& getCovariance(); 00098 00099 /** 00100 * Set the mean of the Gaussian. The new mean vector must have the 00101 * same size as the previous one. 00102 * 00103 * @param mu \f$\mathbf{\mu}\f$; mean of the Gaussian. 00104 */ 00105 virtual void setExpectation(const vector& mu); 00106 00107 /** 00108 * Set the covariance of the Gaussian. The new covariance matrix 00109 * must have the same size as the previous one. 00110 * 00111 * @param sigma \f$\Sigma\f$; covariance of the Gaussian. 00112 */ 00113 virtual void setCovariance(const symmetric_matrix& sigma); 00114 00115 /** 00116 * Sample from the Gaussian. 00117 * 00118 * @li Let \f$\mathbf{z}\f$ be a vector of \f$N\f$ independent 00119 * normal variates. 00120 * @li Then the sample is \f$\mathbf{x} = \mathbf{\mu} + 00121 * L\mathbf{z}\f$, where \f$L\f$ is the Cholesky decomposition of 00122 * the covariance matrix \f$\Sigma\f$. 00123 * 00124 * @return The sample \f$\mathbf{x}\f$ 00125 */ 00126 virtual vector sample(); 00127 00128 /** 00129 * @deprecated Use densityAt() instead. 00130 */ 00131 double calculateDensity(const vector& x); 00132 00133 /** 00134 * Calculate the density at a given point. 00135 * 00136 * \f[p(\mathbf{x}) = \frac{1}{\sqrt{(2\pi)^N|\Sigma|}} 00137 * \exp\big({-\frac{1}{2}(\mathbf{x}-\mathbf{\mu})^T \Sigma^{-1} 00138 * (\mathbf{x}-\mathbf{\mu})\big)} 00139 * \f] 00140 * 00141 * @param x \f$\mathbf{x}\f$; point at which to evaluate the 00142 * density. 00143 * 00144 * @return \f$p(\mathbf{x})\f$; the density at \f$\mathbf{x}\f$. 00145 */ 00146 virtual double densityAt(const vector& x); 00147 00148 protected: 00149 /** 00150 * Called when changes are made to the distribution, such as when 00151 * the expectation or covariance is changed. This allows 00152 * pre-calculations to be refreshed. 00153 */ 00154 virtual void dirty(); 00155 00156 private: 00157 /** 00158 * \f$\mu\f$; mean of the distribution. 00159 */ 00160 vector mu; 00161 00162 /** 00163 * \f$\Sigma\f$; covariance of the distribution. 00164 */ 00165 symmetric_matrix sigma; 00166 00167 /** 00168 * \f$L\f$ where \f$\Sigma = LL^T\f$ is the Cholesky decomposition 00169 * of the covariance matrix. 00170 */ 00171 lower_triangular_matrix L; 00172 00173 /** 00174 * \f$\Sigma^{-1}\f$ 00175 */ 00176 symmetric_matrix sigmaI; 00177 00178 /** 00179 * Diagonal of \f$\Sigma^{-1}\f$. 00180 */ 00181 vector sigmaIDiag; 00182 00183 /** 00184 * \f$\det(\Sigma)\f$ 00185 */ 00186 double sigmaDet; 00187 00188 /** 00189 * \f$\frac{1}{Z}\f$ 00190 */ 00191 double ZI; 00192 00193 /** 00194 * Is the expectation zero? 00195 */ 00196 bool isMuZero; 00197 00198 /** 00199 * Is the covariance matrix diagonal? 00200 */ 00201 bool isSigmaIDiagonal; 00202 00203 /** 00204 * Has the Cholesky decomposition of the covariance matrix been computed? 00205 */ 00206 bool haveCholesky; 00207 00208 /** 00209 * Has the inverse of the covariance matrix been computed? 00210 */ 00211 bool haveInverse; 00212 00213 /** 00214 * Has the determinant of the covariance matrix been computed? 00215 */ 00216 bool haveDeterminant; 00217 00218 /** 00219 * Has the normalising constant for the density function been 00220 * computed? 00221 */ 00222 bool haveZ; 00223 00224 /** 00225 * Have optimisations for density calculations been determined? 00226 */ 00227 bool haveDensityOptimisations; 00228 00229 /** 00230 * Calculate the Cholesky decomposition of the covariance matrix. 00231 */ 00232 void calculateCholesky(); 00233 00234 /** 00235 * Calculate the inverse of the covariance matrix. This exploits the 00236 * calculation of the Cholesky decomposition 00237 */ 00238 void calculateInverse(); 00239 00240 /** 00241 * Calculate the determinant of the covariance matrix, 00242 * \f$|\Sigma|\f$. This exploits the calculation of the Cholesky 00243 * decomposition \f$L\f$. For any matrices \f$A\f$ and \f$B\f$, 00244 * \f$|AB| = |A||B|\f$. Given that \f$\Sigma = LL^T\f$, \f$|\Sigma| 00245 * = |L|^2\f$. The determinant of a triangular matrix is simply the 00246 * product of the elements on its diagonal, so \f$|L|\f$ is easy to 00247 * calculate. 00248 */ 00249 void calculateDeterminant(); 00250 00251 /** 00252 * Calculate the normalising constant \f$\frac{1}{Z}\f$ for the 00253 * density function. 00254 * 00255 * \f[Z = \sqrt{(2\pi)^N|\Sigma|}\f] 00256 */ 00257 void calculateZ(); 00258 00259 /** 00260 * Calculate optimisations for the calculation of densities. 00261 */ 00262 void calculateDensityOptimisations(); 00263 00264 /** 00265 * Serialize. 00266 */ 00267 template<class Archive> 00268 void save(Archive& ar, const unsigned int version) const; 00269 00270 /** 00271 * Restore from serialization. 00272 */ 00273 template<class Archive> 00274 void load(Archive& ar, const unsigned int version); 00275 00276 /* 00277 * Boost.Serialization requirements. 00278 */ 00279 BOOST_SERIALIZATION_SPLIT_MEMBER() 00280 friend class boost::serialization::access; 00281 00282 }; 00283 00284 } 00285 } 00286 } 00287 00288 #include "boost/serialization/base_object.hpp" 00289 00290 template<class Archive> 00291 void indii::ml::aux::GaussianPdf::save(Archive& ar, 00292 const unsigned int version) const { 00293 ar & boost::serialization::base_object<Pdf>(*this); 00294 ar & mu; 00295 00296 /* serialization of symmetric_matrix not yet supported in ublas */ 00297 const aux::matrix tmp(sigma); 00298 ar & tmp; 00299 } 00300 00301 template<class Archive> 00302 void indii::ml::aux::GaussianPdf::load(Archive& ar, 00303 const unsigned int version) { 00304 ar & boost::serialization::base_object<Pdf>(*this); 00305 00306 mu.resize(N, false); 00307 sigma.resize(N, false); 00308 L.resize(N,N, false); 00309 sigmaI.resize(N, false); 00310 sigmaIDiag.resize(N, false); 00311 00312 ar & mu; 00313 00314 /* serialization of symmetric_matrix not yet supported in ublas */ 00315 aux::matrix tmp(N,N); 00316 ar & tmp; 00317 noalias(sigma) = boost::numeric::ublas::symmetric_adaptor<aux::matrix>(tmp); 00318 00319 haveCholesky = false; 00320 haveInverse = false; 00321 haveDeterminant = false; 00322 haveZ = false; 00323 haveDensityOptimisations = false; 00324 } 00325 00326 #endif
1.5.3