core/vpdl/vpdt/vpdt_mixture_of.h
Go to the documentation of this file.
00001 // This is core/vpdl/vpdt/vpdt_mixture_of.h
00002 #ifndef vpdt_mixture_of_h_
00003 #define vpdt_mixture_of_h_
00004 //:
00005 // \file
00006 // \author Matthew Leotta
00007 // \date February 24, 2009
00008 // \brief A mixture of a fixed type of distributions
00009 //
00010 // \verbatim
00011 // Modifications
00012 //   None
00013 // \endverbatim
00014 
00015 #include <vpdl/vpdt/vpdt_dist_traits.h>
00016 #include <vpdl/vpdt/vpdt_probability.h>
00017 #include <vcl_cassert.h>
00018 #include <vcl_vector.h>
00019 #include <vcl_algorithm.h>
00020 #include <vcl_memory.h>
00021 
00022 // forward declarations
00023 template<class dist_t> class vpdt_mixture_of;
00024 
00025 template <class dist>
00026 typename vpdt_dist_traits<vpdt_mixture_of<dist> >::scalar_type
00027 vpdt_box_prob(const vpdt_mixture_of<dist>& d,
00028               const typename vpdt_dist_traits<vpdt_mixture_of<dist> >::field_type& min_pt,
00029               const typename vpdt_dist_traits<vpdt_mixture_of<dist> >::field_type& max_pt);
00030 
00031 
00032 template <class dist>
00033 struct vpdt_is_mixture<vpdt_mixture_of<dist> >
00034 {
00035   static const bool value = true;
00036 };
00037 
00038 
00039 //: A mixture of a fixed type of distributions
00040 // A mixture is a weighted linear combination of other mixtures.
00041 // This class represents a mixture of a specific type of distribution.
00042 // Each component in the mixture has its own weight and parameters,
00043 // but each must be of the same type.
00044 // \tparam dist_t is the type of a component distribution
00045 // \sa vpdl_mixture
00046 template<class dist_t>
00047 class vpdt_mixture_of
00048 {
00049  public:
00050   //: the data type to represent a point in the field
00051   typedef typename dist_t::field_type field_type;
00052   //: define the component type
00053   typedef dist_t component_type;
00054 
00055   //: define the fixed dimension (normally specified by template parameter n)
00056   static const unsigned int n = vpdt_field_traits<field_type>::dimension;
00057   //: the data type to represent a point in the field
00058   typedef typename dist_t::field_type F;
00059   //: define the scalar type (normally specified by template parameter T)
00060   typedef typename vpdt_field_traits<field_type>::scalar_type T;
00061   //: define the vector type
00062   typedef typename vpdt_field_traits<field_type>::vector_type vector;
00063   //: the data type used for matrices
00064   typedef typename vpdt_field_traits<field_type>::matrix_type matrix;
00065 
00066  private:
00067   //: A struct to hold the component distributions and weights
00068   // This class is private and should not be used outside of the mixture.
00069   struct component
00070   {
00071     //: Constructor
00072     component() : distribution(), weight(T(0)) {}
00073     //: Constructor
00074     component(const component_type& d, const T& w = T(0) )
00075       : distribution(d), weight(w) {}
00076 
00077     //: Used to sort by decreasing weight
00078     bool operator< (const component& rhs) const
00079     { return this->weight > rhs.weight; }
00080 
00081     // ============ Data =============
00082 
00083     //: The distribution
00084     component_type distribution;
00085     //: The weight
00086     T weight;
00087   };
00088 
00089   //: This functor is used by default for sorting with STL
00090   // The default sorting is decreasing by weight
00091   class sort_weight
00092   {
00093    public:
00094     bool operator() (const component* c1, const component* c2) const
00095       { return c1->weight > c2->weight; }
00096   };
00097 
00098   //: This adaptor allows users to define ordering functors on the components without accessing the components directly
00099   template <class comp_type_>
00100   class sort_adaptor
00101   {
00102    public:
00103     sort_adaptor(comp_type_ c) : comp(c) {}
00104     bool operator() (const component* const c1, const component* const c2) const
00105     { return comp(c1->distribution, c1->weight, c2->distribution, c2->weight); }
00106     comp_type_ comp;
00107   };
00108 
00109   //: The vector of components
00110   vcl_vector<component*> components_;
00111 
00112  public:
00113   // Default Constructor
00114   vpdt_mixture_of() {}
00115 
00116   // Copy Constructor
00117   vpdt_mixture_of(const vpdt_mixture_of<dist_t>& other)
00118     : components_(other.components_.size(),NULL)
00119   {
00120     // deep copy of the data
00121     for (unsigned int i=0; i<components_.size(); ++i) {
00122       components_[i] = new component(*other.components_[i]);
00123     }
00124   }
00125 
00126   // Destructor
00127   ~vpdt_mixture_of()
00128   {
00129     for (unsigned int i=0; i<components_.size(); ++i) {
00130       delete components_[i];
00131     }
00132   }
00133 
00134   //: Assignment operator
00135   vpdt_mixture_of<dist_t>& operator= (const vpdt_mixture_of<dist_t>& rhs)
00136   {
00137     if (this != &rhs) {
00138       for (unsigned int i=0; i<components_.size(); ++i) {
00139         delete components_[i];
00140       }
00141       components_.clear();
00142       for (unsigned int i=0; i<rhs.components_.size(); ++i) {
00143         components_.push_back(new component(*rhs.components_[i]));
00144       }
00145     }
00146     return *this;
00147   }
00148 
00149   //: Return the run time dimension, which does not equal \c n when \c n==0
00150   unsigned int dimension() const
00151   {
00152     if (n > 0 || num_components() == 0)
00153       return n;
00154     return components_[0]->distribution.dimension();
00155   }
00156 
00157   //: Return the number of components in the mixture
00158   unsigned int num_components() const { return components_.size(); }
00159 
00160   //: Access (const) a component distribution of the mixture
00161   const dist_t& distribution(unsigned int index) const
00162   {
00163     assert(index < num_components());
00164     return components_[index]->distribution;
00165   }
00166 
00167   //: Access a component distribution of the mixture
00168   dist_t& distribution(unsigned int index)
00169   {
00170     assert(index < num_components());
00171     return components_[index]->distribution;
00172   }
00173 
00174   //: Return the weight of a component in the mixture
00175   T weight(unsigned int index) const
00176   {
00177     assert(index < num_components());
00178     return components_[index]->weight;
00179   }
00180 
00181   //: Set the weight of a component in the mixture
00182   void set_weight(unsigned int index, const T& w)
00183   {
00184     assert(index < num_components());
00185     assert(w >= T(0));
00186     components_[index]->weight = w;
00187   }
00188 
00189   //: Insert a new component at the end of the vector
00190   bool insert(const dist_t& d, const T& wght = T(0))
00191   {
00192     assert(d.dimension() == this->dimension() || num_components() == 0);
00193     components_.push_back(new component(d, wght));
00194     return true;
00195   }
00196 
00197   //: Remove the last component in the vector
00198   bool remove_last()
00199   {
00200     if (components_.empty())
00201       return false;
00202     delete components_.back();
00203     components_.pop_back();
00204     return true;
00205   }
00206 
00207   //: Compute the unnormalized density at this point
00208   T density(const F& pt) const
00209   {
00210     typedef typename vcl_vector<component*>::const_iterator comp_itr;
00211     T prob = 0;
00212     for (comp_itr i = components_.begin(); i != components_.end(); ++i) {
00213       // must use prob_density here to get meaningful results
00214       prob += (*i)->weight * (*i)->distribution.prob_density(pt);
00215     }
00216     return prob;
00217   }
00218 
00219   //: Compute the gradient of the unnormalized density at a point
00220   // \return the density at \a pt since it is usually needed as well, and
00221   //         is often trivial to compute while computing gradient
00222   // \retval g the gradient vector
00223   T gradient_density(const F& pt, vector& g) const
00224   {
00225     const unsigned int d = this->dimension();
00226     vpdt_set_size(g,d);
00227     vpdt_fill(g,T(0));
00228     typedef typename vcl_vector<component*>::const_iterator comp_itr;
00229     T dens = 0;
00230     for (comp_itr i = components_.begin(); i != components_.end(); ++i) {
00231       vector g_i;
00232       T w_i = (*i)->distribution.norm_const() * (*i)->weight;
00233       dens +=  w_i * (*i)->distribution.gradient_density(pt,g_i);
00234       g_i *= w_i;
00235       g += g_i;
00236     }
00237     return dens;
00238   }
00239 
00240   //: Evaluate the cumulative distribution function at a point
00241   // This is the integral of the density function from negative infinity
00242   // (in all dimensions) to the point in question
00243   T cumulative_prob(const F& pt) const
00244   {
00245     typedef typename vcl_vector<component*>::const_iterator comp_itr;
00246     T prob = 0;
00247     T sum_w = 0;
00248     for (comp_itr i = components_.begin(); i != components_.end(); ++i) {
00249       prob += (*i)->weight * (*i)->distribution.cumulative_prob(pt);
00250       sum_w += (*i)->weight;
00251     }
00252     assert(sum_w > T(0));
00253     return prob/sum_w;
00254   }
00255 
00256   //: Compute the mean of the distribution.
00257   // weighted average of the component means
00258   void compute_mean(F& mean) const
00259   {
00260     const unsigned int d = this->dimension();
00261     vpdt_set_size(mean,d);
00262     vpdt_fill(mean,T(0));
00263 
00264     typedef typename vcl_vector<component*>::const_iterator comp_itr;
00265     F cmp_mean;
00266     T sum_w = T(0);
00267     for (comp_itr i = components_.begin(); i != components_.end(); ++i) {
00268       (*i)->distribution.compute_mean(cmp_mean);
00269       cmp_mean *= (*i)->weight;
00270       sum_w += (*i)->weight;
00271       mean += cmp_mean;
00272     }
00273     assert(sum_w > 0);
00274     mean /= sum_w;
00275   }
00276 
00277   //: Compute the covariance of the distribution.
00278   void compute_covar(matrix& covar) const
00279   {
00280     const unsigned int d = this->dimension();
00281     F mean;
00282     vpdt_set_size(covar,d);
00283     vpdt_fill(covar,T(0));
00284     vpdt_set_size(mean,d);
00285     vpdt_fill(mean,T(0));
00286 
00287     typedef typename vcl_vector<component*>::const_iterator comp_itr;
00288     F cmp_mean;
00289     matrix cmp_covar;
00290     T sum_w = T(0);
00291     for (comp_itr i = components_.begin(); i != components_.end(); ++i) {
00292       const T& wgt = (*i)->weight;
00293       (*i)->distribution.compute_covar(cmp_covar);
00294       (*i)->distribution.compute_mean(cmp_mean);
00295       cmp_covar += outer_product(cmp_mean,cmp_mean);
00296       cmp_covar *= wgt;
00297       cmp_mean *= wgt;
00298       sum_w += wgt;
00299       covar += cmp_covar;
00300       mean += cmp_mean;
00301     }
00302     assert(sum_w > 0);
00303     covar -= outer_product(mean,mean);
00304     covar /= sum_w;
00305   }
00306 
00307   //: The normalization constant for the density
00308   // When density() is multiplied by this value it becomes prob_density
00309   // norm_const() is reciprocal of the integral of density over the entire field
00310   T norm_const() const
00311   {
00312     typedef typename vcl_vector<component*>::const_iterator comp_itr;
00313     T sum = 0;
00314     for (comp_itr i = components_.begin(); i != components_.end(); ++i)
00315       sum += (*i)->weight;
00316     assert(sum > 0);
00317     return 1/sum;
00318   }
00319 
00320   //: Normalize the weights of the components to add to 1.
00321   void normalize_weights()
00322   {
00323     typedef typename vcl_vector<component*>::iterator comp_itr;
00324     T norm = norm_const();
00325     for (comp_itr i = components_.begin(); i != components_.end(); ++i)
00326       (*i)->weight *= norm;
00327   }
00328 
00329   //: Sort the components in order of decreasing weight
00330   void sort() { vcl_sort(components_.begin(), components_.end(), sort_weight() ); }
00331 
00332   //: Sort the components in the range \a idx1 to \a idx2 in order of decreasing weight
00333   void sort(unsigned int idx1, unsigned int idx2)
00334   { vcl_sort(components_.begin()+idx1, components_.begin()+idx2+1, sort_weight() ); }
00335 
00336   //: Sort the components using any StrictWeakOrdering function
00337   // The prototype should be
00338   // \code
00339   // template <class dist_t>
00340   // bool functor(const dist_t& d1, const vpdt_dist_traits<dist_t>::scalar_type& w1,
00341   //              const dist_t& d2, const vpdt_dist_traits<dist_t>::scalar_type& w2);
00342   // \endcode
00343   template <class comp_type_>
00344   void sort(comp_type_ comp)
00345   { vcl_sort(components_.begin(), components_.end(), sort_adaptor<comp_type_>(comp)); }
00346 
00347   //: Sort the components in the range \a idx1 to \a idx2 using any StrictWeakOrdering function
00348   template <class comp_type_>
00349   void sort(comp_type_ comp, unsigned int idx1, unsigned int idx2)
00350   { vcl_sort(components_.begin()+idx1, components_.begin()+idx2+1, sort_adaptor<comp_type_>(comp)); }
00351 
00352   friend T vpdt_box_prob<dist_t>(const vpdt_mixture_of<dist_t>& d, const F& min_pt, const F& max_pt);
00353 };
00354 
00355 
00356 //: The probability of being in an axis-aligned box.
00357 // The box is defined by two points, the minimum and maximum.
00358 // Implemented in terms of \c vpdt_cumulative_prob() by default.
00359 template <class dist>
00360 typename vpdt_dist_traits<vpdt_mixture_of<dist> >::scalar_type
00361 vpdt_box_prob(const vpdt_mixture_of<dist>& d,
00362               const typename vpdt_dist_traits<vpdt_mixture_of<dist> >::field_type& min_pt,
00363               const typename vpdt_dist_traits<vpdt_mixture_of<dist> >::field_type& max_pt)
00364 {
00365   typedef typename vpdt_dist_traits<vpdt_mixture_of<dist> >::scalar_type T;
00366   typedef typename vcl_vector<typename vpdt_mixture_of<dist>::component*>::const_iterator comp_itr;
00367   T prob = 0;
00368   T sum_w = 0;
00369   for (comp_itr i = d.components_.begin(); i != d.components_.end(); ++i) {
00370     prob += (*i)->weight * vpdt_box_prob((*i)->distribution,min_pt,max_pt);
00371     sum_w += (*i)->weight;
00372   }
00373   assert(sum_w > T(0));
00374   return prob/sum_w;
00375 }
00376 
00377 
00378 #endif // vpdt_mixture_of_h_