LCOV - code coverage report
Current view: top level - STAT - AliTMinuitToolkit.cxx (source / functions) Hit Total Coverage
Test: coverage.info Lines: 1 201 0.5 %
Date: 2016-06-14 17:26:59 Functions: 1 15 6.7 %

          Line data    Source code
       1             : /**************************************************************************
       2             :  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
       3             :  *                                                                        *
       4             :  * Author: The ALICE Off-line Project.                                    *
       5             :  * Contributors are mentioned in the code where appropriate.              *
       6             :  *                                                                        *
       7             :  * Permission to use, copy, modify and distribute this software and its   *
       8             :  * documentation strictly for non-commercial purposes is hereby granted   *
       9             :  * without fee, provided that the above copyright notice appears in all   *
      10             :  * copies and that both the copyright notice and this permission notice   *
      11             :  * appear in the supporting documentation. The authors make no claims     *
      12             :  * about the suitability of this software for any purpose. It is          *
      13             :  * provided "as is" without express or implied warranty.                  *
      14             :  **************************************************************************/
      15             : 
      16             : /* $Id$ */
      17             : 
      18             : #include <TCanvas.h>
      19             : #include <TF1.h>
      20             : #include <TFormula.h>
      21             : #include <TH1F.h>
      22             : #include <TH2F.h>
      23             : #include <TMath.h>
      24             : #include <TMatrix.h>
      25             : #include <TRandom.h>
      26             : #include <TString.h>
      27             : #include <TVector.h>
      28             : #include <TVectorD.h>
      29             : #include <TVirtualFitter.h>
      30             : 
      31             : #include "AliTMinuitToolkit.h"
      32             : 
      33             : //--------------------------------------------------------------------------------------
      34             : //
      35             : // The AliTMinuitToolkit serves as an easy to use interface for the TMinuit 
      36             : // package: 
      37             : // 
      38             : // - It allows to fit a curve to one and two dimensional histograms 
      39             : //   (TH2F::Fit() only allows to fit a hyperplane).
      40             : // - Or n points can be specified directly via a n x 2 matrix.
      41             : // - An option for robust fitting with non-linear functions is implemented.
      42             : //
      43             : // A small example illustrating the usage of AliTMinuitToolkit is given in the function 
      44             : // "AliTMinuitToolkit::Test()".
      45             : // 
      46             : // 
      47             : // 1. Setting the formula:
      48             : //
      49             : //  The formula is simply set via "void SetFitFunction(TFormula * formula)".
      50             : //
      51             : //
      52             : // 2. Adding the data points
      53             : //
      54             : //  - In order to fit a histogram, use "void FitHistogram(TH1F * his)" or 
      55             : //    "void FitHistogram(TH2F * his)". The fitter is started automatically
      56             : //  - Alternatively, the direct specification of the points is possible via
      57             : //    "void SetPoints(TMatrixD * points)". Note, that the each point 
      58             : //    corresponds to one row in the matrix. The fitter is then started with 
      59             : //    the command "void Fit()". The weight of each point can be specified
      60             : //    with an n-dimensional vector using "void SetWeights(TVectorD * weights)".
      61             : //
      62             : //
      63             : // 3. Accessing the fit results
      64             : //
      65             : //  The N parameters of the formula are stored in a N-dimensional vector which
      66             : //  is returned by "TVectorD * GetParameters()". In a similar way the covariance 
      67             : //  matrix of the fit is returned via "TMatrixD * GetCovarianceMatrix()" which
      68             : //  is of the type N x N.
      69             : //
      70             : //
      71             : // 4. Non-linear robust fitting:
      72             : //
      73             : //  Even a few outliers can lead to wrong results of a least-squares fitting 
      74             : //  procedure. In this case the use of robust(resistant) methods can be 
      75             : //  helpful, but a stronger dependence on starting values or convergence to
      76             : //  local minima can occur.
      77             : //
      78             : //  The robust option becomes active if EnableRobust(true, sigma) is called. It is
      79             : //  very much recommended that a normalization value (scale variable) corresponding 
      80             : //  to an expected deviation (sigma) is specified via 
      81             : //  "EnableRobust(Bool_t b, Double_t sigma)".
      82             : //
      83             : //  Performing the fit without knowledge of sigma is also possible if only
      84             : //  "EnableRobust(true)" is activated, but this is NOT RECOMMENDED.
      85             : //
      86             : //  The method is based on another estimator instead of chi^2. For small deviations 
      87             : //  the function behaves like x^2 and for larger deviations like |x| - the so 
      88             : //  called Huber estimator:
      89             : //
      90             : //   h(x) = x^2                              , for x < 2.5*sigma
      91             : //   h(x) = 2*(2.5*sigma)*x - (2.5*sigma)^2  , for x > 2.5*sigma
      92             : //
      93             : //  If a weighting function is specified in addition, a second run with the ordinary 
      94             : //  metric is started, but before entering the iteration every point is weighted 
      95             : //  according to its distance to the outcoming function of the first run. The weighting
      96             : //  function w(x) must be defined on the intervall x in [0,1]. w(0) then 
      97             : //  corresponds to the weight of the closest point and w(1) to the point with the
      98             : //  largest distance.
      99             : //
     100             : //  Some standard weighting functions are predefined in 
     101             : //  "SetWeightFunction(Char_t * name, Float_t param1, Float_t param2 = 0)":
     102             : //   - "BOX" equals to 1 if x < param1 and to 0 if x > param1.
     103             : //   - "EXPONENTIAL" corresponds to "Math::Exp(-TMath::Log(param1)*x)"
     104             : //   - "ERRORFUNCTION" corresponds to "TMath::Erfc((x-param1)/param2)"
     105             : //
     106             : //
     107             : //  REFERENCE for non-linear robust fitting:
     108             : //  Ekblom H. and Madsen K. (1988), Alogrithms for non-linear Huber estimation,
     109             : //  BIT Numerical Mathematics 29 (1989) 60-76.
     110             : //  internet: http://www.springerlink.com/content/m277218542988344/
     111             : //
     112             : //
     113             : // 5. examples:
     114             : //
     115             : //  A small example illustrating the working principles of AliTMinuitToolkit is given
     116             : //  in the function "AliTMinuitToolkit::Test()".
     117             : //
     118             : //
     119             : //
     120             : // Comments and questions are always welcome: A.Kalweit@gsi.de
     121             : //--------------------------------------------------------------------------------------
     122             : 
     123             : 
     124         128 : ClassImp(AliTMinuitToolkit)
     125             : 
     126             : AliTMinuitToolkit::AliTMinuitToolkit() : 
     127           0 :    TNamed(),
     128           0 :    fFormula(0),
     129           0 :    fWeightFunction(0),
     130           0 :    fFitAlgorithm(""),
     131           0 :    fPoints(0),
     132           0 :    fWeights(0),
     133           0 :    fParam(0),
     134           0 :    fParamLimits(0),
     135           0 :    fCovar(0),
     136           0 :    fChi2(0),
     137           0 :    fMaxCalls(0),
     138           0 :    fPrecision(0),
     139           0 :    fUseRobust(0),
     140           0 :    fExpectedSigma(0)
     141           0 : {
     142             :  //
     143             :  // standard constructor
     144             :  //
     145           0 :  fMaxCalls = 500;
     146           0 :  fPrecision = 1;
     147           0 :  fUseRobust = false;
     148           0 :  fExpectedSigma = 0;
     149           0 : }
     150             : 
     151             : 
     152             : AliTMinuitToolkit::AliTMinuitToolkit(const AliTMinuitToolkit&) :
     153           0 :    TNamed(),
     154           0 :    fFormula(0),
     155           0 :    fWeightFunction(0),
     156           0 :    fFitAlgorithm(""),
     157           0 :    fPoints(0),
     158           0 :    fWeights(0),
     159           0 :    fParam(0),
     160           0 :    fParamLimits(0),
     161           0 :    fCovar(0),
     162           0 :    fChi2(0),
     163           0 :    fMaxCalls(0),
     164           0 :    fPrecision(0),
     165           0 :    fUseRobust(0),
     166           0 :    fExpectedSigma(0)
     167           0 : {
     168             : 
     169             : 
     170           0 : }
     171             : 
     172             : 
     173             : AliTMinuitToolkit& AliTMinuitToolkit::operator=(const AliTMinuitToolkit&) {
     174             : 
     175           0 :  return *this;
     176             : }
     177             : 
     178             : 
     179             : 
     180           0 : AliTMinuitToolkit::~AliTMinuitToolkit(){
     181             :   //
     182             :   // destructor
     183             :   //
     184           0 :   delete fPoints;
     185           0 :   delete fWeights;
     186           0 :   delete fWeightFunction;
     187           0 :   delete fParamLimits;
     188           0 :   delete fFormula;
     189           0 :   delete fParam;
     190           0 :   delete fCovar;
     191           0 :   delete fChi2;
     192           0 : }
     193             : 
     194             : void AliTMinuitToolkit::FitHistogram(TH1F *const his) {
     195             :  //
     196             :  // Fit a one dimensional histogram
     197             :  //
     198           0 :  fPoints = new TMatrixD(his->GetNbinsX(), 2);
     199             :  
     200           0 :  for(Int_t ibin=0; ibin < his->GetNbinsX(); ibin++) {
     201           0 :   Double_t x = his->GetXaxis()->GetBinCenter(ibin+1);
     202           0 :   Double_t y = his->GetBinContent(ibin+1);
     203             :   
     204           0 :   (*fPoints)(ibin, 0) = x;
     205           0 :   (*fPoints)(ibin, 1) = y;
     206             :  }
     207             :  
     208           0 :  Fit();
     209           0 : }
     210             : 
     211             : 
     212             : void AliTMinuitToolkit::FitHistogram(TH2F *const his) {
     213             :  //
     214             :  // Fit a curve to a two dimensional histogram
     215             :  //
     216           0 :  fPoints = new TMatrixD((Long64_t)his->GetEntries(), 2);
     217             :  Long64_t entry = 0;
     218             :  
     219           0 :  for(Int_t ibin=0; ibin < his->GetNbinsX(); ibin++) {
     220           0 :   Double_t x = his->GetXaxis()->GetBinCenter(ibin);
     221           0 :   for(Int_t jbin=0; jbin < his->GetNbinsY(); jbin++) {   
     222           0 :    Long64_t n = his->GetBin(ibin, jbin);
     223           0 :    Double_t y = his->GetYaxis()->GetBinCenter(jbin);
     224           0 :    for(Int_t ientries=0; ientries < his->GetBinContent(n); ientries++) {
     225           0 :     (*fPoints)(entry,0) = x;
     226           0 :     (*fPoints)(entry,1) = y;
     227           0 :     entry++;
     228             :    }
     229             :    
     230             :   }
     231             :  }
     232             : 
     233           0 :  Fit();
     234           0 : }
     235             : 
     236             : 
     237             : void AliTMinuitToolkit::SetWeightFunction(const Char_t *name, Float_t param1, Float_t param2) {
     238             :  //
     239             :  // Set the weight function which must be defined on the interval [0,1].
     240             :  //
     241           0 :  TString FuncType(name);
     242           0 :  FuncType.ToUpper();
     243             :  
     244           0 :  if (FuncType == "EXPONENTIAL") fWeightFunction = new TFormula("exp", Form("TMath::Exp(-TMath::Log(%f)*x)", param1));
     245           0 :  if (FuncType == "BOX") fWeightFunction = new TFormula("box", Form("TMath::Erfc((x-%f)/0.0001)", param1));
     246           0 :  if (FuncType == "ERRORFUNCTION") fWeightFunction = new TFormula("err", Form("TMath::Erfc((x-%f)/%f)", param1, param2));
     247             :  
     248           0 : }
     249             : 
     250             : 
     251             : void AliTMinuitToolkit::FitterFCN(int &/*npar*/, double */*dummy*/, double &fchisq, double *gin, int /*iflag*/){
     252             :   //
     253             :   // internal function which gives the specified function to the TMinuit function
     254             :   //  
     255             : 
     256             :   //
     257           0 :   AliTMinuitToolkit * fitter = (AliTMinuitToolkit*)TVirtualFitter::GetFitter()->GetObjectFit();
     258           0 :   fchisq = 0;
     259           0 :   Int_t nvar       = fitter->GetPoints()->GetNcols()-1;
     260           0 :   Int_t npoints    = fitter->GetPoints()->GetNrows();
     261             :   
     262             :   // calculate mean deviation for normalization or use user-defined sigma
     263             :   Double_t dev = 0.;
     264           0 :   if (fitter->GetExpectedSigma() == 0 && fitter->GetStatus() == true) {
     265           0 :    for (Int_t ipoint=0; ipoint<npoints; ipoint++){
     266           0 :     Double_t x[100];
     267           0 :      for (Int_t ivar=0; ivar<nvar; ivar++){
     268           0 :       x[ivar] = (*fitter->GetPoints())(ipoint, ivar);      
     269             :      }
     270           0 :     Float_t funx = fitter->GetFormula()->EvalPar(x,gin);
     271           0 :     Double_t delta = (*fitter->GetPoints())(ipoint, nvar) - funx;
     272           0 :     dev += TMath::Sqrt(TMath::Abs(delta));
     273           0 :    }
     274           0 :    dev = dev/npoints; 
     275           0 :   } else {
     276           0 :    dev = fitter->GetExpectedSigma();
     277             :   }
     278             :   // calculate chisquare  
     279           0 :   for (Int_t ipoint=0; ipoint<npoints; ipoint++){
     280           0 :     Double_t x[100];
     281           0 :     for (Int_t ivar=0; ivar<nvar; ivar++){
     282           0 :       x[ivar] = (*fitter->GetPoints())(ipoint, ivar);      
     283             :     }
     284           0 :     Float_t funx = fitter->GetFormula()->EvalPar(x,gin);   
     285           0 :     Double_t delta = TMath::Abs((*fitter->GetPoints())(ipoint, nvar) - funx);
     286           0 :     if (fitter->GetStatus() == true) {
     287           0 :      delta = delta/dev; // normalization
     288           0 :      if (delta <= 2.5) fchisq+= delta*delta; // new metric: Huber-k-estimator
     289           0 :      if (delta > 2.5) fchisq+= 2*(2.5)*delta - (2.5*2.5);
     290             :     } else {
     291           0 :      Double_t weight = (*fitter->GetWeights())(ipoint);
     292           0 :      fchisq+= delta*delta*weight; //old metric
     293             :     }
     294           0 :   }
     295           0 :  }
     296             :  
     297             : 
     298             : void AliTMinuitToolkit::Fit() {
     299             :   //
     300             :   // internal function that calls the fitter
     301             :   //
     302           0 :   Int_t nparam  = fParam->GetNrows();
     303           0 :   Int_t npoints = fPoints->GetNrows();
     304           0 :   Int_t nvar    = fPoints->GetNcols()-1;
     305             :   
     306             :   // set all paramter limits to infinity as default
     307           0 :   if (fParamLimits == 0) {
     308           0 :    fParamLimits = new TMatrixD(nparam ,2);
     309           0 :    for (Int_t iparam=0; iparam<nparam; iparam++){
     310           0 :     (*fParamLimits)(iparam, 0) = 0;
     311           0 :     (*fParamLimits)(iparam, 1) = 0;
     312             :    }
     313           0 :   }
     314             :   
     315             :   // set all weights to 1 as default
     316             :   Bool_t weightFlag = false;
     317           0 :   if (fWeightFunction == 0) {
     318           0 :    fWeightFunction = new TFormula("constant", "1");
     319           0 :   } else {
     320             :    weightFlag = true;
     321             :   }
     322             :   
     323             :   // migrad fit algorithm as default
     324           0 :   if (fFitAlgorithm == "") {
     325           0 :    fFitAlgorithm = "migrad";
     326           0 :   }
     327             :   
     328             :   // assign weights
     329           0 :   if (fWeights == 0) {
     330           0 :    fWeights = new TVectorD(npoints);
     331           0 :    for (Int_t ipoint=0; ipoint<npoints; ipoint++) (*fWeights)(ipoint) = 1;
     332           0 :   }
     333             :   
     334             :   // set up the fitter
     335           0 :   TVirtualFitter *minuit = TVirtualFitter::Fitter(0, nparam);
     336           0 :   minuit->SetObjectFit(this); 
     337           0 :   minuit->SetFCN(AliTMinuitToolkit::FitterFCN);
     338             :   
     339             :   // initialize paramters (step size???)
     340           0 :   for (Int_t iparam=0; iparam<nparam; iparam++){
     341           0 :    minuit->SetParameter(iparam, Form("p[%d]",iparam), (*fParam)(iparam), (*fParam)(iparam)/10, (*fParamLimits)(iparam, 0), (*fParamLimits)(iparam, 1));
     342             :   }
     343             :    
     344             :   //
     345           0 :   Double_t argList[2];
     346           0 :   argList[0] = fMaxCalls; //maximal number of calls 
     347           0 :   argList[1] = fPrecision; //tolerance normalized to 0.001 
     348           0 :   if (fMaxCalls == 500 && fPrecision == 1) minuit->ExecuteCommand(fFitAlgorithm, 0, 0); 
     349           0 :   if (fMaxCalls != 500 || fPrecision != 1) minuit->ExecuteCommand(fFitAlgorithm, argList, 2);
     350             :   // two additional arguments can be specified ExecuteCommand("migrad", argList, 2) - use 0,0 for default
     351             :   
     352             :   // fill parameter vector
     353           0 :   for (Int_t ivar=0; ivar<nparam; ivar++){
     354           0 :    (*fParam)(ivar) = minuit->GetParameter(ivar);
     355           0 :    fFormula->SetParameter(ivar, minuit->GetParameter(ivar));
     356             :   }
     357             : 
     358             :   // if a weight function is specified -> enter 2nd run with weights
     359           0 :   if (weightFlag == true && fUseRobust == true) {
     360             :    // sort points for weighting
     361           0 :    Double_t *sortList = new Double_t[npoints];
     362           0 :    Int_t  *indexList = new Int_t[npoints];   
     363           0 :    for (Int_t ipoint=0; ipoint<npoints; ipoint++){
     364           0 :     Double_t funx = fFormula->Eval((*fPoints)(ipoint, 0));
     365           0 :     Double_t delta = TMath::Abs((*fPoints)[ipoint][nvar] - funx);
     366           0 :     sortList[ipoint] = delta;
     367             :    } 
     368           0 :    TMath::Sort(npoints, sortList, indexList, false);
     369           0 :    for (Int_t ip=0; ip<npoints; ip++){
     370           0 :     Double_t t = ip/(Double_t)npoints;
     371           0 :     (*fWeights)(indexList[ip]) = fWeightFunction->Eval(t);
     372             :    }
     373             :    
     374             :    // set up the fitter
     375           0 :    fUseRobust = false;
     376           0 :    for (Int_t iparam=0; iparam<nparam; iparam++){
     377           0 :     minuit->SetParameter(iparam, Form("p[%d]",iparam), (*fParam)(iparam), (*fParam)(iparam)/10, (*fParamLimits)(iparam, 0), (*fParamLimits)(iparam, 1));
     378             :    }
     379             :    // start fitting
     380           0 :    if (fMaxCalls == 500 && fPrecision == 1) minuit->ExecuteCommand(fFitAlgorithm, 0, 0); 
     381           0 :    if (fMaxCalls != 500 || fPrecision != 1) minuit->ExecuteCommand(fFitAlgorithm, argList, 2);
     382           0 :    fUseRobust = true;
     383             :    
     384           0 :    delete [] sortList; 
     385           0 :    delete [] indexList;    
     386           0 :   }
     387             :   
     388             :   // fill parameter vector
     389           0 :   for (Int_t ivar=0; ivar<nparam; ivar++){
     390           0 :    (*fParam)(ivar) = minuit->GetParameter(ivar);
     391           0 :    fFormula->SetParameter(ivar, minuit->GetParameter(ivar));
     392             :   }
     393             :   
     394             :   // fill covariance matrix
     395           0 :   fCovar = new TMatrixD(nparam, nparam);
     396           0 :   for(Int_t i=0; i < nparam; i++) {
     397           0 :    for(Int_t j=0; j < nparam; j++) {
     398           0 :     (*fCovar)(i,j) = minuit->GetCovarianceMatrixElement(i,j);
     399             :    }
     400             :   }
     401             :   
     402           0 :   if (weightFlag == false) fWeightFunction = 0;
     403           0 : }
     404             : 
     405             : 
     406             : 
     407             : void AliTMinuitToolkit::Test() {
     408             :  //
     409             :  // This test function shows the basic working principles of this class 
     410             :  // and illustrates how a robust fit can improve the results
     411             :  //
     412             :  
     413             :  // 1. provide some example histogram
     414           0 :  TH1F * hist = new TH1F("test", "with (red) and without (black) robust option", 20,0,4);
     415           0 :  TRandom * rand = new TRandom();
     416           0 :  for (Int_t i = 0; i < 10000; i++) {
     417           0 :   hist->Fill(rand->Exp(1));
     418           0 :   if (i < 1000) hist->Fill(3); //"outliers"
     419           0 :   if (i < 1070) hist->Fill(3.5);
     420           0 :   if (i < 670) hist->Fill(2);
     421           0 :   if (i < 770) hist->Fill(1.5);//"outliers"
     422           0 :   if (i < 740) hist->Fill(1);
     423             :  }
     424           0 :  TCanvas * canv = new TCanvas();
     425           0 :  canv->cd(1);
     426           0 :  hist->Draw();
     427             :  
     428             :  // 2. example fit without robust option
     429           0 :  AliTMinuitToolkit * tool = new AliTMinuitToolkit();
     430           0 :  TFormula *aFormExp = new TFormula("formExp", "[0]*TMath::Exp(-[1]*x)");
     431           0 :  tool->SetFitFunction(aFormExp);
     432           0 :  TVectorD *vec1 = new TVectorD(2); // Set initial values
     433           0 :  (*vec1)(0) = 1800;
     434           0 :  (*vec1)(1) = 1;
     435           0 :  tool->SetInitialParam(vec1);
     436           0 :  tool->FitHistogram(hist);
     437             :  
     438             :  // draw fit function
     439           0 :  TF1 *func = new TF1("test", "[0]*TMath::Exp(-[1]*x)", 0, 6);
     440           0 :  func->SetParameters((*tool->GetParameters())(0), (*tool->GetParameters())(1));
     441           0 :  func->Draw("same");
     442             :  
     443             :  // 3 . robust fit 
     444           0 :  TVectorD *vec2 = new TVectorD(2);
     445           0 :  (*vec2)(0) = 1800;
     446           0 :  (*vec2)(1) = 1;
     447           0 :  tool->SetInitialParam(vec2);
     448           0 :  tool->EnableRobust(true, 10);
     449           0 :  tool->SetWeightFunction("box", 0.75);
     450           0 :  tool->FitHistogram(hist);
     451           0 :  TF1 *func2 = new TF1("test2", "[0]*TMath::Exp(-[1]*x)", 0, 6);
     452           0 :  func2->SetParameter(0, (*tool->GetParameters())(0));
     453           0 :  func2->SetParameter(1, (*tool->GetParameters())(1));
     454           0 :  func2->SetLineColor(kRed);
     455           0 :  func2->Draw("same");
     456             :  
     457           0 : }
     458             : 

Generated by: LCOV version 1.11