AliRoot Core  3dc7879 (3dc7879)
AliTMinuitToolkit.cxx
Go to the documentation of this file.
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 <TH1F.h>
21 #include <TMath.h>
22 #include <TMatrix.h>
23 #include <TRandom.h>
24 #include <TVector.h>
25 #include <TVectorD.h>
26 #include <TVirtualFitter.h>
27 #include <TLegend.h>
28 #include <TStyle.h>
29 #include <TTree.h>
30 #include <TTreeStream.h>
31 #include "AliTMinuitToolkit.h"
32 #include "TGraph.h"
33 #include "TPRegexp.h"
34 #include "TStatToolkit.h"
35 //#include "TFormulaPrimitive.h"
36 #include "TDataMember.h"
37 
38 std::map<std::string, AliTMinuitToolkit*> AliTMinuitToolkit::fPredefinedFitters;
39 
40 
41 ClassImp(AliTMinuitToolkit)
42 
43 AliTMinuitToolkit::AliTMinuitToolkit(const char *streamerName, const char *mode) :
44  TNamed(),
45  fStreamer(nullptr),
46  fVerbose(0),
47  fFormula(nullptr),
48  fLogLikelihoodFunction(nullptr),
49  fFitAlgorithm(""),
50  fPoints(nullptr),
51  fValues(nullptr),
52  fVarNames(nullptr), // variable names
53  fValueNames(nullptr), // value names
54  fPointIndex(0), // point index - to specify points for likelihood calculation (used for Bootstrap and CrossValidation)
55  fInitialParam(nullptr),
56  fParam(nullptr),
57  fRMSEstimator(nullptr), // parameter spread as estimated in Bootstrap resp. TwoFold cross validation
58  fMISACEstimators(nullptr), // MISAC estimators - median, mean, rms
59  fCovar(nullptr),
60  fStatus(0),
61  fChi2(0),
62  fMaxCalls(0),
63  fPrecision(0),
64  fIsHuberCost(0),
65  fFCNCounter(0) // cost function call counter
66 {
67  //
68  // standard constructor
69  //
70  fMaxCalls = 500;
71  fPrecision = 1;
72  fIsHuberCost = false;
73  if (streamerName!= nullptr) fStreamer= new TTreeSRedirector(streamerName,mode);
74 }
75 
76 
77 
79  //
80  // destructor
81  //
82  delete fPoints;
83  delete fValues;
84  delete fInitialParam;
85  // delete fFormula;
86  delete fParam;
87  delete fRMSEstimator;
88  delete fMISACEstimators;
89  delete fCovar;
90  delete fStreamer;
91 }
99 void AliTMinuitToolkit::SetStreamer(const char *streamerName, const char *mode){
100  //
101  // set streamer
102  delete fStreamer;
103  fStreamer= new TTreeSRedirector(streamerName,mode);
104 }
105 
106 
108  delete fPoints;
109  fPoints= nullptr;
110  delete fValues;
111  fValues= nullptr;
112  // delete fParam;
113  // fParam=0;
114  // delete fCovar;
115  // fCovar=0;
116 }
120 void AliTMinuitToolkit::SetFitFunction(TF1 * formula, Bool_t doReset) {
121  //
122  fFormula=formula;
123  if (doReset){
124  delete fParam;
125  delete fRMSEstimator;
126  delete fCovar;
127  fParam=new TVectorD(formula->GetNpar());
128  fRMSEstimator=new TVectorD(formula->GetNpar());
129  fCovar=new TMatrixD(formula->GetNpar(),formula->GetNpar());
130  }
131 }
132 
140 void AliTMinuitToolkit::SetInitialParam(const TMatrixD * paramLimits) {
141  fInitialParam=new TMatrixD(*paramLimits);
142 };
143 
144 
150 void AliTMinuitToolkit::FitHistogram(const TH1 * his, Option_t *option) {
151  ClearData();
152  fPoints = new TMatrixD(his->GetNbinsX(), 1);
153  fValues = new TMatrixD(his->GetNbinsX(), 2);
154  for(Int_t iBin=0; iBin < his->GetNbinsX(); iBin++) {
155  Double_t x = his->GetXaxis()->GetBinCenter(iBin+1);
156  Double_t y = his->GetBinContent(iBin+1);
157  (*fPoints)(iBin, 0) = x;
158  Double_t err=his->GetBinError(iBin+1);
159  (*fValues)(iBin,0)=y;
160  (*fValues)(iBin,1)=(err>0)? 1./err:0;
161  }
162  Fit(option);
163 }
169 void AliTMinuitToolkit::FitGraph(const TGraph * gr, Option_t *option) {
170  ClearData();
171  Int_t nPoints=gr->GetN();
172  fPoints = new TMatrixD(nPoints, 1);
173  fValues = new TMatrixD(nPoints, 2);
174  for(Int_t ip=0; ip < nPoints; ip++) {
175  Double_t x = gr->GetX()[ip];
176  Double_t y = gr->GetY()[ip];
177  (*fPoints)(ip, 0) = x;
178  Double_t err=gr->GetErrorY(ip);
179  (*fValues)(ip,0)=y;
180  (*fValues)(ip,1)=(err>0)? 1./err:0;
181  }
182  Fit(option);
183 }
184 
205 void AliTMinuitToolkit::FitterFCN(int &/*nPar*/, double */*grad*/, double &chi2, double *gin, int iflag){
206  AliTMinuitToolkit * fitter = (AliTMinuitToolkit*)TVirtualFitter::GetFitter()->GetObjectFit();
207  if (!fitter) return;
208  Int_t &fcnCounter=fitter->fFCNCounter;
209  TF1 *logLike=fitter->fLogLikelihoodFunction;
210  const Double_t* likeParam= (logLike!= nullptr) ? logLike->GetParameters(): nullptr;
211  chi2 = 0;
212  const TMatrixD & variables= (*fitter->GetPoints());
213  const TMatrixD & values= (*fitter->GetValues());
214  Int_t nVars = variables.GetNcols();
215  Int_t nPoints = variables.GetNrows();
216  TVectorD param(fitter->fFormula->GetNpar(), gin);
217  // calculate log likelihood
218  //
219  if (fitter->fPointIndex.GetSize()>0) {
220  nPoints=fitter->fPointIndex.GetSize();
221  }
222  for (Int_t jPoint=0; jPoint<nPoints; jPoint++){
223  Int_t iPoint=(fitter->fPointIndex.GetSize()>0) ? fitter->fPointIndex[jPoint]:jPoint;
224  Double_t x[100];
225  for (Int_t ivar=0; ivar<nVars; ivar++){
226  x[ivar] = variables(iPoint, ivar);
227  }
228  Double_t funX = (fitter->GetFormula())->EvalPar(x,gin);
229  Double_t value=values(iPoint,0);
230  Double_t weight=values(iPoint,1);
231  Double_t delta = (value - funX);
232  Double_t toAdd=0;
233  if (logLike){
234  Double_t normDelta = delta*weight;
235  toAdd=logLike->EvalPar(&normDelta,likeParam);
236  }else{
237  if (fitter->IsHuberCost() != 0) { //hubert norm
238  delta = TMath::Abs(delta*weight); // normalization
239  if (delta <= 2.5) toAdd= delta*delta; // new metric: Huber-k-estimator
240  if (delta > 2.5) toAdd= 2*(2.5)*delta - (2.5*2.5);
241  } else {
242  Double_t lChi2 = delta*weight;
243  lChi2*=lChi2;
244  toAdd=lChi2; // chi2 (log likelihood)
245  }
246  }
247  //
248  if (fitter->IsVerbose(kStreamFcnPoint) || (iflag&kStreamFcnPoint)){
249  TVectorD vecX(nVars, x);
250  (*(fitter->fStreamer))<<"fcnDebugPoint"<<
251  "toAdd="<<toAdd<< // log likelihood to add
252  "val="<<value<< // "measurement"
253  "w="<<weight<< // wight of point
254  "fun="<<funX<< // function value
255  "x.="<<&vecX<< // variable vector
256  "p.="<<&param<< // parameter vector
257  "\n";
258  }
259  chi2+=toAdd;
260  }
261  if (fitter->IsVerbose(kStreamFcn) || (iflag&kStreamFcn )){
262  (*(fitter->fStreamer))<<"fcnDebug"<<
263  "chi2="<<chi2<<
264  "p.="<<&param<<
265  "\n";
266  }
267  fcnCounter++;
268 }
269 
275 void AliTMinuitToolkit::Fit(Option_t *option) {
276  TString sOption=(option== nullptr)?"":option;
277  TPRegexp regBootstrap("bootstrap[0-9]*");
278  TPRegexp regTwofold("twofold[0-9]*");
279  TPRegexp regMISAC("misac\\([0-9]*,[0-9]*\\)");
280 
281  // run bootstrap if specified
282  if (regBootstrap.Match(sOption)>0){ // run bootstrap
283  TString newOption=sOption;
284  regBootstrap.Substitute(newOption,"");
285  UInt_t nPoints= static_cast<UInt_t>(TString(&(sOption.Data()[sOption.Index(regBootstrap) + 9])).Atoi());
286  return Bootstrap(nPoints, nullptr,newOption.Data());
287  }
288  // run two fold minimization if specified
289  if (regTwofold.Match(sOption)>0){ // run twofold minimization
290  TString newOption=sOption;
291  regTwofold.Substitute(newOption,"");
292  UInt_t nPoints= static_cast<UInt_t>(TString(&(sOption.Data()[sOption.Index(regTwofold) + 7])).Atoi());
293  return TwoFoldCrossValidation(nPoints, nullptr,newOption.Data());
294  }
295  // run MISAC
296  if (regMISAC.Match(sOption)>0){ // run MISAC minimization - mix of RANSAC and KFold
297  TString newOption=sOption;
298  regMISAC.Substitute(newOption,"");
299  Int_t index1=sOption.Index(regMISAC)+6;
300  Int_t index2=sOption.Index(",",index1+1)+1;
301  Int_t nPoints=TString(&(sOption.Data()[index1])).Atoi();
302  UInt_t nIter= static_cast<UInt_t>(TString(&(sOption.Data()[index2])).Atoi());
303  return MISAC(nPoints,nIter, nullptr,newOption.Data());
304  }
305  fStatus=0;
306 
307  Int_t nParam = fFormula->GetNpar();
308  // create "initial param" in case not set before
309  if (fParam== nullptr) fParam=new TVectorD(nParam);
310  if (fInitialParam == nullptr) {
311  ::Info("AliTMinuitToolkit::Fit","Default initial parameters set");
312  fInitialParam= new TMatrixD(nParam,4);
313  for (Int_t iParam=0; iParam<nParam; iParam++) {
314  (*fInitialParam)(iParam,0)=0;
315  (*fInitialParam)(iParam,1)=1000;
316  (*fInitialParam)(iParam,2)=0;
317  (*fInitialParam)(iParam,3)=0;
318  }
319  }
320 
321  // migrad fit algorithm as default
322  if (fFitAlgorithm == "") {
323  fFitAlgorithm = "migrad";
324  }
325 
326  // assign weights
327  if (fValues == nullptr) {
328  ::Error("AliTMinuitToolkit::Fit()","Invalid fit. Values not set");
329  return ;
330  }
331 
332  // set up the fitter
333  TVirtualFitter *minuit = TVirtualFitter::Fitter(nullptr, nParam);
334  minuit->SetObjectFit(this);
335  minuit->SetFCN(AliTMinuitToolkit::FitterFCN);
336  if ((fVerbose& kPrintMinuit)==0){ // MAKE minuit QUIET!!
337  double p1 = -1;
338  minuit->ExecuteCommand("SET PRINTOUT",&p1,1);
339  }
340  if ((fVerbose& kPrintAll)>0){ // MAKE minuit verbose!!
341  double pprint=2;
342  minuit->ExecuteCommand("SET PRINTOUT",&pprint,1);
343  }
344 
345  // initialize parameters (step size???)
346  for (Int_t iParam=0; iParam<nParam; iParam++){
347  if (sOption.Contains("rndmInit")){
348  Double_t initValue=(*fInitialParam)(iParam, 0)+gRandom->Gaus()*(*fInitialParam)(iParam,1);
349  minuit->SetParameter(iParam, Form("p[%d]",iParam), initValue, (*fInitialParam)(iParam,1), (*fInitialParam)(iParam, 2), (*fInitialParam)(iParam, 3));
350  }else{
351  minuit->SetParameter(iParam, Form("p[%d]",iParam), (*fInitialParam)(iParam,0), (*fInitialParam)(iParam,1), (*fInitialParam)(iParam, 2), (*fInitialParam)(iParam, 3));
352  }
353  /*
354  if (doReset){
355  minuit->SetParameter(iParam, Form("p[%d]",iParam), (*fInitialParam)(iParam,0), (*fInitialParam)(iParam,1), (*fInitialParam)(iParam, 2), (*fInitialParam)(iParam, 3));
356  }else{
357  minuit->SetParameter(iParam, Form("p[%d]",iParam), (*fParam)(iParam), TMath::Sqrt((*fCovar)(iParam,iParam)), (*fInitialParam)(iParam, 2), (*fInitialParam)(iParam, 3));
358  }
359  */
360  }
361 
362  //
363  Double_t argList[2];
364  argList[0] = fMaxCalls; //maximal number of calls
365  argList[1] = fPrecision; //tolerance normalized to 0.001
366  //if (fMaxCalls == 500 && fPrecision == 1) minuit->ExecuteCommand(fFitAlgorithm, 0, 0);
367  //if (fMaxCalls != 500 || fPrecision != 1)
368  minuit->ExecuteCommand(fFitAlgorithm, argList, 2);
369  // two additional arguments can be specified ExecuteCommand("migrad", argList, 2) - use 0,0 for default
370  // fStatus = (((TFitter*)minuit)->GetMinuit())->GetStatus(); // we should add separate status for Minuit and AliTMinuitToolkit
371 
372  // fill parameter vector
373  for (Int_t ivar=0; ivar<nParam; ivar++){
374  (*fParam)(ivar) = minuit->GetParameter(ivar);
375  fFormula->SetParameter(ivar, minuit->GetParameter(ivar));
376  //fFormula->SetPar(ivar, minuit->GetParameter(ivar));
377  }
378  FitterFCN(nParam, nullptr,fChi2, fParam->GetMatrixArray(),0);
379 
380  // fill parameter vector
381  for (Int_t ivar=0; ivar<nParam; ivar++){
382  (*fParam)(ivar) = minuit->GetParameter(ivar);
383  fFormula->SetParameter(ivar, minuit->GetParameter(ivar));
384  }
385 
386  // fill covariance matrix
387  if (fCovar== nullptr) fCovar = new TMatrixD(nParam, nParam);
388  if (minuit->GetCovarianceMatrix()){
389  fCovar->SetMatrixArray(minuit->GetCovarianceMatrix());
390  }else{
391  fStatus|=kCovarFailed;
392  }
393 
394 }
395 
406 
415 Long64_t AliTMinuitToolkit::FillFitter(TTree * inputTree, TString values, TString variables, TString selection, Int_t firstEntry, Int_t nEntries, Bool_t doReset ){
416  TString query=values;
417  query+=":";
418  query+=variables;
419  if (inputTree== nullptr){
420  ::Error("AliTMinuitToolkit::FillFitter","Input tree==NULL");
421  return -1;
422  }
423  fValueNames=values.Tokenize(":");
424  fVarNames=variables.Tokenize(":");
425  Int_t nVal= fValueNames->GetEntries();
426  Int_t nVars= fVarNames->GetEntries();
427  if (doReset == 0 && fPoints != nullptr){
428  if (fPoints->GetNrows()!=nVars){
429  ::Error("AliTMinuitToolkit::FillFitter","Non compatible number of variables: %d instead of %d: variables: %s",nVars, fPoints->GetNrows(), query.Data());
430  return -1;
431  }
432  }
433 
434  Long64_t entries = inputTree->Draw(query.Data(),selection.Data(),"goff para",nEntries,firstEntry);
435  if (entries<=0) {
436  ::Error("AliTMinuitToolkit::FillFitter","badly formatted values or variables: %s",query.Data());
437  ::Error("AliTMinuitToolkit::FillFitter","valueDescription: %s",values.Data());
438  ::Error("AliTMinuitToolkit::FillFitter","variables: %s",variables.Data());
439  return -1;
440  }
441  Int_t index0=0;
442  if (doReset || fPoints== nullptr) {
443  ClearData();
444  fPoints=new TMatrixD(entries,nVars);
445  fValues=new TMatrixD(entries,nVal);
446  }else{
447  index0= fPoints->GetNrows();
448  fPoints->ResizeTo(index0+Int_t(entries),nVars);
449  fValues->ResizeTo(index0+Int_t(entries),nVal);
450  }
451  for (Int_t iPoint=0; iPoint<entries; iPoint++){
452  for (Int_t iVar=0; iVar<nVars; iVar++){
453  (*fPoints)(index0+iPoint,iVar)=inputTree->GetVal(iVar+nVal)[iPoint];
454  }
455  for (Int_t iVal=0; iVal<nVal; iVal++){
456  (*fValues)(index0+iPoint,iVal)=inputTree->GetVal(iVal)[iPoint];
457  }
458  }
459  return entries;
460 }
461 
465 TString AliTMinuitToolkit::GetFitFunctionAsAlias(Option_t *option, TTree * tree){
466  //
467  // construct string TTree alias for fit function
468  TString inputString(fFormula->GetTitle());
469  TString sOption(option);
470  sOption.ToLower();
471 
472  if (fVarNames== nullptr){
473  ::Error("AliTMinuitToolkit::GetFitFunctionAsAlias","Variable names not defined");
474  return "";
475  }
476 
477  for (Int_t iDim=0; iDim<fFormula->GetNdim(); iDim++){
478  TString varName=GetListOfVariables()->At(iDim)->GetName();
479  if (sOption.Contains("latex") &&tree){
480  TNamed * meta = TStatToolkit::GetMetadata(tree,varName+".Title");
481  if (meta) varName=meta->GetTitle();
482  }
483  inputString.ReplaceAll(TString::Format("x[%d]",iDim).Data(), varName.Data());
484  }
485  for (Int_t iPar=0; iPar<fFormula->GetNpar(); iPar++){
486  if (sOption.Contains("latex")==0){
487  inputString.ReplaceAll(TString::Format("[%d]",iPar).Data(), TString::Format("(%f)",(*fParam)[iPar]).Data());
488  }else{
489  if ((*GetRMSEstimator())[0]>0) {
490  inputString.ReplaceAll(TString::Format("[%d]", iPar).Data(),
491  TString::Format("(%2.2f#pm%2.2f)", (*fParam)[iPar], (*GetRMSEstimator())[iPar]).Data());
492  }else{
493  inputString.ReplaceAll(TString::Format("[%d]", iPar).Data(),
494  TString::Format("(%2.2f#pm%2.2f)", (*fParam)[iPar], (*GetRMSEstimator())[iPar]).Data());
495  }
496  }
497  }
498  return inputString;
499 }
500 
505 Double_t AliTMinuitToolkit::RndmGaus(Double_t mean, Double_t sigma){
506  return gRandom->Gaus(mean,sigma);
507 }
508 
513 Double_t AliTMinuitToolkit::RndmLandau(Double_t mean, Double_t sigma){
514  return gRandom->Landau(mean,sigma);
515 }
516 
517 
524 Double_t AliTMinuitToolkit::GaussCachyLogLike(const Double_t *x, const Double_t *p){
525  Double_t vCauchy=p[1]/(TMath::Pi()*(p[1]*p[1]+(*x)*(*x)));
526  Double_t vGaus=(TMath::Abs(*x)<20) ? TMath::Gaus(*x,0,1.,kTRUE):0.;
527  Double_t p0= p[0]*TMath::Gaus(0,0,1,kTRUE)+(1-p[0])/(TMath::Pi()*p[1]);
528  return -2.*TMath::Log((p[0]*vGaus+(1-p[0])*vCauchy)/p0);
529 }
530 
536 Double_t AliTMinuitToolkit::HuberLogLike(const Double_t *x, const Double_t *p){
537  Double_t absX=TMath::Abs(x[0]);
538  if (absX<p[0]) return absX*absX;
539  return p[0]*(2*absX-p[0]);
540 }
541 
547 Double_t AliTMinuitToolkit::PseudoHuberLogLike(const Double_t *x, const Double_t *p){
548  Double_t p2=p[0]*p[0];
549  Double_t x2=x[0]*x[0];
550  Double_t result=2.*p2*(TMath::Sqrt(1.+x2/p2)-1.);
551  return result;
552 }
553 
562 Double_t AliTMinuitToolkit::GausKurtosisSkewness(const Double_t *x, const Double_t *p){
563  // TODO add protection against out of range
564  static Float_t msqrt2=1./TMath::Sqrt(2.);
565  Double_t gaussPart=p[0]*TMath::Exp(-(TMath::Power(TMath::Abs(x[0]-p[1])/p[2],p[3])));
566  Double_t erfPart=(1.+TMath::Erf(p[4]*(x[0]-p[1])/p[2]*msqrt2));
567  return gaussPart*erfPart;
568 }
569 
570 
571 
581 
582 void AliTMinuitToolkit::Bootstrap(ULong_t nIter, const char * reportName, Option_t *option){
583  Int_t nPoints= fPoints->GetNrows();
584  fPointIndex.Set(nPoints);
585  // Double_t info[3]={0,0,0};
586  Int_t nPar= fFormula->GetNpar();
587  Int_t fcnP=-1;
588  TObjString rName("bootstrap");
589  if (reportName!= nullptr) rName=reportName;
590  std::vector< std::vector<double> > vecPar(static_cast<unsigned long>(nPar), std::vector<double>(nIter));
591  for (UInt_t iter=0; iter<nIter; iter++){
592  for (Int_t iPoint=0; iPoint<nPoints; iPoint++){
593  fPointIndex[iPoint]= static_cast<Int_t>(gRandom->Rndm() * nPoints);
594  }
595  Fit(option);
596  FitterFCN(nPar, nullptr,fChi2, fParam->GetMatrixArray(),0);
597  if (fcnP<0) fcnP=fFCNCounter;
598  Int_t fcnCounter=fFCNCounter-fcnP;
599  fcnP=fFCNCounter;
600  for (Int_t iPar=0; iPar<nPar;iPar++) vecPar[iPar][iter]=(*fParam)(iPar);
601  if (fStreamer){
602  (*fStreamer)<<"bootstrap"<<
603  "iter="<<iter<<
604  "fStatus="<<fStatus<< // minuit status
605  "reportName.="<<&rName<<
606  "nPoints="<<nPoints<<
607  "fChi2="<<fChi2<<
608  "fcnCounterI="<<fFCNCounter<<
609  "fcnCounter="<<fcnCounter<<
610  "fParam.="<<fParam<<
611  "fCovar.="<<fCovar<<
612  "\n";
613  }
614  }
615  if (fRMSEstimator== nullptr) fRMSEstimator=new TVectorD(*fParam);
616  TVectorD &par=*fParam;
617  TVectorD &rms=*fRMSEstimator;
618  for (Int_t iPar=0; iPar<nPar;iPar++) {
619  Double_t rmsF,meanF;
620  TStatToolkit::EvaluateUni(static_cast<Int_t>(nIter), vecPar[iPar].data(), meanF, rmsF,
621  static_cast<Int_t>(TMath::Max(nIter * 0.75, 1.)));
622  par[iPar]=TMath::Median(static_cast<Long64_t>(nIter), vecPar[iPar].data());
623  rms[iPar]=rmsF;
624  }
625  fPointIndex.Set(0);
626 }
627 
628 
637 void AliTMinuitToolkit::TwoFoldCrossValidation(UInt_t nIter, const char *reportName, Option_t *option) {
638  Int_t nPoints = fPoints->GetNrows();
639  TArrayI indexFold(nPoints);
640  TArrayD rndmCV(nPoints);
641  fPointIndex.Set(nPoints);
642  Double_t chi2_00, chi2_01, /*chi2_10,*/ chi2_11;
643  //Double_t info[3] = {0, 0, 0};
644  Int_t nPar = fFormula->GetNpar();
645  Int_t fcnP = -1;
646  TObjString rName(reportName);
647  std::vector<std::vector<double> > vecPar(static_cast<unsigned long>(2 * nPar + 4), std::vector<double>(nIter)); //store vectors and chi2
648  for (UInt_t iter = 0; iter < nIter; iter++) {
649  for (Int_t iPoint = 0; iPoint < nPoints; iPoint++) {
650  rndmCV[iPoint] = gRandom->Rndm() * nPoints;
651  }
652  TMath::Sort(nPoints, rndmCV.GetArray(), indexFold.GetArray());
653  //
654  fPointIndex.Set(nPoints / 2, indexFold.GetArray());
655  Fit(option);
656  for (Int_t iPar = 0; iPar < nPar; iPar++) vecPar[iPar][iter] = (*fParam)(iPar);
657  TVectorD param0(*fParam);
658  TMatrixD covar0(*fCovar);
659  FitterFCN(nPar, nullptr, chi2_00, fParam->GetMatrixArray(), 0);
660  //
661  fPointIndex.Set(nPoints - nPoints / 2, &(indexFold.GetArray()[nPoints / 2]));
662  FitterFCN(nPar, nullptr, chi2_01, fParam->GetMatrixArray(), 0);
663  Fit(option);
664  for (Int_t iPar = 0; iPar < nPar; iPar++) vecPar[nPar + iPar][iter] = (*fParam)(iPar);
665  FitterFCN(nPar, nullptr, chi2_11, fParam->GetMatrixArray(), 0);
666  vecPar[2 * nPar][iter] = chi2_00 + chi2_01 + chi2_11;
667  TVectorD param1(*fParam);
668  TMatrixD covar1(*fCovar);
669  // param distance
670  TMatrixD covar(covar0);
671  covar += covar1;
672  covar.Invert();
673  TMatrixD delta(nPar, 1, (param1 - param0).GetMatrixArray());
674  TMatrixD delta2(covar, TMatrixD::kMult, delta);
675  delta.T();
676  TMatrixD chi2(delta, TMatrixD::kMult, delta2);
677  chi2 *= vecPar[2 * nPar][iter];
678  vecPar[2 * nPar + 1][iter] = chi2(0, 0);
679  Double_t logLike = chi2_00 + chi2_01 + chi2_11;
680  Double_t normDistance = TMath::Sqrt(chi2(0, 0));
681  //
682  if (fcnP < 0) fcnP = fFCNCounter;
683  Int_t fcnCounter = fFCNCounter - fcnP;
684  fcnP = fFCNCounter;
685  if (fStreamer) {
686  (*fStreamer) << "crossValidation" <<
687  "reportName.=" << &rName <<
688  "iter=" << iter << // iteration
689  "fStatus=" << fStatus << // minuit status
690  "nPoints=" << nPoints << // number of points
691  "chi2_00=" << chi2_00 << // log likelihood for training sample
692  "chi2_01=" << chi2_01 << // log likelihood for test sample using training fit
693  "chi2_11=" << chi2_11 << // log likelihood for test sample using test fit
694  "fcnCounterI=" << fFCNCounter << // fcn counter integral
695  "fcnCounter=" << fcnCounter << // fcn counter per fit
696  "param0.=" << &param0 << // parameters estimate training sample
697  "covar0.=" << &covar0 << // covariance for training sample
698  "param1.=" << &param1 << // parameters for complementary subsample
699  "covar1.=" << &covar1 << // covariance for complementary subsample
700  "logLike=" << logLike << "normDistance=" << normDistance << // parameters normalized distance
701  "\n";
702  }
703  }
704  fPointIndex.Set(0);
705 }
706 
720 void AliTMinuitToolkit::MISAC(Int_t nFitPoints, UInt_t nIter, const char * reportName, Option_t *option){
721  TObjString rName(reportName);
722  const Double_t kRelMedDistanceCut=2; // outlier rejection cut - should be parameter
723  Int_t nPoints= fPoints->GetNrows(); //
724  Int_t nPar=fFormula->GetNpar(); //
725  Int_t nSamples=nFitPoints*nIter; // number of fit samples
726  Int_t nRepeat=(1+(nSamples/nPoints)); //
727  Int_t *permutationIndex= new Int_t[nRepeat*nPoints]; // generate random permutations
728  Double_t *xrndm=new Double_t[nPoints];
729  std::vector<TMatrixD*> paramArray(nIter); // fit parameters
730  std::vector<TMatrixD*> covarArray(nIter); // fit covariance
731  std::vector<double> logLike(nIter); // fit to points
732  std::vector<double> medDistance(nIter); // fit to other fits
733  std::vector<int> fitStatus(nIter); // fit status
734  if (nullptr == fRMSEstimator){
735  fRMSEstimator=new TVectorD(nPar);
736  }
737  // 0. Generate permutations of data
738  for (Int_t iR=0; iR<nRepeat; iR++){ // make nRepeat random permutations of points
739  for (Int_t iPoint=0; iPoint<nPoints; iPoint++){
740  xrndm[iPoint]=gRandom->Rndm();
741  }
742  TMath::Sort(nPoints, xrndm, &(permutationIndex[iR*nPoints]));
743  }
744  // 1.) Calculate fit parameters for small subset of data
745  for (UInt_t iter=0; iter<nIter; iter++){
746  fPointIndex.Set(nFitPoints, &(permutationIndex[iter*nFitPoints]));
747  Fit(option);
748  fitStatus[iter]=fStatus;
749  FitterFCN(nPar, nullptr,fChi2, fParam->GetMatrixArray(),0);
750  logLike[iter]=fChi2;
751  paramArray[iter] = new TMatrixD(nPar,1,fParam->GetMatrixArray());
752  covarArray[iter] = new TMatrixD(*fCovar);
753  }
754  // 2. Calculate normalized distance between each fits
755  std::vector< std::vector<double> > logDistance(nIter, std::vector<double>(nIter)); //store vectors and chi2
756  for (UInt_t iter0=0; iter0<nIter; iter0++){
757  for (UInt_t iter1=0; iter1<nIter; iter1++){
758  if ( ((fitStatus[iter0]&kCovarFailed)==0 && (fitStatus[iter1]&kCovarFailed))==0){
759  TMatrixD covar(*(covarArray[iter0]));
760  covar+=*(covarArray[iter1]);
761  covar.Invert();
762  TMatrixD delta(*(paramArray[iter0]));
763  delta-=*(paramArray[iter1]);
764  TMatrixD delta2(covar,TMatrixD::kMult,delta);
765  delta.T();
766  TMatrixD chi2(delta,TMatrixD::kMult,delta2);
767  chi2*=logLike[iter0]+logLike[iter1];
768  if (chi2(0,0)>0) {
769  Double_t normDistance = TMath::Sqrt(chi2(0, 0));
770  logDistance[iter0][iter1] = normDistance;
771  }else{
772  logDistance[iter0][iter1]=-1.;
773  };
774  }else{
775  logDistance[iter0][iter1]=-1.;
776  }
777  }
778  }
779  // 3. Reject outliers and calculate robust mean
780  for (UInt_t iter=0; iter<nIter; iter++){
781  medDistance[iter]=TMath::Median(nIter, logDistance[iter].data());
782  }
783  Double_t medMedDistance=TMath::Median(nIter,medDistance.data());
784  // 4. Extract combined statistic
785  // 4.a 1D median,mean,rms
786  // 4.b nD ?
787  if (fMISACEstimators== nullptr) fMISACEstimators = new TMatrixD(4,nPar);
788  for (Int_t iPar=0;iPar<nPar; iPar++){
789  std::vector<double>workingArray(nIter);
790  Int_t nAccepted=0;
791  for (UInt_t iter=0; iter<nIter; iter++){
792  if (medDistance[iter]<0) continue;
793  if (medDistance[iter]/medMedDistance>kRelMedDistanceCut) continue;
794  workingArray[nAccepted]=(*(paramArray[iter]))(iPar,0);
795  nAccepted++;
796  }
797  if (nAccepted>0){
798  (*fMISACEstimators)(0,iPar)=TMath::Median(nAccepted,workingArray.data());
799  (*fMISACEstimators)(1,iPar)=TMath::Mean(nAccepted,workingArray.data());
800  (*fMISACEstimators)(2,iPar)=TMath::RMS(nAccepted,workingArray.data());
801  }
802  (*fParam)(iPar)=(*fMISACEstimators)(0,iPar);
803  (*fRMSEstimator)(iPar)=(*fMISACEstimators)(2,iPar);
804 
805  }
806  // 5. Dump results into tree in verbose mode
807  if (fStreamer){
808  for (UInt_t iter=0; iter<nIter; iter++){
809  (*fStreamer)<<"misac"<<
810  "iter="<<iter<< // iteration
811  "reportName.="<<&rName<<
812  "fStatus="<<fStatus<< // minuit status
813  "nPoints="<<nPoints<< // number of points
814  "param.="<<paramArray[iter]<<
815  "covar.="<<covarArray[iter]<<
816  "medDistance="<<medDistance[iter]<<
817  "medMedDistance="<<medMedDistance<<
818  "misacE.="<<fMISACEstimators<< // estimator matrix (estimator(median,mean,rms),param)
819  "\n";
820  }
821  }
822  fPointIndex.Set(0);
823  delete [] xrndm;
824  delete [] permutationIndex;
825 }
826 
827 
838  TF1 *likeGausCachy = new TF1("likeGausCachy", AliTMinuitToolkit::GaussCachyLogLike,-10,10,2);
839  TF1 *likePseudoHuber = new TF1("likePseudoHuber", AliTMinuitToolkit::PseudoHuberLogLike,-10,10,1);
840  likeGausCachy->SetParameters(0.8,1);
841  likePseudoHuber->SetParameter(0,3);
842  likeGausCachy->GetXaxis()->SetTitle("#Delta");
843  likeGausCachy->GetYaxis()->SetTitle("-logLikelihood");
844  likeGausCachy->SetLineColor(2);
845  likePseudoHuber->GetXaxis()->SetTitle("#Delta");
846  likePseudoHuber->GetYaxis()->SetTitle("-logLikelihood");
847  likePseudoHuber->SetLineColor(4);
848  //
849  for (Int_t iPol=0; iPol<10; iPol++){ //register polynomial fitters
850  TMatrixD initPar(iPol+1,4);
851  for (Int_t iPar=0; iPar<iPol+1; iPar++) initPar(iPar,1)=1;
852  //
853  TF1 *fpol = new TF1(TString::Format("fpol%d",iPol).Data(),TString::Format("pol%d",iPol).Data());
854  AliTMinuitToolkit * fitter1D = new AliTMinuitToolkit();
855  fitter1D->SetVerbose(0x1); fitter1D->SetFitFunction(fpol,kTRUE);
856  fitter1D->SetInitialParam(&initPar);
857  fitter1D->SetName(TString::Format("pol%d",iPol).Data());
858  AliTMinuitToolkit::SetPredefinedFitter(fitter1D->GetName(),fitter1D);
859  // gaus log likelihood cost function
860  AliTMinuitToolkit * fitter1DR = new AliTMinuitToolkit();
861  fitter1DR->SetVerbose(0x1); fitter1DR->SetFitFunction(fpol,kTRUE);
862  fitter1DR->SetLogLikelihoodFunction(likeGausCachy);
863  fitter1DR->SetInitialParam(&initPar);
864  fitter1DR->SetName(TString::Format("pol%dR",iPol).Data());
865  AliTMinuitToolkit::SetPredefinedFitter(fitter1DR->GetName(),fitter1DR);
866  // huber cost function
867  AliTMinuitToolkit * fitter1DH = new AliTMinuitToolkit();
868  fitter1DH->SetVerbose(0x1); fitter1DH->SetFitFunction(fpol,kTRUE);
869  fitter1DH->SetInitialParam(&initPar);
870  //fitter1DH->EnableRobust(true);
871  fitter1DH->SetLogLikelihoodFunction(likePseudoHuber);
872  fitter1DH->SetName(TString::Format("pol%dH",iPol).Data());
873  AliTMinuitToolkit::SetPredefinedFitter(fitter1DH->GetName(),fitter1DH);
874  }
875  //
876  TF1 *fGaus = new TF1("fgaus","gaus");
877  TMatrixD initPar(3,4);
878  initPar(0,0)=0; initPar(0,1)=1;
879  initPar(1,0)=0; initPar(1,1)=1;
880  initPar(2,0)=1; initPar(2,1)=1;
881  AliTMinuitToolkit * fitterGR = new AliTMinuitToolkit();
882  fitterGR->SetVerbose(0x1); fitterGR->SetFitFunction(fGaus,kTRUE);
883  fitterGR->SetLogLikelihoodFunction(likeGausCachy);
884  fitterGR->SetInitialParam(&initPar);
885  AliTMinuitToolkit::SetPredefinedFitter("gausR",fitterGR);
886  //
887  //
888  // TFormulaPrimitive::AddFormula(new TFormulaPrimitive("GausKS","GausKS",(TFormulaPrimitive::GenFuncG)AliTMinuitToolkit::GausKurtosisSkewness,5));
889  // hack number of arguments in the primitive (protected member) - //TODO - fix ROOT
890  // TFormulaPrimitive * p = TFormulaPrimitive::FindFormula("GausKS");
891  // TDataMember *m = (TDataMember*)p->IsA()->GetListOfDataMembers()->FindObject("fNArguments");
892  // *((Int_t*)(((char*)p)+m->GetOffset()))=1;
893 }
894 
902 AliTMinuitToolkit * AliTMinuitToolkit::RegisterPlaneFitter(Int_t nPlanes, Int_t logLikeType){
903  AliTMinuitToolkit *fitter = new AliTMinuitToolkit(TString::Format("AliTMinuitToolkitTest%d.root", nPlanes));
904  TString sFormula = "[0]*x[0]";
905  for (Int_t iPlane=1; iPlane<nPlanes; iPlane++) sFormula+=TString::Format("+[%d]*x[%d]", iPlane,iPlane);
906  TFormula *formula = new TFormula(TString::Format("hyp%dR",nPlanes).Data(), sFormula.Data());
907  fitter->SetFitFunction((TF1 *) formula, kTRUE);
908  //
909  if (logLikeType==0) { // standard minimization
910  TF1 *likeGaus = new TF1("likeGausCachy", "x*x*0.5", -10, 10);
911  fitter->SetLogLikelihoodFunction(likeGaus);
912  fitter->SetName(TString::Format("hyp%d", nPlanes).Data());
913  }
914  if (logLikeType==1){
915  TF1 *likeGausCachy = new TF1("likeGausCachy", AliTMinuitToolkit::GaussCachyLogLike, -10, 10, 2);
916  likeGausCachy->SetParameters(0.8, 1);
917  fitter->SetLogLikelihoodFunction(likeGausCachy);
918  fitter->SetName(TString::Format("hyp%dR", nPlanes).Data());
919  }
920  if (logLikeType==2){
921  TF1 *likePseudoHuber = new TF1("likePseudoHuber", AliTMinuitToolkit::PseudoHuberLogLike, -10, 10, 1);
922  likePseudoHuber->SetParameter(0,3);
923  fitter->SetLogLikelihoodFunction(likePseudoHuber);
924  fitter->SetName(TString::Format("hyp%dH", nPlanes).Data());
925  }
926  fitter->SetInitialParam(new TMatrixD(nPlanes,4));
927  AliTMinuitToolkit::SetPredefinedFitter(fitter->GetName(), fitter);
928  return fitter;
929 }
930 
931 
942 
943 AliTMinuitToolkit * AliTMinuitToolkit::Fit(TH1* his, const char *fitterName, Option_t* fitOption, Option_t* hisOption,Option_t* funOption, Double_t xMin, Double_t xMax){
944  AliTMinuitToolkit *fitter=GetPredefinedFitter(fitterName);
945  if (fitter== nullptr){
946  ::Error("AliTMinuitToolkit::Fit","Non supported fitter %s. \nfitter can be added to the list using. SetPredefinedFitter", fitterName);
947  return nullptr;
948  }
949  if (his== nullptr){
950  ::Error("AliTMinuitToolkit::Fit","NULL pointer");
951  return nullptr;
952  }
953  fitter->FitHistogram(his,fitOption);
954  TF1 * fitFun = (TF1*)(fitter->GetFormula()->Clone());
955  fitFun->SetParameters(fitter->GetParameters()->GetMatrixArray());
956  SetFunctionDrawOption(fitFun,funOption);
957  his->GetListOfFunctions()->AddLast(fitFun);
958  if (xMin<xMax) {
959  fitFun->SetRange(xMin,xMax);
960  }else{
961  fitFun->SetRange(his->GetXaxis()->GetXmin(), his->GetXaxis()->GetXmax());
962  }
963  if (hisOption) his->Draw(hisOption);
964  return fitter;
965 }
966 
967 
978 
979 AliTMinuitToolkit * AliTMinuitToolkit::Fit(TGraph *graph, const char *fitterName, Option_t* fitOption, Option_t* graphOption, Option_t* funOption, Double_t xMin, Double_t xMax){
980  //
981  //
982  AliTMinuitToolkit *fitter=GetPredefinedFitter(fitterName);
983  if (fitter== nullptr){
984  ::Error("AliTMinuitToolkit::Fit","Non supported fitter %s. \nfitter can be added to the list using. SetPredefinedFitter", fitterName);
985  return nullptr;
986  }
987  if (graph== nullptr){
988  ::Error("AliTMinuitToolkit::Fit","NULL pointer");
989  return nullptr;
990  }
991  fitter->FitGraph(graph,fitOption);
992  TF1 * fitFun = (TF1*)(fitter->GetFormula()->Clone());
993  fitFun->SetParameters(fitter->GetParameters()->GetMatrixArray());
994  fitFun->SetName(TString::Format("%s:%s",fitter->GetName(), fitOption).Data());
995  fitFun->SetTitle(TString::Format("%s:%s",fitter->GetName(), fitOption).Data());
996  SetFunctionDrawOption(fitFun,funOption);
997  graph->GetListOfFunctions()->AddLast(fitFun);
998  if (xMin<xMax) {
999  fitFun->SetRange(xMin,xMax);
1000  }else{
1001  fitFun->SetRange(graph->GetXaxis()->GetXmin(), graph->GetXaxis()->GetXmax());
1002  }
1003  if (graphOption) graph->Draw(graphOption);
1004  return fitter;
1005 }
1006 
1013 
1014 void AliTMinuitToolkit::SetFunctionDrawOption(TF1 *fun, Option_t *option){
1015  TString funOption=option; // e.g "funOption(1,1,1)"
1016  TPRegexp regFunOption("funOption\\(");
1017  // Error message
1018  if (regFunOption.Match(funOption)){
1019  Int_t index0=funOption.Index(regFunOption)+9;
1020  Int_t index1=funOption.Index(")",index0+1);
1021  if (index1<index0) {
1022  ::Error("AliTMinuitToolkit::SetFunctionDrawOption","Invalid function draw option syntax %s", option);
1023  return;
1024  }
1025  Int_t index=index0+1;
1026  Int_t color=TString(&(funOption.Data()[index])).Atoi();
1027  if (color>0) fun->SetLineColor(static_cast<Color_t>(color));
1028  index=funOption.Index(",",index+1)+1;
1029  if (index>0) {
1030  Int_t width=TString(&(funOption.Data()[index])).Atoi();
1031  if (width>0) fun->SetLineWidth(static_cast<Width_t>(width));
1032  index=funOption.Index(",",index+1)+1;
1033  if (index>0) {
1034  Int_t style=TString(&(funOption.Data()[index])).Atoi();
1035  if (style>0) fun->SetLineStyle(static_cast<Style_t>(style));
1036  }
1037  }
1038  }
1039 }
AliTMinuitToolkit(const char *streamerName=nullptr, const char *mode="recreate")
void FitHistogram(const TH1 *his, Option_t *option="")
Fit a one dimensional histogram.
TNamed * GetMetadata(TTree *tree, const char *vartagName, TString *prefix=0, Bool_t fullMatch=kFALSE)
void SetInitialParam(const TMatrixD *initialParam)
Set initial parameters matrix.
static Double_t RndmGaus(Double_t mean=0, Double_t sigma=1)
Random gaus generator as static function (to use in root TFormula, TTreeFormula)
const TVectorD * GetRMSEstimator() const
const TMatrixD * GetPoints() const
void Fit(Option_t *option="")
Internal function that calls the fitter.
TTreeSRedirector * fStreamer
TObjArray * GetListOfVariables()
static AliTMinuitToolkit * GetPredefinedFitter(const std::string &fitterName)
Float_t p[]
Definition: kNNTest.C:133
static Double_t RndmLandau(Double_t mean=0, Double_t sigma=1)
Random gaus generator as static function (to use in root TFormula, TTreeFormula)
static Double_t PseudoHuberLogLike(const Double_t *x, const Double_t *p)
Bool_t IsVerbose(Int_t flag)
Bool_t IsHuberCost() const
const TVectorD * GetParameters() const
static Double_t HuberLogLike(const Double_t *x, const Double_t *p)
TTree * tree
Double_t chi2
Definition: AnalyzeLaser.C:7
TString GetFitFunctionAsAlias(Option_t *option="", TTree *tree=nullptr)
Format alias string for the for tree alias or for fit title.
static void SetFunctionDrawOption(TF1 *fun, Option_t *option)
TGraph * gr
Definition: CalibTime.C:25
static void SetPredefinedFitter(const std::string &fitterName, AliTMinuitToolkit *fitter)
void SetVerbose(Int_t verbose)
void Bootstrap(ULong_t nIter, const char *reportName, Option_t *option=nullptr)
Bootstrap minimization.
void FitGraph(const TGraph *gr, Option_t *option="")
Fit a TGraph object.
TF1 likeGausCachy("likeGausCachy", AliTMinuitToolkit::GaussCachyLogLike,-10, 10, 2)
void EvaluateUni(Int_t nvectors, Double_t *data, Double_t &mean, Double_t &sigma, Int_t hh)
TF1 * GetFormula() const
static Double_t GaussCachyLogLike(const Double_t *x, const Double_t *p)
static std::map< std::string, AliTMinuitToolkit * > fPredefinedFitters
const TMatrixD * GetValues() const
void SetStreamer(const char *streamerName, const char *mode="recreate")
Set stream for writing info into TTree. see examples(STEER/STEERBase/TTreeStream.cxx) ...
The AliTMinuitToolkit serves as an easy to use interface for the TMinuit.
void SetLogLikelihoodFunction(TF1 *logLikelihood)
static void RegisterDefaultFitters()
TTree * inputTree
Long64_t FillFitter(TTree *inputTree, TString values, TString variables, TString selection, Int_t firstEntry, Int_t nEntries, Bool_t doReset=kTRUE)
Fill input matrices of the fitter.
for(Int_t itree=0;itree< arrInputTreesDistortionCalib->GetEntriesFast();++itree)
void TwoFoldCrossValidation(UInt_t nIter, const char *reportName, Option_t *option=nullptr)
void SetFitFunction(TF1 *formula, Bool_t doReset)
Set formula of the fitted function and reset parameter. See doReset.
void MISAC(Int_t nFitPoints, UInt_t nIter, const char *reportName, Option_t *option=nullptr)
Fitting with outlier removal inspired by RANSAC - see https://en.wikipedia.org/w/index.php?title=Random_sample_consensus&oldid=767657742.
TMatrixD * fMISACEstimators
static AliTMinuitToolkit * RegisterPlaneFitter(Int_t nPlanes, Int_t logLikeType)
Register default fitters.
static Double_t GausKurtosisSkewness(const Double_t *x, const Double_t *p)
Gaus convoluted with Erf to emulate kurtosis and skewness.
static void FitterFCN(int &nParam, double *info, double &chi2, double *gin, int iflag)
Internal function used in the TMinuit minimization.