00001 // This is mul/pdf1d/pdf1d_epanech_kernel_pdf_sampler.cxx 00002 #include "pdf1d_epanech_kernel_pdf_sampler.h" 00003 //: 00004 // \file 00005 // \brief Implements sampling for an Epanechnikov kernel PDF 00006 // \author Ian Scott 00007 00008 //======================================================================= 00009 00010 #include <vcl_string.h> 00011 #include <vcl_cmath.h> 00012 #include <vcl_complex.h> 00013 #include <pdf1d/pdf1d_sampler.h> 00014 00015 //======================================================================= 00016 00017 pdf1d_epanech_kernel_pdf_sampler::pdf1d_epanech_kernel_pdf_sampler(): 00018 rng_(9667566ul) 00019 { 00020 } 00021 00022 pdf1d_epanech_kernel_pdf_sampler::~pdf1d_epanech_kernel_pdf_sampler() 00023 { 00024 } 00025 00026 //======================================================================= 00027 00028 const pdf1d_epanech_kernel_pdf& pdf1d_epanech_kernel_pdf_sampler::epanech_kernel_pdf() const 00029 { 00030 return static_cast<const pdf1d_epanech_kernel_pdf&>( model()); 00031 } 00032 00033 // ==================================================================== 00034 00035 static const double root5 = 2.23606797749978969641; 00036 static const double root3 = 1.73205080756887729353; 00037 00038 //: Transform a unit uniform distribution x into an Epanech distribution y 00039 // $0 <= x <= 1 => -sqrt(5) <= y <= sqrt(5)$ 00040 // Matlab found 6 solutions to 00041 // ${\frac {3}{20}}\,\left |{\it Dy}\right |\sqrt {5}\left (1-1/5\,{y}^{2}\right )=1$ 00042 // The 6th, which has the correct properties is 00043 // $-1/2\,\sqrt [3]{-10\,\sqrt {5}x+5\,\sqrt {-5+20\,{x}^{2}}}-5/2\, 00044 // {\frac {1}{\sqrt [3]{-10\,\sqrt {5}x+5\,\sqrt {-5+20\,{x}^{2}}}}}-1/2\, 00045 // \sqrt {-1}\sqrt {3}\left (\sqrt [3]{-10\,\sqrt {5}x+5\,\sqrt {-5+20\, 00046 // {x}^{2}}}-5\,{\frac {1}{\sqrt [3]{-10\,\sqrt {5}x+5\,\sqrt {-5+20\, 00047 // {x}^{2}}}}}\right )$ 00048 double pdf1d_epanech_kernel_pdf_sampler::epan_transform(double x) 00049 { 00050 // The Matlab code for the differential equation is 00051 // d = dsolve('1=vcl_abs(Dy) * (3 * vcl_sqrt(5) / 20) *(1-0.2*y*y)') 00052 x -= 0.5; 00053 const vcl_complex<double> z(10.0 * root5 * x, 5 * vcl_sqrt(5-20*x*x)); 00054 const vcl_complex<double> cuberoot_z = vcl_pow(z,(1.0/3.0)); 00055 const vcl_complex<double> recip_cuberoot_z = 1.0/cuberoot_z; 00056 const vcl_complex<double> im(0,1); 00057 00058 // The imaginary terms cancel out in theory, but not necessarily numerically - strip them. 00059 return vcl_real(-0.5 * cuberoot_z - 2.5 * recip_cuberoot_z - 00060 im * root3 / 2.0 * ( cuberoot_z - 5.0 * recip_cuberoot_z)); 00061 } 00062 00063 00064 // For generating plausible examples: 00065 double pdf1d_epanech_kernel_pdf_sampler::sample() 00066 { 00067 const pdf1d_epanech_kernel_pdf& kpdf = epanech_kernel_pdf(); 00068 00069 int n = kpdf.centre().size(); 00070 00071 int i = 0; 00072 if (n>1) i=rng_.lrand32(0,n-1); 00073 00074 double x = epan_transform(rng_.drand32()); 00075 00076 00077 x*=kpdf.width()[i]; 00078 x+=kpdf.centre()[i]; 00079 00080 return x; 00081 } 00082 00083 //: Fill x with samples possibly chosen so as to represent the distribution 00084 // Samples equal numbers from each kernel. 00085 void pdf1d_epanech_kernel_pdf_sampler::regular_samples(vnl_vector<double>& x) 00086 { 00087 const pdf1d_epanech_kernel_pdf& kpdf = epanech_kernel_pdf(); 00088 int n = x.size(); 00089 double* x_data = x.data_block(); 00090 int nk = kpdf.centre().size(); 00091 int n_per_k = n/nk; 00092 double lim = (n_per_k-1)/2.0; 00093 00094 const double* c = kpdf.centre().data_block(); 00095 const double* w = kpdf.width().data_block(); 00096 00097 for (int i=0;i<n;++i) 00098 { 00099 // Select components in order 00100 int j = i%nk; 00101 00102 if (n_per_k>5) 00103 { 00104 x_data[i] = c[j]+epan_transform(rng_.drand32())*w[j]; 00105 } 00106 else 00107 { 00108 // Spread points about 00109 // Note that this isn't quite right - should be equally spaced in CDF space 00110 int a = j/nk; 00111 double f = double(a)/(n_per_k-1); // in [0,1] 00112 x_data[i] = c[j] + lim*(2*f-1)*w[j]; 00113 } 00114 } 00115 } 00116 00117 //======================================================================= 00118 00119 //: Reseeds the static random number generator (one per derived class) 00120 void pdf1d_epanech_kernel_pdf_sampler::reseed(unsigned long seed) 00121 { 00122 rng_.reseed(seed); 00123 } 00124 00125 //======================================================================= 00126 00127 vcl_string pdf1d_epanech_kernel_pdf_sampler::is_a() const 00128 { 00129 return vcl_string("pdf1d_epanech_kernel_pdf_sampler"); 00130 } 00131 00132 //======================================================================= 00133 00134 bool pdf1d_epanech_kernel_pdf_sampler::is_class(vcl_string const& s) const 00135 { 00136 return pdf1d_sampler::is_class(s) || s==pdf1d_epanech_kernel_pdf_sampler::is_a(); 00137 } 00138 00139 //======================================================================= 00140 00141 short pdf1d_epanech_kernel_pdf_sampler::version_no() const 00142 { 00143 return 1; 00144 } 00145 00146 //======================================================================= 00147 00148 pdf1d_sampler* pdf1d_epanech_kernel_pdf_sampler::clone() const 00149 { 00150 return new pdf1d_epanech_kernel_pdf_sampler(*this); 00151 } 00152