/* ======================================================================== *\ ! ! * ! * This file is part of MARS, the MAGIC Analysis and Reconstruction ! * Software. It is distributed to you in the hope that it can be a useful ! * and timesaving tool in analysing Data of imaging Cerenkov telescopes. ! * It is distributed WITHOUT ANY WARRANTY. ! * ! * Permission to use, copy, modify and distribute this software and its ! * documentation for any purpose is hereby granted without fee, ! * provided that the above copyright notice appear in all copies and ! * that both that copyright notice and this permission notice appear ! * in supporting documentation. It is provided "as is" without express ! * or implied warranty. ! * ! ! ! Author(s): Thomas Bretz 3/2004 ! ! Copyright: MAGIC Software Development, 2000-2009 ! ! \* ======================================================================== */ ///////////////////////////////////////////////////////////////////////////// // // MMath // // Mars - Math package (eg Significances, etc) // ///////////////////////////////////////////////////////////////////////////// #include "MMath.h" #include // atof (Ubuntu 8.10) #ifndef ROOT_TVector2 #include #endif #ifndef ROOT_TVector3 #include #endif #ifndef ROOT_TArrayD #include #endif #ifndef ROOT_TComplex #include #endif #ifndef ROOT_TRandom #include // gRandom in RndmExp #endif #include "MString.h" //NamespaceImp(MMath); // -------------------------------------------------------------------------- // // Calculate Significance as // significance = (s-b)/sqrt(s+k*k*b) mit k=s/b // // s: total number of events in signal region // b: number of background events in signal region // Double_t MMath::Significance(Double_t s, Double_t b) { const Double_t k = b==0 ? 0 : s/b; const Double_t f = s+k*k*b; return f==0 ? 0 : (s-b)/TMath::Sqrt(f); } // -------------------------------------------------------------------------- // // Symmetrized significance - this is somehow analog to // SignificanceLiMaSigned // // Returns Significance(s,b) if s>b otherwise -Significance(b, s); // Double_t MMath::SignificanceSym(Double_t s, Double_t b) { return s>b ? Significance(s, b) : -Significance(b, s); } // -------------------------------------------------------------------------- // // calculates the significance according to Li & Ma // ApJ 272 (1983) 317, Formula 17 // // s // s: number of on events // b // b: number of off events // alpha = t_on/t_off; // t: observation time // // The significance has the same (positive!) value for s>b and b>s. // // Returns -1 if s<0 or b<0 or alpha<0 or the argument of sqrt<0 // // Here is some eMail written by Daniel Mazin about the meaning of the arguments: // // > Ok. Here is my understanding: // > According to Li&Ma paper (correctly cited in MMath.cc) alpha is the // > scaling factor. The mathematics behind the formula 17 (and/or 9) implies // > exactly this. If you scale OFF to ON first (using time or using any other // > method), then you cannot use formula 17 (9) anymore. You can just try // > the formula before scaling (alpha!=1) and after scaling (alpha=1), you // > will see the result will be different. // // > Here are less mathematical arguments: // // > 1) the better background determination you have (smaller alpha) the more // > significant is your excess, thus your analysis is more sensitive. If you // > normalize OFF to ON first, you loose this sensitivity. // // > 2) the normalization OFF to ON has an error, which naturally depends on // > the OFF and ON. This error is propagating to the significance of your // > excess if you use the Li&Ma formula 17 correctly. But if you normalize // > first and use then alpha=1, the error gets lost completely, you loose // > somehow the criteria of goodness of the normalization. // Double_t MMath::SignificanceLiMa(Double_t s, Double_t b, Double_t alpha) { const Double_t sum = s+b; if (s<0 || b<0 || alpha<=0) return -1; const Double_t l = s==0 ? 0 : s*TMath::Log(s/sum*(alpha+1)/alpha); const Double_t m = b==0 ? 0 : b*TMath::Log(b/sum*(alpha+1) ); return l+m<0 ? -1 : TMath::Sqrt((l+m)*2); } /* Double_t MMath::SignificanceLiMaErr(Double_t s, Double_t b, Double_t alpha) { Double_t S = SignificanceLiMa(s, b, alpha); if (S<0) return -1; const Double_t sum = s+b; Double_t l = TMath::Log(s/sum*(alpha+1)/alpha)/TMath::Sqrt(2*S); Double_t m = TMath::Log(s/sum*(alpha+1)/alpha)/TMath::Sqrt(2*S); const Double_t sum = s+b; if (s<0 || b<0 || alpha<=0) return -1; const Double_t l = s==0 ? 0 : s*TMath::Log(s/sum*(alpha+1)/alpha); const Double_t m = b==0 ? 0 : b*TMath::Log(b/sum*(alpha+1) ); return l+m<0 ? -1 : TMath::Sqrt((l+m)*2); } */ // -------------------------------------------------------------------------- // // Calculates MMath::SignificanceLiMa(s, b, alpha). Returns 0 if the // calculation has failed. Otherwise the Li/Ma significance which was // calculated. If s1) return 1; return rc; } // -------------------------------------------------------------------------- // // Returns: 1 - exp((x/sigma)^2 / 2) // Double_t MMath::GaussProb2D(Double_t x, Double_t sigma) { const Double_t xs = x/sigma; return 1 - TMath::Exp(-xs*xs/2); } // ------------------------------------------------------------------------ // // Return the "median" (at 68.3%) value of the distribution of // abs(a[i]-Median) // template Double_t MMath::MedianDevImp(Size n, const Element *a, Double_t &med) { static const Double_t prob = 0.682689477208650697; //MMath::GaussProb(1.0); // Sanity check if (n <= 0 || !a) return 0; // Get median of distribution med = TMath::Median(n, a); // Create the abs(a[i]-med) distribution Double_t arr[n]; for (int i=0; i void MMath::ReSortImp(Size n, Element *a, Bool_t down) { Element *cpy = new Element[n]; Size *pos = new Size[n]; memcpy(cpy, a, n*sizeof(Element)); TMath::Sort(n, a, pos, down); Size *idx = pos; for (Element *ptr=a; ptr= 0) then % complex or duplicate roots // // S := sgn(R + sqrt(D))*abs(R + sqrt(D))^(1/3) // T := sgn(R - sqrt(D))*abs(R - sqrt(D))^(1/3) // // z1 := -a/3 + (S + T) % real root // z2 := -a/3 - (S + T)/2 % real part of complex root // z3 := -a/3 - (S + T)/2 % real part of complex root // im := abs(sqrt(3)*(S - T)/2) % complex part of root pair // // else % distinct real roots // // th := arccos(R/sqrt( -Q^3)) // // z1 := 2*sqrt(-Q)*cos(th/3) - a/3 // z2 := 2*sqrt(-Q)*cos((th + 2*pi)/3) - a/3 // z3 := 2*sqrt(-Q)*cos((th + 4*pi)/3) - a/3 // im := 0 // // end if // // return im % imaginary part // // end function // // see also http://en.wikipedia.org/wiki/Cubic_equation // Int_t MMath::SolvePol3(Double_t a, Double_t b, Double_t c, Double_t &x1, Double_t &x2, Double_t &x3) { // Double_t coeff[4] = { 1, a, b, c }; // return TMath::RootsCubic(coeff, x1, x2, x3) ? 1 : 3; const Double_t Q = (a*a - 3*b)/9; const Double_t R = (9*b*a - 27*c - 2*a*a*a)/54; const Double_t D = R*R - Q*Q*Q; // polynomial discriminant // ----- The single-real / duplicate-roots solution ----- // D<0: three real roots // D>0: one real root // D==0: maximum two real roots (two identical roots) // R==0: only one unique root // R!=0: two roots if (D==0) { const Double_t r = MMath::Sqrt3(R); x1 = r - a/3.; // real root if (R==0) return 1; x2 = 2*r - a/3.; // real root return 2; } if (D>0) // complex or duplicate roots { const Double_t sqrtd = TMath::Sqrt(D); const Double_t A = TMath::Sign(1., R)*MMath::Sqrt3(TMath::Abs(R)+sqrtd); // The case A==0 cannot happen. This would imply D==0 // if (A==0) // { // x1 = -a/3; // return 1; // } x1 = (A+Q/A)-a/3; //const Double_t S = MMath::Sqrt3(R + sqrtd); //const Double_t T = MMath::Sqrt3(R - sqrtd); //x1 = (S+T) - a/3.; // real root return 1; //z2 = (S + T)/2 - a/3.; // real part of complex root //z3 = (S + T)/2 - a/3.; // real part of complex root //im = fabs(sqrt(3)*(S - T)/2) // complex part of root pair } // ----- The general solution with three roots --- if (Q==0) return 0; if (Q>0) // This is here for speed reasons { const Double_t sqrtq = TMath::Sqrt(Q); const Double_t rq = R/TMath::Abs(Q); const Double_t t = TMath::ACos(rq/sqrtq)/3; static const Double_t sqrt3 = TMath::Sqrt(3.); const Double_t sn = TMath::Sin(t)*sqrt3; const Double_t cs = TMath::Cos(t); x1 = 2*sqrtq * cs - a/3; x2 = -sqrtq * (sn + cs) - a/3; x3 = sqrtq * (sn - cs) - a/3; /* --- Easier to understand but slower --- const Double_t th1 = TMath::ACos(rq/sqrtq); const Double_t th2 = th1 + TMath::TwoPi(); const Double_t th3 = th2 + TMath::TwoPi(); x1 = 2.*sqrtq * TMath::Cos(th1/3.) - a/3.; x2 = 2.*sqrtq * TMath::Cos(th2/3.) - a/3.; x3 = 2.*sqrtq * TMath::Cos(th3/3.) - a/3.; */ return 3; } const TComplex sqrtq = TComplex::Sqrt(Q); const Double_t rq = R/TMath::Abs(Q); const TComplex th1 = TComplex::ACos(rq/sqrtq); const TComplex th2 = th1 + TMath::TwoPi(); const TComplex th3 = th2 + TMath::TwoPi(); // For ReMul, see bove x1 = ReMul(2.*sqrtq, th1) - a/3.; x2 = ReMul(2.*sqrtq, th2) - a/3.; x3 = ReMul(2.*sqrtq, th3) - a/3.; return 3; } // -------------------------------------------------------------------------- // // Format a value and its error corresponding to the rules (note // this won't work if the error is more then eight orders smaller than // the value) // void MMath::Format(Double_t &v, Double_t &e) { // Valid digits Int_t i = TMath::FloorNint(TMath::Log10(v))-TMath::FloorNint(TMath::Log10(e)); // Check if error starts with 1 or 2. In this case use one // more valid digit TString error = MString::Format("%.0e", e); if (error[0]=='1' || error[0]=='2') { i++; error = MString::Format("%.1e", e); } const TString fmt = MString::Format("%%.%de", i); v = MString::Format(fmt.Data(), v).Atof(); e = error.Atof(); } // -------------------------------------------------------------------------- // // Returns an exponential distribution // // -tau*log(x) with x=[0;1[ and y=[0;1[ // Double_t MMath::RndmExp(Double_t tau) { const Double_t x = gRandom->Rndm(); // uniform on ] 0, 1 ] return -tau * TMath::Log(x); // convert to exponential distribution } // -------------------------------------------------------------------------- // // Returns a distribution according to // // atanh((y+1)/x*sigma) with x and y [-1;1] and x^2+y^2=1 // Double_t MMath::RndmPSF(Double_t sigma) { double x, y; gRandom->Circle(x, y, 1); return TMath::ATanH((y+1)/x*sigma); }