contrib/rpl/rrel/rrel_kernel_density_obj.cxx
Go to the documentation of this file.
00001 // This is rpl/rrel/rrel_kernel_density_obj.cxx
00002 #include "rrel_kernel_density_obj.h"
00003 #include <rrel/rrel_muset_obj.h>
00004 #include <vnl/vnl_vector.h>
00005 #include <vnl/vnl_math.h>
00006 #include <vcl_algorithm.h>
00007 #include <vcl_cassert.h>
00008 #include <vcl_iostream.h>
00009 
00010 namespace {
00011   inline void shft2(double &a, double &b, const double c)
00012   {
00013     a = b;
00014     b = c;
00015   }
00016 
00017   inline void shft3(double &a, double &b, double &c, const double d)
00018   {
00019     a = b;
00020     b = c;
00021     c = d;
00022   }
00023 }
00024 
00025 rrel_kernel_density_obj::rrel_kernel_density_obj(rrel_kernel_scale_type scale_type)
00026   : scale_type_(scale_type),
00027     fix_x_( false )
00028 {
00029 }
00030 
00031 double
00032 rrel_kernel_density_obj::fcn(vect_const_iter /*res_begin*/, vect_const_iter /*res_end*/,
00033                              vect_const_iter /*scale_begin*/,
00034                              vnl_vector<double>* /*param_vector*/ ) const
00035 {
00036   vcl_cerr << "rrel_kernel_density_obj::fcn() not implemented\n";
00037   return 0;
00038 }
00039 
00040 double
00041 rrel_kernel_density_obj::fcn(vect_const_iter res_begin,
00042                              vect_const_iter res_end,
00043                              double prior_scale,
00044                              vnl_vector<double>* ) const
00045 {
00046   double h = bandwidth ( res_begin, res_end, prior_scale );
00047   assert ( h != 0 );
00048   double x = best_x ( res_begin, res_end, prior_scale );
00049 
00050   return -1 * kernel_density( res_begin, res_end, x, h );
00051 }
00052 
00053 double
00054 rrel_kernel_density_obj::best_x(vect_const_iter res_begin,
00055                                 vect_const_iter res_end,
00056                                 double prior_scale) const
00057 {
00058   if (fix_x_)
00059     return 0;
00060 
00061   //Golden Section Search is adapted from "Numerical Recipes in C++"
00062   //to find x that maximizes kernel_density.
00063   const double R = 0.61803399, C = 1.0 - R; //The golden ratios.
00064   double f1, f2, x0, x1, x2, x3;
00065   double tol = 1.0e-9;
00066   double f0 = 0;
00067 
00068   double h = bandwidth(res_begin, res_end, prior_scale);
00069   assert(h!=0);
00070 
00071   vcl_vector<double> sort_res( res_begin, res_end );
00072   vcl_sort( sort_res.begin(), sort_res.end() );
00073 
00074   unsigned int loc = 0;
00075   unsigned int i = 0;
00076   for ( ; i<sort_res.size(); ++i ) {
00077     double x = sort_res[i];
00078     double f = kernel_density( res_begin, res_end, x, h );
00079     if (f > f0) {
00080       f0 = f;
00081       loc = i;
00082     }
00083   }
00084 
00085   x0 = sort_res[loc-1];
00086   x3 = sort_res[loc+1];
00087 
00088   if ( vnl_math_abs( x3 - sort_res[loc] ) > vnl_math_abs( sort_res[loc] - x0 ) ) {
00089     x1 = sort_res[loc];
00090     x2 = sort_res[loc] + C * ( x3 - sort_res[loc] );
00091   }
00092   else {
00093     x2 = sort_res[loc];
00094     x1 = sort_res[loc] - C * ( sort_res[loc] - x0 );
00095   }
00096 
00097   f1 = kernel_density( res_begin, res_end, x1, h );
00098   f2 = kernel_density( res_begin, res_end, x2, h );
00099   while ( vnl_math_abs( x3 - x0 ) >
00100           tol * vnl_math_abs( x1 ) + vnl_math_abs( x2 ) ) {
00101     if ( f2 > f1 ) {
00102       shft3( x0, x1, x2, R * x2 + C * x3 );
00103       shft2( f1, f2, kernel_density( res_begin, res_end, x2, h ) );
00104     }
00105     else {
00106       shft3( x3, x2, x1, R * x1 + C * x0 );
00107       shft2( f2, f1, kernel_density( res_begin, res_end, x1, h ) );
00108     }
00109   }
00110   if (f1 < f2)
00111     return x2;
00112   else
00113     return x1;
00114 }
00115 
00116 double
00117 rrel_kernel_density_obj::bandwidth(vect_const_iter res_begin, vect_const_iter res_end,
00118                                    double prior_scale) const
00119 {
00120   vcl_vector<double>::difference_type n = res_end - res_begin;
00121   double scale = 1.0;
00122 
00123   switch ( scale_type_ )
00124   {
00125    case RREL_KERNEL_MAD: {
00126     //A median absolute deviations (MAD) scale estimate.
00127 
00128     //Here I avoid using rrel_util_median_abs_dev_scale
00129     //because it assumes residuals are zero-based and have a Gaussian distribution.
00130 
00131     vcl_vector<double> residuals(res_begin, res_end);
00132     vcl_vector<double>::iterator loc = residuals.begin() + n / 2;
00133     vcl_nth_element( residuals.begin(), loc, residuals.end() );
00134 
00135     double res_median = *loc;
00136     vcl_vector<double> abs_res_median;
00137     abs_res_median.reserve( n );
00138     for (vcl_vector<double>::difference_type i=0; i<n; ++i ) {
00139       abs_res_median.push_back( vnl_math_abs( residuals[i] - res_median ) );
00140     }
00141     loc = abs_res_median.begin() + n / 2;
00142     vcl_nth_element( abs_res_median.begin(), loc, abs_res_median.end() );
00143     // c=0.5 is chosen to avoid over-smoothing of the estimated density.
00144     scale = 0.5 * (*loc);
00145     break;
00146    }
00147 
00148    case RREL_KERNEL_PRIOR:
00149     scale = prior_scale;
00150     break;
00151 
00152    case RREL_KERNEL_MUSE: {
00153     rrel_muset_obj muse( (int)n );
00154     scale = muse.fcn( res_begin, res_end );
00155     break; }
00156 
00157    default:
00158     assert(!"invalid scale_type");
00159   }
00160 
00161   // h = [243 * R(K) / 35 / Mu(K)^2 / n]^0.2 * scale
00162   // R(K) = Integral ( K(u)^2 ) du
00163   // Mu(K) = Integral ( u^2 * K(u) ) du
00164   const double c = 65610.0 / 143;
00165   return vcl_pow( c / n , 0.2 ) * scale;
00166 }
00167 
00168 double
00169 rrel_kernel_density_obj::kernel_density(vect_const_iter res_begin,
00170                                         vect_const_iter res_end,
00171                                         double x,
00172                                         double h) const
00173 {
00174   double f=0;
00175   vcl_vector<double>::difference_type n = res_end - res_begin;
00176   for ( ; res_begin!= res_end; ++res_begin ) {
00177     f += kernel_function( ( *res_begin - x ) / h );
00178   }
00179   f /= n*h;
00180   return f;
00181 }
00182 
00183 double
00184 rrel_kernel_density_obj::kernel_function(double u) const
00185 {
00186   if (vnl_math_abs(u) > 1)
00187     return 0;
00188 
00189   double t = 1 - u * u;
00190   return 1.09375 * t * t * t;
00191 }
00192 
00193