contrib/mul/pdf1d/pdf1d_mixture.cxx
Go to the documentation of this file.
00001 // This is mul/pdf1d/pdf1d_mixture.cxx
00002 #include "pdf1d_mixture.h"
00003 //:
00004 // \file
00005 // \brief Implements a mixture model (a set of individual pdfs + weights)
00006 // \author Tim Cootes and Ian Scott
00007 
00008 //=======================================================================
00009 
00010 #include <vcl_cmath.h>
00011 #include <vcl_cstdlib.h>
00012 #include <vcl_string.h>
00013 #include <vsl/vsl_indent.h>
00014 #include <vsl/vsl_binary_loader.h>
00015 #include <pdf1d/pdf1d_mixture_sampler.h>
00016 #include <vcl_cassert.h>
00017 #include <vsl/vsl_vector_io.h>
00018 #include <vnl/vnl_c_vector.h>
00019 
00020 //=======================================================================
00021 
00022 void pdf1d_mixture::init()
00023 {
00024 }
00025 
00026 pdf1d_mixture::pdf1d_mixture()
00027 {
00028   init();
00029 }
00030 
00031 pdf1d_mixture::pdf1d_mixture(const pdf1d_mixture& m):
00032   pdf1d_pdf()
00033 {
00034   init();
00035   *this = m;
00036 }
00037 
00038 pdf1d_mixture& pdf1d_mixture::operator=(const pdf1d_mixture& m)
00039 {
00040   if (this==&m) return *this;
00041 
00042   delete_stuff();
00043 
00044   pdf1d_pdf::operator=(m);
00045 
00046   int n = m.component_.size();
00047   component_.resize(n);
00048   for (int i=0;i<n;++i)
00049     component_[i] = m.component_[i]->clone();
00050 
00051   weight_ = m.weight_;
00052 
00053   return *this;
00054 }
00055 
00056 
00057 //=======================================================================
00058 
00059 void pdf1d_mixture::delete_stuff()
00060 {
00061   int n = component_.size();
00062   for (int i=0;i<n;++i)
00063     delete component_[i];
00064   component_.resize(0);
00065   weight_.resize(0);
00066 }
00067 
00068 pdf1d_mixture::~pdf1d_mixture()
00069 {
00070   delete_stuff();
00071 }
00072 
00073 
00074 //: Return instance of this PDF
00075 pdf1d_sampler* pdf1d_mixture::new_sampler() const
00076 {
00077   pdf1d_mixture_sampler* i = new pdf1d_mixture_sampler;
00078   i->set_model(*this);
00079 
00080   return i;
00081 }
00082 
00083 //: Initialise to use n components of type comp_type
00084 void pdf1d_mixture::init(const pdf1d_pdf& comp_type, int n)
00085 {
00086   delete_stuff();
00087   component_.resize(n);
00088   weight_.resize(n);
00089   for (int i=0;i<n;++i)
00090   {
00091     component_[i] = comp_type.clone();
00092     weight_[i] = 1.0/n;
00093   }
00094 }
00095 
00096 //=======================================================================
00097 
00098 void pdf1d_mixture::add_component(const pdf1d_pdf& comp)
00099 {
00100   vcl_vector<pdf1d_pdf*> old_comps = component_;
00101   vcl_vector<double> old_wts = weight_;
00102   unsigned int n = component_.size();
00103   assert(n == weight_.size());
00104 
00105   component_.resize(n+1);
00106   weight_.resize(n+1);
00107 
00108   for (unsigned int i=0;i<n;++i)
00109   {
00110     component_[i] = old_comps[i];
00111     weight_[i] = old_wts[i];
00112   }
00113 
00114   weight_[n] = 0.0;
00115   component_[n] = comp.clone();
00116 }
00117 
00118 //=======================================================================
00119 
00120 void pdf1d_mixture::clear()
00121 {
00122   delete_stuff();
00123 }
00124 
00125 //=======================================================================
00126 
00127 //: Return true if the object represents a valid PDF.
00128 // This will return false, if n_dims() is 0, for example just ofter
00129 // default construction.
00130 bool pdf1d_mixture::is_valid_pdf() const
00131 {
00132   if (!pdf1d_pdf::is_valid_pdf()) return false;
00133   const unsigned n = n_components();
00134     // the number of components should be consistent
00135   if (weight_.size() != n || component_.size() != n || n < 1) return false;
00136     // weights should sum to 1.
00137   double sum = vnl_c_vector<double>::sum(&weight_[0]/*.begin()*/, n);
00138   if (vcl_fabs(1.0 - sum) > 1e-10 ) return false;
00139     // the number of dimensions should be consistent
00140   for (unsigned i=0; i<n; ++i)
00141   {
00142     if (!components()[i]->is_valid_pdf()) return false;
00143   }
00144   return true;
00145 }
00146 
00147 //: Set the whole pdf mean and variance values.
00148 void pdf1d_mixture::set_mean_and_variance(double m, double v)
00149 {
00150   set_mean(m);
00151   set_variance(v);
00152 }
00153 
00154 
00155 //=======================================================================
00156 
00157 vcl_string pdf1d_mixture::is_a() const
00158 {
00159   return vcl_string("pdf1d_mixture");
00160 }
00161 
00162 //=======================================================================
00163 
00164 bool pdf1d_mixture::is_class(vcl_string const& s) const
00165 {
00166   return pdf1d_pdf::is_class(s) || s==pdf1d_mixture::is_a();
00167 }
00168 
00169 //=======================================================================
00170 
00171 short pdf1d_mixture::version_no() const
00172 {
00173   return 1;
00174 }
00175 
00176 //=======================================================================
00177 
00178 pdf1d_pdf* pdf1d_mixture::clone() const
00179 {
00180   return new pdf1d_mixture(*this);
00181 }
00182 
00183 
00184 //=======================================================================
00185 
00186 
00187 void pdf1d_mixture::print_summary(vcl_ostream& os) const
00188 {
00189   os<<'\n'<<vsl_indent();
00190   pdf1d_pdf::print_summary(os);
00191   os<<'\n';
00192   for (unsigned int i=0;i<component_.size();++i)
00193   {
00194     os<<vsl_indent()<<"Component "<<i<<" :  Wt: "<<weight_[i] <<'\n'
00195       <<vsl_indent()<<"PDF: " << component_[i]<<'\n';
00196   }
00197 }
00198 
00199 //=======================================================================
00200 
00201 void pdf1d_mixture::b_write(vsl_b_ostream& bfs) const
00202 {
00203   vsl_b_write(bfs, is_a());
00204   vsl_b_write(bfs, version_no());
00205   pdf1d_pdf::b_write(bfs);
00206   vsl_b_write(bfs, component_);
00207   vsl_b_write(bfs, weight_);
00208 }
00209 
00210 //=======================================================================
00211 
00212 void pdf1d_mixture::b_read(vsl_b_istream& bfs)
00213 {
00214   if (!bfs) return;
00215 
00216   vcl_string name;
00217   vsl_b_read(bfs,name);
00218   if (name != is_a())
00219   {
00220     vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, pdf1d_mixture &)\n"
00221              << "           Attempted to load object of type "
00222              << name <<" into object of type " << is_a() << vcl_endl;
00223     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00224     return;
00225   }
00226 
00227   delete_stuff();
00228 
00229   short version;
00230   vsl_b_read(bfs,version);
00231   switch (version)
00232   {
00233     case (1):
00234       pdf1d_pdf::b_read(bfs);
00235       vsl_b_read(bfs, component_);
00236       vsl_b_read(bfs, weight_);
00237       break;
00238     default:
00239       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, pdf1d_mixture &)\n"
00240                << "           Unknown version number "<< version << vcl_endl;
00241       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00242       return;
00243   }
00244 }
00245 
00246 //=======================================================================
00247 
00248 
00249 double pdf1d_mixture::operator()(double x) const
00250 {
00251   return vcl_exp(log_p(x));
00252 }
00253 
00254 //=======================================================================
00255 
00256 double pdf1d_mixture::log_p(double x) const
00257 {
00258   int n = n_components();
00259 
00260   vcl_vector<double> log_ps(n);
00261   double max_log_p = 0;
00262   for (int i=0;i<n;++i)
00263   {
00264     if (weight_[i]>0.0)
00265     {
00266       log_ps[i] = component_[i]->log_p(x);
00267       if (i==0 || log_ps[i]>max_log_p) max_log_p = log_ps[i];
00268     }
00269   }
00270 
00271   double sum=0.0;
00272 
00273   for (int i=0;i<n;i++)
00274   {
00275     if (weight_[i]>0.0)
00276       sum += weight_[i] * vcl_exp(log_ps[i]-max_log_p);
00277   }
00278 
00279   return vcl_log(sum) + max_log_p;
00280 }
00281 
00282 //: Cumulative Probability (P(x'<x) for x' drawn from the distribution)
00283 double pdf1d_mixture::cdf(double x) const
00284 {
00285   double sum = 0;
00286 
00287   int n = n_components();
00288   for (int i=0;i<n;++i)
00289     sum += weight_[i]*component_[i]->cdf(x);
00290 
00291   return sum;
00292 }
00293 
00294 //: Return true if cdf() uses an analytic implementation
00295 bool pdf1d_mixture::cdf_is_analytic() const
00296 {
00297   return true;
00298 }
00299 
00300 //=======================================================================
00301 
00302 double pdf1d_mixture::gradient(double x, double& p) const
00303 {
00304   double p1;
00305   double g1 = component_[0]->gradient(x,p1);
00306   double g = g1*weight_[0];
00307   p = p1*weight_[0];
00308 
00309   for (unsigned int i=1;i<n_components();i++)
00310   {
00311     g1 = component_[i]->gradient(x,p1);
00312     g += g1*weight_[i];
00313     double p_comp = p1*weight_[i];
00314     p += p_comp;
00315   }
00316 
00317   return g;
00318 }
00319 
00320 
00321 //=======================================================================
00322 
00323 unsigned pdf1d_mixture::nearest_comp(double x) const
00324 {
00325   assert(component_.size()>=1);
00326 
00327   int n = n_components();
00328   if (n==1) return 0;
00329 
00330   int best_i=0;
00331   double min_d2 = vcl_fabs(x-component_[0]->mean());
00332 
00333   for (int i=1;i<n;i++)
00334   {
00335     double d2 = vcl_fabs(x-component_[i]->mean());
00336     if (d2<min_d2)
00337     {
00338       best_i=i;
00339       min_d2=d2;
00340     }
00341   }
00342 
00343   return best_i;
00344 }
00345 
00346 //=======================================================================
00347 
00348 
00349 //: Compute nearest point to x which has a density above a threshold
00350 //  If log_p(x)>log_p_min then x unchanged.  Otherwise x is moved
00351 //  (typically up the gradient) until log_p(x)>=log_p_min.
00352 // \param x This may be modified to the nearest plausible position.
00353 double pdf1d_mixture::nearest_plausible(double /*x*/, double /*log_p_min*/) const
00354 {
00355   vcl_cerr << "ERROR: pdf1d_mixture::nearest_plausible NYI\n";
00356   vcl_abort();
00357   return 0.0; // dummy return
00358 }
00359 
00360 
00361 //==================< end of file: pdf1d_mixture.cxx >====================