Where are the math functions?
Tom Loredo
loredo at spacenet.tn.cornell.edu
Thu Jun 15 15:54:16 EDT 2000
Howdy-
Below are some pure Python special functions, some adapted from
Numerical Recipes, some from formulas in standard references. They are
mostly related to the Gamma function. This
is old stuff, practically my first Python, so don't expect much! 8-)
I didn't know about cephes at the time, and mostly use cephes now.
But these have the virtue of being pure python, so they may be helpful
to you if you can't compile cephes on your platform.
I've done some other Python translations of NR routines, soon
to appear on a web page. Others are already on the web:
http://www.python.org/topics/scicomp/recipes_in_python.html
Peace,
Tom loredo
PS: One of the Numerical Recipes coauthors is in the office next
door, and knows I've been up to this stuff. When I first told him
I'd been translating some NR routines into Python, his response
was, "Why?!" 8-)
##### sp_funcs.py --- Not too thoroughly tested as yet!
from math import *
import Numeric
N = Numeric
# Exceptions:
max_iters = 'Too many iterations: '
#============= Globals ===============
rt2 = sqrt(2.)
gammln_cof = N.array([76.18009173, -86.50532033, 24.01409822,
-1.231739516e0, 0.120858003e-2, -0.536382e-5])
gammln_stp = 2.50662827465
#============= Gamma, Incomplete Gamma ===========
def gammln(xx):
"""Logarithm of the gamma function."""
global gammln_cof, gammln_stp
x = xx - 1.
tmp = x + 5.5
tmp = (x + 0.5)*log(tmp) - tmp
ser = 1.
for j in range(6):
x = x + 1.
ser = ser + gammln_cof[j]/x
return tmp + log(gammln_stp*ser)
def gser(a, x, itmax=700, eps=3.e-7):
"""Series approx'n to the incomplete gamma function."""
gln = gammln(a)
if (x < 0.):
raise bad_arg, x
if (x == 0.):
return(0.)
ap = a
sum = 1. / a
delta = sum
n = 1
while n <= itmax:
ap = ap + 1.
delta = delta * x / ap
sum = sum + delta
if (abs(delta) < abs(sum)*eps):
return (sum * exp(-x + a*log(x) - gln), gln)
n = n + 1
raise max_iters, str((abs(delta), abs(sum)*eps))
def gcf(a, x, itmax=200, eps=3.e-7):
"""Continued fraction approx'n of the incomplete gamma function."""
gln = gammln(a)
gold = 0.
a0 = 1.
a1 = x
b0 = 0.
b1 = 1.
fac = 1.
n = 1
while n <= itmax:
an = n
ana = an - a
a0 = (a1 + a0*ana)*fac
b0 = (b1 + b0*ana)*fac
anf = an*fac
a1 = x*a0 + anf*a1
b1 = x*b0 + anf*b1
if (a1 != 0.):
fac = 1. / a1
g = b1*fac
if (abs((g-gold)/g) < eps):
return (g*exp(-x+a*log(x)-gln), gln)
gold = g
n = n + 1
raise max_iters, str(abs((g-gold)/g))
def gammp(a, x):
"""Incomplete gamma function."""
if (x < 0. or a <= 0.):
raise ValueError, (a, x)
if (x < a+1.):
return gser(a,x)[0]
else:
return 1.-gcf(a,x)[0]
def gammq(a, x):
"""Incomplete gamma function."""
if (x < 0. or a <= 0.):
raise ValueError, repr((a, x))
if (x < a+1.):
return 1.-gser(a,x)[0]
else:
return gcf(a,x)[0]
#======== Error function, normal CDF and inverse ================
def ncdf_inv(p):
"""Inverse of the normal CDF."""
c0 = 2.515517
c1 = 0.802853
c2 = 0.010328
d1 = 1.432788
d2 = 0.189269
d3 = 0.001308
sign = -1.
if (p > 0.5):
sign = 1.
p = 1. - p
arg = -2.*log(p)
t = sqrt(arg)
g = t - (c0 + t*(c1 + t*c2)) / (1. + t*(d1 + t*(d2 + t*d3)))
return sign*g
def erfcc(x):
"""Complementary error function."""
z = abs(x)
t = 1. / (1. + 0.5*z)
r = t * exp(-z*z-1.26551223+t*(1.00002368+t*(.37409196+
t*(.09678418+t*(-.18628806+t*(.27886807+
t*(-1.13520398+t*(1.48851587+t*(-.82215223+
t*.17087277)))))))))
if (x >= 0.):
return r
else:
return 2. - r
def ncdf(x):
"""Cumulative normal dist'n."""
global rt2
return 1. - 0.5*erfcc(x/rt2)
def ncdf_sig (nsig):
"""Cummulative normal dist'n inside nsig sigmas.
ncdf_sig = 1 - 2 * (upper tail) = 1 - erfc(sigfac/rt(2))"""
global rt2
return 1. - erfcc(nsig/rt2)
#=============== Chi squared dist'n ==============
def pchisq(chisq, nu):
"""Lower tail area of the chi**2 dist'n with nu dof.
Note that chisq is *not* the reduced chis**2!"""
hnu = 0.5 * nu
hchi = 0.5 * chisq
return gammp(hnu, hchi)
def qchisq(chisq, nu):
"""Upper tail area of the chi**2 dist'n with nu dof.
Note that chisq is *not* the reduced chis**2!"""
hnu = 0.5 * nu
hchi = 0.5 * chisq
return gammq(hnu, hchi)
def chisq_crit(nu, p, tol=1.e-5):
"""Critical chi**2 with lower tail area of p for nu dof."""
# For the first guess, use the assyptotic normal limit of the
# chi**2 distribution: chi**2 ~ N(nu,sqrt(2*nu)).
chi = nu + ncdf_inv(p)*sqrt(2.*nu)
pcur = pchisq(chi,nu)
# Now do a Newton-Raphson loop...
while 1:
dfdc = (pchisq(1.001*chi,nu) - pcur) / (0.001*chi)
chi = chi - (pcur - p)/dfdc
pcur = pchisq(chi,nu)
if (abs(pcur-p) <= tol):
return chi
# Allow it to run as a script
if __name__ == "__main__":
print 'gser: ', gser(5., 5.)
print 'gcf: ', gcf(5., 7.)
print 'gammp, gammq: ', gammp(5.,5.), gammq(5.,5.)
print 'erfcc: ', erfcc(.5)
print 'ncdf_inv: ', ncdf_inv(0.977), ncdf_inv(0.5)
print 'ncdf: ', ncdf(1.)
print 'ncdf_sig: ', ncdf_sig(2.)
print 'q & p chisq: ', qchisq(4.,1), pchisq(4.,1)
print 'chisq_crit: ', chisq_crit(1.,0.954)
More information about the Python-list
mailing list