contrib/mul/pdf1d/pdf1d_weighted_epanech_kernel_sampler.cxx
Go to the documentation of this file.
00001 // This is mul/pdf1d/pdf1d_weighted_epanech_kernel_sampler.cxx
00002 #include "pdf1d_weighted_epanech_kernel_sampler.h"
00003 //:
00004 // \file
00005 // \brief Implements sampling for a weighted Epanechnikov kernel PDF
00006 // \author Ian Scott
00007 //
00008 //=======================================================================
00009 
00010 #include <vcl_string.h>
00011 #include <vcl_cassert.h>
00012 #include <vnl/vnl_math.h>
00013 #include <vnl/vnl_vector.h>
00014 #include <pdf1d/pdf1d_weighted_epanech_kernel_pdf.h>
00015 #include <pdf1d/pdf1d_epanech_kernel_pdf_sampler.h>
00016 
00017 //=======================================================================
00018 
00019 pdf1d_weighted_epanech_kernel_sampler::pdf1d_weighted_epanech_kernel_sampler():
00020   rng_(9667566ul)
00021 {
00022 }
00023 
00024 //=======================================================================
00025 
00026 pdf1d_weighted_epanech_kernel_sampler::~pdf1d_weighted_epanech_kernel_sampler()
00027 {
00028 }
00029 
00030 //=======================================================================
00031 
00032 const pdf1d_weighted_epanech_kernel_pdf& pdf1d_weighted_epanech_kernel_sampler::weighted_epanech_kernel_pdf() const
00033 {
00034   return static_cast<const pdf1d_weighted_epanech_kernel_pdf&>(model());
00035 }
00036 
00037 
00038 // ====================================================================
00039 
00040 //: For generating plausible examples
00041 double pdf1d_weighted_epanech_kernel_sampler::sample()
00042 {
00043 // Need to deal with weights.
00044 
00045   const pdf1d_weighted_epanech_kernel_pdf& kpdf = weighted_epanech_kernel_pdf();
00046 
00047   int n = kpdf.centre().size();
00048 
00049 
00050   // Randomly choose a component according to the weights (assumed to sum to 1)
00051   double r = rng_.drand32(0.0, kpdf.sum_weights_);
00052   int i=0;
00053   r-=kpdf.weight()[i];
00054   while (r>0 && (i<n))
00055   {
00056     i++;
00057     r-=kpdf.weight()[i];
00058   }
00059 
00060   double x = pdf1d_epanech_kernel_pdf_sampler::epan_transform(rng_.drand32());
00061 
00062   x*=kpdf.width()[i];
00063   x+=kpdf.centre()[i];
00064 
00065   return x;
00066 }
00067 
00068 //: Fill x with samples possibly chosen so as to represent the distribution
00069 //  Samples equal numbers from each kernel.
00070 void pdf1d_weighted_epanech_kernel_sampler::regular_samples(vnl_vector<double>& x)
00071 {
00072 // Need to deal with weights.
00073 
00074   const pdf1d_weighted_epanech_kernel_pdf& kpdf = weighted_epanech_kernel_pdf();
00075   const unsigned n = x.size();
00076   double* x_data = x.data_block();
00077   const unsigned nk = kpdf.centre().size();
00078   int n_per_k = n/nk;
00079   double lim = (n_per_k-1)/2.0;
00080 
00081   const double* c = kpdf.centre().data_block();
00082   const double* w = kpdf.width().data_block();
00083   const double* s = kpdf.weight().data_block();
00084 
00085   double dist = 0; // This marks the distance (in number of samples)
00086                    // to the end of the next kernel.
00087   // In order to get a regular distribution consistent with the weights,
00088   // we use dist to measure our way through the kernels.
00089   // Think of dist as being split up in proportion to the weight values.
00090 
00091   unsigned int i=0;
00092   for (unsigned int kernel = 0; kernel<nk; ++kernel)
00093   {
00094     // n_this_k is approximately the number of samples for this kernel.
00095     // the exact number depends on the non-integer part of dist.
00096     // If we didn't do this, we would have a problem with accumulation of rounding errors,
00097     // causing us to find too many or too few samples when we reach the end of dist.
00098     double n_this_k = n * s[kernel] /  kpdf.sum_weights_;
00099 
00100     dist += n_this_k;
00101 
00102     if (n_this_k>5.5) // randomly sample
00103     {
00104       while (dist > double(i))
00105       {
00106         x_data[i] = c[kernel]+ w[kernel]*
00107           pdf1d_epanech_kernel_pdf_sampler::epan_transform(rng_.drand32());
00108         ++i;
00109       }
00110     }
00111     else
00112     {
00113       // Spread points about
00114       // Note that this isn't quite right - should be equally spaced in CDF space
00115       unsigned i_this_k = 0;
00116       while (dist > double(i))
00117       {
00118         double f = (double(i_this_k) - dist + n_this_k)/(n_this_k);  // in [0,1]
00119         assert(0<=f && f<=1);
00120         x_data[i] = c[kernel] + lim*(2*f-1)*w[kernel];
00121       ++i; ++i_this_k;
00122       }
00123     }
00124   }
00125   assert (vnl_math_abs(dist - double(n)) < 1.0e-10 * dist);
00126   assert (i == n);
00127 }
00128 
00129 //=======================================================================
00130 
00131 //: Reseeds the static random number generator (one per derived class)
00132 void pdf1d_weighted_epanech_kernel_sampler::reseed(unsigned long seed)
00133 {
00134   rng_.reseed(seed);
00135 }
00136 
00137 //=======================================================================
00138 
00139 vcl_string pdf1d_weighted_epanech_kernel_sampler::is_a() const
00140 {
00141   return vcl_string("pdf1d_weighted_epanech_kernel_sampler");
00142 }
00143 
00144 //=======================================================================
00145 
00146 bool pdf1d_weighted_epanech_kernel_sampler::is_class(vcl_string const& s) const
00147 {
00148   return pdf1d_sampler::is_class(s) || s==pdf1d_weighted_epanech_kernel_sampler::is_a();
00149 }
00150 
00151 //=======================================================================
00152 
00153 short pdf1d_weighted_epanech_kernel_sampler::version_no() const
00154 {
00155   return 1;
00156 }
00157 
00158 //=======================================================================
00159 
00160 pdf1d_sampler* pdf1d_weighted_epanech_kernel_sampler::clone() const
00161 {
00162   return new pdf1d_weighted_epanech_kernel_sampler(*this);
00163 }
00164