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