// @(#)root/tmva $Id$ 
// Author: Asen Christov

/**********************************************************************************
 * Project: TMVA - a Root-integrated toolkit for multivariate Data analysis       *
 * Package: TMVA                                                                  *
 * Class  : TMVA::KDEKernel                                                       *
 * Web    : http://tmva.sourceforge.net                                           *
 *                                                                                *
 * Description:                                                                   *
 *      Implementation (see header for description)                               *
 *                                                                                *
 * Authors (alphabetical):                                                        *
 *      Asen Christov   <christov@physik.uni-freiburg.de> - Freiburg U., Germany  *
 *                                                                                *
 * Copyright (c) 2005:                                                            *
 *      CERN, Switzerland                                                         * 
 *      MPI-K Heidelberg, Germany                                                 * 
 *      Freiburg U., Germany                                                      * 
 *                                                                                *
 * Redistribution and use in source and binary forms, with or without             *
 * modification, are permitted according to the terms listed in LICENSE           *
 * (http://tmva.sourceforge.net/LICENSE)                                          *
 **********************************************************************************/

#include "TH1.h"
#include "TH1F.h"
#include "TF1.h"

// #if ROOT_VERSION_CODE >= 364802
// #ifndef ROOT_TMathBase
// #include "TMathBase.h"
// #endif
// #else
// #ifndef ROOT_TMath
#include "TMath.h"
// #endif
// #endif

#include "TMVA/KDEKernel.h"
#include "TMVA/MsgLogger.h"

ClassImp(TMVA::KDEKernel)

//_______________________________________________________________________
TMVA::KDEKernel::KDEKernel( EKernelIter kiter, const TH1 *hist, Float_t lower_edge, Float_t upper_edge,
                            EKernelBorder kborder, Float_t FineFactor )
   : fSigma( 1. ),
     fIter ( kiter ),
     fLowerEdge (lower_edge ),
     fUpperEdge (upper_edge),
     fFineFactor ( FineFactor ),
     fKernel_integ ( 0 ),
     fKDEborder ( kborder ),
     fLogger( new MsgLogger("KDEKernel") )
{
   // constructor
   // sanity check
   if (hist == NULL) {
      Log() << kFATAL << "Called without valid histogram pointer (hist)!" << Endl;
   } 

   fHist          = (TH1F*)hist->Clone();
   fFirstIterHist = (TH1F*)hist->Clone();
   fFirstIterHist->Reset(); // now it is empty but with the proper binning
   fSigmaHist     = (TH1F*)hist->Clone();
   fSigmaHist->Reset(); // now fSigmaHist is empty but with the proper binning
   
   fHiddenIteration=false;
}

//_______________________________________________________________________
TMVA::KDEKernel::~KDEKernel()
{
   // destructor
   if (fHist           != NULL) delete fHist;
   if (fFirstIterHist  != NULL) delete fFirstIterHist;
   if (fSigmaHist      != NULL) delete fSigmaHist;
   if (fKernel_integ   != NULL) delete fKernel_integ;
   delete fLogger;
}

//_______________________________________________________________________
Double_t GaussIntegral(Double_t *x, Double_t *par)
{
   // when using Gaussian as Kernel function this is faster way to calculate the integrals
   if ( (par[1]<=0) || (x[0]>x[1])) return -1.;
  
   Float_t xs1=(x[0]-par[0])/par[1];
   Float_t xs2=(x[1]-par[0])/par[1];
  
   if (xs1==0) {
      if (xs2==0) return 0.;
      if (xs2>0 ) return 0.5*TMath::Erf(xs2);
   }
   if (xs2==0) return 0.5*TMath::Erf(TMath::Abs(xs1));
   if (xs1>0) return 0.5*(TMath::Erf(xs2)-TMath::Erf(xs1));
   if (xs1<0) {
      if (xs2>0 ) return 0.5*(TMath::Erf(xs2)+TMath::Erf(TMath::Abs(xs1)));
      else return 0.5*(TMath::Erf(TMath::Abs(xs1))-TMath::Erf(TMath::Abs(xs2)));
   }
   return -1.;
}

