Main Page | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Class Members | File Members

vpdfl_gaussian.cxx

Go to the documentation of this file.
00001 // This is mul/vpdfl/vpdfl_gaussian.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief Multi-variate Gaussian PDF with arbitrary axes.
00008 // \author Tim Cootes
00009 // \date 16-Oct-98
00010 //
00011 // \verbatim
00012 //  Modifications
00013 //    IMS   Converted to VXL 18 April 2000
00014 // \endverbatim
00015 
00016 #include "vpdfl_gaussian.h"
00017 
00018 #include <vcl_cassert.h>
00019 #include <vcl_cstdlib.h>
00020 #include <vcl_string.h>
00021 #include <vcl_cmath.h>
00022 
00023 #include <vsl/vsl_indent.h>
00024 #include <vnl/vnl_math.h>
00025 #include <mbl/mbl_matxvec.h>
00026 #include <mbl/mbl_matrix_products.h>
00027 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00028 #include <vnl/io/vnl_io_matrix.h>
00029 #include <vpdfl/vpdfl_gaussian_sampler.h>
00030 #include <vpdfl/vpdfl_sampler_base.h>
00031 #include <vpdfl/vpdfl_prob_chi2.h>
00032 
00033 //=======================================================================
00034 static inline bool almostEqualsOne(double value);
00035 static bool columnsAreUnitNorm(const vnl_matrix<double>& vecs);
00036 #if 0
00037 static bool vectorHasDescendingOrder(const vnl_vector<double>& v);
00038 #endif
00039 
00040 //=======================================================================
00041 
00042 vpdfl_gaussian::vpdfl_gaussian()
00043 {
00044 }
00045 
00046 //=======================================================================
00047 
00048 vpdfl_gaussian::~vpdfl_gaussian()
00049 {
00050 }
00051 
00052 //=======================================================================
00053 
00054 void vpdfl_gaussian::calcLogK()
00055 {
00056   const double *v_data = evals_.data_block();
00057   int n = n_dims();
00058   double log_v_sum = 0.0;
00059   for (int i=0;i<n;i++) log_v_sum+=vcl_log(v_data[i]);
00060 
00061   log_k_ = -0.5 * (n*vcl_log(2 * vnl_math::pi) + log_v_sum);
00062 }
00063 
00064 //: Initialise safely
00065 // Calculates the variance, and checks that
00066 // the Eigenvalues are ordered and the Eigenvectors are unit normal
00067 // Turn off assertions to remove error checking.
00068 void vpdfl_gaussian::set(const vnl_vector<double>& mean,
00069                          const vnl_matrix<double>& evecs,
00070                          const vnl_vector<double>& evals)
00071 {
00072   const unsigned int m = evecs.rows();
00073   const unsigned int n = evecs.cols();
00074   assert(evals.size() == m);
00075   assert(mean.size() == m);
00076 
00077 // Ensure that every Eigenvector has a unit norm;
00078   assert(columnsAreUnitNorm(evecs));
00079 // Ensure that every Eigenvalues are properly ordered is a unit norm;
00080 //assert(vectorHasDescendingOrder(evals));
00081 
00082   // calculate the variance
00083   // Notionally - apply the inverse diagonalisation to get
00084   // back to the Covariance matrix, and select the diagonal
00085   // Efficiently - Var(i) = Sum Wij * Li * Wij
00086   vnl_vector<double> v(m);
00087   for (unsigned int i=0; i<m; i++)
00088   {
00089     double &vi = v(i);
00090     vi = 0.0;
00091     for (unsigned int j=0; j<n; j++)
00092       vi += vnl_math_sqr(evecs(i,j)) * evals(i);
00093   }
00094 
00095   set(mean, v, evecs, evals);
00096 }
00097 
00098 //=======================================================================
00099 
00100 void vpdfl_gaussian::set(const vnl_vector<double>& m,
00101                          const vnl_vector<double>& v,
00102                          const vnl_matrix<double>& evecs,
00103                          const vnl_vector<double>& evals)
00104 {
00105   set_mean(m);
00106   set_variance(v);
00107 
00108   evecs_ = evecs;
00109   evals_ = evals;
00110 
00111   calcLogK();
00112 }
00113 
00114 //: Modify just the mean of the distribution
00115 // This functions should only be used by builders.
00116 void vpdfl_gaussian::set_mean(const vnl_vector<double>& mean)
00117 {
00118   vpdfl_pdf_base::set_mean(mean);
00119 }
00120 
00121 
00122 //=======================================================================
00123 //: Initialise from mean and covariance matrix
00124 //  Note, eigenvectors computed from covar, and those corresponding
00125 //  to evals smaller than min_eval are truncated
00126 void vpdfl_gaussian::set(const vnl_vector<double>& mean,
00127                          const vnl_matrix<double>& covar,
00128                          double min_eval)
00129 {
00130   unsigned int n = mean.size();
00131   assert(covar.rows()==n && covar.cols()==n);
00132 
00133   vnl_matrix<double> evecs;
00134   vnl_vector<double> evals;
00135 
00136   vnl_symmetric_eigensystem_compute(covar, evecs, evals);
00137   // eigenvalues are lowest first here
00138   evals.flip();
00139   evecs.fliplr();
00140   // eigenvalues are highest first now
00141 
00142   // Apply threshold to variance
00143   for (unsigned int i=0;i<n;++i)
00144     if (evals(i)<min_eval) evals(i)=min_eval;
00145 
00146   set(mean, evecs, evals);
00147 }
00148 
00149 
00150 //=======================================================================
00151 static bool almostEqualsOne(double value)
00152 {
00153   const double upper = 1 + 1e-06;
00154   const double lower = 1 - 1e-06;
00155   return value > lower && value < upper;
00156 }
00157 
00158 //=======================================================================
00159 
00160 static bool columnsAreUnitNorm(const vnl_matrix<double>& vecs)
00161 {
00162   const int m = vecs.rows();
00163   const int n = vecs.cols();
00164   for (int j=0; j<n; j++)
00165   {
00166     double sumsq = 0.0;
00167     for (int i=0; i<m; i++)
00168       sumsq += vnl_math_sqr(vecs(i,j));
00169     if (!almostEqualsOne(sumsq)) return false;
00170   }
00171   return true;
00172 }
00173 //=======================================================================
00174 
00175 #if 0
00176 static bool vectorHasDescendingOrder(const vnl_vector<double>& v)
00177 {
00178   int n = v.size();
00179   for (int i = 1; i < n; i++)
00180     if (v(i-1) < v(i)) return false;
00181   return true;
00182 }
00183 #endif
00184 
00185 //=======================================================================
00186 
00187 vnl_matrix<double> vpdfl_gaussian::covariance() const
00188 {
00189   vnl_matrix<double> Cov;
00190   mbl_matrix_product_adb(Cov, evecs_, evals_, evecs_.transpose());
00191   return Cov;
00192 }
00193 
00194 //=======================================================================
00195 
00196 vpdfl_sampler_base* vpdfl_gaussian::new_sampler() const
00197 {
00198   vpdfl_gaussian_sampler *i = new vpdfl_gaussian_sampler;
00199   i->set_model(*this);
00200   return i;
00201 }
00202 
00203 //=======================================================================
00204 
00205 //: Calculate (x-mu)' * Sigma^-1 * (x-mu)
00206 // This is the Mahalanobis distance squared from the mean.
00207 double vpdfl_gaussian::dx_sigma_dx(const vnl_vector<double>& x) const
00208 {
00209   unsigned int n = n_dims();
00210  #ifndef NDEBUG
00211   if (n!=x.size())
00212   {
00213     vcl_cerr<<"ERROR: vpdfl_gaussian::dx_sigma_dx: Target vector has "
00214             <<n<<" dimensions, not the required "<<n_dims()<<vcl_endl;
00215     vcl_abort();
00216   }
00217 #endif
00218 
00219   b_.set_size(n);
00220 
00221   dx_=x;
00222   dx_-=mean();
00223   // Rotate dx_ into co-ordinate frame of axes of Gaussian
00224   // b_ = P'dx_
00225   mbl_matxvec_prod_vm(dx_, eigenvecs(),b_);
00226 
00227   const double* b_data = b_.data_block() ;
00228   const double* v_data = eigenvals().data_block() ;
00229 
00230   double sum=0.0;
00231 
00232   unsigned int i=n;
00233   while (i-- != 0)
00234   {
00235     double db=b_data[i];
00236     sum+=(db*db)/v_data[i];
00237   }
00238   return sum;
00239 }
00240 
00241 // Probability densities:
00242 double vpdfl_gaussian::log_p(const vnl_vector<double>& x) const
00243 {
00244   return log_k() - 0.5*dx_sigma_dx(x);
00245 }
00246 
00247 //=======================================================================
00248 
00249 void vpdfl_gaussian::gradient(vnl_vector<double>& g,
00250                               const vnl_vector<double>& x,
00251                               double& p) const
00252 {
00253   unsigned int n = n_dims();
00254   dx_ = x;
00255   dx_ -= mean();
00256 
00257   if (b_.size()!=n) b_.set_size(n);
00258 
00259   // Rotate dx_ into co-ordinate frame of axes of Gaussian
00260   // b_ = P'dx_
00261   mbl_matxvec_prod_vm(dx_, eigenvecs(),b_);
00262 
00263   if (g.size()!=n) g.set_size(n);
00264 
00265   double* b_data = b_.data_block();
00266   const double* v_data = eigenvals().data_block();
00267 
00268   double sum=0.0;
00269 
00270   for (unsigned int i=0;i<n;++i)
00271   {
00272     double db=b_data[i];
00273     sum+=(db*db)/v_data[i];
00274     // Record gradient in dx_
00275     b_data[i]/=v_data[i];
00276   }
00277 
00278   p = vcl_exp(log_k() - 0.5*sum);
00279 
00280   b_*=(-1.0*p);
00281 
00282   // Rotate back into x frame
00283   mbl_matxvec_prod_mv(eigenvecs(),b_,g);
00284 }
00285 
00286 // ====================================================================
00287 
00288 double vpdfl_gaussian::log_prob_thresh(double pass_proportion) const
00289 {
00290   // The Mahalanobis distance of n-D Gaussian is distributed as Chi^2(n),
00291   // by definition, Chi^2 is the sum of independent Normal RVs.
00292   return log_k() - 0.5 * vpdfl_chi2_for_cum_prob (pass_proportion, n_dims());
00293 }
00294 
00295 //=======================================================================
00296 
00297 void vpdfl_gaussian::nearest_plausible(vnl_vector<double>& x, double log_p_min) const
00298 {
00299   // calculate radius of plausible region in standard deviations.
00300   log_p_min -= log_k();
00301   assert(log_p_min <0); // Check sd_limit is positive and real.
00302   const double sd_limit_sqr = -2.0*log_p_min;
00303   const double x_dist_sqr = dx_sigma_dx(x);
00304 
00305   unsigned int n = n_dims();
00306 
00307   if (sd_limit_sqr >= x_dist_sqr) return;
00308 
00309   const double corrective_factor = vcl_sqrt(sd_limit_sqr / x_dist_sqr);
00310 
00311   for (unsigned i=0;i<n;++i)
00312     x(i) = ((x(i)-mean()(i)) * corrective_factor) + mean()(i);
00313 }
00314 
00315 //=======================================================================
00316 // Method: is_a
00317 //=======================================================================
00318 
00319 vcl_string vpdfl_gaussian::is_a() const
00320 {
00321   static vcl_string class_name_ = "vpdfl_gaussian";
00322   return class_name_;
00323 }
00324 
00325 //=======================================================================
00326 // Method: is_class
00327 //=======================================================================
00328 
00329 bool vpdfl_gaussian::is_class(vcl_string const& s) const
00330 {
00331   return vpdfl_pdf_base::is_class(s) || s==vpdfl_gaussian::is_a();
00332 }
00333 
00334 //=======================================================================
00335 // Method: version_no
00336 //=======================================================================
00337 
00338 short vpdfl_gaussian::version_no() const
00339 {
00340   return 1;
00341 }
00342 
00343 //=======================================================================
00344 // Method: clone
00345 //=======================================================================
00346 
00347 vpdfl_pdf_base* vpdfl_gaussian::clone() const
00348 {
00349   return new vpdfl_gaussian(*this);
00350 }
00351 
00352 //=======================================================================
00353 // Method: print
00354 //=======================================================================
00355 
00356 #if 0 // commented out
00357 static void ShowStartVec(vcl_ostream& os, const vnl_vector<double>& v)
00358 {
00359   int n = 3;
00360   if (n>v.size()) n=v.size();
00361   os<<'(';
00362   for (int i=0;i<n;++i) os<<v(i)<<' ';
00363   if (v.size()>n) os<<"...";
00364   os<<")\n";
00365 }
00366 
00367 static void ShowStartMat(vcl_ostream& os, const vnl_matrix<double>& A)
00368 {
00369   os << A.rows() << " by " << A.cols() << " Matrix\n";
00370 
00371   int m = 3, n= 3;
00372   if (m>A.rows()) m=A.rows();
00373   if (n>A.cols()) n=A.cols();
00374   vsl_indent_inc(os);
00375 
00376   for (int i=0;i<m;++i)
00377   {
00378     os<<vsl_indent()<<'(';
00379 
00380     for ( int j=0; j<n; ++j)
00381       os<<A(i,j)<<' ';
00382     if (A.cols()>n) os<<"...";
00383     os<<")\n";
00384   }
00385   if (A.rows()>m) os <<vsl_indent()<<"(...\n";
00386 
00387   vsl_indent_dec(os);
00388 }
00389 #endif // commented out
00390 
00391 void vpdfl_gaussian::print_summary(vcl_ostream& os) const
00392 {
00393   vpdfl_pdf_base::print_summary(os);
00394   os << vcl_endl;
00395   if (n_dims()!=1)
00396   {
00397     os<<vsl_indent()<<"Eigenvectors: "; vsl_print_summary(os, eigenvecs() );
00398     os<<vsl_indent()<<"log_k: "<< log_k_ <<vcl_endl;
00399 //  os<<vsl_indent()<<"Covariance: "; ShowStartMat(os, covariance() );
00400   }
00401 }
00402 
00403 //=======================================================================
00404 // Method: save
00405 //=======================================================================
00406 
00407 void vpdfl_gaussian::b_write(vsl_b_ostream& bfs) const
00408 {
00409   vsl_b_write(bfs,is_a());
00410   vsl_b_write(bfs,version_no());
00411   vpdfl_pdf_base::b_write(bfs);
00412   vsl_b_write(bfs,evecs_);
00413   vsl_b_write(bfs,evals_);
00414   vsl_b_write(bfs,log_k_);
00415 }
00416 
00417 //=======================================================================
00418 // Method: load
00419 //=======================================================================
00420 
00421 void vpdfl_gaussian::b_read(vsl_b_istream& bfs)
00422 {
00423   if (!bfs) return;
00424 
00425   vcl_string name;
00426   vsl_b_read(bfs,name);
00427   if (name != is_a())
00428   {
00429     vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vpdfl_gaussian &)\n"
00430              << "           Attempted to load object of type "
00431              << name <<" into object of type " << is_a() << vcl_endl;
00432     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00433     return;
00434   }
00435 
00436   short version;
00437   vsl_b_read(bfs,version);
00438   switch (version)
00439   {
00440     case (1):
00441       vpdfl_pdf_base::b_read(bfs);
00442       vsl_b_read(bfs,evecs_);
00443       vsl_b_read(bfs,evals_);
00444       vsl_b_read(bfs,log_k_);
00445       break;
00446     default:
00447       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vpdfl_gaussian &)\n"
00448                << "           Unknown version number "<< version << vcl_endl;
00449       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00450       return;
00451   }
00452 }
00453 
00454 //==================< end of vpdfl_gaussian.cxx >====================

Generated on Thu Jan 10 14:43:32 2008 for contrib/mul/vpdfl by  doxygen 1.4.4