|
||||
File indexing completed on 2025-01-18 10:11:24
0001 /***************************************************************************** 0002 * Project: RooFit * 0003 * Package: RooFitCore * 0004 * File: $Id: RooMath.h,v 1.16 2007/05/11 09:11:30 verkerke Exp $ 0005 * Authors: * 0006 * WV, Wouter Verkerke, UC Santa Barbara, verkerke@slac.stanford.edu * 0007 * DK, David Kirkby, UC Irvine, dkirkby@uci.edu * 0008 * * 0009 * Copyright (c) 2000-2005, Regents of the University of California * 0010 * and Stanford University. All rights reserved. * 0011 * * 0012 * Redistribution and use in source and binary forms, * 0013 * with or without modification, are permitted according to the terms * 0014 * listed in LICENSE (http://roofit.sourceforge.net/license.txt) * 0015 *****************************************************************************/ 0016 #ifndef ROO_MATH 0017 #define ROO_MATH 0018 0019 #include <TMath.h> 0020 0021 #include <complex> 0022 0023 typedef double *pdouble; 0024 0025 class RooMath { 0026 public: 0027 virtual ~RooMath(){}; 0028 0029 /** @brief evaluate Faddeeva function for complex argument 0030 * 0031 * @author Manuel Schiller <manuel.schiller@nikhef.nl> 0032 * @date 2013-02-21 0033 * 0034 * Calculate the value of the Faddeeva function @f$w(z) = \exp(-z^2) 0035 * \mathrm{erfc}(-i z)@f$. 0036 * 0037 * The method described in 0038 * 0039 * S.M. Abrarov, B.M. Quine: "Efficient algorithmic implementation of 0040 * Voigt/complex error function based on exponential series approximation" 0041 * published in Applied Mathematics and Computation 218 (2011) 1894-1902 0042 * doi:10.1016/j.amc.2011.06.072 0043 * 0044 * is used. At the heart of the method (equation (14) of the paper) is the 0045 * following Fourier series based approximation: 0046 * 0047 * @f[ w(z) \approx \frac{i}{2\sqrt{\pi}}\left( 0048 * \sum^N_{n=nullptr} a_n \tau_m\left( 0049 * \frac{1-e^{i(n\pi+\tau_m z)}}{n\pi + \tau_m z} - 0050 * \frac{1-e^{i(-n\pi+\tau_m z)}}{n\pi - \tau_m z} 0051 * \right) - a_0 \frac{1-e^{i \tau_m z}}{z} 0052 * \right) @f] 0053 * 0054 * The coefficients @f$a_b@f$ are given by: 0055 * 0056 * @f[ a_n=\frac{2\sqrt{\pi}}{\tau_m} 0057 * \exp\left(-\frac{n^2\pi^2}{\tau_m^2}\right) @f] 0058 * 0059 * To achieve machine accuracy in double precision floating point arithmetic 0060 * for most of the upper half of the complex plane, chose @f$t_m=12@f$ and 0061 * @f$N=23@f$ as is done in the paper. 0062 * 0063 * There are two complications: For Im(z) negative, the exponent in the 0064 * equation above becomes so large that the roundoff in the rest of the 0065 * calculation is amplified enough that the result cannot be trusted. 0066 * Therefore, for Im(z) < 0, the symmetry of the erfc function under the 0067 * transformation z --> -z is used to avoid accuracy issues for Im(z) < 0 by 0068 * formulating the problem such that the calculation can be done for Im(z) > 0 0069 * where the accuracy of the method is fine, and some postprocessing then 0070 * yields the desired final result. 0071 * 0072 * Second, the denominators in the equation above become singular at 0073 * @f$z = n * pi / 12@f$ (for 0 <= n < 24). In a tiny disc around these 0074 * points, Taylor expansions are used to overcome that difficulty. 0075 * 0076 * This routine precomputes everything it can, and tries to write out complex 0077 * operations to minimise subroutine calls, e.g. for the multiplication of 0078 * complex numbers. 0079 * 0080 * In the square -8 <= Re(z) <= 8, -8 <= Im(z) <= 8, the routine is accurate 0081 * to better than 4e-13 relative, the average relative error is better than 0082 * 7e-16. On a modern x86_64 machine, the routine is roughly three times as 0083 * fast than the old CERNLIB implementation and offers better accuracy. 0084 * 0085 * For large @f$|z|@f$, the familiar continued fraction approximation 0086 * 0087 * @f[ w(z)=\frac{-iz/\sqrt{\pi}}{-z^2+\frac{1/2}{1+\frac{2/2}{-z^2 + 0088 * \frac{3/2}{1+\frac{4/2}{-z^2+\frac{5/2}{1+\frac{6/2}{-z^2+\frac{7/2 0089 * }{1+\frac{8/2}{-z^2+\frac{9/2}{1+\ldots}}}}}}}}}} @f] 0090 * 0091 * is used, truncated at the ellipsis ("...") in the formula; for @f$|z| > 0092 * 12@f$, @f$Im(z)>0@f$ it will give full double precision at a smaller 0093 * computational cost than the method described above. (For @f$|z|>12@f$, 0094 * @f$Im(z)<0@f$, the symmetry property @f$w(x-iy)=2e^{-(x+iy)^2-w(x+iy)}@f$ 0095 * is used. 0096 */ 0097 static std::complex<double> faddeeva(std::complex<double> z); 0098 /** @brief evaluate Faddeeva function for complex argument (fast version) 0099 * 0100 * @author Manuel Schiller <manuel.schiller@nikhef.nl> 0101 * @date 2013-02-21 0102 * 0103 * Calculate the value of the Faddeeva function @f$w(z) = \exp(-z^2) 0104 * \mathrm{erfc}(-i z)@f$. 0105 * 0106 * This is the "fast" version of the faddeeva routine above. Fast means that 0107 * is takes roughly half the amount of CPU of the slow version of the 0108 * routine, but is a little less accurate. 0109 * 0110 * To be fast, chose @f$t_m=8@f$ and @f$N=11@f$ which should give accuracies 0111 * around 1e-7. 0112 * 0113 * In the square -8 <= Re(z) <= 8, -8 <= Im(z) <= 8, the routine is accurate 0114 * to better than 4e-7 relative, the average relative error is better than 0115 * 5e-9. On a modern x86_64 machine, the routine is roughly five times as 0116 * fast than the old CERNLIB implementation, or about 30% faster than the 0117 * interpolation/lookup table based fast method used previously in RooFit, 0118 * and offers better accuracy than the latter (the relative error is roughly 0119 * a factor 280 smaller than the old interpolation/table lookup routine). 0120 * 0121 * For large @f$|z|@f$, the familiar continued fraction approximation 0122 * 0123 * @f[ w(z)=\frac{-iz/\sqrt{\pi}}{-z^2+\frac{1/2}{1+\frac{2/2}{-z^2 + 0124 * \frac{3/2}{1+\ldots}}}} @f] 0125 * 0126 * is used, truncated at the ellipsis ("...") in the formula; for @f$|z| > 0127 * 8@f$, @f$Im(z)>0@f$ it will give full float precision at a smaller 0128 * computational cost than the method described above. (For @f$|z|>8@f$, 0129 * @f$Im(z)<0@f$, the symmetry property @f$w(x-iy)=2e^{-(x+iy)^2-w(x+iy)}@f$ 0130 * is used. 0131 */ 0132 static std::complex<double> faddeeva_fast(std::complex<double> z); 0133 0134 /** @brief complex erf function 0135 * 0136 * @author Manuel Schiller <manuel.schiller@nikhef.nl> 0137 * @date 2013-02-21 0138 * 0139 * Calculate erf(z) for complex z. 0140 */ 0141 static std::complex<double> erf(const std::complex<double> z); 0142 0143 /** @brief complex erf function (fast version) 0144 * 0145 * @author Manuel Schiller <manuel.schiller@nikhef.nl> 0146 * @date 2013-02-21 0147 * 0148 * Calculate erf(z) for complex z. Use the code in faddeeva_fast to save some time. 0149 */ 0150 static std::complex<double> erf_fast(const std::complex<double> z); 0151 /** @brief complex erfc function 0152 * 0153 * @author Manuel Schiller <manuel.schiller@nikhef.nl> 0154 * @date 2013-02-21 0155 * 0156 * Calculate erfc(z) for complex z. 0157 */ 0158 static std::complex<double> erfc(const std::complex<double> z); 0159 /** @brief complex erfc function (fast version) 0160 * 0161 * @author Manuel Schiller <manuel.schiller@nikhef.nl> 0162 * @date 2013-02-21 0163 * 0164 * Calculate erfc(z) for complex z. Use the code in faddeeva_fast to save some time. 0165 */ 0166 static std::complex<double> erfc_fast(const std::complex<double> z); 0167 0168 // 1-D nth order polynomial interpolation routines 0169 static double interpolate(double yArr[], Int_t nOrder, double x); 0170 static double interpolate(double xa[], double ya[], Int_t n, double x); 0171 0172 static inline double erf(double x) { return TMath::Erf(x); } 0173 0174 static inline double erfc(double x) { return TMath::Erfc(x); } 0175 }; 0176 0177 #endif
[ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |