MathUtils.hh

Go to the documentation of this file.
00001 // -*- C++ -*-
00002 #ifndef RIVET_MathUtils_HH
00003 #define RIVET_MathUtils_HH
00004 
00005 #include "Rivet/Math/MathHeader.hh"
00006 #include "Rivet/RivetBoost.hh"
00007 #include <cassert>
00008 
00009 namespace Rivet {
00010 
00011 
00012   /// @name Comparison functions for safe floating point equality tests
00013   //@{
00014 
00015   /// Compare a floating point number to zero with a degree
00016   /// of fuzziness expressed by the absolute @a tolerance parameter.
00017   inline bool isZero(double val, double tolerance=1E-8) {
00018     return (fabs(val) < tolerance);
00019   }
00020 
00021   /// Compare an integral-type number to zero. Since there is no
00022   /// risk of floating point error, this function just exists in
00023   /// case @c isZero is accidentally used on an integer type, to avoid
00024   /// implicit type conversion. The @a tolerance parameter is ignored.
00025   inline bool isZero(long val, double UNUSED(tolerance)=1E-8) {
00026     return val == 0;
00027   }
00028 
00029 
00030   /// @brief Compare two floating point numbers for equality with a degree of fuzziness
00031   /// The @a tolerance parameter is fractional.
00032   inline bool fuzzyEquals(double a, double b, double tolerance=1E-5) {
00033     const double absavg = fabs(a + b)/2.0;
00034     const double absdiff = fabs(a - b);
00035     const bool rtn = (absavg == 0.0 && absdiff == 0.0) || absdiff < tolerance*absavg;
00036     return rtn;
00037   }
00038 
00039   /// @brief Compare two integral-type numbers for equality with a degree of fuzziness.
00040   /// Since there is no risk of floating point error with integral types,
00041   /// this function just exists in case @c fuzzyEquals is accidentally
00042   /// used on an integer type, to avoid implicit type conversion. The @a
00043   /// tolerance parameter is ignored, even if it would have an
00044   /// absolute magnitude greater than 1.
00045   inline bool fuzzyEquals(long a, long b, double UNUSED(tolerance)=1E-5) {
00046     return a == b;
00047   }
00048 
00049 
00050   /// @brief Compare two floating point numbers for >= with a degree of fuzziness
00051   /// The @a tolerance parameter on the equality test is as for @c fuzzyEquals.
00052   inline bool fuzzyGtrEquals(double a, double b, double tolerance=1E-5) {
00053     return a > b || fuzzyEquals(a, b, tolerance);
00054   }
00055 
00056   /// @brief Compare two integral-type numbers for >= with a degree of fuzziness.
00057   /// Since there is no risk of floating point error with integral types,
00058   /// this function just exists in case @c fuzzyGtrEquals is accidentally
00059   /// used on an integer type, to avoid implicit type conversion. The @a
00060   /// tolerance parameter is ignored, even if it would have an
00061   /// absolute magnitude greater than 1.
00062   inline bool fuzzyGtrEquals(long a, long b, double UNUSED(tolerance)=1E-5) {
00063     return a >= b;
00064   }
00065 
00066 
00067   /// @brief Compare two floating point numbers for <= with a degree of fuzziness
00068   /// The @a tolerance parameter on the equality test is as for @c fuzzyEquals.
00069   inline bool fuzzyLessEquals(double a, double b, double tolerance=1E-5) {
00070     return a < b || fuzzyEquals(a, b, tolerance);
00071   }
00072 
00073   /// @brief Compare two integral-type numbers for <= with a degree of fuzziness.
00074   /// Since there is no risk of floating point error with integral types,
00075   /// this function just exists in case @c fuzzyLessEquals is accidentally
00076   /// used on an integer type, to avoid implicit type conversion. The @a
00077   /// tolerance parameter is ignored, even if it would have an
00078   /// absolute magnitude greater than 1.
00079   inline bool fuzzyLessEquals(long a, long b, double UNUSED(tolerance)=1E-5) {
00080     return a <= b;
00081   }
00082 
00083   //@}
00084 
00085 
00086   /// @name Ranges and intervals
00087   //@{
00088 
00089   /// Represents whether an interval is open (non-inclusive) or closed
00090   /// (inclusive). For example, the interval \f$ [0, \pi) \f$ is closed (an inclusive
00091   /// boundary) at 0, and open (a non-inclusive boundary) at \f$ \pi \f$.
00092   enum RangeBoundary { OPEN=0, SOFT=0, CLOSED=1, HARD=1 };
00093 
00094 
00095   /// @brief Determine if @a value is in the range @a low to @a high, for floating point numbers
00096   /// Interval boundary types are defined by @a lowbound and @a highbound.
00097   /// @todo Optimise to one-line at compile time?
00098   template<typename NUM>
00099   inline bool inRange(NUM value, NUM low, NUM high,
00100                       RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
00101     if (lowbound == OPEN && highbound == OPEN) {
00102       return (value > low && value < high);
00103     } else if (lowbound == OPEN && highbound == CLOSED) {
00104       return (value > low && (value < high || fuzzyEquals(value, high)));
00105     } else if (lowbound == CLOSED && highbound == OPEN) {
00106       return ((value > low || fuzzyEquals(value, low)) && value < high);
00107     } else { // if (lowbound == CLOSED && highbound == CLOSED) {
00108       return ((value > low || fuzzyEquals(value, low)) && (value < high || fuzzyEquals(value, high)));
00109     }
00110   }
00111 
00112   /// Alternative version of inRange for doubles, which accepts a pair for the range arguments.
00113   template<typename NUM>
00114   inline bool inRange(NUM value, pair<NUM, NUM> lowhigh,
00115                       RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
00116     return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
00117   }
00118 
00119 
00120   /// @brief Determine if @a value is in the range @a low to @a high, for integer types
00121   /// Interval boundary types are defined by @a lowbound and @a highbound.
00122   /// @todo Optimise to one-line at compile time?
00123   inline bool inRange(int value, int low, int high,
00124                       RangeBoundary lowbound=CLOSED, RangeBoundary highbound=CLOSED) {
00125     if (lowbound == OPEN && highbound == OPEN) {
00126       return (value > low && value < high);
00127     } else if (lowbound == OPEN && highbound == CLOSED) {
00128       return (value > low && value <= high);
00129     } else if (lowbound == CLOSED && highbound == OPEN) {
00130       return (value >= low && value < high);
00131     } else { // if (lowbound == CLOSED && highbound == CLOSED) {
00132       return (value >= low && value <= high);
00133     }
00134   }
00135 
00136   /// Alternative version of @c inRange for ints, which accepts a pair for the range arguments.
00137   inline bool inRange(int value, pair<int, int> lowhigh,
00138                       RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
00139     return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
00140   }
00141 
00142   //@}
00143 
00144 
00145   /// @name Miscellaneous numerical helpers
00146   //@{
00147 
00148   /// Named number-type squaring operation.
00149   template <typename NUM>
00150   inline NUM sqr(NUM a) {
00151     return a*a;
00152   }
00153 
00154   /// Named number-type addition in quadrature operation.
00155   template <typename Num>
00156   inline Num add_quad(Num a, Num b) {
00157     return sqrt(a*a + b*b);
00158   }
00159 
00160   /// Named number-type addition in quadrature operation.
00161   template <typename Num>
00162   inline Num add_quad(Num a, Num b, Num c) {
00163     return sqrt(a*a + b*b + c*c);
00164   }
00165 
00166   /// A more efficient version of pow for raising numbers to integer powers.
00167   template <typename Num>
00168   inline Num intpow(Num val, unsigned int exp) {
00169     assert(exp >= 0);
00170     if (exp == 0) return (Num) 1;
00171     else if (exp == 1) return val;
00172     else return val * intpow(val, exp-1);
00173   }
00174 
00175   /// Find the sign of a number
00176   inline int sign(double val) {
00177     if (isZero(val)) return ZERO;
00178     const int valsign = (val > 0) ? PLUS : MINUS;
00179     return valsign;
00180   }
00181 
00182   /// Find the sign of a number
00183   inline int sign(int val) {
00184     if (val == 0) return ZERO;
00185     return (val > 0) ? PLUS : MINUS;
00186   }
00187 
00188   /// Find the sign of a number
00189   inline int sign(long val) {
00190     if (val == 0) return ZERO;
00191     return (val > 0) ? PLUS : MINUS;
00192   }
00193 
00194   //@}
00195 
00196 
00197   /// @name Binning helper functions
00198   //@{
00199 
00200   /// Make a list of @a nbins + 1 values equally spaced between @a start and @a end inclusive.
00201   inline vector<double> linspace(double start, double end, size_t nbins) {
00202     assert(end >= start);
00203     assert(nbins > 0);
00204     vector<double> rtn;
00205     const double interval = (end-start)/static_cast<double>(nbins);
00206     double edge = start;
00207     while (inRange(edge, start, end, CLOSED, CLOSED)) {
00208       rtn.push_back(edge);
00209       edge += interval;
00210     }
00211     assert(rtn.size() == nbins+1);
00212     return rtn;
00213   }
00214 
00215 
00216   /// Make a list of @a nbins + 1 values exponentially spaced between @a start and @a end inclusive.
00217   inline vector<double> logspace(double start, double end, size_t nbins) {
00218     assert(end >= start);
00219     assert(start > 0);
00220     assert(nbins > 0);
00221     const double logstart = std::log(start);
00222     const double logend = std::log(end);
00223     const vector<double> logvals = linspace(logstart, logend, nbins);
00224     vector<double> rtn;
00225     foreach (double logval, logvals) {
00226       rtn.push_back(std::exp(logval));
00227     }
00228     assert(rtn.size() == nbins+1);
00229     return rtn;
00230   }
00231 
00232 
00233   /// @brief Return the bin index of the given value, @a val, given a vector of bin edges
00234   /// NB. The @a binedges vector must be sorted
00235   template <typename NUM>
00236   inline int index_between(const NUM& val, const vector<NUM>& binedges) {
00237     if (!inRange(val, binedges.front(), binedges.back())) return -1; //< Out of histo range
00238     int index = -1;
00239     for (size_t i = 1; i < binedges.size(); ++i) {
00240       if (val < binedges[i]) {
00241         index = i-1;
00242         break;
00243       }
00244     }
00245     assert(inRange(index, -1, binedges.size()-1));
00246     return index;
00247   }
00248 
00249   //@}
00250 
00251 
00252   /// @name Statistics functions
00253   //@{
00254 
00255   /// Calculate the mean of a sample
00256   inline double mean(const vector<int>& sample) {
00257     double mean = 0.0;
00258     for (size_t i=0; i<sample.size(); ++i) {
00259       mean += sample[i];
00260     }
00261     return mean/sample.size();
00262   }
00263 
00264   // Calculate the error on the mean, assuming poissonian errors
00265   inline double mean_err(const vector<int>& sample) {
00266     double mean_e = 0.0;
00267     for (size_t i=0; i<sample.size(); ++i) {
00268       mean_e += sqrt(sample[i]);
00269     }
00270     return mean_e/sample.size();
00271   }
00272 
00273   /// Calculate the covariance (variance) between two samples
00274   inline double covariance(const vector<int>& sample1, const vector<int>& sample2) {
00275     const double mean1 = mean(sample1);
00276     const double mean2 = mean(sample2);
00277     const int N = sample1.size();
00278     double cov = 0.0;
00279     for (int i = 0; i < N; i++) {
00280       const double cov_i = (sample1[i] - mean1)*(sample2[i] - mean2);
00281       cov += cov_i;
00282     }
00283     if (N > 1) return cov/(N-1);
00284     else return 0.0;
00285   }
00286 
00287   /// Calculate the error on the covariance (variance) of two samples, assuming poissonian errors
00288   inline double covariance_err(const vector<int>& sample1, const vector<int>& sample2) {
00289     const double mean1 = mean(sample1);
00290     const double mean2 = mean(sample2);
00291     const double mean1_e = mean_err(sample1);
00292     const double mean2_e = mean_err(sample2);
00293     const int N = sample1.size();
00294     double cov_e = 0.0;
00295     for (int i = 0; i < N; i++) {
00296       const double cov_i = (sqrt(sample1[i]) - mean1_e)*(sample2[i] - mean2) +
00297         (sample1[i] - mean1)*(sqrt(sample2[i]) - mean2_e);
00298       cov_e += cov_i;
00299     }
00300     if (N > 1) return cov_e/(N-1);
00301     else return 0.0;
00302   }
00303 
00304 
00305   /// Calculate the correlation strength between two samples
00306   inline double correlation(const vector<int>& sample1, const vector<int>& sample2) {
00307     const double cov = covariance(sample1, sample2);
00308     const double var1 = covariance(sample1, sample1);
00309     const double var2 = covariance(sample2, sample2);
00310     const double correlation = cov/sqrt(var1*var2);
00311     const double corr_strength = correlation*sqrt(var2/var1);
00312     return corr_strength;
00313   }
00314 
00315   /// Calculate the error of the correlation strength between two samples assuming Poissonian errors
00316   inline double correlation_err(const vector<int>& sample1, const vector<int>& sample2) {
00317     const double cov = covariance(sample1, sample2);
00318     const double var1 = covariance(sample1, sample1);
00319     const double var2 = covariance(sample2, sample2);
00320     const double cov_e = covariance_err(sample1, sample2);
00321     const double var1_e = covariance_err(sample1, sample1);
00322     const double var2_e = covariance_err(sample2, sample2);
00323 
00324     // Calculate the correlation
00325     const double correlation = cov/sqrt(var1*var2);
00326     // Calculate the error on the correlation
00327     const double correlation_err = cov_e/sqrt(var1*var2) -
00328       cov/(2*pow(3./2., var1*var2)) * (var1_e * var2 + var1 * var2_e);
00329 
00330 
00331     // Calculate the error on the correlation strength
00332     const double corr_strength_err = correlation_err*sqrt(var2/var1) + 
00333       correlation/(2*sqrt(var2/var1)) * (var2_e/var1 - var2*var1_e/pow(2, var2));
00334 
00335     return corr_strength_err;
00336   }
00337   //@}
00338 
00339 
00340   /// @name Angle range mappings
00341   //@{
00342 
00343   /// Reduce any number to the range [-2PI, 2PI] by repeated addition or
00344   /// subtraction of 2PI as required. Used to normalise angular measures.
00345   inline double _mapAngleM2PITo2Pi(double angle) {
00346     double rtn = fmod(angle, TWOPI);
00347     if (isZero(rtn)) return 0;
00348     assert(rtn >= -TWOPI && rtn <= TWOPI);
00349     return rtn;
00350   }
00351 
00352   /// Map an angle into the range (-PI, PI].
00353   inline double mapAngleMPiToPi(double angle) {
00354     double rtn = _mapAngleM2PITo2Pi(angle);
00355     if (isZero(rtn)) return 0;
00356     rtn = (rtn >   PI ? rtn-TWOPI :
00357            rtn <= -PI ? rtn+TWOPI : rtn);
00358     assert(rtn > -PI && rtn <= PI);
00359     return rtn;
00360   }
00361 
00362   /// Map an angle into the range [0, 2PI).
00363   inline double mapAngle0To2Pi(double angle) {
00364     double rtn = _mapAngleM2PITo2Pi(angle);
00365     if (isZero(rtn)) return 0;
00366     if (rtn < 0) rtn += TWOPI;
00367     if (rtn == TWOPI) rtn = 0;
00368     assert(rtn >= 0 && rtn < TWOPI);
00369     return rtn;
00370   }
00371 
00372   /// Map an angle into the range [0, PI].
00373   inline double mapAngle0ToPi(double angle) {
00374     double rtn = fabs(mapAngleMPiToPi(angle));
00375     if (isZero(rtn)) return 0;
00376     assert(rtn > 0 && rtn <= PI);
00377     return rtn;
00378   }
00379 
00380   //@}
00381 
00382 
00383   /// @name Phase space measure helpers
00384   //@{
00385 
00386   /// Calculate the difference between two angles in radians,
00387   /// returning in the range [0, PI].
00388   inline double deltaPhi(double phi1, double phi2) {
00389     return mapAngle0ToPi(phi1 - phi2);
00390   }
00391 
00392   /// Calculate the difference between two pseudorapidities,
00393   /// returning the unsigned value.
00394   inline double deltaEta(double eta1, double eta2) {
00395     return fabs(eta1 - eta2);
00396   }
00397 
00398   /// Calculate the distance between two points in 2D rapidity-azimuthal
00399   /// ("\f$ \eta-\phi \f$") space. The phi values are given in radians.
00400   inline double deltaR(double rap1, double phi1, double rap2, double phi2) {
00401     const double dphi = deltaPhi(phi1, phi2);
00402     return sqrt( sqr(rap1-rap2) + sqr(dphi) );
00403   }
00404 
00405   /// Calculate a rapidity value from the supplied energy @a E and longitudinal momentum @a pz.
00406   inline double rapidity(double E, double pz) {
00407     if (isZero(E - pz)) {
00408       throw std::runtime_error("Divergent positive rapidity");
00409       return MAXDOUBLE;
00410     }
00411     if (isZero(E + pz)) {
00412       throw std::runtime_error("Divergent negative rapidity");
00413       return -MAXDOUBLE;
00414     }
00415     return 0.5*log((E+pz)/(E-pz));
00416   }
00417 
00418   //@}
00419 
00420 
00421 }
00422 
00423 
00424 #endif