core/vpdl/vpdt/vpdt_gaussian.h
Go to the documentation of this file.
00001 // This is core/vpdl/vpdt/vpdt_gaussian.h
00002 #ifndef vpdt_gaussian_h_
00003 #define vpdt_gaussian_h_
00004 //:
00005 // \file
00006 // \author Matthew Leotta
00007 // \date March 5, 2009
00008 // \brief A generic Gaussian distribution
00009 //
00010 // \verbatim
00011 // Modifications
00012 //   <None yet>
00013 // \endverbatim
00014 
00015 
00016 #include <vpdl/vpdt/vpdt_field_traits.h>
00017 #include <vpdl/vpdt/vpdt_field_default.h>
00018 #include <vpdl/vpdt/vpdt_dist_traits.h>
00019 #include <vpdl/vpdt/vpdt_access.h>
00020 #include <vpdl/vpdt/vpdt_eigen_sym_matrix.h>
00021 #include <vpdl/vpdt/vpdt_norm_metric.h>
00022 #include <vcl_limits.h>
00023 #include <vnl/vnl_erf.h>
00024 
00025 
00026 //: Forward declare integration helper struct
00027 template <class F, class Covar, class Metric, class Disambiguate= void >
00028 struct vpdt_gaussian_integrator;
00029 
00030 
00031 //: A Gaussian with variance independent in each dimension
00032 template<class F,
00033          class Covar = typename vpdt_eigen_sym_matrix_gen<F>::type,
00034          class Metric = vpdt_norm_metric<F,Covar> >
00035 class vpdt_gaussian
00036 {
00037  public:
00038   //: The field type
00039   typedef F field_type;
00040   //: The covariance type
00041   typedef Covar covar_type;
00042   //: The metric type
00043   typedef Metric metric_type;
00044 
00045   //: the data type used for scalars
00046   typedef typename vpdt_field_traits<F>::scalar_type T;
00047   //: the data type used for vectors
00048   typedef typename vpdt_field_traits<F>::vector_type vector;
00049   //: the data type used for matrices
00050   typedef typename vpdt_field_traits<F>::matrix_type matrix;
00051 
00052   //: Constructor
00053   // Optionally initialize the dimension for when n==0.
00054   // Otherwise var_dim is ignored
00055   vpdt_gaussian(unsigned int var_dim = vpdt_field_traits<F>::dimension)
00056   {
00057     vpdt_set_size(mean,var_dim);
00058     vpdt_set_size(covar,var_dim);
00059     vpdt_fill(mean,T(0));
00060     vpdt_fill(covar,T(0));
00061   }
00062 
00063   //: Constructor - from mean and variance
00064   vpdt_gaussian(const F& m, const covar_type& c)
00065   : mean(m), covar(c) {}
00066 
00067   //: Return the dimension
00068   unsigned int dimension() const { return vpdt_size(mean); }
00069 
00070   //: Evaluate the unnormalized density at a point \a pt
00071   // This must be multiplied by norm_const() to integrate to 1
00072   T density(const F& pt) const
00073   {
00074     return static_cast<T>(vcl_exp(-sqr_mahal_dist(pt)/2));
00075   }
00076 
00077   //: Compute the gradient of the density function, returned in \a g
00078   // The return value of the function is the density itself
00079   T gradient_density(const F& pt, vector& g) const
00080   {
00081     T d = Metric::sqr_distance_deriv(pt,mean,covar,g);
00082     d = vcl_exp(-d/2);
00083     g *= -d/2;
00084     return d;
00085   }
00086 
00087   //: compute the normalization constant (independent of sample point).
00088   // Can be precomputed when evaluating at multiple points
00089   T norm_const() const
00090   {
00091     return T(1)/vpdt_gaussian_integrator<F,Covar,Metric>::
00092                     domain_integral(*this);
00093   }
00094 
00095   //: The squared Mahalanobis distance to this point
00096   // Non-virtual for efficiency
00097   T sqr_mahal_dist(const F& pt) const
00098   {
00099     return Metric::sqr_distance(pt,mean,covar);
00100   }
00101 
00102   //: Evaluate the cumulative distribution function at a point
00103   // This is the integral of the density function from negative infinity
00104   // (in all dimensions) to the point in question
00105   T cumulative_prob(const F& pt) const
00106   {
00107     return vpdt_gaussian_integrator<F,Covar,Metric>::
00108                partial_integral(*this,pt);
00109   }
00110 
00111   //: Compute the mean of the distribution.
00112   void compute_mean(vector& m) const { m = mean; }
00113 
00114   //: Compute the covariance matrix of the distribution.
00115   void compute_covar(matrix& c) const
00116   {
00117     // use the metric to compute the covariance at the mean
00118     Metric::compute_covar(c, mean, covar);
00119   }
00120 
00121   //=========================================
00122   // member variables - public for efficiency
00123 
00124   //: the mean
00125   F mean;
00126   //: the matrix covariance
00127   covar_type covar;
00128 };
00129 
00130 
00131 //: Compute the log of the unnormalized density
00132 template<class F, class C, class M >
00133 inline typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::scalar_type
00134 vpdt_log_density(const vpdt_gaussian<F,C,M>& d,
00135                  const typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::field_type& pt)
00136 {
00137   typedef typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::scalar_type T;
00138   return static_cast<T>(-d.sqr_mahal_dist(pt)/2);
00139 }
00140 
00141 //: Compute the gradient of the log of the unnormalized density
00142 template<class F, class C, class M >
00143 inline typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::scalar_type
00144 vpdt_gradient_log_density(const vpdt_gaussian<F,C,M>& d,
00145                           const typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::field_type& pt,
00146                           typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::field_type& g)
00147 {
00148   typedef typename vpdt_dist_traits<vpdt_gaussian<F,C,M> >::scalar_type T;
00149   T logd = M::sqr_distance_deriv(pt,d.mean,d.covar,g);
00150   g /= -2;
00151   return static_cast<T>(-logd/2);
00152 }
00153 
00154 
00155 //=============================================================================
00156 // Implementations of Gaussian integration structs
00157 
00158 //: integrate over a Gaussian distribution
00159 //  This is the variation for multivariate with general covariance
00160 template <class F>
00161 struct vpdt_gaussian_integrator<F, typename vpdt_eigen_sym_matrix_gen<F>::type,
00162            vpdt_norm_metric<F, typename vpdt_eigen_sym_matrix_gen<F>::type>,
00163            typename vpdt_field_traits<F>::type_is_vector >
00164 {
00165   typedef typename vpdt_eigen_sym_matrix_gen<F>::type Covar;
00166   typedef vpdt_norm_metric<F,typename vpdt_eigen_sym_matrix_gen<F>::type> Metric;
00167   typedef typename vpdt_field_traits<F>::scalar_type T;
00168 
00169   //: integrate over the entire domain
00170   static inline T domain_integral(const vpdt_gaussian<F,Covar>& g)
00171   {
00172     const unsigned int d = g.dimension();
00173     const double two_pi = 2.0*vnl_math::pi;
00174     double two_pi_n = two_pi;
00175     for (unsigned int i=1; i<d; ++i)
00176       two_pi_n *= two_pi;
00177 
00178     return static_cast<T>(vcl_sqrt(two_pi_n*Metric::covar_det(g.mean,g.covar)));
00179   }
00180 
00181   //: integrate from -infinity to \c pt
00182   static inline T partial_integral(const vpdt_gaussian<F,Covar>& /*g*/, const F& /*pt*/)
00183   {
00184     // FIXME: implement this
00185     // probably requires numerical integration
00186     return vcl_numeric_limits<T>::quiet_NaN();
00187   }
00188 };
00189 
00190 
00191 //: integrate over a Gaussian distribution
00192 //  This is the variation for multivariate with independent covariance
00193 template <class F>
00194 struct vpdt_gaussian_integrator<F,F,vpdt_norm_metric<F,F>,
00195            typename vpdt_field_traits<F>::type_is_vector >
00196 {
00197   typedef typename vpdt_field_traits<F>::scalar_type T;
00198 
00199   //: integrate over the entire domain
00200   static inline T domain_integral(const vpdt_gaussian<F,F>& g)
00201   {
00202     const unsigned int d = g.dimension();
00203     const double two_pi = 2.0*vnl_math::pi;
00204     double val = 1.0;
00205     for (unsigned int i=0; i<d; ++i)
00206       val *= two_pi*g.covar[i];
00207 
00208     return static_cast<T>(vcl_sqrt(val));
00209   }
00210 
00211   //: integrate from -infinity to \c pt
00212   static inline T partial_integral(const vpdt_gaussian<F,F>& g, const F& pt)
00213   {
00214     const unsigned int d = g.dimension();
00215     double val = 1.0;
00216     for (unsigned int i=0; i<d; ++i)
00217     {
00218       if (vpdt_index(g.covar,i) <= T(0))
00219       {
00220         if (vpdt_index(pt,i) < vpdt_index(g.mean,i))
00221           return T(0);
00222       }
00223       else{
00224         double s2 = vcl_sqrt(2.0*vpdt_index(g.covar,i));
00225         val *= 0.5*vnl_erf((vpdt_index(pt,i)-vpdt_index(g.mean,i))/s2) + 0.5;
00226       }
00227     }
00228     return static_cast<T>(val);
00229   }
00230 };
00231 
00232 
00233 //: integrate over a Gaussian distribution
00234 //  This is the variation for multivariate with hyper-spherical covariance
00235 template <class F>
00236 struct vpdt_gaussian_integrator<F,typename vpdt_field_traits<F>::scalar_type,
00237           vpdt_norm_metric<F,typename vpdt_field_traits<F>::scalar_type>,
00238           typename vpdt_field_traits<F>::type_is_vector>
00239 {
00240   typedef typename vpdt_field_traits<F>::vector_type vector;
00241   typedef typename vpdt_field_traits<F>::scalar_type T;
00242 
00243   //: integrate over the entire domain
00244   static inline T domain_integral(const vpdt_gaussian<F,T>& g)
00245   {
00246     const unsigned int d = g.dimension();
00247     const double two_pi = 2.0*vnl_math::pi;
00248     double val = 1.0;
00249     for (unsigned int i=0; i<d; ++i)
00250       val *= two_pi*g.covar;
00251 
00252     return static_cast<T>(vcl_sqrt(val));
00253   }
00254 
00255   //: integrate from -infinity to \c pt
00256   static inline T partial_integral(const vpdt_gaussian<F,T>& g, const F& pt)
00257   {
00258     const unsigned int d = g.dimension();
00259     if (g.covar<=T(0))
00260     {
00261       for (unsigned int i=0; i<d; ++i)
00262         if (vpdt_index(pt,i) < vpdt_index(g.mean,i))
00263           return T(0);
00264       return T(1);
00265     }
00266     double s2 = 1/vcl_sqrt(2.0*g.covar);
00267     double val = 1.0;
00268     for (unsigned int i=0; i<d; ++i)
00269     {
00270       val *= 0.5*vnl_erf(s2*(vpdt_index(pt,i)-vpdt_index(g.mean,i))) + 0.5;
00271     }
00272     return static_cast<T>(val);
00273   }
00274 };
00275 
00276 
00277 //: integrate over a Gaussian distribution
00278 //  This is the variation for univariate
00279 template <class F>
00280 struct vpdt_gaussian_integrator<F,F,vpdt_norm_metric<F,F>,
00281 typename vpdt_field_traits<F>::type_is_scalar>
00282 {
00283   typedef typename vpdt_field_traits<F>::scalar_type T;
00284 
00285   //: integrate over the entire domain
00286   static inline T domain_integral(const vpdt_gaussian<F,F>& g)
00287   {
00288     return static_cast<T>(vcl_sqrt(2.0*vnl_math::pi*g.covar));
00289   }
00290 
00291   //: integrate from -infinity to \c pt
00292   static inline T partial_integral(const vpdt_gaussian<F,F>& g, const F& pt)
00293   {
00294     if (g.covar<=T(0))
00295     {
00296       if (pt < g.mean)
00297         return T(0);
00298       return T(1);
00299     }
00300     double val = 0.5*vnl_erf((pt-g.mean)/vcl_sqrt(2.0*g.covar)) + 0.5;
00301     return static_cast<T>(val);
00302   }
00303 };
00304 
00305 
00306 #endif // vpdt_gaussian_h_