contrib/mul/vpdfl/vpdfl_pc_gaussian_builder.cxx

Go to the documentation of this file.
00001 // This is mul/vpdfl/vpdfl_pc_gaussian_builder.cxx
00002 #ifdef VCL_NEEDS_PRAGMA_INTERFACE
00003 #pragma implementation
00004 #endif
00005 //:
00006 // \file
00007 // \brief Interface for Multi-variate Principle Component gaussian PDF Builder.
00008 // \author Ian Scott
00009 // \date 21-Jul-2000
00010 //
00011 // Modifications
00012 // \verbatim
00013 // 23 April 2001 IMS - Ported to VXL
00014 // \endverbatim
00015 
00016 //=======================================================================
00017 // inclusions
00018 //=======================================================================
00019 
00020 #include "vpdfl_pc_gaussian_builder.h"
00021 
00022 #include <vcl_string.h>
00023 #include <vcl_cassert.h>
00024 #include <vcl_cstdlib.h> // for vcl_abort()
00025 #include <mbl/mbl_data_wrapper.h>
00026 #include <vnl/vnl_math.h>
00027 #include <vnl/vnl_c_vector.h>
00028 #include <vnl/algo/vnl_symmetric_eigensystem.h>
00029 #include <vpdfl/vpdfl_gaussian_builder.h>
00030 #include <vpdfl/vpdfl_pdf_base.h>
00031 #include <vpdfl/vpdfl_pc_gaussian.h>
00032 #include <mbl/mbl_parse_block.h>
00033 #include <mbl/mbl_read_props.h>
00034 #include <mbl/mbl_exception.h>
00035 #include <vul/vul_string.h>
00036 
00037 //=======================================================================
00038 
00039 vpdfl_pc_gaussian_builder::vpdfl_pc_gaussian_builder() :
00040   partitionMethod_(vpdfl_pc_gaussian_builder::fixed),
00041   proportionOfVariance_(0),
00042   fixed_partition_(1)
00043 {
00044 }
00045 
00046 //=======================================================================
00047 
00048 vpdfl_pc_gaussian_builder::~vpdfl_pc_gaussian_builder()
00049 {
00050 }
00051 
00052 //=======================================================================
00053 
00054 //: Use proportion of variance to decide on the number of principle components.
00055 // Specify the proportion (between 0 and 1).
00056 // The default setting uses a fixed number of principle components.
00057 void vpdfl_pc_gaussian_builder::set_proportion_partition( double proportion)
00058 {
00059   assert(proportion >= 0.0);
00060   assert(proportion <= 1.0);
00061 
00062   proportionOfVariance_ = proportion;
00063   partitionMethod_ = proportionate;
00064 }
00065 
00066 //=======================================================================
00067 
00068 //: Set the number of principle components when using fixed partition.
00069 void vpdfl_pc_gaussian_builder::set_fixed_partition(int n_principle_components)
00070 {
00071   assert(n_principle_components >=0);
00072   fixed_partition_ = n_principle_components;
00073   partitionMethod_ = vpdfl_pc_gaussian_builder::fixed;
00074 }
00075 
00076 //=======================================================================
00077 
00078 vpdfl_pc_gaussian& vpdfl_pc_gaussian_builder::gaussian(vpdfl_pdf_base& model) const
00079 {
00080     // need a vpdfl_gaussian
00081   assert(model.is_class("vpdfl_pc_gaussian"));
00082   return static_cast<vpdfl_pc_gaussian&>( model);
00083 }
00084 
00085 //=======================================================================
00086 
00087 vpdfl_pdf_base* vpdfl_pc_gaussian_builder::new_model() const
00088 {
00089   return new vpdfl_pc_gaussian();
00090 }
00091 
00092 //=======================================================================
00093 
00094 void vpdfl_pc_gaussian_builder::build(vpdfl_pdf_base& model,
00095                                       const vnl_vector<double>& mean) const
00096 {
00097   vpdfl_pc_gaussian& g = gaussian(model);
00098   int n = mean.size();
00099 
00100   // Generate an identity matrix for eigenvectors
00101   vnl_matrix<double> P(n,n);
00102   P.fill(0);
00103   P.fill_diagonal(1.0);
00104 
00105   g.set(mean,P,vnl_vector<double>(0), min_var());
00106 }
00107 
00108 #if 0 // this doesn't work
00109     //: Build model from mean and covariance
00110 void vpdfl_pc_gaussian_builder::buildFromCovar(vpdfl_pc_gaussian& g,
00111                                                const vnl_vector<double>& mean,
00112                                                const vnl_matrix<double>& S,
00113                                                unsigned nPrinComps) const
00114 {
00115   int n = mean.size();
00116   vnl_matrix<double> evecs;
00117   vnl_vector<double> evals;
00118 
00119   NR_CalcSymEigens(S,evecs,evals,0);
00120   vnl_vector<double> principleEVals(nPrinComps);
00121 
00122   // Apply threshold to variance
00123   for (int i=1;i<=nPrinComps;++i)
00124     if (evals(i)<min_var())
00125       principleEVals(i)=min_var();
00126     else
00127       principleEVals(i)=evals(i);
00128 
00129   double sum = 0.0; // The sum of the complementary space eigenvalues.
00130   for (int i=nPrinComps+1; i <= n; i++)
00131     sum += evals(i);
00132 
00133     // The Eigenvalue of the complementary space basis vectors
00134   double complementaryEVals = sum / (n - nPrinComps);
00135 
00136   if (complementaryEVals < min_var()) complementaryEVals = min_var();
00137 
00138   g.set(mean, evecs, principleEVals, complementaryEVals);
00139 }
00140 #endif
00141 
00142 
00143 //: replace any eigenvalues that are less than zero, with zero.
00144 // Small negative eigenvalues can be generated due to rounding errors.
00145 // This function assumes that the eigenvalues are stored in descending order.
00146 static void eValsFloorZero(vnl_vector<double> &v)
00147 {
00148   int n = v.size();
00149   double *v_data = v.data_block();
00150   int i=n-1;
00151   while (i && v_data[i] < 0.0)
00152   {
00153     v_data[i]=0.0;
00154     i--;
00155   }
00156 }
00157 
00158 
00159 void vpdfl_pc_gaussian_builder::build(vpdfl_pdf_base& model,
00160                                       mbl_data_wrapper<vnl_vector<double> >& data) const
00161 {
00162   vpdfl_pc_gaussian& g = gaussian(model);
00163 
00164   unsigned long n_samples = data.size();
00165   assert (n_samples>=2L);
00166 
00167   int n = data.current().size();
00168 
00169   vnl_vector<double> mean;
00170 //vnl_matrix<double> evecs;
00171 //vnl_vector<double> evals;
00172   vnl_matrix<double> evecs(n,n);
00173   vnl_vector<double> evals(n);
00174   vnl_matrix<double> S;
00175 
00176   meanCovar(mean,S,data);
00177 
00178   vnl_symmetric_eigensystem_compute(S, evecs, evals);
00179   // eigenvalues are lowest first here
00180   evals.flip();
00181   evecs.fliplr();
00182   // eigenvalues are highest first now
00183 
00184   int n_principle_components = decide_partition(evals, n_samples, 0);
00185 
00186   vnl_vector<double> principleEVals(n_principle_components);
00187 
00188   // Apply threshold to variance
00189   for (int i=0;i<n_principle_components;++i)
00190     if (evals(i)<min_var())
00191       principleEVals(i)=min_var();
00192     else
00193       principleEVals(i)=evals(i);
00194 
00195   double eVsum = 0.0; // The sum of the complementary space eigenvalues.
00196   for (int i=n_principle_components; i < n; i++)
00197     eVsum += evals(i);
00198 
00199     // The Eigenvalue of the complementary space basis vectors
00200   double complementaryEVals = eVsum / (n - n_principle_components);
00201 
00202   if (complementaryEVals < min_var()) complementaryEVals = min_var();
00203 
00204   g.set(mean, evecs, principleEVals, complementaryEVals);
00205 }
00206 
00207 //: Computes mean and covariance of given data
00208 void vpdfl_pc_gaussian_builder::mean_covar(vnl_vector<double>& mean, vnl_matrix<double>& S,
00209                                            mbl_data_wrapper<vnl_vector<double> >& data) const
00210 {
00211   unsigned long n_samples = data.size();
00212 
00213   assert (n_samples!=0L);
00214 
00215   int n_dims = data.current().size();
00216   vnl_vector<double> sum(n_dims);
00217   sum.fill(0);
00218 
00219   S.set_size(0,0);
00220 
00221   data.reset();
00222   for (unsigned long i=0;i<n_samples;i++)
00223   {
00224     sum += data.current();
00225     updateCovar(S,data.current(),1.0);
00226 
00227     data.next();
00228   }
00229 
00230   mean = sum;
00231   mean/=n_samples;
00232   S/=n_samples;
00233   updateCovar(S,mean,-1.0);
00234 }
00235 
00236 
00237 void vpdfl_pc_gaussian_builder::weighted_build(vpdfl_pdf_base& model,
00238                                                mbl_data_wrapper<vnl_vector<double> >& data,
00239                                                const vcl_vector<double>& wts) const
00240 {
00241   vpdfl_pc_gaussian& g = gaussian(model);
00242 
00243   unsigned long n_samples = data.size();
00244 
00245   if (n_samples<2L)
00246   {
00247     vcl_cerr<<"vpdfl_gaussian_builder::weighted_build() Too few examples available.\n";
00248     vcl_abort();
00249   }
00250 
00251   data.reset();
00252   const int n = data.current().size();
00253   vnl_vector<double> sum(n);
00254   sum.fill(0.0);
00255   vnl_matrix<double> evecs(n,n);
00256   vnl_vector<double> evals(n);
00257   vnl_matrix<double> S;
00258   double w_sum = 0.0;
00259   double w;
00260   unsigned actual_samples = 0;
00261 
00262   for (unsigned long i=0;i<n_samples;i++)
00263   {
00264     w = wts[i];
00265     if (w != 0.0) // Common case - save time.
00266     {
00267       actual_samples ++;
00268       w_sum += w;
00269       data.current().assert_finite();
00270       sum += w*data.current();
00271       updateCovar(S,data.current(),w);
00272     }
00273     data.next();
00274   }
00275 
00276   updateCovar(S,sum,-1.0/w_sum);
00277   S*=actual_samples/((actual_samples - 1) *w_sum);
00278   sum/=w_sum;
00279   // now sum = weighted mean
00280   // and S = weighted covariance corrected for unbiased rather than ML result.
00281 
00282 
00283   vnl_symmetric_eigensystem_compute(S, evecs, evals);
00284   // eigenvalues are lowest first here
00285   evals.flip();
00286   evecs.fliplr();
00287   // eigenvalues are highest first now
00288 
00289 #if 0
00290   vcl_cerr << 'S' << S <<vcl_endl
00291            << "evals" << evals <<vcl_endl
00292            << "evecs" << evecs <<vcl_endl;
00293 #endif
00294 
00295   eValsFloorZero(evals);
00296 
00297   int n_principle_components = decide_partition(evals, n);
00298 
00299   vnl_vector<double> principleEVals(n_principle_components);
00300 
00301   // Apply threshold to variance
00302   for (int i=0;i<n_principle_components;++i)
00303     if (evals(i)<min_var())
00304       principleEVals(i)=min_var();
00305     else
00306       principleEVals(i)=evals(i);
00307   double eVsum = 0.0; // The sum of the complementary space eigenvalues.
00308   for (int i=n_principle_components; i < n; i++)
00309     eVsum += evals(i);
00310 
00311     // The Eigenvalue of the complementary space basis vectors
00312   double complementaryEVals;
00313   if (n_principle_components != n) // avoid divide by 0
00314     complementaryEVals = eVsum / (n - n_principle_components);
00315   else
00316     complementaryEVals = 0.0; // actual could be any value.
00317 
00318   if (complementaryEVals < min_var()) complementaryEVals = min_var();
00319 
00320   g.set(sum, evecs, principleEVals, complementaryEVals);
00321 }
00322 
00323 
00324 //: Decide where to partition an Eigenvector space
00325 // Returns the number of principle components to be used.
00326 // Pass in the Eigenvalues (eVals), the number of samples
00327 // that went to make up this Gaussian (nSamples), and the noise floor
00328 // for the dataset. The method may use simplified algorithms if
00329 // you indicate that the number of samples or noise floor is unknown
00330 // (by setting the latter parameters to 0.)
00331 unsigned vpdfl_pc_gaussian_builder::decide_partition(const vnl_vector<double>& eVals, unsigned /*nSamples*/ /*=0*/,
00332   double /*noise*/ /*=0.0*/) const
00333 {
00334   assert (eVals.size() > 0);
00335   if (partitionMethod_ == vpdfl_pc_gaussian_builder::fixed)
00336   {
00337     return vnl_math_min(eVals.size(), (unsigned)fixed_partition()+1);;
00338   }
00339   else if (partitionMethod_ == proportionate)
00340   {
00341     double sum = vnl_c_vector<double>::sum(eVals.data_block(), eVals.size());
00342     assert (proportionOfVariance_ < 1.0 && proportionOfVariance_ > 0.0);
00343     double stopWhen = sum * proportionOfVariance_;
00344     sum = eVals(0);
00345     unsigned i=0;
00346     while (sum <= stopWhen)
00347     {
00348       i++;
00349       sum += eVals(i);
00350     }
00351     return i;
00352   }
00353   else
00354   {
00355     vcl_cerr << "vpdfl_pc_gaussian_builder::decide_partition(): Unexpected partition method: "
00356              << (short)partitionMethod_ <<vcl_endl;
00357     vcl_abort();
00358     return 0;
00359   }
00360 }
00361 
00362 //: Read initialisation settings from a stream.
00363 // Parameters:
00364 // \verbatim
00365 // {
00366 //   mode_choice: fixed  // Alternative: proportionate
00367 //   var_prop: 0.95
00368 //   n_modes: 3
00369 //   min_var: 1.0e-6
00370 // }
00371 // \endverbatim
00372 // \throw mbl_exception_parse_error if the parse fails.
00373 void vpdfl_pc_gaussian_builder::config_from_stream(vcl_istream & is)
00374 {
00375   vcl_string s = mbl_parse_block(is);
00376 
00377   vcl_istringstream ss(s);
00378   mbl_read_props_type props = mbl_read_props_ws(ss);
00379 
00380   if (props.find("mode_choice")!=props.end())
00381   {
00382     if (props["mode_choice"]=="fixed") 
00383       partitionMethod_=fixed;
00384     else
00385     if (props["mode_choice"]=="proportionate") 
00386       partitionMethod_=proportionate;
00387     else
00388     {
00389       vcl_string err_msg = "Unknown mode_choice: "+props["mode_choice"];
00390       throw mbl_exception_parse_error(err_msg);
00391     }
00392 
00393     props.erase("mode_choice");
00394   }
00395 
00396   if (props.find("var_prop")!=props.end())
00397   {
00398     proportionOfVariance_=vul_string_atof(props["var_prop"]);
00399     props.erase("var_prop");
00400   }
00401 
00402   if (props.find("n_modes")!=props.end())
00403   {
00404     fixed_partition_=vul_string_atoi(props["n_modes"]);
00405     props.erase("n_modes");
00406   }
00407 
00408   double mv=1.0e-6;
00409   if (props.find("min_var")!=props.end())
00410   {
00411     mv=vul_string_atof(props["min_var"]);
00412     props.erase("min_var");
00413   }
00414   set_min_var(mv);
00415 
00416   try
00417   {
00418     mbl_read_props_look_for_unused_props(
00419         "vpdfl_axis_gaussian_builder::config_from_stream", props);
00420   }
00421   catch(mbl_exception_unused_props &e)
00422   {
00423     throw mbl_exception_parse_error(e.what());
00424   }
00425 
00426 }
00427 
00428 
00429 //=======================================================================
00430 
00431 vcl_string vpdfl_pc_gaussian_builder::is_a() const
00432 {
00433   static vcl_string class_name_ = "vpdfl_pc_gaussian_builder";
00434   return class_name_;
00435 }
00436 
00437 //=======================================================================
00438 // Method: is_class
00439 //=======================================================================
00440 
00441 bool vpdfl_pc_gaussian_builder::is_class(vcl_string const& s) const
00442 {
00443   return vpdfl_gaussian_builder::is_class(s) || s==vpdfl_pc_gaussian_builder::is_a();
00444 }
00445 
00446 //=======================================================================
00447 // Method: version_no
00448 //=======================================================================
00449 
00450 short vpdfl_pc_gaussian_builder::version_no() const
00451 {
00452   return 2;
00453 }
00454 
00455 //=======================================================================
00456 // Method: clone
00457 //=======================================================================
00458 
00459 vpdfl_builder_base* vpdfl_pc_gaussian_builder::clone() const
00460 {
00461   return new vpdfl_pc_gaussian_builder(*this);
00462 }
00463 
00464 //=======================================================================
00465 // Method: print
00466 //=======================================================================
00467 
00468 void vpdfl_pc_gaussian_builder::print_summary(vcl_ostream& os) const
00469 {
00470   vpdfl_gaussian_builder::print_summary(os);
00471   if (partitionMethod_==fixed) os<<" mode_choice: fixed ";
00472   if (partitionMethod_==proportionate) 
00473     os<<" mode_choice: proportionate ";
00474   os<<" var_prop: "<<proportionOfVariance_;
00475   os<<" n_fixed: "<<fixed_partition_<<' ';
00476 }
00477 
00478 //=======================================================================
00479 // Method: save
00480 //=======================================================================
00481 
00482 void vpdfl_pc_gaussian_builder::b_write(vsl_b_ostream& bfs) const
00483 {
00484   vsl_b_write(bfs, is_a());
00485   vsl_b_write(bfs, version_no());
00486   vpdfl_gaussian_builder::b_write(bfs);
00487   vsl_b_write(bfs,(short)partitionMethod_);
00488   vsl_b_write(bfs, proportionOfVariance_);
00489   vsl_b_write(bfs, fixed_partition_);
00490 }
00491 
00492 //=======================================================================
00493 // Method: load
00494 //=======================================================================
00495 
00496 void vpdfl_pc_gaussian_builder::b_read(vsl_b_istream& bfs)
00497 {
00498   if (!bfs) return;
00499 
00500   vcl_string name;
00501   vsl_b_read(bfs,name);
00502   if (name != is_a())
00503   {
00504     vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vpdfl_pc_gaussian_builder &)\n"
00505              << "           Attempted to load object of type "
00506              << name <<" into object of type " << is_a() << vcl_endl;
00507     bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00508     return;
00509   }
00510 
00511   short temp;
00512   short version;
00513   vsl_b_read(bfs,version);
00514   switch (version)
00515   {
00516     case 1:
00517       vpdfl_gaussian_builder::b_read(bfs);
00518       vsl_b_read(bfs, temp);
00519       partitionMethod_ = partitionMethods(temp);
00520       vsl_b_read(bfs, proportionOfVariance_);
00521       fixed_partition_ = 75;
00522       break;
00523     case 2:
00524       vpdfl_gaussian_builder::b_read(bfs);
00525       vsl_b_read(bfs, temp);
00526       partitionMethod_ = partitionMethods(temp);
00527       vsl_b_read(bfs, proportionOfVariance_);
00528       vsl_b_read(bfs, fixed_partition_);
00529       break;
00530     default:
00531       vcl_cerr << "I/O ERROR: vsl_b_read(vsl_b_istream&, vpdfl_pc_gaussian_builder &)\n"
00532                << "           Unknown version number "<< version << vcl_endl;
00533       bfs.is().clear(vcl_ios::badbit); // Set an unrecoverable IO error on stream
00534       return;
00535   }
00536 }

Generated on Tue Dec 2 05:11:22 2008 for contrib/mul/vpdfl by  doxygen 1.5.1