//_______________________________________________________________________
void TMVA::KDEKernel::SetKernelType( EKernelType ktype )
{
   // fIter == 1 ---> nonadaptive KDE
   // fIter == 2 ---> adaptive KDE
 
   if (ktype == kGauss) {

      // i.e. gauss kernel
      //
      // this is going to be done for both (nonadaptive KDE and adaptive KDE)
      // for nonadaptive KDE this is the only = final thing to do
      // for adaptive KDE this is going to be used in the first (hidden) iteration
      fKernel_integ = new TF1("GaussIntegral",GaussIntegral,fLowerEdge,fUpperEdge,4); 
      fSigma = ( TMath::Sqrt(2.0)
                 *TMath::Power(4./3., 0.2)
                 *fHist->GetRMS()
                 *TMath::Power(fHist->Integral(), -0.2) ); 
      // this formula for sigma is valid for Gaussian Kernel function (nonadaptive KDE).
      // formula found in:
      // Multivariate Density Estimation, Theory, Practice and Visualization D. W. SCOTT, 1992 New York, Wiley
      if (fSigma <= 0 ) {
         Log() << kFATAL << "<SetKernelType> KDE sigma has invalid value ( <=0 ) !" << Endl;
      }
   }

   if (fIter == kAdaptiveKDE) {

      // this is done only for adaptive KDE      
      
      // fill a temporary histo using nonadaptive KDE
      // this histo is identical with the final output when using only nonadaptive KDE
      fHiddenIteration=true;
      
      Float_t histoLowEdge=fHist->GetBinLowEdge(1);
      Float_t histoUpperEdge=fHist->GetBinLowEdge(fHist->GetNbinsX()+1);

      for (Int_t i=1;i<fHist->GetNbinsX();i++) {
         // loop over the bins of the original histo
         for (Int_t j=1;j<fFirstIterHist->GetNbinsX();j++) {
            // loop over the bins of the PDF histo and fill it
            fFirstIterHist->AddBinContent(j,fHist->GetBinContent(i)*
                                    this->GetBinKernelIntegral(fFirstIterHist->GetBinLowEdge(j),
                                                               fFirstIterHist->GetBinLowEdge(j+1),
                                                               fHist->GetBinCenter(i),
                                                               i)
                                    );
         }
         if (fKDEborder == 3) { // mirror the saples and fill them again
         // in order to save time do the mirroring only for the first (the lowwer) 1/5 of the histo to the left; 
         // and the last (the higher) 1/5 of the histo to the right.
         // the middle part of the histo, which is not mirrored, has no influence on the border effects anyway ...
            if (i < fHist->GetNbinsX()/5  ) {  // the first (the lowwer) 1/5 of the histo
               for (Int_t j=1;j<fFirstIterHist->GetNbinsX();j++) {
               // loop over the bins of the PDF histo and fill it
               fFirstIterHist->AddBinContent(j,fHist->GetBinContent(i)*
                                       this->GetBinKernelIntegral(fFirstIterHist->GetBinLowEdge(j),
                                                               fFirstIterHist->GetBinLowEdge(j+1),
                                                               2*histoLowEdge-fHist->GetBinCenter(i), // mirroring to the left
                                                               i)
                                      );
               }
            }
            if (i > 4*fHist->GetNbinsX()/5) { // the last (the higher) 1/5 of the histo
               for (Int_t j=1;j<fFirstIterHist->GetNbinsX();j++) {
               // loop over the bins of the PDF histo and fill it
               fFirstIterHist->AddBinContent(j,fHist->GetBinContent(i)*
                                       this->GetBinKernelIntegral(fFirstIterHist->GetBinLowEdge(j),
                                                               fFirstIterHist->GetBinLowEdge(j+1),
                                                               2*histoUpperEdge-fHist->GetBinCenter(i), // mirroring to the right
                                                               i)
                                      );
               }            
            }
         }
      }
      
      fFirstIterHist->SetEntries(fHist->GetEntries()); //set the number of entries to be the same as the original histo

      // do "function like" integration = sum of (bin_width*bin_content):
      Float_t integ=0;
      for (Int_t j=1;j<fFirstIterHist->GetNbinsX();j++) 
         integ+=fFirstIterHist->GetBinContent(j)*fFirstIterHist->GetBinWidth(j);
      fFirstIterHist->Scale(1./integ);
      
      fHiddenIteration=false;

      // OK, now we have the first iteration, 
      // next: calculate the Sigmas (Widths) for the second (adaptive) iteration 
      // based on the output of the first iteration 
      // these Sigmas will be stored in histo called fSigmaHist
      for (Int_t j=1;j<fFirstIterHist->GetNbinsX();j++) {
         // loop over the bins of the PDF histo and fill fSigmaHist
         if (fSigma*TMath::Sqrt(1.0/fFirstIterHist->GetBinContent(j)) <= 0 ) {
            Log() << kFATAL << "<SetKernelType> KDE sigma has invalid value ( <=0 ) !" << Endl;
         }
         
         fSigmaHist->SetBinContent(j,fFineFactor*fSigma/TMath::Sqrt(fFirstIterHist->GetBinContent(j)));
      }
   }

   if (fKernel_integ ==0 ) {
      Log() << kFATAL << "KDE kernel not correctly initialized!" << Endl;
   }
}

