Main Page | Modules | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

NCSpectrumInterpolator.cxx

Go to the documentation of this file.
00001 
00002 //$Id: NCSpectrumInterpolator.cxx,v 1.18 2009/11/18 18:58:24 bckhouse Exp $
00003 //
00004 // NCSpectrumInterpolator.cxx
00005 //
00006 // Given a set of reference histograms, interpolate between them.
00007 //
00008 // C. Backhouse 10/2008
00010 
00011 #include "NCSpectrumInterpolator.h"
00012 
00013 #include "MessageService/MsgService.h"
00014 
00015 #include "NCUtils/NCUtility.h"
00016 using namespace NC::Utility;
00017 
00018 #include "TCanvas.h"
00019 #include "TH1.h"
00020 #include "TH2.h"
00021 #include "TH3.h"
00022 #include "TMath.h"
00023 #include "TMatrixD.h"
00024 
00025 #include <cassert>
00026 #include <cmath>
00027 
00028 CVSID("$Id: NCSpectrumInterpolator.cxx,v 1.18 2009/11/18 18:58:24 bckhouse Exp $");
00029 
00030 using std::vector;
00031 
00032 bool NC::ISpectrumInterpolator::ShiftInfo::IsNominal() const
00033 {
00034   for(unsigned int n = 0; n < shift.size(); ++n)
00035     if(shift[n] != 0) return false;
00036   return true;
00037 }
00038 
00039 
00040 NC::ISpectrumInterpolator::~ISpectrumInterpolator()
00041 {
00042   for(unsigned int n = 0; n < shifted.size(); ++n)
00043     for(unsigned int m = 0; m < shifted[n].spectra.size(); ++m)
00044       delete shifted[n].spectra[m];
00045 }
00046 
00047 
00048 void NC::ISpectrumInterpolator::AddInputSpectra(std::vector<double> shift,
00049                                                 std::vector<TH1*> spectra)
00050 {
00051   for(unsigned int n = 0; n < shifted.size(); ++n){
00052     if(shifted[n].shift == shift){
00053       assert(0 && "We already added spectra at this shift");
00054       //      shifted[n].spectra.insert(shifted[n].spectra.end(),
00055       //                                spectra.begin(), spectra.end());
00056       //      return;
00057     }
00058   }
00059 
00060   // Didn't find a matching one so make a new one
00061   shifted.push_back(ShiftInfo(shift, spectra));
00062 }
00063 
00064 
00065 vector<TH1*> NC::ISpectrumInterpolator::
00066 GetInputSpectra(vector<double> shift) const
00067 {
00068   vector<TH1*> ret;
00069 
00070   for(unsigned int n = 0; n < shifted.size(); ++n){
00071     const ShiftInfo& sn = shifted[n];
00072     if(sn.shift == shift){
00073       ret.reserve(sn.spectra.size());
00074       for(unsigned int n = 0; n < sn.spectra.size(); ++n)
00075         ret.push_back(CloneFast(sn.spectra[n]));
00076       return ret;
00077     }
00078   }
00079 
00080   return ret; // empty
00081 }
00082 
00083 
00084 vector<TH1*> NC::ISpectrumInterpolator::
00085 InterpolateHists(const vector<ShiftInfo>& hs,
00086                  const vector<double>& shift) const
00087 {
00088   assert(!hs.empty());
00089 
00090   vector<vector<double> > xs;
00091   xs.reserve(hs.size());
00092   for(unsigned int n = 0; n < hs.size(); ++n) xs.push_back(hs[n].shift);
00093 
00094   const TMatrixD coeffs = FindCoeffs(xs, shift);
00095 
00096   const unsigned int I = hs[0].spectra.size();
00097   vector<TH1*> ret;
00098   ret.reserve(I);
00099   for(unsigned int i = 0; i < I; ++i){
00100     // Get the correct binning
00101     TH1* h = CloneFast(hs[0].spectra[i]);
00102     h->Reset();
00103 
00104     const unsigned int J = hs.size();
00105 
00106     for(unsigned int j = 0; j < J; ++j)
00107       AddFast(h, hs[j].spectra[i], coeffs(0, j));
00108 
00109     ret.push_back(h);
00110   } // end for i
00111 
00112   return ret;
00113 }
00114 
00115 
00116 int NC::SpectrumInterpolatorFancy::intintpow(int a, int b) const
00117 {
00118   int ret = 1;
00119   while(b--) ret *= a;
00120   return ret;
00121 }
00122 
00123 
00124 double NC::SpectrumInterpolatorFancy::doubleintpow(double a, int b) const
00125 {
00126   double ret = 1;
00127   while(b--) ret *= a;
00128   return ret;
00129 }
00130 
00131 /*
00132 double NC::SpectrumInterpolator::
00133 InterpolateMultiPoints(const vector<vector<double> >& xs,
00134                        const vector<double>& ys,
00135                        const vector<double>& x) const
00136 {
00137   assert(xs.size() == ys.size());
00138 
00139   assert(xs[0].size() == x.size());
00140 
00141   // 2 to fit a general quadratic, 1 to fit a general linear function, etc.
00142   const int fitOrder = 2;
00143 
00144   // The dimensionality of the space we are interpolating in
00145   const int D = x.size();
00146 
00147   // The size of the matrices, which is the number of coefficients we
00148   // need to determine, which is the number of different products of the
00149   // components of the x coordinate each raised to a power up to fitOrder
00150   const int K = intintpow(fitOrder+1, D);
00151 
00152   assert(xs.size() >= (unsigned int)K);
00153 
00154   TMatrixD mat(K, K);
00155   TMatrixD vec(K, 1);
00156 
00157   // What powers to raise the xs to for each row in the matrix
00158   vector<vector<int> > pows(K);
00159   for(int n = 0; n < K; ++n) pows[n] = vector<int>(xs.size());
00160 
00161   // Calculate every combination of powers
00162   // This produces a matrix like |012012012012012012012012012|
00163   // (where k is the col number, |000111222000111222000111222|
00164   // and d the row number)       |000000000111111111222222222|
00165   for(int k = 0; k < K; ++k)
00166     for(int d = 0; d < D; ++d)
00167       pows[k][d] = (k%intintpow(fitOrder+1, d+1))/intintpow(fitOrder+1, d);
00168 
00169   // The number of data points we are fitting
00170   const int N = xs.size();
00171 
00172   // How close is really close?
00173   const double eps = 1e-12;
00174   // If evaluating really close to a known point, just use that point
00175   // since when  x-x_n -> 0 we get a divide-by-zero
00176   for(int n = 0; n < N; ++n){
00177     double dist = 0;
00178     for(int d = 0; d < D; ++d){
00179       dist += SQR(xs[n][d]-x[d]);
00180     }
00181     if(dist < eps) return ys[n];
00182   }
00183 
00184   // The equation we are solving is mat*a = vec
00185 
00186   // Loop over all the data points
00187   for(int n = 0; n < N; ++n){
00188     // The weight by which this point is discounted in the fit
00189     // denom=1 corresponds to the usual unweighted fit
00190     // TODO - only taking weight in one dimension
00191     double denom = 0;
00192     for(int d = 0; d < D; ++d) denom += SQR(x[d]-xs[n][d]);
00193 
00194     // Loop over the rows in the vector
00195     for(int j = 0; j < K; ++j){
00196       double vecadd = ys[n]/denom;
00197       for(int d = 0; d < D; ++d)
00198         vecadd *= doubleintpow(xs[n][d], pows[j][d]);
00199       vec(j, 0) += vecadd;
00200     }
00201 
00202     // Loop over the cells of the matrix
00203     for(int j = 0; j < K; ++j){
00204       for(int i = 0; i < K; ++i){
00205         double matadd = 1/denom;
00206         for(int d = 0; d < D; ++d){
00207           matadd *= doubleintpow(xs[n][d], pows[j][d]);
00208           matadd *= doubleintpow(xs[n][d], pows[i][d]);
00209         }
00210         mat(j, i) += matadd;
00211       }
00212     }
00213   }
00214 
00215   // This part solves the matrix equation
00216   mat.Invert();
00217 
00218   const TMatrixD coeffs = mat*vec;
00219 
00220   // And now calculate the value of the polynomial at x
00221   double ret = 0;
00222   for(int k = 0; k < K; ++k){
00223     double retadd = coeffs(k, 0);
00224     for(int d = 0; d < D; ++d){
00225       retadd *= doubleintpow(x[d], pows[k][d]);
00226     }
00227     ret += retadd;
00228   }
00229   return ret;
00230 }
00231 */
00232 
00233 
00234 void NC::ISpectrumInterpolator::WriteResources() const
00235 {
00236   MSG("NCISpectrumInterpolator",
00237       Msg::kInfo) << "NC::ISpectrumInterpolator::WriteResources()" << endl;
00238 
00239   const int N = int(shifted.size());
00240   if(N == 0) return;
00241   const int M = int(shifted[0].spectra.size());
00242 
00243   // Go through all the plots, find the maximum range and extend by 10%
00244   // then clamp to the range 0.5-2.0
00245   double min = FLT_MAX;
00246   double max = FLT_MIN;
00247   for(int n = 0; n < N; ++n){
00248     for(int m = 0; m < M; ++m){
00249       TH1* s = shifted[n].spectra[m];
00250       if(s->GetMinimum(0) < min) min = s->GetMinimum(0);
00251       if(s->GetMaximum() > max) max = s->GetMaximum();
00252     }
00253   }
00254   max = 1+(max-1)*1.1;
00255   min = 1-(1-min)*1.1;
00256   if(max > 2) max = 2;
00257   if(min < 0.5) min = 0.5;
00258 
00259   for(int n = 0; n < N; ++n){
00260     for(int m = 0; m < M; ++m){
00261       TH1* s = shifted[n].spectra[m];
00262       s->SetMinimum(min);
00263       s->SetMaximum(max);
00264       s->Write();
00265     }
00266   }
00267 
00268   TCanvas canv("all_spectra_canv");
00269   // Try dividing as a square. If this isn't big enough then expand
00270   // horizontally. If still not then also expand vertically.
00271   int X = int(TMath::Sqrt(N*M));
00272   int Y = int(TMath::Sqrt(N*M));
00273   if(X*Y < N*M) ++X;
00274   if(X*Y < N*M) ++Y;
00275   canv.Divide(X, Y);
00276 
00277   for(int n = 0; n < N; ++n){
00278     for(int m = 0; m < M; ++m){
00279       canv.cd(M*n+m+1);
00280       TH1* s = shifted[n].spectra[m];
00281       s->SetTitle(s->GetName());
00282       if(s->GetXaxis()->GetXmin() == 0 && s->GetXaxis()->GetXmax() == 120)
00283         s->GetXaxis()->SetRangeUser(0, 19.5);
00284       if(s->GetDimension() == 2) s->Draw("colz"); else s->Draw();
00285     }
00286   }
00287   canv.Write();
00288 
00289   MSG("NCISpectrumInterpolator",
00290       Msg::kInfo) << "Done writing interpolator resources" << endl;
00291 }
00292 
00293 
00294 vector<TH1*> NC::ISpectrumInterpolator::GetNominal() const
00295 {
00296   for(unsigned int n = 0; n < shifted.size(); ++n){
00297     if(shifted[n].IsNominal()) return shifted[n].spectra;
00298   }
00299   assert(0 && "No nominal spectrum exists");
00300 }
00301 
00302 
00303 ostream& operator<<(ostream& op, const TMatrixD& m)
00304 {
00305   op << "| ";
00306   for(int n = 0; n < m.GetNcols(); ++n) op << m(0, n) << " ";
00307   op << "|";
00308   return op;
00309 }
00310 
00311 ostream& operator<<(ostream& op, const vector<double>& m)
00312 {
00313   op << "[ ";
00314   for(unsigned int n = 0; n < m.size(); ++n) op << m[n] << " ";
00315   op << "]";
00316   return op;
00317 }
00318 
00319 
00320 TMatrixD NC::SpectrumInterpolatorFancy::
00321 FindCoeffs(const vector<vector<double> >& xs, const vector<double>& x) const
00322 {
00323   assert(xs[0].size() == x.size());
00324 
00325   // 2 to fit a general quadratic, 1 to fit a general linear function, etc.
00326   const int fitOrder = 1;
00327 
00328   // The dimensionality of the space we are interpolating in
00329   const int D = x.size();
00330 
00331   // The size of the matrices, which is the number of coefficients we
00332   // need to determine, which is the number of different products of the
00333   // components of the x coordinate each raised to a power up to fitOrder
00334   const int K = intintpow(fitOrder+1, D);
00335 
00336   assert(xs.size() >= (unsigned int)K);
00337 
00338 
00339   // What powers to raise the xs to for each row in the matrix
00340   vector<vector<int> > pows(K);
00341   for(int k = 0; k < K; ++k) pows[k] = vector<int>(xs.size());
00342 
00343   // Calculate every combination of powers
00344   // This produces a matrix like |012012012012012012012012012|
00345   // (where k is the col number, |000111222000111222000111222|
00346   // and d the row number)       |000000000111111111222222222|
00347   for(int k = 0; k < K; ++k)
00348     for(int d = 0; d < D; ++d)
00349       pows[k][d] = (k%intintpow(fitOrder+1, d+1))/intintpow(fitOrder+1, d);
00350 
00351 
00352   // Calculate the value of the different polynomial parts at x
00353   TMatrixD basis(1, K);
00354   for(int k = 0; k < K; ++k){
00355     double basisAdd = 1;
00356     for(int d = 0; d < D; ++d){
00357       basisAdd *= doubleintpow(x[d], pows[k][d]);
00358     }
00359     basis(0, k) = basisAdd;
00360   } // end for k
00361 
00362 
00363   // The number of data points we are fitting
00364   const int N = xs.size();
00365 
00366   // How close is really close?
00367   const double eps = 1e-12;
00368   // If evaluating really close to a known point, just use that point
00369   // since when  x-x_n -> 0 we get a divide-by-zero
00370   for(int n = 0; n < N; ++n){
00371     double dist = 0;
00372     for(int d = 0; d < D; ++d){
00373       dist += SQR(xs[n][d]-x[d]);
00374     }
00375     if(dist < eps){
00376       TMatrixD coeffs(1, N);
00377       coeffs(0, n) = 1;
00378       return coeffs;
00379     }
00380   } // end for n
00381 
00382   TMatrixD mat(K, K);
00383 
00384   TMatrixD vecPart(K, N);
00385 
00386   // Loop over all the data points
00387   for(int n = 0; n < N; ++n){
00388     // The weight by which this point is discounted in the fit
00389     // denom=1 corresponds to the usual unweighted fit
00390     // TODO - only taking weight in one dimension
00391     double denom = 0;
00392     for(int d = 0; d < D; ++d) denom += SQR(x[d]-xs[n][d]);
00393 
00394     // Loop over the rows in the vector
00395     for(int j = 0; j < K; ++j){
00396       double vecadd = 1/denom;
00397       for(int d = 0; d < D; ++d)
00398         vecadd *= doubleintpow(xs[n][d], pows[j][d]);
00399       vecPart(j, n) = vecadd;
00400     }
00401 
00402     // Loop over the cells of the matrix
00403     for(int j = 0; j < K; ++j){
00404       for(int i = 0; i < K; ++i){
00405         double matadd = 1/denom;
00406         for(int d = 0; d < D; ++d){
00407           matadd *= doubleintpow(xs[n][d], pows[j][d]);
00408           matadd *= doubleintpow(xs[n][d], pows[i][d]);
00409         }
00410         mat(j, i) += matadd;
00411       } // end for i
00412     } // end for j
00413   } // end for n
00414   mat.Invert();
00415 
00416   return basis*mat*vecPart;
00417 }
00418 
00419 
00420 TMatrixD NC::SpectrumInterpolatorSimple::
00421 FindCoeffs(const vector<vector<double> >& xs, const vector<double>& x) const
00422 {
00423   //  const unsigned int D = x.size(); // dimensionality of the space
00424 
00425   // Ideally these should match, but the case where the reference
00426   // points are in more dimensions than the input should still work.
00427   assert(xs[0].size() <= x.size());
00428   const unsigned int D = TMath::Min(x.size(), xs[0].size());
00429 
00430   const unsigned int N = xs.size(); // number of input points
00431 
00432   TMatrixD ret(1, N);
00433 
00434   for(unsigned int n = 0; n < N; ++n){ // for every point
00435     double weight = 1;
00436     for(unsigned int d = 0; d < D; ++d){ // for every dimension
00437       const bool ltOneSigma = x[d] > -1 && x[d] < +1;
00438 
00439       if(ltOneSigma){
00440         const double dist = fabs(xs[n][d]-x[d]);
00441         if(dist > 1)
00442           weight = 0;
00443         else
00444           weight *= (1-dist);
00445       }
00446       else{
00447         const bool sign = xs[n][d]/x[d] >= 0; // right sign
00448 
00449         if(sign){
00450           if(fabs(xs[n][d]) < 1)
00451             weight *= 1-fabs(x[d]);
00452           else
00453             weight *= fabs(x[d]);
00454         }
00455         else weight = 0;
00456       }
00457     } // end for d
00458     ret(0, n) = weight;
00459   } // end for n
00460 
00461   //  cout << x << " -> " << ret << endl;
00462 
00463   double norm = 0;
00464   for(unsigned int n = 0; n < N; ++n) norm += ret(0, n);
00465   assert(fabs(norm - 1) < 1e-3);
00466 
00467 
00468   return ret;
00469 }
00470 
00471 
00472 // Need every point to be shifted in one dimension only. Give weight from
00473 // each point linear in how far 'x' is shifted. We are dealing with ratios
00474 // not shifts here - so need to subtract off a sufficient amount of the nominal
00475 // spectrum.
00476 TMatrixD NC::SpectrumInterpolatorSimplest::
00477 FindCoeffs(const vector<vector<double> >& xs, const vector<double>& x) const
00478 {
00479   const unsigned int N = xs.size(); // number of input points
00480   TMatrixD ret(1, N);
00481 
00482   const unsigned int D = TMath::Min(x.size(), xs[0].size());
00483 
00484   // If there is more than one point, and no dimensions, then they'll all be
00485   // picked as nominal. Go find out why your systematic shift is empty.
00486   // (perhaps because we don't support fitting the systematic you're trying
00487   // to use).
00488   assert((N == 1 || D > 0) && "Empty shift");
00489 
00490   int nominalIdx = -1;
00491 
00492   for(unsigned int n = 0; n < N; ++n){ // for every point
00493     bool isNominal = true;
00494     double weight = 0;
00495     for(unsigned int d = 0; d < D; ++d){ // for every dimension
00496       if(xs[n][d] != 0) isNominal = false;
00497       if((xs[n][d] < 0 && x[d] < 0) ||
00498          (xs[n][d] > 0 && x[d] > 0)){
00499         assert(weight == 0); // No point should contribute more than once
00500         weight = x[d] / xs[n][d];
00501       } // end if
00502     } // end for d
00503     ret(0, n) = weight;
00504     if(isNominal){
00505       // Should only be one nominal point
00506       assert(nominalIdx == -1 && "Multiple nominal spectra");
00507       nominalIdx = n;
00508     }
00509   } // end for n
00510   assert(nominalIdx >= 0);
00511 
00512   assert(ret(0, nominalIdx) == 0); // Nominal shouldn't have got weight
00513   double norm = 0;
00514   for(unsigned int n = 0; n < N; ++n) norm += ret(0, n);
00515   ret(0, nominalIdx) = 1-norm; // Make the total up to one
00516 
00517   //  cout << x << " -> " << ret << endl;
00518 
00519   return ret;
00520 }

Generated on Mon Feb 15 11:07:04 2010 for loon by  doxygen 1.3.9.1