https://github.com/cran/scModels
Raw File
Tip revision: ba14d424c697de7a21f22f10407afbab9c5aaf7d authored by Lisa Amrhein on 24 January 2023, 07:20:02 UTC
version 1.0.4
Tip revision: ba14d42
pb.cpp
#include "shared.h"
#define N_TERMS(x) (x / 1000 + 1) * 2000
using namespace Rcpp;

// kummer fn: Taylor series method
double kummer_taylor(double x, double a, double b) {
  mpfr::mpreal aj = 1;
  mpfr::mpreal sj = aj;
  mpfr::mpreal tol = 1e-6, err = 1.0, j = 0.0;
  mpfr::mpreal aj1 = 0.0, sj1 = 0.0;
  mpfr::mpreal x_mp = mpfr::mpreal(x), a_mp = mpfr::mpreal(a), b_mp = mpfr::mpreal(b);
  while (err > tol) {
    aj1 = aj*(a+j)*x/((b+j)*(j+1));
    sj1 = sj + aj1;
    aj = aj1;
    sj = sj1;
    err = mpfr::abs(aj1);
    j = j+1;
  }
  return mpfr::log(sj).toDouble();
}


// Exponential transformation of the kummer fn for -ve x
// kummer_(x=-c, a=alpha+x, b=beta+alpha+x)
// since positive x is easier to compute
// M(a,b,x) = exp(x)M(b-a,b,-x)
double kummer_exp(double x, double a, double b) {
  return x + kummer_taylor(-x, b-a, b);
}


// kummer series using mpfr
// returns only logarithmic values
double kummer_(double x, double a, double b) {
  if(!validKummerParameters(a, b)) {
    return R_NaN;
  }
  if(x < 0) {
    return(kummer_exp(x, a, b));
  }
  else {
    return(kummer_taylor(x, a, b));
  }
}


// density function
double dpb_(double x, double alpha, double beta, double c, const bool& log_p, bool& throw_warning) {
  if( isInadmissible(x) || isInadmissible(alpha) || isInadmissible(beta) || isInadmissible(c) )
    return x+alpha+beta+c;

  if( !isInteger(x) || x < 0  || traits::is_infinite<REALSXP>(x) )
    return 0;

  if(!validPbParameters(alpha, beta, c)) {
    throw_warning = true;
    return R_NaN;
  }

  double cre = kummer_(-c, alpha+x, beta+alpha+x);
  double res;
  if(isInadmissible(cre))
    return R_NaN;

  if(x <= 0) {
    res = cre;
  } else {
    int sign = (x-1 < 0) ? -1 : 1;
    int x2 = (x-1 > 0) ? (int)std::floor(x-1) : (int)std::floor(1-x);
    double num = 0, denom = 0;
    for(int i=0; i <= x2; i++) {
      num += log((alpha + sign*i));
      denom += log(alpha + beta + sign * i);
    }
    num += x * log(c);
    denom += lgamma(x+1);
    res = num-denom+cre;
  }
  if(log_p)
    return res;
  else
    return exp(res);
}

// distribution function
double ppb_(double x, double alpha, double beta, double c, bool& throw_warning) {
  if( isInadmissible(x) || isInadmissible(alpha) || isInadmissible(beta) || isInadmissible(c) )
    return x+alpha+beta+c;

  if(!validPbParameters(alpha, beta, c)) {
    throw_warning = true;
    return R_NaN;
  }

  if( !isInteger(x) )
    return 0;
  if(traits::is_infinite<REALSXP>(x))
    return 1;
  double res = 0;
  for(int i = 0; i <= x; i++) {
    res += dpb_(i, alpha, beta, c, false, throw_warning);
  }
  return res;
}

// distribution function array
double* ppb_(double alpha, double beta, double c) {
  double *res = (double *)std::malloc(Q_LIMIT * sizeof(double));
  bool throw_warning = false;
  res[0] = dpb_(0, alpha, beta, c, false, throw_warning);
  for(int i = 1; i < Q_LIMIT; i++) {
    res[i] = res[i-1] + dpb_(i, alpha, beta, c, false, throw_warning);
  }
  return res;
}

// quantiles for single parameters
double qpb_(double p, double *p_distr) {
  if(isInadmissible(p))
    return NA_REAL;
  if(!validProbability(p) || isInadmissible(p_distr[0])){
    warning("NaNs produced");
    return R_NaN;
  }

  if(p == 0.0)
    return 0.0;
  if(p == 1.0 || p > p_distr[Q_LIMIT-1])
    return R_PosInf;

  for(int i = 1; i < Q_LIMIT; i++) {
    if(p > p_distr[i-1] && p < p_distr[i]) {
      return i;
    }
  }

  return R_PosInf;
}

// quantiles for vectorised parameters
double qpb_(double p, double alpha, double beta, double c) {
  if(isInadmissible(p) || isInadmissible(alpha) || isInadmissible(beta) || isInadmissible(c))
    return NA_REAL;
  if(!validProbability(p)){
    warning("NaNs produced");
    return R_NaN;
  }

  if(p == 0.0)
    return 0.0;

  double *p_distr = ppb_(alpha, beta, c);

  if(p == 1.0 || p > p_distr[Q_LIMIT-1])
    return R_PosInf;

  for(int i = 1; i < Q_LIMIT; i++) {
    if(p > p_distr[i-1] && p < p_distr[i]) {
      return i;
    }
  }

  return R_PosInf;
}