//_______________________________________________________________________
Float_t TMVA::KDEKernel::GetBinKernelIntegral( Float_t lowr, Float_t highr, Float_t mean, Int_t binnum )
{
   // calculates the integral of the Kernel
   if ((fIter == kNonadaptiveKDE) || fHiddenIteration  ) 
      fKernel_integ->SetParameters(mean,fSigma); // non adaptive KDE
   else if ((fIter == kAdaptiveKDE) && !fHiddenIteration ) 
      fKernel_integ->SetParameters(mean,fSigmaHist->GetBinContent(binnum)); // adaptive KDE

   if ( fKDEborder == 2 ) {  // renormalization of the kernel function
      Float_t renormFactor=1.0/fKernel_integ->Eval(fLowerEdge,fUpperEdge);
      return (renormFactor*fKernel_integ->Eval(lowr,highr));
   }
                                   
   // the RenormFactor takes care aboud the "border" effects, i.e. sets the 
   // integral to one inside the histogram borders
   return (fKernel_integ->Eval(lowr,highr));  
}

 KDEKernel.cxx:1
 KDEKernel.cxx:2
 KDEKernel.cxx:3
 KDEKernel.cxx:4
 KDEKernel.cxx:5
 KDEKernel.cxx:6
 KDEKernel.cxx:7
 KDEKernel.cxx:8
 KDEKernel.cxx:9
 KDEKernel.cxx:10
 KDEKernel.cxx:11
 KDEKernel.cxx:12
 KDEKernel.cxx:13
 KDEKernel.cxx:14
 KDEKernel.cxx:15
 KDEKernel.cxx:16
 KDEKernel.cxx:17
 KDEKernel.cxx:18
 KDEKernel.cxx:19
 KDEKernel.cxx:20
 KDEKernel.cxx:21
 KDEKernel.cxx:22
 KDEKernel.cxx:23
 KDEKernel.cxx:24
 KDEKernel.cxx:25
 KDEKernel.cxx:26
 KDEKernel.cxx:27
 KDEKernel.cxx:28
 KDEKernel.cxx:29
 KDEKernel.cxx:30
 KDEKernel.cxx:31
 KDEKernel.cxx:32
 KDEKernel.cxx:33
 KDEKernel.cxx:34
 KDEKernel.cxx:35
 KDEKernel.cxx:36
 KDEKernel.cxx:37
 KDEKernel.cxx:38
 KDEKernel.cxx:39
 KDEKernel.cxx:40
 KDEKernel.cxx:41
 KDEKernel.cxx:42
 KDEKernel.cxx:43
 KDEKernel.cxx:44
 KDEKernel.cxx:45
 KDEKernel.cxx:46
 KDEKernel.cxx:47
 KDEKernel.cxx:48
 KDEKernel.cxx:49
 KDEKernel.cxx:50
 KDEKernel.cxx:51
 KDEKernel.cxx:52
 KDEKernel.cxx:53
 KDEKernel.cxx:54
 KDEKernel.cxx:55
 KDEKernel.cxx:56
 KDEKernel.cxx:57
 KDEKernel.cxx:58
 KDEKernel.cxx:59
 KDEKernel.cxx:60
 KDEKernel.cxx:61
 KDEKernel.cxx:62
 KDEKernel.cxx:63
 KDEKernel.cxx:64
 KDEKernel.cxx:65
 KDEKernel.cxx:66
 KDEKernel.cxx:67
 KDEKernel.cxx:68
 KDEKernel.cxx:69
 KDEKernel.cxx:70
 KDEKernel.cxx:71
 KDEKernel.cxx:72
 KDEKernel.cxx:73
 KDEKernel.cxx:74
 KDEKernel.cxx:75
 KDEKernel.cxx:76
 KDEKernel.cxx:77
 KDEKernel.cxx:78
 KDEKernel.cxx:79
 KDEKernel.cxx:80
 KDEKernel.cxx:81
 KDEKernel.cxx:82
 KDEKernel.cxx:83
 KDEKernel.cxx:84
 KDEKernel.cxx:85
 KDEKernel.cxx:86
 KDEKernel.cxx:87
 KDEKernel.cxx:88
 KDEKernel.cxx:89
 KDEKernel.cxx:90
 KDEKernel.cxx:91
 KDEKernel.cxx:92
 KDEKernel.cxx:93
 KDEKernel.cxx:94
 KDEKernel.cxx:95
 KDEKernel.cxx:96
 KDEKernel.cxx:97
 KDEKernel.cxx:98
 KDEKernel.cxx:99
 KDEKernel.cxx:100
 KDEKernel.cxx:101
 KDEKernel.cxx:102
 KDEKernel.cxx:103
 KDEKernel.cxx:104
 KDEKernel.cxx:105
 KDEKernel.cxx:106
 KDEKernel.cxx:107
 KDEKernel.cxx:108
 KDEKernel.cxx:109
 KDEKernel.cxx:110
 KDEKernel.cxx:111
 KDEKernel.cxx:112
 KDEKernel.cxx:113
 KDEKernel.cxx:114
 KDEKernel.cxx:115
 KDEKernel.cxx:116
 KDEKernel.cxx:117
 KDEKernel.cxx:118
 KDEKernel.cxx:119
 KDEKernel.cxx:120
 KDEKernel.cxx:121
 KDEKernel.cxx:122
 KDEKernel.cxx:123
 KDEKernel.cxx:124
 KDEKernel.cxx:125
 KDEKernel.cxx:126
 KDEKernel.cxx:127
 KDEKernel.cxx:128
 KDEKernel.cxx:129
 KDEKernel.cxx:130
 KDEKernel.cxx:131
 KDEKernel.cxx:132
 KDEKernel.cxx:133
 KDEKernel.cxx:134
 KDEKernel.cxx:135
 KDEKernel.cxx:136
 KDEKernel.cxx:137
 KDEKernel.cxx:138
 KDEKernel.cxx:139
 KDEKernel.cxx:140
 KDEKernel.cxx:141
 KDEKernel.cxx:142
 KDEKernel.cxx:143
 KDEKernel.cxx:144
 KDEKernel.cxx:145
 KDEKernel.cxx:146
 KDEKernel.cxx:147
 KDEKernel.cxx:148
 KDEKernel.cxx:149
 KDEKernel.cxx:150
 KDEKernel.cxx:151
 KDEKernel.cxx:152
 KDEKernel.cxx:153
 KDEKernel.cxx:154
 KDEKernel.cxx:155
 KDEKernel.cxx:156
 KDEKernel.cxx:157
 KDEKernel.cxx:158
 KDEKernel.cxx:159
 KDEKernel.cxx:160
 KDEKernel.cxx:161
 KDEKernel.cxx:162
 KDEKernel.cxx:163
 KDEKernel.cxx:164
 KDEKernel.cxx:165
 KDEKernel.cxx:166
 KDEKernel.cxx:167
 KDEKernel.cxx:168
 KDEKernel.cxx:169
 KDEKernel.cxx:170
 KDEKernel.cxx:171
 KDEKernel.cxx:172
 KDEKernel.cxx:173
 KDEKernel.cxx:174
 KDEKernel.cxx:175
 KDEKernel.cxx:176
 KDEKernel.cxx:177
 KDEKernel.cxx:178
 KDEKernel.cxx:179
 KDEKernel.cxx:180
 KDEKernel.cxx:181
 KDEKernel.cxx:182
 KDEKernel.cxx:183
 KDEKernel.cxx:184
 KDEKernel.cxx:185
 KDEKernel.cxx:186
 KDEKernel.cxx:187
 KDEKernel.cxx:188
 KDEKernel.cxx:189
 KDEKernel.cxx:190
 KDEKernel.cxx:191
 KDEKernel.cxx:192
 KDEKernel.cxx:193
 KDEKernel.cxx:194
 KDEKernel.cxx:195
 KDEKernel.cxx:196
 KDEKernel.cxx:197
 KDEKernel.cxx:198
 KDEKernel.cxx:199
 KDEKernel.cxx:200
 KDEKernel.cxx:201
 KDEKernel.cxx:202
 KDEKernel.cxx:203
 KDEKernel.cxx:204
 KDEKernel.cxx:205
 KDEKernel.cxx:206
 KDEKernel.cxx:207
 KDEKernel.cxx:208
 KDEKernel.cxx:209
 KDEKernel.cxx:210
 KDEKernel.cxx:211
 KDEKernel.cxx:212
 KDEKernel.cxx:213
 KDEKernel.cxx:214
 KDEKernel.cxx:215
 KDEKernel.cxx:216
 KDEKernel.cxx:217
 KDEKernel.cxx:218
 KDEKernel.cxx:219
 KDEKernel.cxx:220
 KDEKernel.cxx:221
 KDEKernel.cxx:222
 KDEKernel.cxx:223
 KDEKernel.cxx:224
 KDEKernel.cxx:225
 KDEKernel.cxx:226
 KDEKernel.cxx:227
 KDEKernel.cxx:228
 KDEKernel.cxx:229