gaussianDistribution.h

Go to the documentation of this file.
00001 
00014 #ifndef _DLR_GAUSSIANDISTRIBUTION_H_
00015 #define _DLR_GAUSSIANDISTRIBUTION_H_
00016 
00017 #include <iostream>
00018 #include <string>
00019 #include <dlrNumeric/array1D.h>
00020 #include <dlrNumeric/array2D.h>
00021 
00022 namespace dlr {
00023 
00047   template <class VectorType>
00048   class GaussianDistribution {
00049   public:
00050     /* ******** Public typedefs ******** */
00051     
00057     typedef VectorType vector_type;
00058     
00059     /* ******** Public member functions ******** */
00060 
00064     GaussianDistribution();
00065 
00066     
00074      GaussianDistribution(const Array1D<double>& mean,
00075                           const Array2D<double>& covariance);
00076 
00077     
00083     GaussianDistribution(const GaussianDistribution<VectorType> &source);
00084 
00085     
00090     ~GaussianDistribution();
00091 
00092     
00101     inline void
00102     addSample(const VectorType& point);
00103 
00104     
00115     inline size_t
00116     getCardinality() const {return m_sampleData.size();}
00117 
00118     
00127     inline const Array2D<double>&
00128     getCovariance() const;
00129 
00130     
00139     inline const Array1D<double>&
00140     getMean() const;
00141 
00142     
00156     void
00157     setSampleData(const std::vector<VectorType>& sampleData);
00158 
00159     
00178     double
00179     operator()(const VectorType& x) const;
00180 
00181     
00190     GaussianDistribution<VectorType>&
00191     operator=(const GaussianDistribution<VectorType>& source);
00192     
00193   private:
00194     /* ********Private member functions******** */
00195 
00202     void
00203     computeSecondaryStatistics();
00204 
00205     
00213     void
00214     computeStatistics();
00215 
00216     
00230     inline void
00231     lazyEvaluationFunction() const;
00232 
00233     
00234     /* ********Private data members******** */
00235 
00236     Array2D<double> m_covariance;
00237     Array2D<double> m_inverseCovariance;
00238     Array1D<double> m_mean;
00239     bool m_mutable;
00240     bool m_needsUpdate;
00241     std::vector<VectorType> m_sampleData;
00242     double m_scalingConstant;
00243   };
00244 
00245   /* Non-member functions */
00246 
00247   // None
00248   
00249 }; // namespace dlr
00250 
00251 
00252 /*================================================================
00253  * Member function definitions follow.  This would be a .C file
00254  * if it weren't templated.
00255  *================================================================*/
00256 
00257 #include <cmath>
00258 #include <sstream>
00259 #include <vector>
00260 #include <dlrCommon/exception.h>
00261 #include <dlrNumeric/numericTraits.h>
00262 #include <dlrNumeric/utilities.h>
00263 #include <dlrLinearAlgebra/linearAlgebra.h>
00264 
00265 namespace dlr {
00266 
00267   // Default constructor creates an uninitialized distribution.
00268   template <class VectorType>
00269   GaussianDistribution<VectorType>::
00270   GaussianDistribution()
00271     : m_covariance(),
00272       m_inverseCovariance(),
00273       m_mean(),
00274       m_mutable(true),
00275       m_needsUpdate(true),
00276       m_sampleData(),
00277       m_scalingConstant(0.0)
00278   {
00279     // Empty
00280   }
00281 
00282   // This constructor explicitly sets the mean, and covariance.
00283   template <class VectorType>
00284   GaussianDistribution<VectorType>::
00285   GaussianDistribution(const Array1D<double>& mean,
00286                        const Array2D<double>& covariance)
00287     : m_covariance(covariance.copy()),
00288       m_inverseCovariance(),
00289       m_mean(mean.copy()),
00290       m_mutable(false),
00291       m_needsUpdate(false),
00292       m_sampleData(),
00293       m_scalingConstant(0.0)
00294   {
00295     this->computeSecondaryStatistics();
00296   }
00297 
00298   // The copy constructor does a deep copy.
00299   template <class VectorType>
00300   GaussianDistribution<VectorType>::
00301   GaussianDistribution(const GaussianDistribution<VectorType> &source)
00302     : m_covariance(source.m_covariance.copy()),
00303       m_inverseCovariance(source.m_inverseCovariance.copy()),
00304       m_mean(source.m_mean.copy()),
00305       m_mutable(source.m_mutable),
00306       m_needsUpdate(source.m_needsUpdate),
00307       m_sampleData(source.m_sampleData),
00308       m_scalingConstant(source.m_scalingConstant)
00309 
00310   {
00311     // Empty
00312   }
00313 
00314   // The destructor destroys the GaussianDistribution instance and
00315   // cleans up any allocated storage.
00316   template <class VectorType>
00317   GaussianDistribution<VectorType>::
00318   ~GaussianDistribution()
00319   {
00320     // Empty
00321   }
00322 
00323   // This method adds a sample to the internal list of points from
00324   // which to compute the distribution statistics.
00325   template <class VectorType>
00326   void
00327   GaussianDistribution<VectorType>::
00328   addSample(const VectorType& point)
00329   {
00330     if(!m_mutable) {
00331       DLR_THROW3(LogicException,
00332                  "GaussianDistribution::addSample(const VectorType&)",
00333                  "Attempt to add sample points when mean and covariance "
00334                  "have been set explicitly.");
00335     }
00336     // Check argument.
00337     // ... is this the first point?
00338     if(m_sampleData.size() == 0) {
00339       // Yes.  Make sure it has a valid size.
00340       if(point.size() == 0) {
00341         DLR_THROW3(ValueException,
00342                    "GaussianDistribution::addSample(...)",
00343                    "Sample points cannot have size == 0.");
00344       }
00345     } else {
00346       // No.  Make sure it matches the previous points.
00347       if(point.size() != m_sampleData[0].size()) {
00348         std::ostringstream message;
00349         message << "Sample point should have size == "
00350                 << m_sampleData[0].size() << ", but instead has size == "
00351                 << point.size() << ".";
00352         DLR_THROW3(ValueException,
00353                    "GaussianDistribution::addSample(...)",
00354                    message.str().c_str());
00355       }
00356     }
00357 
00358     // Done with checks.  Copy the data.
00359     m_sampleData.push_back(point);
00360 
00361     // And mark *this as dirty.
00362     m_needsUpdate = true;
00363   }
00364 
00365   // This member function returns the covariance matrix of the
00366   // distribution.
00367   template <class VectorType>
00368   const Array2D<double>&
00369   GaussianDistribution<VectorType>::
00370   getCovariance() const
00371   {
00372     // Make sure there aren't any pending operations.
00373     this->lazyEvaluationFunction();
00374 
00375     // And return the covariance.
00376     return m_covariance;
00377   }
00378 
00379   // This member function returns the mean of the distribution.
00380   template <class VectorType>
00381   const Array1D<double>&
00382   GaussianDistribution<VectorType>::
00383   getMean() const
00384   {
00385     // Make sure there aren't any pending operations.
00386     this->lazyEvaluationFunction();
00387 
00388     // And return the mean.
00389     return m_mean;
00390   }
00391 
00392   // This member function replaces the internal list of points from
00393   // which to compute the distribution statistics.
00394   template <class VectorType>
00395   void
00396   GaussianDistribution<VectorType>::
00397   setSampleData(const std::vector<VectorType>& sampleData)
00398   {
00399     if(!m_mutable) {
00400       DLR_THROW3(LogicException,
00401                  "GaussianDistribution::addSample(const VectorType&)",
00402                  "Attempt to add sample points when mean and covariance "
00403                  "have been set explicitly.");
00404     }
00405     // Check argument.
00406     // ... Are there any sample points specified?
00407     if(sampleData.size() != 0) {
00408       // Yes!  Make sure the first one isn't zero size.
00409       size_t dimensionality = sampleData[0].size();
00410       if(dimensionality == 0) {
00411         DLR_THROW3(ValueException,
00412                    "GaussianDistribution::setSampleData(...)",
00413                    "Sample points cannot have size == 0.");
00414       }
00415       // And make sure each subsequent point matches the 1st.
00416       for(size_t index = 0; index < sampleData.size(); ++index) {
00417         if(sampleData[index].size() != dimensionality) {
00418           std::ostringstream message;
00419           message << "Sample point number " << index << " should have size == "
00420                   << dimensionality << ", but instead has size == "
00421                   << sampleData[index].size() << ".";
00422           DLR_THROW3(ValueException,
00423                      "GaussianDistribution::setSampleData(...)",
00424                      message.str().c_str());
00425         }
00426       }
00427     }
00428 
00429     // Done with checks.  Copy the data.
00430     m_sampleData = sampleData;
00431 
00432     // And mark *this as dirty.
00433     m_needsUpdate = true;
00434   }
00435   
00436   // This member function returns the value of the Gaussian
00437   // distribution evaluated at the point specified by the argument.
00438   template <class VectorType>
00439   double
00440   GaussianDistribution<VectorType>::
00441   operator()(const VectorType& x) const
00442   {
00443     // First do the stuff we're supposed to have already done...
00444     this->lazyEvaluationFunction();
00445 
00446     // Check the argument.
00447     if(x.size() != m_mean.size()) {
00448       std::ostringstream message;
00449       message << "Distribution has dimensionality " << m_mean.size() << ", "
00450               << "but input point has dimensionality " << x.size() << "."
00451               << std::endl;
00452       DLR_THROW3(ValueException,
00453                  "GaussianDistribution::operator()(const VectorType&)",
00454                  message.str().c_str());
00455     }
00456 
00457     // First calculate the exponent.
00458     // ... Actually, we compute -1 * (x - u) here, but it doesn't affect
00459     // ... the result.
00460     Array1D<double> normalizedX = m_mean.copy();
00461     for(size_t index = 0; index < m_mean.size(); ++index) {
00462       normalizedX -= x[index];
00463     }
00464     Array1D<double> inverseCovarianceTimesNormalizedX =
00465       matrixMultiply(m_inverseCovariance, normalizedX);
00466     double exponent = dot(normalizedX, inverseCovarianceTimesNormalizedX);
00467 
00468     // Now compute the Gaussian value.
00469     double returnValue = m_scalingConstant * std::exp(exponent);
00470     return returnValue;
00471   }
00472 
00473   // The assignment operator deep copies its argument.
00474   template <class VectorType>
00475   GaussianDistribution<VectorType>&
00476   GaussianDistribution<VectorType>::
00477   operator=(const GaussianDistribution<VectorType>& source)
00478   {
00479     m_covariance = source.m_covariance.copy();
00480     m_inverseCovariance = source.m_inverseCovariance.copy();
00481     m_mean = source.m_mean.copy();
00482     m_mutable = source.m_mutable;
00483     m_needsUpdate = source.m_needsUpdate;
00484     m_sampleData = source.m_sampleData;
00485     m_scalingConstant = source.m_scalingConstant;
00486     return *this;
00487   }
00488 
00489   // This private member function computes the inverse covariance
00490   // matrix and the distribution scaling constant, based on the
00491   // value of m_covariance.
00492   template <class VectorType>
00493   void
00494   GaussianDistribution<VectorType>::
00495   computeSecondaryStatistics()
00496   {
00497     // We assume that m_covariance has already been set.
00498     
00499     // First compute the determinant of the covariance matrix and make
00500     // sure covariance isn't singular.
00501     double determinantValue = determinant(m_covariance);
00502     if(std::fabs(determinantValue) < 1.0E-10) {
00503       DLR_THROW3(StateException,
00504                  "GaussianDistribution::computeSecondaryStatistics()",
00505                  "Computed covariance matrix is singular.  Perhaps "
00506                  "more sample points are needed?");
00507     }
00508 
00509     // Use the determinant to compute the scaling constant.
00510     m_scalingConstant =
00511                 1.0 / std::sqrt(std::pow(static_cast<double>(2.0 * dlr::numeric::constants::pi),
00512                                static_cast<double>(m_mean.size()))
00513                       * determinantValue);
00514 
00515     // Next compute inverse covariance.  Should never fail, since
00516     // determinant is non-zero.
00517     try {
00518       m_inverseCovariance = inverse(m_covariance);
00519     } catch(const ValueException&) {
00520       DLR_THROW3(RunTimeException,
00521                  "GaussianDistribution::computeSecondaryStatistics()",
00522                  "Something is dreadfully wrong, m_covariance has "
00523                  "non-zero determinant, yet inverse(m_covariance) fails.");
00524     }
00525   }
00526     
00527   // This private member function estimates the mean, covariance,
00528   // and any other relevant characteristics of the distribution,
00529   // based on the provided sample points.
00530   template <class VectorType>
00531   void
00532   GaussianDistribution<VectorType>::
00533   computeStatistics()
00534   {
00535     // First check state.
00536     if(m_sampleData.size() == 0) {
00537       DLR_THROW3(StateException,
00538                  "GaussianDistribution::computeStatistics()",
00539                  "Can't compute statistics since no sample points have been "
00540                  "provided.");
00541     }
00542 
00543     // Now compute the mean value of the sample points.
00544     // For efficiency, we simultaneously compute covariance.
00545     // We make use of the identity:
00546     //   covariance = E[(x - u)*(x - u)^T] = E[x * x^T] - u * u^T
00547     // where E[.] denotes expected value.
00548     size_t numberOfSamples = m_sampleData.size();
00549     size_t dimensionality = m_sampleData[0].size();
00550     
00551     m_mean.reinit(dimensionality);
00552     m_mean = 0.0;
00553     m_covariance.reinit(dimensionality, dimensionality);
00554     m_covariance = 0.0;
00555     for(typename std::vector<VectorType>::iterator
00556           pointIterator = m_sampleData.begin();
00557         pointIterator != m_sampleData.end();
00558         ++pointIterator) {
00559       for(size_t index0 = 0; index0 < dimensionality; ++index0) {
00560         m_mean[index0] += (*pointIterator)[index0];
00561         for(size_t index1 = index0; index1 < dimensionality; ++index1) {
00562           // Only compute the top half of the covariance matrix for now,
00563           // since it's symmetric.
00564           // Note(xxx): Should fix this to run faster.
00565           m_covariance(index0, index1) +=
00566             (*pointIterator)[index0] * (*pointIterator)[index1];
00567         }
00568       }
00569     }
00570     m_mean /= static_cast<double>(numberOfSamples);
00571 
00572     // Warning(xxx): Shouldn't we divide m_covariance by numberOfSamples?
00573     
00574     // Now finish up the computation of the covariance matrix.
00575     for(size_t index0 = 0; index0 < dimensionality; ++index0) {
00576       for(size_t index1 = index0; index1 < dimensionality; ++index1) {
00577         // Add correction to the top half of the covariance matrix.
00578         m_covariance(index0, index1) -=
00579           numberOfSamples * m_mean[index0] * m_mean[index1];
00580         // And copy to the bottom half.
00581         if(index0 != index1) {
00582           m_covariance(index1, index0) = m_covariance(index0, index1);
00583         }
00584       }
00585     }
00586     m_covariance /= static_cast<double>(numberOfSamples - 1);
00587     
00588     // Finish up by computing anything that depends on these values.
00589     this->computeSecondaryStatistics();
00590   }
00591   
00592   // This private member function performs any pending calculations.
00593   template <class VectorType>
00594   void
00595   GaussianDistribution<VectorType>::
00596   lazyEvaluationFunction() const
00597   {
00598     if(m_needsUpdate) {
00599       // This const_cast is one of the penalties of lazy evaluation. :-/
00600       const_cast<GaussianDistribution<VectorType>*>(this)->computeStatistics();
00601     }
00602   }
00603 
00604 
00605 }; // namespace dlr
00606 
00607 #endif // #ifdef _DLR_GAUSSIANDISTRIBUTION_H_
00608 

Generated on Mon Jul 9 20:34:03 2007 for dlrLibs Utility Libraries by  doxygen 1.5.2