file /home/anarendran/Documents/temp/rivet/include/Rivet/Math/MathUtils.hh

/home/anarendran/Documents/temp/rivet/include/Rivet/Math/MathUtils.hh

Namespaces

Name
Rivet

Source code

// -*- C++ -*-
#ifndef RIVET_MathUtils_HH
#define RIVET_MathUtils_HH

#include "Rivet/Math/MathConstants.hh"
#include <type_traits>
#include <cassert>

namespace Rivet {






  template <typename NUM>
  inline typename std::enable_if<std::is_floating_point<NUM>::value, bool>::type
  isZero(NUM val, double tolerance=1e-8) {
    return fabs(val) < tolerance;
  }

  template <typename NUM>
  inline typename std::enable_if<std::is_integral<NUM>::value, bool>::type
  isZero(NUM val, double=1e-5) { //< NB. unused tolerance parameter for ints, still needs a default value!
    return val == 0;
  }

  template <typename NUM>
  inline typename std::enable_if<std::is_floating_point<NUM>::value, bool>::type
  isNaN(NUM val) { return std::isnan(val); }

  template <typename NUM>
  inline typename std::enable_if<std::is_floating_point<NUM>::value, bool>::type
  notNaN(NUM val) { return !std::isnan(val); }

  template <typename N1, typename N2>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value &&
   (std::is_floating_point<N1>::value || std::is_floating_point<N2>::value), bool>::type
  fuzzyEquals(N1 a, N2 b, double tolerance=1e-5) {
    const double absavg = (std::abs(a) + std::abs(b))/2.0;
    const double absdiff = std::abs(a - b);
    const bool rtn = (isZero(a) && isZero(b)) || absdiff < tolerance*absavg;
    return rtn;
  }

  template <typename N1, typename N2>
  inline typename std::enable_if<
    std::is_integral<N1>::value && std::is_integral<N2>::value, bool>::type
    fuzzyEquals(N1 a, N2 b, double) { //< NB. unused tolerance parameter for ints, still needs a default value!
    return a == b;
  }


  template <typename N1, typename N2>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value, bool>::type
  fuzzyGtrEquals(N1 a, N2 b, double tolerance=1e-5) {
    return a > b || fuzzyEquals(a, b, tolerance);
  }


  template <typename N1, typename N2>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value, bool>::type
  fuzzyLessEquals(N1 a, N2 b, double tolerance=1e-5) {
    return a < b || fuzzyEquals(a, b, tolerance);
  }

  template <typename N1, typename N2>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value,
    typename std::common_type<N1,N2>::type >::type
  min(N1 a, N2 b) {
    return a > b ? b : a;
  }

  template <typename N1, typename N2>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value,
    typename std::common_type<N1,N2>::type >::type
  max(N1 a, N2 b) {
    return a > b ? a : b;
  }





  enum RangeBoundary { OPEN=0, SOFT=0, CLOSED=1, HARD=1 };

  template <typename N1, typename N2, typename N3>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
  inRange(N1 value, N2 low, N3 high,
          RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
    if (lowbound == OPEN && highbound == OPEN) {
      return (value > low && value < high);
    } else if (lowbound == OPEN && highbound == CLOSED) {
      return (value > low && value <= high);
    } else if (lowbound == CLOSED && highbound == OPEN) {
      return (value >= low && value < high);
    } else { // if (lowbound == CLOSED && highbound == CLOSED) {
      return (value >= low && value <= high);
    }
  }

  template <typename N1, typename N2, typename N3>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
  fuzzyInRange(N1 value, N2 low, N3 high,
               RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
    if (lowbound == OPEN && highbound == OPEN) {
      return (value > low && value < high);
    } else if (lowbound == OPEN && highbound == CLOSED) {
      return (value > low && fuzzyLessEquals(value, high));
    } else if (lowbound == CLOSED && highbound == OPEN) {
      return (fuzzyGtrEquals(value, low) && value < high);
    } else { // if (lowbound == CLOSED && highbound == CLOSED) {
      return (fuzzyGtrEquals(value, low) && fuzzyLessEquals(value, high));
    }
  }

  template <typename N1, typename N2, typename N3>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
  inRange(N1 value, pair<N2, N3> lowhigh,
          RangeBoundary lowbound=CLOSED, RangeBoundary highbound=OPEN) {
    return inRange(value, lowhigh.first, lowhigh.second, lowbound, highbound);
  }


