def ibincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning an integer (possibly long). For integer=True integer arithmetics is used throughout and there is no risk of overflow. For integer=False a floating- point gamma function approximation is used and the result converted to integer at the end (if an overflow occurs ERRCODE is returned). """ assert is_posinteger(n), \ "n in n_over_k in ibincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in ibincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in ibincoeff!" if integer: ibico = _bicolongint(n, k) else: try: lnbico = lngamma(n + 1) - lngamma(k + 1) - lngamma(n - k + 1) ibico = safeint(round(exp(lnbico)), 'ibincoeff') except OverflowError: ibico = ERRCODE return ibico
def fbincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning a float (an OverflowError returns float('inf'), which would occur for n > 1029 for IEEE754 floating-point standard). """ assert is_posinteger(n), \ "n in n_over_k in fbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in fbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in fbincoeff!" if integer: bico = _bicolongint(n, k) try: fbico = round(float(bico)) except OverflowError: fbico = float('inf') else: try: lnbico = lngamma(n + 1) - lngamma(k + 1) - lngamma(n - k + 1) fbico = round(exp(lnbico)) except OverflowError: fbico = float('inf') return fbico
def ibincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning an integer (possibly long). For integer=True integer arithmetics is used throughout and there is no risk of overflow. For integer=False a floating- point gamma function approximation is used and the result converted to integer at the end (if an overflow occurs ERRCODE is returned). """ assert is_posinteger(n), \ "n in n_over_k in ibincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in ibincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in ibincoeff!" if integer: ibico = _bicolongint(n, k) else: try: lnbico = lngamma(n+1) - lngamma(k+1) - lngamma(n-k+1) ibico = safeint(round(exp(lnbico)), 'ibincoeff') except OverflowError: ibico = ERRCODE return ibico
def fbincoeff(n, k, integer=True): """ Computation of a single binomial coefficient n over k, returning a float (an OverflowError returns float('inf'), which would occur for n > 1029 for IEEE754 floating-point standard). """ assert is_posinteger(n), \ "n in n_over_k in fbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in fbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in fbincoeff!" if integer: bico = _bicolongint(n, k) try: fbico = round(float(bico)) except OverflowError: fbico = float('inf') else: try: lnbico = lngamma(n+1) - lngamma(k+1) - lngamma(n-k+1) fbico = round(exp(lnbico)) except OverflowError: fbico = float('inf') return fbico
def dexppower(loc, scale, alpha, x, lngam1oalpha=False): """ The exponential power distribution f = (a/s) * exp(-abs([x-l]/s)**a) / [2*gamma(1/a)] F = 1/2 * [1 + sgn(x-l) * Fgamma(1/a, abs([x-l]/s)**a)], x in R s, a > 0 where Fgamma is the gamma distribution cdf. NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(1.0/alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. """ assert scale > 0.0, \ "scale parameter must be a positive float in dexppower!" assert alpha > 0.0, \ "shape parameter alpha must be a positive float in dexppower!" if alpha == 1.0: return dlaplace(loc, scale, x) sinv = 1.0/float(scale) aux1 = - (sinv*abs(x-loc)) ** alpha if not lngam1oalpha: aux2 = lngamma(1.0/float(alpha)) else: aux2 = lngam1oalpha pdf = 0.5*sinv*alpha * exp(aux1-aux2) # Will always be >= 0 return pdf
def cexppower(loc, scale, alpha, x, lngam1oalpha=False, \ tolf=FOURMACHEPS, itmax=128): """ The exponential power distribution f = (a/s) * exp(-abs([x-l]/s)**a) / [2*gamma(1/a)] F = 1/2 * [1 + sgn(x-l) * Fgamma(1/a, abs([x-l]/s)**a)], x in R s, a > 0 where Fgamma is the gamma distribution cdf. NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(1.0/alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. tolf and itmax are the numerical control parameters of cgamma. """ assert scale > 0.0, \ "scale parameter must be a positive float in cexppower!" assert alpha > 0.0, \ "shape parameter alpha must be a positive float in cexppower!" if alpha == 1.0: return claplace(loc, scale, x) ainv = 1.0 / alpha xml = x - loc if not lngam1oalpha: lng1oa = lngamma(ainv) else: lng1oa = lngam1oalpha cg = cgamma(ainv, 1.0, abs(xml / scale)**alpha, lng1oa, tolf, itmax) cdf = 0.5 * (fsign(xml) * cg + 1.0) cdf = kept_within(0.0, cdf, 1.0) return cdf
def cexppower(loc, scale, alpha, x, lngam1oalpha=False, tolf=FOURMACHEPS, itmax=128): """ The exponential power distribution f = (a/s) * exp(-abs([x-l]/s)**a) / [2*gamma(1/a)] F = 1/2 * [1 + sgn(x-l) * Fgamma(1/a, abs([x-l]/s)**a)], x in R s, a > 0 where Fgamma is the gamma distribution cdf. NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(1.0/alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. tolf and itmax are the numerical control parameters of cgamma. """ assert scale > 0.0, "scale parameter must be a positive float in cexppower!" assert alpha > 0.0, "shape parameter alpha must be a positive float in cexppower!" if alpha == 1.0: return claplace(loc, scale, x) ainv = 1.0 / alpha xml = x - loc if not lngam1oalpha: lng1oa = lngamma(ainv) else: lng1oa = lngam1oalpha cg = cgamma(ainv, 1.0, abs(xml / scale) ** alpha, lng1oa, tolf, itmax) cdf = 0.5 * (fsign(xml) * cg + 1.0) cdf = kept_within(0.0, cdf, 1.0) return cdf
def _stable_sym_tail(alpha, x): """ An asymptotic expression for the tail. """ # calpha = exp(lngamma(alpha)) * sin(PIHALF*alpha) / PI calpha = PIINV * exp(lngamma(alpha)) * sin(PIHALF * alpha) try: cdf = calpha / x ** alpha except ZeroDivisionError: cdf = log(calpha) - alpha * log(x) try: cdf = exp(cdf) except OverflowError: cdf = 0.0 except OverflowError: cdf = log(calpha) - alpha * log(x) try: cdf = exp(cdf) except OverflowError: cdf = 0.0 cdf = 1.0 - cdf cdf = kept_within(0.5, cdf, 1.0) return cdf
def dexppower(loc, scale, alpha, x, lngam1oalpha=False): """ The exponential power distribution f = (a/s) * exp(-abs([x-l]/s)**a) / [2*gamma(1/a)] F = 1/2 * [1 + sgn(x-l) * Fgamma(1/a, abs([x-l]/s)**a)], x in R s, a > 0 where Fgamma is the gamma distribution cdf. NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(1.0/alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. """ assert scale > 0.0, \ "scale parameter must be a positive float in dexppower!" assert alpha > 0.0, \ "shape parameter alpha must be a positive float in dexppower!" if alpha == 1.0: return dlaplace(loc, scale, x) sinv = 1.0 / float(scale) aux1 = -(sinv * abs(x - loc))**alpha if not lngam1oalpha: aux2 = lngamma(1.0 / float(alpha)) else: aux2 = lngam1oalpha pdf = 0.5 * sinv * alpha * exp(aux1 - aux2) # Will always be >= 0 return pdf
def _stable_sym_tail(alpha, x): """ An asymptotic expression for the tail. """ #calpha = exp(lngamma(alpha)) * sin(PIHALF*alpha) / PI calpha = PIINV * exp(lngamma(alpha)) * sin(PIHALF * alpha) try: cdf = calpha / x**alpha except ZeroDivisionError: cdf = log(calpha) - alpha * log(x) try: cdf = exp(cdf) except OverflowError: cdf = 0.0 except OverflowError: cdf = log(calpha) - alpha * log(x) try: cdf = exp(cdf) except OverflowError: cdf = 0.0 cdf = 1.0 - cdf cdf = kept_within(0.5, cdf, 1.0) return cdf
def lnbincoeff(n, k, integer=True): """ Computation of the natural logarithm of a single binomial coefficient n over k """ assert is_posinteger(n), \ "n in n_over_k in lnbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in lnbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in lnbincoeff!" if integer: bico = _bicolongint(n, k) lnbico = log(bico) else: lnbico = lngamma(n+1) - lngamma(k+1) - lngamma(n-k+1) return lnbico
def lnbincoeff(n, k, integer=True): """ Computation of the natural logarithm of a single binomial coefficient n over k """ assert is_posinteger(n), \ "n in n_over_k in lnbincoeff must be a positive integer!" assert is_nonneginteger(k), \ "k in n_over_k in lnbincoeff must be a non-negative integer!" assert n >= k, "n must be >= k in n_over_k in lnbincoeff!" if integer: bico = _bicolongint(n, k) lnbico = log(bico) else: lnbico = lngamma(n + 1) - lngamma(k + 1) - lngamma(n - k + 1) return lnbico
def _dstable_sym_small(alpha, x, tolr): """ A series expansion for small x due to Bergstrom. Converges for x < 1.0 and in practice also for somewhat larger x. The function uses the Kahan summation procedure (cf. Dahlquist, Bjorck & Anderson). """ summ = 0.0 c = 0.0 fact = -1.0 xx = x*x xpart = 1.0 k = 0 zero2 = zero1 = False while True: k += 1 summo = summ twokm1 = 2*k - 1 twokm1oa = float(twokm1)/alpha r = lngamma(twokm1oa) - lnfactorial(twokm1) term = twokm1 * exp(r) * xpart fact = - fact term *= fact y = term + c t = summ + y if fsign(y) == fsign(summ): f = (0.46*t-t) + t c = ((summ-f)-(t-f)) + y else: c = (summ-t) + y summ = t if abs(summ-summo) < tolr*abs(summ) and abs(term) < tolr and zero2: break xpart *= xx if abs(term) < tolr: if zero1: zero2 = True else: zero1 = True summ += c pdf = summ / (PI*alpha) pdf = kept_within(0.0, pdf) return pdf
def _dstable_sym_small(alpha, x, tolr): """ A series expansion for small x due to Bergstrom. Converges for x < 1.0 and in practice also for somewhat larger x. The function uses the Kahan summation procedure (cf. Dahlquist, Bjorck & Anderson). """ summ = 0.0 c = 0.0 fact = -1.0 xx = x * x xpart = 1.0 k = 0 zero2 = zero1 = False while True: k += 1 summo = summ twokm1 = 2 * k - 1 twokm1oa = float(twokm1) / alpha r = lngamma(twokm1oa) - lnfactorial(twokm1) term = twokm1 * exp(r) * xpart fact = -fact term *= fact y = term + c t = summ + y if fsign(y) == fsign(summ): f = (0.46 * t - t) + t c = ((summ - f) - (t - f)) + y else: c = (summ - t) + y summ = t if abs(summ - summo) < tolr * abs(summ) and abs(term) < tolr and zero2: break xpart *= xx if abs(term) < tolr: if zero1: zero2 = True else: zero1 = True summ += c pdf = summ / (PI * alpha) pdf = kept_within(0.0, pdf) return pdf
def dpoisson(lam, tspan, n): """ The Poisson distribution: p(N=n) = exp(-lam*tspan) * (lam*tspan)**n / n! n = 0, 1,...., inf """ # Input check ----------- assert lam >= 0.0, "Poisson rate must not be negative in dpoisson!" assert tspan >= 0.0, "time span must not be negative in dpoisson!" assert is_nonneginteger(n), \ " must be a non-negative integer in dpoisson!" # ----------------------- lamtau = lam*tspan ln = lamtau + lngamma(n+1) - n*log(lamtau) ln = kept_within(0.0, ln) pdf = exp(-ln) # Will always be >= 0.0 return pdf
def dpoisson(lam, tspan, n): """ The Poisson distribution: p(N=n) = exp(-lam*tspan) * (lam*tspan)**n / n! n = 0, 1,...., inf """ # Input check ----------- assert lam >= 0.0, "Poisson rate must not be negative in dpoisson!" assert tspan >= 0.0, "time span must not be negative in dpoisson!" assert is_nonneginteger(n), \ " must be a non-negative integer in dpoisson!" # ----------------------- lamtau = lam * tspan ln = lamtau + lngamma(n + 1) - n * log(lamtau) ln = kept_within(0.0, ln) pdf = exp(-ln) # Will always be >= 0.0 return pdf
def _dstable_sym_big(alpha, x, tolr): """ A series expansion for large x due to Bergstrom. Converges for x > 1.0 The function uses the Kahan summation procedure (cf. Dahlquist, Bjorck & Anderson). """ summ = 0.0 c = 0.0 fact = 1.0 k = 0 zero2 = zero1 = False while True: k += 1 summo = summ ak = alpha * k akh = 0.5 * ak r = lngamma(ak) - lnfactorial(k) term = -ak * exp(r) * sin(PIHALF * ak) / pow(x, ak + 1) fact = -fact term *= fact y = term + c t = summ + y if fsign(y) == fsign(summ): f = (0.46 * t - t) + t c = ((summ - f) - (t - f)) + y else: c = (summ - t) + y summ = t if abs(summ - summo) < tolr * abs(summ) and abs(term) < tolr and zero2: break if abs(term) < tolr: if zero1: zero2 = True else: zero1 = True summ += c #pdf = summ / PI pdf = PIINV * summ pdf = kept_within(0.0, pdf) return pdf
def _dstable_sym_big(alpha, x, tolr): """ A series expansion for large x due to Bergstrom. Converges for x > 1.0 The function uses the Kahan summation procedure (cf. Dahlquist, Bjorck & Anderson). """ summ = 0.0 c = 0.0 fact = 1.0 k = 0 zero2 = zero1 = False while True: k += 1 summo = summ ak = alpha * k akh = 0.5 * ak r = lngamma(ak) - lnfactorial(k) term = - ak * exp(r) * sin(PIHALF*ak) / pow(x, ak+1) fact = - fact term *= fact y = term + c t = summ + y if fsign(y) == fsign(summ): f = (0.46*t-t) + t c = ((summ-f)-(t-f)) + y else: c = (summ-t) + y summ = t if abs(summ-summo) < tolr*abs(summ) and abs(term) < tolr and zero2: break if abs(term) < tolr: if zero1: zero2 = True else: zero1 = True summ += c #pdf = summ / PI pdf = PIINV * summ pdf = kept_within(0.0, pdf) return pdf
def dgamma(alpha, lam, x, lngamalpha=False): """ The gamma distrib. f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) F is the integral = the incomplete gamma or the incomplete gamma / complete gamma depending on how the incomplete gamma function is defined. x, lam, alpha >= 0 NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. NB dgamma may return float('inf') for alpha < 1.0! """ assert alpha >= 0.0, "alpha must not be negative in dgamma!" assert lam >= 0.0, "lambda must not be negative i dgamma!" assert x >= 0.0, "variate must not be negative in dgamma!" lamx = lam * x if lngamalpha: lga = lngamalpha else: lga = lngamma(alpha) if alpha < 1.0: if lamx == 0.0: pdf = float('inf') else: alpham1 = alpha - 1.0 try: pdf = lam * exp(-lamx - lga) / pow(lamx, -alpham1) except OverflowError: pdf = lam * exp(-lamx + alpham1 * log(lamx) - lga) else: alpham1 = alpha - 1.0 try: pdf = lam * exp(-lamx - lga) * pow(lamx, alpham1) except OverflowError: pdf = lam * exp(-lamx + alpham1 * log(lamx) - lga) return pdf # Will always be >= 0.0
def dgamma(alpha, lam, x, lngamalpha=False): """ The gamma distrib. f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) F is the integral = the incomplete gamma or the incomplete gamma / complete gamma depending on how the incomplete gamma function is defined. x, lam, alpha >= 0 NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. NB dgamma may return float('inf') for alpha < 1.0! """ assert alpha >= 0.0, "alpha must not be negative in dgamma!" assert lam >= 0.0, "lambda must not be negative i dgamma!" assert x >= 0.0, "variate must not be negative in dgamma!" lamx = lam * x if lngamalpha: lga = lngamalpha else: lga = lngamma(alpha) if alpha < 1.0: if lamx == 0.0: pdf = float('inf') else: alpham1 = alpha - 1.0 try: pdf = lam * exp(-lamx-lga) / pow(lamx, -alpham1) except OverflowError: pdf = lam * exp(-lamx + alpham1*log(lamx) - lga) else: alpham1 = alpha - 1.0 try: pdf = lam * exp(-lamx-lga) * pow(lamx, alpham1) except OverflowError: pdf = lam * exp(-lamx + alpham1*log(lamx) - lga) return pdf # Will always be >= 0.0
def cgamma(alpha, lam, x, lngamalpha=False, tolf=FOURMACHEPS, itmax=128): """ The gamma distrib. f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) F is the integral = the incomplete gamma or the incomplete gamma / complete gamma depending on how the incomplete gamma function is defined. x, lam, alpha >= 0 tolf = allowed fractional error in computation of the incomplete function itmax = maximum number of iterations to obtain accuracy NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. """ assert alpha >= 0.0, "alpha must not be negative in cgamma!" assert lam >= 0.0, "lambda must not be negative i cgamma!" assert x >= 0.0, "variate must not be negative in cgamma!" assert tolf >= 0.0, "tolerance must not be negative in cgamma!" assert is_posinteger(itmax), \ "maximum number of iterations must be a positive integer in cgamma!" if alpha == 1.0: return cexpo(1.0 / lam, x) lamx = lam * x if lamx == 0.0: return 0.0 if lngamalpha: lnga = lngamalpha else: lnga = lngamma(alpha) # ------------------------------------------------------------------------- def _gamser(): # A series expansion is used for lamx < alpha + 1.0 # (cf. Abramowitz & Stegun) apn = alpha summ = 1.0 / apn dela = summ converged = False for k in range(0, itmax): apn += 1.0 dela = dela * lamx / apn summ += dela if abs(dela) < abs(summ) * tolf: converged = True return summ * exp(-lamx + alpha * log(lamx) - lnga), converged return summ * exp(-lamx + alpha * log(lamx) - lnga), converged # ------------------------------------------------------------------------- def _gamcf(): # A continued fraction expansion is used for # lamx >= alpha + 1.0 (cf. Abramowitz & Stegun): gold = 0.0 a0 = 1.0 a1 = lamx b0 = 0.0 b1 = 1.0 fac = 1.0 converged = False for k in range(0, itmax): ak = float(k + 1) aka = ak - alpha a0 = (a1 + a0 * aka) * fac b0 = (b1 + b0 * aka) * fac akf = ak * fac a1 = lamx * a0 + akf * a1 b1 = lamx * b0 + akf * b1 if a1 != 0.0: fac = 1.0 / a1 g = b1 * fac if abs(g - gold) < abs(g) * tolf: converged = True return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged gold = g return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged # ------------------------------------------------------------------------- if lamx < alpha + 1.0: cdf, converged = _gamser() else: cdf, converged = _gamcf() if not converged: warn("cgamma has not converged for itmax = " + \ str(itmax) + " and tolf = " + str(tolf)) cdf = kept_within(0.0, cdf, 1.0) return cdf
def cgamma(alpha, lam, x, lngamalpha=False, tolf=FOURMACHEPS, itmax=128): """ The gamma distrib. f = lam * exp(-lam*x) * (lam*x)**(alpha-1) / gamma(alpha) F is the integral = the incomplete gamma or the incomplete gamma / complete gamma depending on how the incomplete gamma function is defined. x, lam, alpha >= 0 tolf = allowed fractional error in computation of the incomplete function itmax = maximum number of iterations to obtain accuracy NB It is possible to gain efficiency by providing the value of the natural logarithm of the complete gamma function ln(gamma(alpha)) as a pre-computed input (may be computed using numlib.specfunc.lngamma) instead of the default 'False'. """ assert alpha >= 0.0, "alpha must not be negative in cgamma!" assert lam >= 0.0, "lambda must not be negative i cgamma!" assert x >= 0.0, "variate must not be negative in cgamma!" assert tolf >= 0.0, "tolerance must not be negative in cgamma!" assert is_posinteger(itmax), "maximum number of iterations must be a positive integer in cgamma!" if alpha == 1.0: return cexpo(1.0 / lam, x) lamx = lam * x if lamx == 0.0: return 0.0 if lngamalpha: lnga = lngamalpha else: lnga = lngamma(alpha) # ------------------------------------------------------------------------- def _gamser(): # A series expansion is used for lamx < alpha + 1.0 # (cf. Abramowitz & Stegun) apn = alpha summ = 1.0 / apn dela = summ converged = False for k in range(0, itmax): apn += 1.0 dela = dela * lamx / apn summ += dela if abs(dela) < abs(summ) * tolf: converged = True return summ * exp(-lamx + alpha * log(lamx) - lnga), converged return summ * exp(-lamx + alpha * log(lamx) - lnga), converged # ------------------------------------------------------------------------- def _gamcf(): # A continued fraction expansion is used for # lamx >= alpha + 1.0 (cf. Abramowitz & Stegun): gold = 0.0 a0 = 1.0 a1 = lamx b0 = 0.0 b1 = 1.0 fac = 1.0 converged = False for k in range(0, itmax): ak = float(k + 1) aka = ak - alpha a0 = (a1 + a0 * aka) * fac b0 = (b1 + b0 * aka) * fac akf = ak * fac a1 = lamx * a0 + akf * a1 b1 = lamx * b0 + akf * b1 if a1 != 0.0: fac = 1.0 / a1 g = b1 * fac if abs(g - gold) < abs(g) * tolf: converged = True return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged gold = g return 1.0 - exp(-lamx + alpha * log(lamx) - lnga) * g, converged # ------------------------------------------------------------------------- if lamx < alpha + 1.0: cdf, converged = _gamser() else: cdf, converged = _gamcf() if not converged: warn("cgamma has not converged for itmax = " + str(itmax) + " and tolf = " + str(tolf)) cdf = kept_within(0.0, cdf, 1.0) return cdf
def dstable_sym(alpha, location, scale, x): """ The pdf of a SYMMETRICAL stable distribution where alpha is the tail exponent. For numerical reasons alpha is restricted to [0.1, 0.9] and [1.125, 1.9] - but alpha = 1.0 (the Cauchy) and alpha = 2.0 (scaled normal) are also allowed! Numerics are somewhat crude but the fractional error is mostly < 0.001 - sometimes much less - and the absolute error is almost always < 0.001 - sometimes much less... NB This function is somewhat slow, particularly for small alpha !!!!! """ # NB Do not change the numerical parameters - they are matched! The # corresponding cdf function cstable_sym is partly based on this # function so changes in one of them are likely to require changes # in the others! assert 0.1 <= alpha and alpha <= 2.0, \ "alpha must be in [0.1, 2.0] in dstable_sym!" if alpha < 1.0: assert alpha <= 0.9, \ "alpha <= 1.0 must be <= 0.9 in dstable_sym!" if alpha > 1.0: assert alpha >= 1.125, \ "alpha > 1.0 must be >= 1.125 in dstable_sym!" if alpha > 1.9: assert alpha == 2.0, \ "alpha > 1.9 must be = 2.0 in dstable_sym!" assert scale > 0.0, "scale must be a positive float in dstable_sym!" if alpha == 1.0: return dcauchy(location, scale, x) if alpha == 2.0: return dnormal(location, SQRT2 * scale, x) x = (x - location) / float(scale) x = abs(x) # Compute the value at the peak/mode (for x = 0) for later use: #peak = exp(lngamma(1.0+1.0/alpha)) / PI peak = PIINV * exp(lngamma(1.0 + 1.0 / alpha)) if alpha < 1.0: # For sufficiently small abs(x) the value at the peak is used # (heuristically; based on experimentation). For x >= 1.0 the # series expansion for large x due to Bergstrom is used. For x # "in between" the integral formulation is used: if alpha <= 0.25: point = 0.5**37 else: point = 1.25 * pow(10.0, -4.449612602 + 3.078368893 * alpha) if x <= point: pdf = peak elif x >= 1.0: pdf = _dstable_sym_big(alpha, x, MACHEPS) else: pdf = _dstable_sym_int(alpha, x, 0.5**17, 17) elif alpha > 1.0: # For sufficiently small abs(x) the series expansion for small x due # to Bergstrom is used. For x sufficiently large x an asymptotic # expression is used. For x "in between" the integral formulation # is used (all limits heuristically based): y1 = -2.212502985 + alpha * (3.03077875081 - alpha * 0.742811132) if x <= pow(10.0, y1): pdf = _dstable_sym_small(alpha, x, MACHEPS) else: pdf = _dstable_sym_int(alpha, x, 0.5**19, 21) pdf = kept_within(0.0, pdf, peak) return pdf
def dstable_sym(alpha, location, scale, x): """ The pdf of a SYMMETRICAL stable distribution where alpha is the tail exponent. For numerical reasons alpha is restricted to [0.1, 0.9] and [1.125, 1.9] - but alpha = 1.0 (the Cauchy) and alpha = 2.0 (scaled normal) are also allowed! Numerics are somewhat crude but the fractional error is mostly < 0.001 - sometimes much less - and the absolute error is almost always < 0.001 - sometimes much less... NB This function is somewhat slow, particularly for small alpha !!!!! """ # NB Do not change the numerical parameters - they are matched! The # corresponding cdf function cstable_sym is partly based on this # function so changes in one of them are likely to require changes # in the others! assert 0.1 <= alpha and alpha <= 2.0, \ "alpha must be in [0.1, 2.0] in dstable_sym!" if alpha < 1.0: assert alpha <= 0.9, \ "alpha <= 1.0 must be <= 0.9 in dstable_sym!" if alpha > 1.0: assert alpha >= 1.125, \ "alpha > 1.0 must be >= 1.125 in dstable_sym!" if alpha > 1.9: assert alpha == 2.0, \ "alpha > 1.9 must be = 2.0 in dstable_sym!" assert scale > 0.0, "scale must be a positive float in dstable_sym!" if alpha == 1.0: return dcauchy(location, scale, x) if alpha == 2.0: return dnormal(location, SQRT2*scale, x) x = (x-location) / float(scale) x = abs(x) # Compute the value at the peak/mode (for x = 0) for later use: #peak = exp(lngamma(1.0+1.0/alpha)) / PI peak = PIINV * exp(lngamma(1.0+1.0/alpha)) if alpha < 1.0: # For sufficiently small abs(x) the value at the peak is used # (heuristically; based on experimentation). For x >= 1.0 the # series expansion for large x due to Bergstrom is used. For x # "in between" the integral formulation is used: if alpha <= 0.25: point = 0.5**37 else: point = 1.25 * pow(10.0, -4.449612602 + 3.078368893*alpha) if x <= point: pdf = peak elif x >= 1.0: pdf = _dstable_sym_big(alpha, x, MACHEPS) else: pdf = _dstable_sym_int(alpha, x, 0.5**17, 17) elif alpha > 1.0: # For sufficiently small abs(x) the series expansion for small x due # to Bergstrom is used. For x sufficiently large x an asymptotic # expression is used. For x "in between" the integral formulation # is used (all limits heuristically based): y1 = -2.212502985 + alpha*(3.03077875081 - alpha*0.742811132) if x <= pow(10.0, y1): pdf = _dstable_sym_small(alpha, x, MACHEPS) else: pdf = _dstable_sym_int(alpha, x, 0.5**19, 21) pdf = kept_within(0.0, pdf, peak) return pdf