contrib/mul/pdf1d/pdf1d_kernel_pdf.cxx
Go to the documentation of this file.
00001 // This is mul/pdf1d/pdf1d_kernel_pdf.cxx
00002 
00003 //:
00004 // \file
00005 // \brief Base class for kernel PDFs.
00006 // \author Tim Cootes
00007 // \verbatim
00008 //  Modifications
00009 //  IMS 28 Feb 2002 Added inverse CDF.
00010 // \endverbatim
00011 
00012 #include "pdf1d_kernel_pdf.h"
00013 #include <vcl_cstdlib.h>
00014 #include <vcl_cassert.h>
00015 #include <vcl_string.h>
00016 #include <vnl/vnl_math.h>
00017 #include <vnl/io/vnl_io_vector.h>
00018 #include <mbl/mbl_index_sort.h>
00019 #include <pdf1d/pdf1d_calc_mean_var.h>
00020 
00021 //=======================================================================
00022 
00023 pdf1d_kernel_pdf::pdf1d_kernel_pdf()
00024 {
00025 }
00026 
00027 //=======================================================================
00028 
00029 pdf1d_kernel_pdf::~pdf1d_kernel_pdf()
00030 {
00031 }
00032 
00033 //=======================================================================
00034 //: Initialise so all kernels have the same width
00035 void pdf1d_kernel_pdf::set_centres(const vnl_vector<double>& x, double width)
00036 {
00037   x_ = x;
00038   width_.set_size(x.size());
00039   width_.fill(width);
00040   all_same_width_ = true;
00041 
00042   double m,v;
00043   pdf1d_calc_mean_var(m,v,x);
00044   set_mean(m);
00045   set_variance(v+width*width);
00046 }
00047 
00048 //=======================================================================
00049 //: Initialise so all kernels have given width
00050 void pdf1d_kernel_pdf::set_centres(const vnl_vector<double>& x,
00051                                    const vnl_vector<double>& width)
00052 {
00053   assert(x.size()==width.size());
00054   x_ = x;
00055   width_= width;
00056   all_same_width_ = false;
00057 
00058   double m,v;
00059   pdf1d_calc_mean_var(m,v,x);
00060   double w_sd = width.rms();
00061   set_mean(m);
00062   set_variance(v+w_sd*w_sd);
00063   index_.clear();
00064 }
00065 
00066 //=======================================================================
00067 //: The inverse cdf.
00068 // The value of x: P(x'<x) = P for x' drawn from distribution pdf.
00069 double pdf1d_kernel_pdf::inverse_cdf(double P) const
00070 {
00071   assert (cdf_is_analytic()); // if it isn't then we would be better off using sampling
00072   assert (0.0 < P && P < 1.0);
00073 
00074   if (index_.empty())
00075     mbl_index_sort(x_.data_block(), x_.size(), index_);
00076 
00077   // Assume kernels are deltas to get the starting point.
00078   double x_init=x_(index_[int(P * x_.size())]);
00079 
00080   double f_init = cdf(x_init);
00081 
00082   // guess initial step size. assuming a triangular kernel.
00083   double step = width_(index_[int(P * x_.size())]);
00084 
00085   double x_above, x_below;
00086   if (f_init > P)
00087   {
00088     x_above = x_init;
00089     while (true)
00090     {
00091       x_below = x_above - step;
00092       double f_below = cdf(x_below);
00093       if (f_below < P) break;
00094       x_above = x_below;
00095       step *= 2.0;
00096     }
00097   }
00098   else
00099   {
00100     x_below = x_init;
00101     while (true)
00102     {
00103       x_above = x_below + step;
00104       double f_above = cdf(x_above);
00105       if (f_above > P) break;
00106       x_below = x_above;
00107       step *= 2.0;
00108     }
00109   }
00110 #if 0
00111   double x_middle = (x_above + x_below) / 2;
00112   while (x_above - x_below > vnl_math::sqrteps)
00113   {
00114     double f_middle = pdf.cdf(x_middle) - P;
00115     if (f_middle < 0) x_below=x_middle;
00116     else x_above = x_middle;
00117   }
00118   return (x_above + x_below) / 2;
00119 #endif
00120   // bracketed Newton-Raphson.
00121 
00122 
00123   double x_middle=0.5*(x_above+x_below);
00124   double dxold= x_above-x_below;
00125   double dx=dxold;
00126   double f_middle = cdf(x_middle)-P;
00127   double df_middle = operator() (x_middle);
00128   for (unsigned j=100;j>0;j--)
00129   {
00130     if ( !((x_above - x_middle)*df_middle + f_middle > 0.0 &&
00131            (x_below - x_middle)*df_middle + f_middle < 0.0   ) ||
00132       (vnl_math_abs((2.0*f_middle)) > vnl_math_abs(dxold*df_middle)))
00133     { // Bisect if Newton-Raphson isn't working
00134       x_middle=0.5*(x_above+x_below);
00135       dxold=dx;
00136       dx=x_above-x_middle;
00137     } else // Newton-Raphson step
00138     {
00139       dxold=dx;
00140       dx=f_middle/df_middle;
00141       x_middle -= dx;
00142       assert (x_below <= x_middle && x_middle <= x_above);
00143     }
00144 
00145     if (vnl_math_abs(dx) < vnl_math_abs(x_middle * vnl_math::sqrteps))
00146     {
00147       return x_middle; // Converged .
00148     }
00149 
00150     f_middle = cdf(x_middle)-P;
00151     df_middle = operator()(x_middle);
00152 
00153     if (f_middle < 0) //bracket on the root.
00154       x_below=x_middle;
00155     else
00156       x_above=x_middle;
00157   }
00158   vcl_cerr << "ERROR: pdf1d_kernel_pdf::inverse_cdf() failed to converge.\n";
00159   vcl_abort();
00160   return 0.0; // dummy return
00161 }
00162 
00163 //=======================================================================
00164 
00165 vcl_string pdf1d_kernel_pdf::is_a() const
00166 {
00167   static vcl_string class_name_ = "pdf1d_kernel_pdf";
00168   return class_name_;
00169 }
00170 
00171 //=======================================================================
00172 
00173 bool pdf1d_kernel_pdf::is_class(vcl_string const& s) const
00174 {
00175   return pdf1d_pdf::is_class(s) || s==pdf1d_kernel_pdf::is_a();
00176 }
00177 
00178 //=======================================================================
00179 
00180 short pdf1d_kernel_pdf::version_no() const
00181 {
00182   return 1;
00183 }
00184 
00185 //=======================================================================
00186 
00187 void pdf1d_kernel_pdf::print_summary(vcl_ostream& os) const
00188 {
00189   pdf1d_pdf::print_summary(os);
00190   os << '\n';
00191 }
00192 
00193 //=======================================================================
00194 
00195 void pdf1d_kernel_pdf::b_write(vsl_b_ostream& bfs) const
00196 {
00197   vsl_b_write(bfs,is_a());
00198   vsl_b_write(bfs,version_no());
00199   pdf1d_pdf::b_write(bfs);
00200   vsl_b_write(bfs,x_);
00201   vsl_b_write(bfs,width_);
00202 }
00203 
00204 //=======================================================================
00205 
00206 void pdf1d_kernel_pdf::b_read(vsl_b_istream& bfs)
00207 {
00208   if (!bfs) return;
00209 
00210   vcl_string name;
00211   vsl_b_read(bfs,name);
00212   if (name != is_a())
00213   {
00214     vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, pdf1d_kernel_pdf &)\n"
00215              << "           Attempted to load object of type "
00216              << name <<" into object of type " << is_a() << '\n';
00217     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00218     return;
00219   }
00220 
00221   short version;
00222   vsl_b_read(bfs,version);
00223   switch (version)
00224   {
00225     case (1):
00226       pdf1d_pdf::b_read(bfs);
00227       vsl_b_read(bfs,x_);
00228       vsl_b_read(bfs,width_);
00229       break;
00230     default:
00231       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, pdf1d_kernel_pdf &)\n"
00232                << "           Unknown version number "<< version << '\n';
00233       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00234       return;
00235   }
00236 }
00237