  // Alternative forms, with snake_case names and boundary types in names rather than as args -- from MCUtils

  template <typename N1, typename N2, typename N3>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
  in_range(N1 val, N2 low, N3 high) {
    return inRange(val, low, high, CLOSED, OPEN);
  }

  template <typename N1, typename N2, typename N3>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
  in_closed_range(N1 val, N2 low, N3 high) {
    return inRange(val, low, high, CLOSED, CLOSED);
  }

  template <typename N1, typename N2, typename N3>
  inline typename std::enable_if<
    std::is_arithmetic<N1>::value && std::is_arithmetic<N2>::value && std::is_arithmetic<N3>::value, bool>::type
  in_open_range(N1 val, N2 low, N3 high) {
    return inRange(val, low, high, OPEN, OPEN);
  }






  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
  sqr(NUM a) {
    return a*a;
  }

  // template <typename N1, typename N2>
  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
  //std::common_type<N1, N2>::type
  add_quad(NUM a, NUM b) {
    return sqrt(a*a + b*b);
  }

  // template <typename N1, typename N2>
  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
  //std::common_type<N1, N2, N3>::type
  add_quad(NUM a, NUM b, NUM c) {
    return sqrt(a*a + b*b + c*c);
  }

  inline double safediv(double num, double den, double fail=0.0) {
    return (!isZero(den)) ? num/den : fail;
  }

  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
  intpow(NUM val, unsigned int exp) {
    assert(exp >= 0);
    if (exp == 0) return (NUM) 1;
    else if (exp == 1) return val;
    return val * intpow(val, exp-1);
  }

  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, int>::type
  sign(NUM val) {
    if (isZero(val)) return ZERO;
    const int valsign = (val > 0) ? PLUS : MINUS;
    return valsign;
  }





  inline double cdfBW(double x, double mu, double gamma) {
    // normalize to (0;1) distribution
    const double xn = (x - mu)/gamma;
    return std::atan(xn)/M_PI + 0.5;
  }

  inline double invcdfBW(double p, double mu, double gamma) {
    const double xn = std::tan(M_PI*(p-0.5));
    return gamma*xn + mu;
  }





  inline vector<double> linspace(size_t nbins, double start, double end, bool include_end=true) {
    assert(end >= start);
    assert(nbins > 0);
    vector<double> rtn;
    const double interval = (end-start)/static_cast<double>(nbins);
    for (size_t i = 0; i < nbins; ++i) {
      rtn.push_back(start + i*interval);
    }
    assert(rtn.size() == nbins);
    if (include_end) rtn.push_back(end); //< exact end, not result of n * interval
    return rtn;
  }


  inline vector<double> aspace(double step, double start, double end, bool include_end=true, double tol=1e-2) {
    assert(end >= start);
    assert(step > 0);
    vector<double> rtn;
    double next = start;
    while (true) {
      if (next > end) break;
      rtn.push_back(next);
      next += step;
    }
    if (include_end) {
      if (end - rtn[rtn.size()-1] > tol*step) rtn.push_back(end);
    }
    return rtn;
  }


  inline vector<double> logspace(size_t nbins, double start, double end, bool include_end=true) {
    assert(end >= start);
    assert(start > 0);
    assert(nbins > 0);
    const double logstart = std::log(start);
    const double logend = std::log(end);
    const vector<double> logvals = linspace(nbins, logstart, logend, false);
    assert(logvals.size() == nbins);
    vector<double> rtn; rtn.reserve(nbins+1);
    rtn.push_back(start); //< exact start, not exp(log(start))
    for (size_t i = 1; i < logvals.size(); ++i) {
      rtn.push_back(std::exp(logvals[i]));
    }
    assert(rtn.size() == nbins);
    if (include_end) rtn.push_back(end); //< exact end, not exp(n * loginterval)
    return rtn;
  }




  inline vector<double> bwspace(size_t nbins, double start, double end, double mu, double gamma) {
    assert(end >= start);
    assert(nbins > 0);
    const double pmin = cdfBW(start, mu, gamma);
    const double pmax = cdfBW(end,   mu, gamma);
    const vector<double> edges = linspace(nbins, pmin, pmax);
    assert(edges.size() == nbins+1);
    vector<double> rtn;
    for (double edge : edges) {
      rtn.push_back(invcdfBW(edge, mu, gamma));
    }
    assert(rtn.size() == nbins+1);
    return rtn;
  }


