Back to home page

EIC code displayed by LXR

 
 

    


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