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