  template <typename NUM, typename CONTAINER>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value && std::is_arithmetic<typename CONTAINER::value_type>::value, int>::type
  _binIndex(NUM val, const CONTAINER& binedges, bool allow_overflow=false) {
    if (val < *begin(binedges)) return -1; 
    // CONTAINER::iterator_type itend =
    if (val >= *(end(binedges)-1)) return allow_overflow ? int(binedges.size())-1 : -1; 
    auto it = std::upper_bound(begin(binedges), end(binedges), val);
    return std::distance(begin(binedges), --it);
  }

  template <typename NUM1, typename NUM2>
  inline typename std::enable_if<std::is_arithmetic<NUM1>::value && std::is_arithmetic<NUM2>::value, int>::type
  binIndex(NUM1 val, std::initializer_list<NUM2> binedges, bool allow_overflow=false) {
    return _binIndex(val, binedges, allow_overflow);
  }

  template <typename NUM, typename CONTAINER>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value && std::is_arithmetic<typename CONTAINER::value_type>::value, int>::type
  binIndex(NUM val, const CONTAINER& binedges, bool allow_overflow=false) {
    return _binIndex(val, binedges, allow_overflow);
  }





  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, NUM>::type
  median(const vector<NUM>& sample) {
    if (sample.empty()) throw RangeError("Can't compute median of an empty set");
    vector<NUM> tmp = sample;
    std::sort(tmp.begin(), tmp.end());
    const size_t imid = tmp.size()/2; // len1->idx0, len2->idx1, len3->idx1, len4->idx2, ...
    if (sample.size() % 2 == 0) return (tmp.at(imid-1) + tmp.at(imid)) / 2.0;
    else return tmp.at(imid);
  }


  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
  mean(const vector<NUM>& sample) {
    if (sample.empty()) throw RangeError("Can't compute mean of an empty set");
    double mean = 0.0;
    for (size_t i = 0; i < sample.size(); ++i) {
      mean += sample[i];
    }
    return mean/sample.size();
  }

  // Calculate the error on the mean, assuming Poissonian errors
  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
  mean_err(const vector<NUM>& sample) {
    if (sample.empty()) throw RangeError("Can't compute mean_err of an empty set");
    double mean_e = 0.0;
    for (size_t i = 0; i < sample.size(); ++i) {
      mean_e += sqrt(sample[i]);
    }
    return mean_e/sample.size();
  }


  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
  covariance(const vector<NUM>& sample1, const vector<NUM>& sample2) {
    if (sample1.empty() || sample2.empty()) throw RangeError("Can't compute covariance of an empty set");
    if (sample1.size() != sample2.size()) throw RangeError("Sizes of samples must be equal for covariance calculation");
    const double mean1 = mean(sample1);
    const double mean2 = mean(sample2);
    const size_t N = sample1.size();
    double cov = 0.0;
    for (size_t i = 0; i < N; i++) {
      const double cov_i = (sample1[i] - mean1)*(sample2[i] - mean2);
      cov += cov_i;
    }
    if (N > 1) return cov/(N-1);
    else return 0.0;
  }

  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
  covariance_err(const vector<NUM>& sample1, const vector<NUM>& sample2) {
    if (sample1.empty() || sample2.empty()) throw RangeError("Can't compute covariance_err of an empty set");
    if (sample1.size() != sample2.size()) throw RangeError("Sizes of samples must be equal for covariance_err calculation");
    const double mean1 = mean(sample1);
    const double mean2 = mean(sample2);
    const double mean1_e = mean_err(sample1);
    const double mean2_e = mean_err(sample2);
    const size_t N = sample1.size();
    double cov_e = 0.0;
    for (size_t i = 0; i < N; i++) {
      const double cov_i = (sqrt(sample1[i]) - mean1_e)*(sample2[i] - mean2) +
        (sample1[i] - mean1)*(sqrt(sample2[i]) - mean2_e);
      cov_e += cov_i;
    }
    if (N > 1) return cov_e/(N-1);
    else return 0.0;
  }


  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
  correlation(const vector<NUM>& sample1, const vector<NUM>& sample2) {
    const double cov = covariance(sample1, sample2);
    const double var1 = covariance(sample1, sample1);
    const double var2 = covariance(sample2, sample2);
    const double correlation = cov/sqrt(var1*var2);
    const double corr_strength = correlation*sqrt(var2/var1);
    return corr_strength;
  }