// random number generator
double rpb_(double alpha, double beta, double c, bool& throw_warning) {
  if(isInadmissible(alpha) || isInadmissible(beta) || isInadmissible(c)) {
    throw_warning = true;
    return NA_REAL;
  }

  if(!validPbParameters(alpha, beta, c)) {
    throw_warning = true;
    return R_NaN;
  }

  NumericVector poissonParameter = rbeta(1, alpha, beta) * c;
  NumericVector t = rpois(1, poissonParameter[0]);

  return t[0];
}

//' Kummer's (confluent hypergeometric) function in log-scale
//'
//' Kummer's function (also: confluent hypergeometric function of the first kind)
//' for numeric (non-complex) values and input parameters in log-scale.
//' @param x numeric value or vector
//' @param a,b numeric parameters of the Kummer function
//' @name chf_1F1
//' @rdname chf_1F1
//' @export
//' @details Note that the output is in log-scale. So the evaluated function is:
//' \deqn{\log \left[\sum_{n=0}^\infty \frac{a^{(n)} x^n}{ b^(n) n!}\right]}{log [ \sum from n to \infty (a^(n) x^n)/ (b^(n) n!)]}
//' where \eqn{a^{(n)}}{a^(n)} and \eqn{b^{(n)}}{b^(n)} describe the rising factorial.
//' @examples
//' x <- chf_1F1(-100:100, 5, 7)
//' plot(-100:100, x, type='l')
// [[Rcpp::export]]
NumericVector chf_1F1(NumericVector x, NumericVector a, NumericVector b) {
    if(min(NumericVector::create(x.length(), a.length(), b.length())) < 1) {
      return NumericVector(0);
    }
    int n = max(NumericVector::create(x.length(), a.length(), b.length()));
    NumericVector res(n);
    for(int i = 0; i < n; i++) {
      res[i] = kummer_(GETV(x, i), GETV(a, i), GETV(b, i));
    }
    return res;
}


// [[Rcpp::export]]
NumericVector cpp_dpb(NumericVector& x, NumericVector& alpha, NumericVector& beta, NumericVector& c, const bool& log_p = false) {
  if(std::min({x.length(), alpha.length(), beta.length(), c.length()}) < 1) {
    return NumericVector(0);
  }

  int n = std::max({x.length(), alpha.length(), beta.length(), c.length()});
  NumericVector p(n);
  bool throw_warning = false;

  for(int i = 0; i < n; i++) {
    p[i] = dpb_(GETV(x, i), GETV(alpha, i), GETV(beta, i), GETV(c, i), log_p, throw_warning);
  }

  if(throw_warning)
    warning("NaNs produced");

  return p;
}



//[[Rcpp::export]]
NumericVector cpp_ppb(NumericVector& q, NumericVector& alpha, NumericVector& beta, NumericVector& c, const bool& lower_tail, const bool& log_p) {
  if(std::min({ q.length(), alpha.length(), beta.length(), c.length() }) < 1) {
    return NumericVector(0);
  }

  int n = std::max({ q.length(), alpha.length(), beta.length(), c.length() });
  NumericVector p(n);

  bool throw_warning = false;

  for(int i = 0; i < n; i++) {
    p[i] = ppb_(GETV(q, i), GETV(alpha, i), GETV(beta, i), GETV(c, i), throw_warning);
  }

  if(!lower_tail)
    p = 1.0 - p;

  if(log_p)
    p = log(p);

  if(throw_warning)
    warning("NaNs produced");

  return p;
}


// [[Rcpp::export]]
NumericVector cpp_rpb(const int& n, NumericVector& alpha, NumericVector& beta, NumericVector& c) {
  if(std::min({ alpha.length(), beta.length(), c.length() }) < 1) {
    warning("NAs produced");
    return NumericVector(n, NA_REAL);
  }

  NumericVector x(n);
  bool throw_warning = false;

  for(int i = 0; i < n; i++) {
    x[i] = rpb_(GETV(alpha, i), GETV(beta, i), GETV(c, i), throw_warning);
  }

  if(throw_warning)
    warning("NAs produced");

  return x;
}


// [[Rcpp::export]]
NumericVector cpp_qpb(NumericVector& p, NumericVector& alpha, NumericVector& beta, NumericVector& c, const bool& lower_tail, const bool& log_p) {
  if(std::min({ p.length(), alpha.length(), beta.length(), c.length() }) < 1) {
    return NumericVector(0);
  }

  int n = std::max({ p.length(), alpha.length(), beta.length(), c.length()});
  NumericVector res(n);

  if(log_p)
    p = exp(p);

  if(lower_tail)
    p = 1.0 - p;

  if (min(alpha) == max(alpha) && min(beta) == max(beta) && min(c) == max(c)) {
    // single parameters
    // optmized to compute cdf only once
    if(isInadmissible(alpha[0]) || isInadmissible(beta[0]) || isInadmissible(c[0])) {
      return NumericVector(n, NA_REAL);
    } else {
      double* p_distr = ppb_(min(na_omit(alpha)), min(na_omit(beta)), min(na_omit(c)));
      for(int i = 0; i < n; i++) {
        res[i] = qpb_(GETV(p, i), p_distr);
      }
    }
  } else {
    // vectorised parameters
    for(int i = 0; i < n; i++) {
      res[i] = qpb_(GETV(p, i), GETV(alpha, i), GETV(beta, i), GETV(c, i));
    }
  }
  return res;
}
back to top