contrib/mul/pdf1d/pdf1d_epanech_kernel_pdf_sampler.cxx
Go to the documentation of this file.
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