//////////////////////////////////////////////////////////////////////////
// //
// GFMath //
// //
// Encapsulate Gero's math routines. //
// //
//////////////////////////////////////////////////////////////////////////
#include "TError.h"
#include "TMath.h"
#include "GFMath.h"
#include "TArrayD.h"
#include "TMatrixD.h"
// #include <iostream>
extern "C" { // FORTRAN function
Float_t binoml_(Int_t& nRec, Int_t& nGen, Float_t& confLev);
}
ClassImp(GFMath)
//__________________________________________________________________
TArrayD GFMath::X1ThroughX2CorrErr(Double_t x1, Double_t x2,
Double_t sigmax1, Double_t sigmax2)
{
// returns an array of length=2 with result[0] = x1/x2
// result[1] = sigma(x1/x2)
// assuming that x1 and x2 are 100% correlated
// cf. logbook 18.12.2002
TArrayD result(2);
if(x2 == 0.) {
::Error("GFMath::X1ThroughX2CorrErr", "x2 = 0.0, return {0.,0.}");
return result;
}
result[0] = x1/x2;
result[1] = TMath::Abs((sigmax1 - result[0]*sigmax2) / x2);
return result;
}
//__________________________________________________________________
TArrayD GFMath::X1ThroughX2BinomErr(Double_t x1, Double_t x2)
{
// returns an array of length=2 with result[0] = x1/x2
// result[1] = sigma(x1/x2)
// assuming that x1 is taken with binomial distribution from a sample of size x2
// cf. logbook 18.12.2002
TArrayD result(2);
if(x2 == 0.) {
::Error("GFMath::X1ThroughX2BinomErr", "x2 = 0.0, return {0.,0.}");
return result;
}
result[0] = x1/x2;
result[1] = TMath::Sqrt((result[0] * (1. - result[0])) / x2);
return result;
}
//__________________________________________________________________
TArrayD GFMath::NormaliseTo1(const TArrayD& input, const TArrayD* inputErr,
TArrayD* outputErr, TMatrixD* outputVariance)
{
// return input normalised to its sum (no normalisation if sum == 0.)
// if inputErr is given, calculate outputErr and/or outputVariance (if given)
// assuming inputErr to be non-correlated
// no error calculation if inputErr has wrong length or sum == 0
const Int_t size = input.GetSize();
TArrayD result(size);
const Double_t sum = input.GetSum();
for(Int_t i = 0; i < size; ++i){
result[i] = input[i] / (sum ? sum : 1.);
}
if(outputErr) {// make valid structure if given - even if not to be filled
outputErr->Set(size);
outputErr->Reset();
}
if(outputVariance) { // dito
outputVariance->ResizeTo(size, size);
outputVariance->Zero();
}
if(sum && inputErr && size==inputErr->GetSize() && (outputErr||outputVariance)){
// The error propagation is done according to:
// V. Blobel/E. Lohrmann: Statistische und numerische Methoden der Datenanlyse, p. 148
// y_i = x_i/(sum_j(x_j)), B_ij = dy_i/dx_j
// cf. log book, May 4th, 2004
TArrayD inputVar(size);
for(Int_t i = 0; i < size; ++i){
inputVar[i] = inputErr->At(i) * inputErr->At(i);
}
const Double_t sumInputVar = inputVar.GetSum();
const Double_t sumTo2 = sum * sum;
TArrayD outDiagSigma(size);
for(Int_t i = 0; i < size; ++i){
const Double_t first = (sum-input[i]) * (sum-input[i]) * inputVar[i];
const Double_t second= input[i] * input[i] * (sumInputVar-inputVar[i]);
outDiagSigma[i] = TMath::Sqrt(first + second)/sumTo2;
}
if(outputErr){
(*outputErr) = outDiagSigma;
}
if(outputVariance){
for(Int_t i = 0; i < size; ++i){
(*outputVariance)(i,i) = outDiagSigma[i] * outDiagSigma[i];
}
for(Int_t i = 0; i < size; ++i){
for(Int_t j = i+1; j < size; ++j){
const Double_t first = input[i] * input[j] * (sumInputVar-inputVar[i]-inputVar[j]);
const Double_t second= input[i] * inputVar[j] * (sum - input[j]);
const Double_t third = input[j] * inputVar[i] * (sum - input[i]);
(*outputVariance)(i,j) = (*outputVariance)(j,i) =
(first - (second + third))/ sumTo2 / sumTo2;
}
}
}// calculate variance
}// calculate errors
return result;
}
//__________________________________________________________________
Float_t GFMath::BinomialError(Int_t nRec, Int_t nGen, Bool_t upper)
{
// upper or lower 'error' of nRec/nGen from binomial according to V. Blobel
// *************************************************************
// *
// * NND=... ! n
// * NNN=... ! k
// * RATIO=NNN/NND ! p = k/n
// * RTLEFT= BINOML(NNN,NND,0.16)
// * IF(RTLEFT.GE.RATIO) RTLEFT=0.0
// * RTRIGT= BINOML(NNN,NND,0.84)
// * IF(RTRIGT.LE.RATIO) RTRIGT=1.0
// * RL=RATIO-RTLEFT
// * IF(RL.LT.0.0) RL=RATIO ! left "error"
// * RR=RTRIGT-RATIO
// * IF(RR.LT.0.0) RR=1.0-RATIO ! right "error"
// *
// *************************************************************
const Float_t ratio = nRec/static_cast<Float_t>(nGen);
Float_t result = 0.;
if(upper){
Float_t limit = 0.84;
Float_t rtright = binoml_(nRec, nGen, limit);
if(rtright <= ratio) rtright = 1.;
result = rtright - ratio;
if(result < 0.0) result = 1.0 - ratio;
} else {
Float_t limit = 0.16;
Float_t rtleft = binoml_(nRec, nGen, limit);
if(rtleft >= ratio) rtleft = 0.;
result = ratio - rtleft;
if(result < 0.0) result = ratio;
}
return result;
}
//____________________________________
TArrayD GFMath::Efficiency(Double_t nCount, Double_t nDenom, Double_t nEffDenom,
Double_t nFillDenom)
{
// returning 'efficiency = nCount/nDenom' and its error(s):
// nCount: surviving, nDenom: reference, nEffDenom: effective number entries for nDenom
// nFillDenom: hist fill actions for nDenom
// if(nEffDenom/nDenom >= 0.7): do asymm. binomial error GFMath::BinomialError
// then result[2/3] are the asymmetric errors
// result[1]: symmetric easy binomial error, but if asymmetric errors are possible,
// 'scaled to nEffDenom'
const Double_t minNeffRatio = 0.7; // cf. GFHistManip::CreateRatioBinomErrors
TArrayD eff(4);
eff[0] = nDenom ? nCount/nDenom : 0.;
if(!nDenom || nEffDenom/nFillDenom < minNeffRatio){
::Warning("GFMath::Efficiency", "N_eff^rec %.4g, N^rec %.4g", nEffDenom, nFillDenom);
eff.Set(2);
eff[1] = nDenom ? TMath::Sqrt(eff[0] * (1.- eff[0])/nDenom) : 0.;
} else {
eff[1] = nDenom ? TMath::Sqrt(eff[0] * (1.- eff[0])/nEffDenom) : 0.;
const Int_t scaledNcount = TMath::Nint(nCount*nEffDenom/nDenom);//scale to effective number
eff[2]= GFMath::BinomialError(scaledNcount, TMath::Nint(nEffDenom), kTRUE);
eff[3]= -GFMath::BinomialError(scaledNcount, TMath::Nint(nEffDenom), kFALSE);//lower error
}
return eff;
}
//__________________________________________________________________
TArrayD GFMath::EfficiencyGassnerError(Double_t nCount, Double_t nDenom,
Double_t errCount, Double_t errDenom)
{
// efficiency nCount/nDenom with 'Gassner' errors
TArrayD effPlusErr(2);
effPlusErr[0] = (nDenom ? nCount/nDenom : 0.);
Double_t relErrEff2 = 0.;
if(nCount && nDenom) {
relErrEff2 = errDenom * errDenom / nDenom / nDenom
+ (1. - 2.*effPlusErr[0]) * errCount * errCount / nCount / nCount;
}
if(relErrEff2 >= 0.) {
effPlusErr[1] = TMath::Sqrt(relErrEff2) * effPlusErr[0];
} else {
::Error("GFMath::EfficiencyGassnerError", "square of relative error is %.2g", relErrEff2);
}
return effPlusErr;
}
//__________________________________________________________________
TArrayD GFMath::EfficiencyBlobelError(Double_t nCount, Double_t nDenom,
Double_t errCount, Double_t errDenom)
{
// according to Blobel, 2004-09-08
TArrayD effPlusErr(2);
effPlusErr[0] = (nDenom ? nCount/nDenom : 0.);
// error from correlation, approximating rho by
// common events/all events ~ errCount^2/errDenom^2
const Double_t errRelDenom = (nDenom ? errDenom / nDenom : 0.);
const Double_t errRelCount = (nCount ? errCount / nCount : 0.);
const Double_t rho = (errDenom ? errCount * errCount / (errDenom * errDenom) : 0.);
const Double_t relErrEff2 =
errRelDenom * errRelDenom + errRelCount * errRelCount - 2.*rho * errRelDenom * errRelCount;
if(relErrEff2 >= 0.) {
effPlusErr[1] = TMath::Sqrt(relErrEff2) * effPlusErr[0];
} else {
::Error("GFMath::EfficiencyBlobelError", "square of relative error is %.2g", relErrEff2);
}
// now unsymmetric error from correct binomial, symmetrise by averaging squares
TArrayD unsymErrs(4);
// if no denominator, the next would put an error... But unsymm. Binomial errors are senseless
if(nDenom) unsymErrs = GFMath::Efficiency(nCount, nDenom, nDenom, nDenom);
const Double_t unsymmErr2 = (unsymErrs[2]*unsymErrs[2] + unsymErrs[3]*unsymErrs[3])/2.;
// add them quadratically:
effPlusErr[1] = TMath::Sqrt(effPlusErr[1]*effPlusErr[1] + unsymmErr2);
return effPlusErr;
}
//__________________________________________________________________
TArrayD GFMath::EfficiencyTestError(Double_t nCount, Double_t nDenom,
Double_t errCount, Double_t errDenom)
{
// similar ansatz as Blobel, but rho = nCount/nDenom
// EXPERIMENTAL!!!
TArrayD effPlusErr(2);
effPlusErr[0] = (nDenom ? nCount/nDenom : 0.);
// error from correlation, approximating rho by
// common events/all events ~ errCount^2/errDenom^2
const Double_t errRelDenom = (nDenom ? errDenom / nDenom : 0.);
const Double_t errRelCount = (nCount ? errCount / nCount : 0.);
const Double_t rho = (nDenom ? nCount/nDenom : 0.);
const Double_t relErrEff2 =
errRelDenom * errRelDenom + errRelCount * errRelCount - 2.*rho * errRelDenom * errRelCount;
if(relErrEff2 >= 0.) {
effPlusErr[1] = TMath::Sqrt(relErrEff2) * effPlusErr[0];
} else {
::Error("GFMath::EfficiencyTestError", "square of relative error is %.2g", relErrEff2);
}
// now unsymmetric error from correct binomial, symmetrise by averaging squares
TArrayD unsymErrs(4);
// if no denominator, the next would put an error... But unsymm. Binomial errors are senseless
if(nDenom) unsymErrs = GFMath::Efficiency(nCount, nDenom, nDenom, nDenom);
const Double_t unsymmErr2 = (unsymErrs[2]*unsymErrs[2] + unsymErrs[3]*unsymErrs[3])/2.;
// add them quadratically:
effPlusErr[1] = TMath::Sqrt(effPlusErr[1]*effPlusErr[1] + unsymmErr2);
return effPlusErr;
}
//__________________________________________________________________
Bool_t GFMath::EquidistLogBins(Double_t* bins, Int_t nBins,Double_t first,Double_t last)
{
// Filling 'bins' with borders of 'nBins' bins between 'first' and 'last' that are
// equidistant when viewed in log scale, so 'bins' must have length nBins+1;
// If 'first', 'last' or 'nBins' are not positive, failure is reported.
//
if(nBins < 1 || first <= 0. || last <= 0.) return kFALSE;
bins[0] = first;
bins[nBins] = last;
const Double_t firstLog = TMath::Log10(bins[0]);
const Double_t lastLog = TMath::Log10(bins[nBins]);
for (Int_t i = 1; i < nBins; ++i){
bins[i] = TMath::Power(10., firstLog + i*(lastLog-firstLog)/(nBins));
}
return kTRUE;
}
//__________________________________________________________________
Bool_t GFMath::EquidistBins(Double_t* bins, Int_t nBins,Double_t first,Double_t last)
{
// Filling 'bins' with borders of 'nBins' bins between 'first' and 'last' that are
// equidistant, so 'bins' must have length nBins+1;
// If 'first' >= 'last' or 'nBins' < 1, failure is reported.
//
if(nBins < 1 || first >= last) return kFALSE;
bins[0] = first;
bins[nBins] = last;
for (Int_t i = 1; i < nBins; ++i){
bins[i] = first + i*(last-first)/(nBins);
}
return kTRUE;
}
//__________________________________________________________________
Bool_t GFMath::IsEqualFloat(Double_t x1, Double_t x2)
{
// true if x1 , x2 are equal besides floating point surprises:
// difference < 1.e-6 and
// (if x1 != 0 && x2 != 0) relative deviation < 1.e-4
// (for the latter 0 means |x| < 1.e-6
const Double_t posZeroDiff = 1.e-6;
const Double_t posZeroQuot = 1.e-4;
if(TMath::Abs(x1 - x2) > posZeroDiff
|| (TMath::Abs(x2) > posZeroDiff && TMath::Abs((x1/x2) -1.) > posZeroQuot)
|| (TMath::Abs(x1) > posZeroDiff && TMath::Abs((x2/x1) -1.) > posZeroQuot)
){
// std::cout << "IsEqualFloat " << " x1 x2: " << x1 << " " << x2;
// if(x2) std::cout << ", (x1/x2)-1 = " << (x1/x2) -1.;
// if(x1) std::cout << ", (x2/x1)-1 = " << (x2/x1) -1.;
// std::cout << ", (x2-x1) = " << x2-x1
// << std::endl;
return kFALSE;
}
return kTRUE;
}
ROOT page - Class index - Top of the page
This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.