  template <typename NUM>
  inline typename std::enable_if<std::is_arithmetic<NUM>::value, double>::type
  correlation_err(const vector<NUM>& sample1, const vector<NUM>& sample2) {
    const double cov = covariance(sample1, sample2);
    const double var1 = covariance(sample1, sample1);
    const double var2 = covariance(sample2, sample2);
    const double cov_e = covariance_err(sample1, sample2);
    const double var1_e = covariance_err(sample1, sample1);
    const double var2_e = covariance_err(sample2, sample2);

    // Calculate the correlation
    const double correlation = cov/sqrt(var1*var2);
    // Calculate the error on the correlation
    const double correlation_err = cov_e/sqrt(var1*var2) -
      cov/(2*pow(3./2., var1*var2)) * (var1_e * var2 + var1 * var2_e);

    // Calculate the error on the correlation strength
    const double corr_strength_err = correlation_err*sqrt(var2/var1) +
      correlation/(2*sqrt(var2/var1)) * (var2_e/var1 - var2*var1_e/pow(2, var2));

    return corr_strength_err;
  }





  inline double _mapAngleM2PITo2Pi(double angle) {
    double rtn = fmod(angle, TWOPI);
    if (isZero(rtn)) return 0;
    assert(rtn >= -TWOPI && rtn <= TWOPI);
    return rtn;
  }

  inline double mapAngleMPiToPi(double angle) {
    double rtn = _mapAngleM2PITo2Pi(angle);
    if (isZero(rtn)) return 0;
    if (rtn > PI) rtn -= TWOPI;
    if (rtn <= -PI) rtn += TWOPI;
    assert(rtn > -PI && rtn <= PI);
    return rtn;
  }

  inline double mapAngle0To2Pi(double angle) {
    double rtn = _mapAngleM2PITo2Pi(angle);
    if (isZero(rtn)) return 0;
    if (rtn < 0) rtn += TWOPI;
    if (rtn == TWOPI) rtn = 0;
    assert(rtn >= 0 && rtn < TWOPI);
    return rtn;
  }

  inline double mapAngle0ToPi(double angle) {
    double rtn = fabs(mapAngleMPiToPi(angle));
    if (isZero(rtn)) return 0;
    assert(rtn > 0 && rtn <= PI);
    return rtn;
  }

  inline double mapAngle(double angle, PhiMapping mapping) {
    switch (mapping) {
    case MINUSPI_PLUSPI:
      return mapAngleMPiToPi(angle);
    case ZERO_2PI:
      return mapAngle0To2Pi(angle);
    case ZERO_PI:
      return mapAngle0To2Pi(angle);
    default:
      throw Rivet::UserError("The specified phi mapping scheme is not implemented");
    }
  }





  inline double deltaPhi(double phi1, double phi2, bool sign=false) {
    const double x = mapAngleMPiToPi(phi1 - phi2);
    return sign ? x : fabs(x);
  }

  inline double deltaEta(double eta1, double eta2, bool sign=false) {
    const double x = eta1 - eta2;
    return sign ? x : fabs(x);
  }

  inline double deltaRap(double y1, double y2, bool sign=false) {
    const double x = y1 - y2;
    return sign? x : fabs(x);
  }

  inline double deltaR2(double rap1, double phi1, double rap2, double phi2) {
    const double dphi = deltaPhi(phi1, phi2);
    return sqr(rap1-rap2) + sqr(dphi);
  }

  inline double deltaR(double rap1, double phi1, double rap2, double phi2) {
    return sqrt(deltaR2(rap1, phi1, rap2, phi2));
  }

  inline double rapidity(double E, double pz) {
    if (isZero(E - pz)) {
      throw std::runtime_error("Divergent positive rapidity");
      return DBL_MAX;
    }
    if (isZero(E + pz)) {
      throw std::runtime_error("Divergent negative rapidity");
      return -DBL_MAX;
    }
    return 0.5*log((E+pz)/(E-pz));
  }



  inline double mT(double pT1, double pT2, double dphi) {
    return sqrt(2*pT1*pT2 * (1 - cos(dphi)) );
  }


}


#endif

Updated on 2022-08-07 at 20:17:18 +0100