switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
from . import functions
|
||||
# Hack to update methods
|
||||
from . import factorials
|
||||
from . import hypergeometric
|
||||
from . import expintegrals
|
||||
from . import bessel
|
||||
from . import orthogonal
|
||||
from . import theta
|
||||
from . import elliptic
|
||||
from . import signals
|
||||
from . import zeta
|
||||
from . import rszeta
|
||||
from . import zetazeros
|
||||
from . import qfunctions
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,425 @@
|
||||
from .functions import defun, defun_wrapped
|
||||
|
||||
@defun_wrapped
|
||||
def _erf_complex(ctx, z):
|
||||
z2 = ctx.square_exp_arg(z, -1)
|
||||
#z2 = -z**2
|
||||
v = (2/ctx.sqrt(ctx.pi))*z * ctx.hyp1f1((1,2),(3,2), z2)
|
||||
if not ctx._re(z):
|
||||
v = ctx._im(v)*ctx.j
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def _erfc_complex(ctx, z):
|
||||
if ctx.re(z) > 2:
|
||||
z2 = ctx.square_exp_arg(z)
|
||||
nz2 = ctx.fneg(z2, exact=True)
|
||||
v = ctx.exp(nz2)/ctx.sqrt(ctx.pi) * ctx.hyperu((1,2),(1,2), z2)
|
||||
else:
|
||||
v = 1 - ctx._erf_complex(z)
|
||||
if not ctx._re(z):
|
||||
v = 1+ctx._im(v)*ctx.j
|
||||
return v
|
||||
|
||||
@defun
|
||||
def erf(ctx, z):
|
||||
z = ctx.convert(z)
|
||||
if ctx._is_real_type(z):
|
||||
try:
|
||||
return ctx._erf(z)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
if ctx._is_complex_type(z) and not z.imag:
|
||||
try:
|
||||
return type(z)(ctx._erf(z.real))
|
||||
except NotImplementedError:
|
||||
pass
|
||||
return ctx._erf_complex(z)
|
||||
|
||||
@defun
|
||||
def erfc(ctx, z):
|
||||
z = ctx.convert(z)
|
||||
if ctx._is_real_type(z):
|
||||
try:
|
||||
return ctx._erfc(z)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
if ctx._is_complex_type(z) and not z.imag:
|
||||
try:
|
||||
return type(z)(ctx._erfc(z.real))
|
||||
except NotImplementedError:
|
||||
pass
|
||||
return ctx._erfc_complex(z)
|
||||
|
||||
@defun
|
||||
def square_exp_arg(ctx, z, mult=1, reciprocal=False):
|
||||
prec = ctx.prec*4+20
|
||||
if reciprocal:
|
||||
z2 = ctx.fmul(z, z, prec=prec)
|
||||
z2 = ctx.fdiv(ctx.one, z2, prec=prec)
|
||||
else:
|
||||
z2 = ctx.fmul(z, z, prec=prec)
|
||||
if mult != 1:
|
||||
z2 = ctx.fmul(z2, mult, exact=True)
|
||||
return z2
|
||||
|
||||
@defun_wrapped
|
||||
def erfi(ctx, z):
|
||||
if not z:
|
||||
return z
|
||||
z2 = ctx.square_exp_arg(z)
|
||||
v = (2/ctx.sqrt(ctx.pi)*z) * ctx.hyp1f1((1,2), (3,2), z2)
|
||||
if not ctx._re(z):
|
||||
v = ctx._im(v)*ctx.j
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def erfinv(ctx, x):
|
||||
xre = ctx._re(x)
|
||||
if (xre != x) or (xre < -1) or (xre > 1):
|
||||
return ctx.bad_domain("erfinv(x) is defined only for -1 <= x <= 1")
|
||||
x = xre
|
||||
#if ctx.isnan(x): return x
|
||||
if not x: return x
|
||||
if x == 1: return ctx.inf
|
||||
if x == -1: return ctx.ninf
|
||||
if abs(x) < 0.9:
|
||||
a = 0.53728*x**3 + 0.813198*x
|
||||
else:
|
||||
# An asymptotic formula
|
||||
u = ctx.ln(2/ctx.pi/(abs(x)-1)**2)
|
||||
a = ctx.sign(x) * ctx.sqrt(u - ctx.ln(u))/ctx.sqrt(2)
|
||||
ctx.prec += 10
|
||||
return ctx.findroot(lambda t: ctx.erf(t)-x, a)
|
||||
|
||||
@defun_wrapped
|
||||
def npdf(ctx, x, mu=0, sigma=1):
|
||||
sigma = ctx.convert(sigma)
|
||||
return ctx.exp(-(x-mu)**2/(2*sigma**2)) / (sigma*ctx.sqrt(2*ctx.pi))
|
||||
|
||||
@defun_wrapped
|
||||
def ncdf(ctx, x, mu=0, sigma=1):
|
||||
a = (x-mu)/(sigma*ctx.sqrt(2))
|
||||
if a < 0:
|
||||
return ctx.erfc(-a)/2
|
||||
else:
|
||||
return (1+ctx.erf(a))/2
|
||||
|
||||
@defun_wrapped
|
||||
def betainc(ctx, a, b, x1=0, x2=1, regularized=False):
|
||||
if x1 == x2:
|
||||
v = 0
|
||||
elif not x1:
|
||||
if x1 == 0 and x2 == 1:
|
||||
v = ctx.beta(a, b)
|
||||
else:
|
||||
v = x2**a * ctx.hyp2f1(a, 1-b, a+1, x2) / a
|
||||
else:
|
||||
m, d = ctx.nint_distance(a)
|
||||
if m <= 0:
|
||||
if d < -ctx.prec:
|
||||
h = +ctx.eps
|
||||
ctx.prec *= 2
|
||||
a += h
|
||||
elif d < -4:
|
||||
ctx.prec -= d
|
||||
s1 = x2**a * ctx.hyp2f1(a,1-b,a+1,x2)
|
||||
s2 = x1**a * ctx.hyp2f1(a,1-b,a+1,x1)
|
||||
v = (s1 - s2) / a
|
||||
if regularized:
|
||||
v /= ctx.beta(a,b)
|
||||
return v
|
||||
|
||||
@defun
|
||||
def gammainc(ctx, z, a=0, b=None, regularized=False):
|
||||
regularized = bool(regularized)
|
||||
z = ctx.convert(z)
|
||||
if a is None:
|
||||
a = ctx.zero
|
||||
lower_modified = False
|
||||
else:
|
||||
a = ctx.convert(a)
|
||||
lower_modified = a != ctx.zero
|
||||
if b is None:
|
||||
b = ctx.inf
|
||||
upper_modified = False
|
||||
else:
|
||||
b = ctx.convert(b)
|
||||
upper_modified = b != ctx.inf
|
||||
# Complete gamma function
|
||||
if not (upper_modified or lower_modified):
|
||||
if regularized:
|
||||
if ctx.re(z) < 0:
|
||||
return ctx.inf
|
||||
elif ctx.re(z) > 0:
|
||||
return ctx.one
|
||||
else:
|
||||
return ctx.nan
|
||||
return ctx.gamma(z)
|
||||
if a == b:
|
||||
return ctx.zero
|
||||
# Standardize
|
||||
if ctx.re(a) > ctx.re(b):
|
||||
return -ctx.gammainc(z, b, a, regularized)
|
||||
# Generalized gamma
|
||||
if upper_modified and lower_modified:
|
||||
return +ctx._gamma3(z, a, b, regularized)
|
||||
# Upper gamma
|
||||
elif lower_modified:
|
||||
return ctx._upper_gamma(z, a, regularized)
|
||||
# Lower gamma
|
||||
elif upper_modified:
|
||||
return ctx._lower_gamma(z, b, regularized)
|
||||
|
||||
@defun
|
||||
def _lower_gamma(ctx, z, b, regularized=False):
|
||||
# Pole
|
||||
if ctx.isnpint(z):
|
||||
return type(z)(ctx.inf)
|
||||
G = [z] * regularized
|
||||
negb = ctx.fneg(b, exact=True)
|
||||
def h(z):
|
||||
T1 = [ctx.exp(negb), b, z], [1, z, -1], [], G, [1], [1+z], b
|
||||
return (T1,)
|
||||
return ctx.hypercomb(h, [z])
|
||||
|
||||
@defun
|
||||
def _upper_gamma(ctx, z, a, regularized=False):
|
||||
# Fast integer case, when available
|
||||
if ctx.isint(z):
|
||||
try:
|
||||
if regularized:
|
||||
# Gamma pole
|
||||
if ctx.isnpint(z):
|
||||
return type(z)(ctx.zero)
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
return ctx._gamma_upper_int(z, a) / ctx.gamma(z)
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
else:
|
||||
return ctx._gamma_upper_int(z, a)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
# hypercomb is unable to detect the exact zeros, so handle them here
|
||||
if z == 2 and a == -1:
|
||||
return (z+a)*0
|
||||
if z == 3 and (a == -1-1j or a == -1+1j):
|
||||
return (z+a)*0
|
||||
nega = ctx.fneg(a, exact=True)
|
||||
G = [z] * regularized
|
||||
# Use 2F0 series when possible; fall back to lower gamma representation
|
||||
try:
|
||||
def h(z):
|
||||
r = z-1
|
||||
return [([ctx.exp(nega), a], [1, r], [], G, [1, -r], [], 1/nega)]
|
||||
return ctx.hypercomb(h, [z], force_series=True)
|
||||
except ctx.NoConvergence:
|
||||
def h(z):
|
||||
T1 = [], [1, z-1], [z], G, [], [], 0
|
||||
T2 = [-ctx.exp(nega), a, z], [1, z, -1], [], G, [1], [1+z], a
|
||||
return T1, T2
|
||||
return ctx.hypercomb(h, [z])
|
||||
|
||||
@defun
|
||||
def _gamma3(ctx, z, a, b, regularized=False):
|
||||
pole = ctx.isnpint(z)
|
||||
if regularized and pole:
|
||||
return ctx.zero
|
||||
try:
|
||||
ctx.prec += 15
|
||||
# We don't know in advance whether it's better to write as a difference
|
||||
# of lower or upper gamma functions, so try both
|
||||
T1 = ctx.gammainc(z, a, regularized=regularized)
|
||||
T2 = ctx.gammainc(z, b, regularized=regularized)
|
||||
R = T1 - T2
|
||||
if ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10:
|
||||
return R
|
||||
if not pole:
|
||||
T1 = ctx.gammainc(z, 0, b, regularized=regularized)
|
||||
T2 = ctx.gammainc(z, 0, a, regularized=regularized)
|
||||
R = T1 - T2
|
||||
# May be ok, but should probably at least print a warning
|
||||
# about possible cancellation
|
||||
if 1: #ctx.mag(R) - max(ctx.mag(T1), ctx.mag(T2)) > -10:
|
||||
return R
|
||||
finally:
|
||||
ctx.prec -= 15
|
||||
raise NotImplementedError
|
||||
|
||||
@defun_wrapped
|
||||
def expint(ctx, n, z):
|
||||
if ctx.isint(n) and ctx._is_real_type(z):
|
||||
try:
|
||||
return ctx._expint_int(n, z)
|
||||
except NotImplementedError:
|
||||
pass
|
||||
if ctx.isnan(n) or ctx.isnan(z):
|
||||
return z*n
|
||||
if z == ctx.inf:
|
||||
return 1/z
|
||||
if z == 0:
|
||||
# integral from 1 to infinity of t^n
|
||||
if ctx.re(n) <= 1:
|
||||
# TODO: reasonable sign of infinity
|
||||
return type(z)(ctx.inf)
|
||||
else:
|
||||
return ctx.one/(n-1)
|
||||
if n == 0:
|
||||
return ctx.exp(-z)/z
|
||||
if n == -1:
|
||||
return ctx.exp(-z)*(z+1)/z**2
|
||||
return z**(n-1) * ctx.gammainc(1-n, z)
|
||||
|
||||
@defun_wrapped
|
||||
def li(ctx, z, offset=False):
|
||||
if offset:
|
||||
if z == 2:
|
||||
return ctx.zero
|
||||
return ctx.ei(ctx.ln(z)) - ctx.ei(ctx.ln2)
|
||||
if not z:
|
||||
return z
|
||||
if z == 1:
|
||||
return ctx.ninf
|
||||
return ctx.ei(ctx.ln(z))
|
||||
|
||||
@defun
|
||||
def ei(ctx, z):
|
||||
try:
|
||||
return ctx._ei(z)
|
||||
except NotImplementedError:
|
||||
return ctx._ei_generic(z)
|
||||
|
||||
@defun_wrapped
|
||||
def _ei_generic(ctx, z):
|
||||
# Note: the following is currently untested because mp and fp
|
||||
# both use special-case ei code
|
||||
if z == ctx.inf:
|
||||
return z
|
||||
if z == ctx.ninf:
|
||||
return ctx.zero
|
||||
if ctx.mag(z) > 1:
|
||||
try:
|
||||
r = ctx.one/z
|
||||
v = ctx.exp(z)*ctx.hyper([1,1],[],r,
|
||||
maxterms=ctx.prec, force_series=True)/z
|
||||
im = ctx._im(z)
|
||||
if im > 0:
|
||||
v += ctx.pi*ctx.j
|
||||
if im < 0:
|
||||
v -= ctx.pi*ctx.j
|
||||
return v
|
||||
except ctx.NoConvergence:
|
||||
pass
|
||||
v = z*ctx.hyp2f2(1,1,2,2,z) + ctx.euler
|
||||
if ctx._im(z):
|
||||
v += 0.5*(ctx.log(z) - ctx.log(ctx.one/z))
|
||||
else:
|
||||
v += ctx.log(abs(z))
|
||||
return v
|
||||
|
||||
@defun
|
||||
def e1(ctx, z):
|
||||
try:
|
||||
return ctx._e1(z)
|
||||
except NotImplementedError:
|
||||
return ctx.expint(1, z)
|
||||
|
||||
@defun
|
||||
def ci(ctx, z):
|
||||
try:
|
||||
return ctx._ci(z)
|
||||
except NotImplementedError:
|
||||
return ctx._ci_generic(z)
|
||||
|
||||
@defun_wrapped
|
||||
def _ci_generic(ctx, z):
|
||||
if ctx.isinf(z):
|
||||
if z == ctx.inf: return ctx.zero
|
||||
if z == ctx.ninf: return ctx.pi*1j
|
||||
jz = ctx.fmul(ctx.j,z,exact=True)
|
||||
njz = ctx.fneg(jz,exact=True)
|
||||
v = 0.5*(ctx.ei(jz) + ctx.ei(njz))
|
||||
zreal = ctx._re(z)
|
||||
zimag = ctx._im(z)
|
||||
if zreal == 0:
|
||||
if zimag > 0: v += ctx.pi*0.5j
|
||||
if zimag < 0: v -= ctx.pi*0.5j
|
||||
if zreal < 0:
|
||||
if zimag >= 0: v += ctx.pi*1j
|
||||
if zimag < 0: v -= ctx.pi*1j
|
||||
if ctx._is_real_type(z) and zreal > 0:
|
||||
v = ctx._re(v)
|
||||
return v
|
||||
|
||||
@defun
|
||||
def si(ctx, z):
|
||||
try:
|
||||
return ctx._si(z)
|
||||
except NotImplementedError:
|
||||
return ctx._si_generic(z)
|
||||
|
||||
@defun_wrapped
|
||||
def _si_generic(ctx, z):
|
||||
if ctx.isinf(z):
|
||||
if z == ctx.inf: return 0.5*ctx.pi
|
||||
if z == ctx.ninf: return -0.5*ctx.pi
|
||||
# Suffers from cancellation near 0
|
||||
if ctx.mag(z) >= -1:
|
||||
jz = ctx.fmul(ctx.j,z,exact=True)
|
||||
njz = ctx.fneg(jz,exact=True)
|
||||
v = (-0.5j)*(ctx.ei(jz) - ctx.ei(njz))
|
||||
zreal = ctx._re(z)
|
||||
if zreal > 0:
|
||||
v -= 0.5*ctx.pi
|
||||
if zreal < 0:
|
||||
v += 0.5*ctx.pi
|
||||
if ctx._is_real_type(z):
|
||||
v = ctx._re(v)
|
||||
return v
|
||||
else:
|
||||
return z*ctx.hyp1f2((1,2),(3,2),(3,2),-0.25*z*z)
|
||||
|
||||
@defun_wrapped
|
||||
def chi(ctx, z):
|
||||
nz = ctx.fneg(z, exact=True)
|
||||
v = 0.5*(ctx.ei(z) + ctx.ei(nz))
|
||||
zreal = ctx._re(z)
|
||||
zimag = ctx._im(z)
|
||||
if zimag > 0:
|
||||
v += ctx.pi*0.5j
|
||||
elif zimag < 0:
|
||||
v -= ctx.pi*0.5j
|
||||
elif zreal < 0:
|
||||
v += ctx.pi*1j
|
||||
return v
|
||||
|
||||
@defun_wrapped
|
||||
def shi(ctx, z):
|
||||
# Suffers from cancellation near 0
|
||||
if ctx.mag(z) >= -1:
|
||||
nz = ctx.fneg(z, exact=True)
|
||||
v = 0.5*(ctx.ei(z) - ctx.ei(nz))
|
||||
zimag = ctx._im(z)
|
||||
if zimag > 0: v -= 0.5j*ctx.pi
|
||||
if zimag < 0: v += 0.5j*ctx.pi
|
||||
return v
|
||||
else:
|
||||
return z * ctx.hyp1f2((1,2),(3,2),(3,2),0.25*z*z)
|
||||
|
||||
@defun_wrapped
|
||||
def fresnels(ctx, z):
|
||||
if z == ctx.inf:
|
||||
return ctx.mpf(0.5)
|
||||
if z == ctx.ninf:
|
||||
return ctx.mpf(-0.5)
|
||||
return ctx.pi*z**3/6*ctx.hyp1f2((3,4),(3,2),(7,4),-ctx.pi**2*z**4/16)
|
||||
|
||||
@defun_wrapped
|
||||
def fresnelc(ctx, z):
|
||||
if z == ctx.inf:
|
||||
return ctx.mpf(0.5)
|
||||
if z == ctx.ninf:
|
||||
return ctx.mpf(-0.5)
|
||||
return z*ctx.hyp1f2((1,4),(1,2),(5,4),-ctx.pi**2*z**4/16)
|
||||
@@ -0,0 +1,187 @@
|
||||
from ..libmp.backend import xrange
|
||||
from .functions import defun, defun_wrapped
|
||||
|
||||
@defun
|
||||
def gammaprod(ctx, a, b, _infsign=False):
|
||||
a = [ctx.convert(x) for x in a]
|
||||
b = [ctx.convert(x) for x in b]
|
||||
poles_num = []
|
||||
poles_den = []
|
||||
regular_num = []
|
||||
regular_den = []
|
||||
for x in a: [regular_num, poles_num][ctx.isnpint(x)].append(x)
|
||||
for x in b: [regular_den, poles_den][ctx.isnpint(x)].append(x)
|
||||
# One more pole in numerator or denominator gives 0 or inf
|
||||
if len(poles_num) < len(poles_den): return ctx.zero
|
||||
if len(poles_num) > len(poles_den):
|
||||
# Get correct sign of infinity for x+h, h -> 0 from above
|
||||
# XXX: hack, this should be done properly
|
||||
if _infsign:
|
||||
a = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_num]
|
||||
b = [x and x*(1+ctx.eps) or x+ctx.eps for x in poles_den]
|
||||
return ctx.sign(ctx.gammaprod(a+regular_num,b+regular_den)) * ctx.inf
|
||||
else:
|
||||
return ctx.inf
|
||||
# All poles cancel
|
||||
# lim G(i)/G(j) = (-1)**(i+j) * gamma(1-j) / gamma(1-i)
|
||||
p = ctx.one
|
||||
orig = ctx.prec
|
||||
try:
|
||||
ctx.prec = orig + 15
|
||||
while poles_num:
|
||||
i = poles_num.pop()
|
||||
j = poles_den.pop()
|
||||
p *= (-1)**(i+j) * ctx.gamma(1-j) / ctx.gamma(1-i)
|
||||
for x in regular_num: p *= ctx.gamma(x)
|
||||
for x in regular_den: p /= ctx.gamma(x)
|
||||
finally:
|
||||
ctx.prec = orig
|
||||
return +p
|
||||
|
||||
@defun
|
||||
def beta(ctx, x, y):
|
||||
x = ctx.convert(x)
|
||||
y = ctx.convert(y)
|
||||
if ctx.isinf(y):
|
||||
x, y = y, x
|
||||
if ctx.isinf(x):
|
||||
if x == ctx.inf and not ctx._im(y):
|
||||
if y == ctx.ninf:
|
||||
return ctx.nan
|
||||
if y > 0:
|
||||
return ctx.zero
|
||||
if ctx.isint(y):
|
||||
return ctx.nan
|
||||
if y < 0:
|
||||
return ctx.sign(ctx.gamma(y)) * ctx.inf
|
||||
return ctx.nan
|
||||
xy = ctx.fadd(x, y, prec=2*ctx.prec)
|
||||
return ctx.gammaprod([x, y], [xy])
|
||||
|
||||
@defun
|
||||
def binomial(ctx, n, k):
|
||||
n1 = ctx.fadd(n, 1, prec=2*ctx.prec)
|
||||
k1 = ctx.fadd(k, 1, prec=2*ctx.prec)
|
||||
nk1 = ctx.fsub(n1, k, prec=2*ctx.prec)
|
||||
return ctx.gammaprod([n1], [k1, nk1])
|
||||
|
||||
@defun
|
||||
def rf(ctx, x, n):
|
||||
xn = ctx.fadd(x, n, prec=2*ctx.prec)
|
||||
return ctx.gammaprod([xn], [x])
|
||||
|
||||
@defun
|
||||
def ff(ctx, x, n):
|
||||
x1 = ctx.fadd(x, 1, prec=2*ctx.prec)
|
||||
xn1 = ctx.fadd(ctx.fsub(x, n, prec=2*ctx.prec), 1, prec=2*ctx.prec)
|
||||
return ctx.gammaprod([x1], [xn1])
|
||||
|
||||
@defun_wrapped
|
||||
def fac2(ctx, x):
|
||||
if ctx.isinf(x):
|
||||
if x == ctx.inf:
|
||||
return x
|
||||
return ctx.nan
|
||||
return 2**(x/2)*(ctx.pi/2)**((ctx.cospi(x)-1)/4)*ctx.gamma(x/2+1)
|
||||
|
||||
@defun_wrapped
|
||||
def barnesg(ctx, z):
|
||||
if ctx.isinf(z):
|
||||
if z == ctx.inf:
|
||||
return z
|
||||
return ctx.nan
|
||||
if ctx.isnan(z):
|
||||
return z
|
||||
if (not ctx._im(z)) and ctx._re(z) <= 0 and ctx.isint(ctx._re(z)):
|
||||
return z*0
|
||||
# Account for size (would not be needed if computing log(G))
|
||||
if abs(z) > 5:
|
||||
ctx.dps += 2*ctx.log(abs(z),2)
|
||||
# Reflection formula
|
||||
if ctx.re(z) < -ctx.dps:
|
||||
w = 1-z
|
||||
pi2 = 2*ctx.pi
|
||||
u = ctx.expjpi(2*w)
|
||||
v = ctx.j*ctx.pi/12 - ctx.j*ctx.pi*w**2/2 + w*ctx.ln(1-u) - \
|
||||
ctx.j*ctx.polylog(2, u)/pi2
|
||||
v = ctx.barnesg(2-z)*ctx.exp(v)/pi2**w
|
||||
if ctx._is_real_type(z):
|
||||
v = ctx._re(v)
|
||||
return v
|
||||
# Estimate terms for asymptotic expansion
|
||||
# TODO: fixme, obviously
|
||||
N = ctx.dps // 2 + 5
|
||||
G = 1
|
||||
while abs(z) < N or ctx.re(z) < 1:
|
||||
G /= ctx.gamma(z)
|
||||
z += 1
|
||||
z -= 1
|
||||
s = ctx.mpf(1)/12
|
||||
s -= ctx.log(ctx.glaisher)
|
||||
s += z*ctx.log(2*ctx.pi)/2
|
||||
s += (z**2/2-ctx.mpf(1)/12)*ctx.log(z)
|
||||
s -= 3*z**2/4
|
||||
z2k = z2 = z**2
|
||||
for k in xrange(1, N+1):
|
||||
t = ctx.bernoulli(2*k+2) / (4*k*(k+1)*z2k)
|
||||
if abs(t) < ctx.eps:
|
||||
#print k, N # check how many terms were needed
|
||||
break
|
||||
z2k *= z2
|
||||
s += t
|
||||
#if k == N:
|
||||
# print "warning: series for barnesg failed to converge", ctx.dps
|
||||
return G*ctx.exp(s)
|
||||
|
||||
@defun
|
||||
def superfac(ctx, z):
|
||||
return ctx.barnesg(z+2)
|
||||
|
||||
@defun_wrapped
|
||||
def hyperfac(ctx, z):
|
||||
# XXX: estimate needed extra bits accurately
|
||||
if z == ctx.inf:
|
||||
return z
|
||||
if abs(z) > 5:
|
||||
extra = 4*int(ctx.log(abs(z),2))
|
||||
else:
|
||||
extra = 0
|
||||
ctx.prec += extra
|
||||
if not ctx._im(z) and ctx._re(z) < 0 and ctx.isint(ctx._re(z)):
|
||||
n = int(ctx.re(z))
|
||||
h = ctx.hyperfac(-n-1)
|
||||
if ((n+1)//2) & 1:
|
||||
h = -h
|
||||
if ctx._is_complex_type(z):
|
||||
return h + 0j
|
||||
return h
|
||||
zp1 = z+1
|
||||
# Wrong branch cut
|
||||
#v = ctx.gamma(zp1)**z
|
||||
#ctx.prec -= extra
|
||||
#return v / ctx.barnesg(zp1)
|
||||
v = ctx.exp(z*ctx.loggamma(zp1))
|
||||
ctx.prec -= extra
|
||||
return v / ctx.barnesg(zp1)
|
||||
|
||||
'''
|
||||
@defun
|
||||
def psi0(ctx, z):
|
||||
"""Shortcut for psi(0,z) (the digamma function)"""
|
||||
return ctx.psi(0, z)
|
||||
|
||||
@defun
|
||||
def psi1(ctx, z):
|
||||
"""Shortcut for psi(1,z) (the trigamma function)"""
|
||||
return ctx.psi(1, z)
|
||||
|
||||
@defun
|
||||
def psi2(ctx, z):
|
||||
"""Shortcut for psi(2,z) (the tetragamma function)"""
|
||||
return ctx.psi(2, z)
|
||||
|
||||
@defun
|
||||
def psi3(ctx, z):
|
||||
"""Shortcut for psi(3,z) (the pentagamma function)"""
|
||||
return ctx.psi(3, z)
|
||||
'''
|
||||
@@ -0,0 +1,645 @@
|
||||
from ..libmp.backend import xrange
|
||||
|
||||
class SpecialFunctions(object):
|
||||
"""
|
||||
This class implements special functions using high-level code.
|
||||
|
||||
Elementary and some other functions (e.g. gamma function, basecase
|
||||
hypergeometric series) are assumed to be predefined by the context as
|
||||
"builtins" or "low-level" functions.
|
||||
"""
|
||||
defined_functions = {}
|
||||
|
||||
# The series for the Jacobi theta functions converge for |q| < 1;
|
||||
# in the current implementation they throw a ValueError for
|
||||
# abs(q) > THETA_Q_LIM
|
||||
THETA_Q_LIM = 1 - 10**-7
|
||||
|
||||
def __init__(self):
|
||||
cls = self.__class__
|
||||
for name in cls.defined_functions:
|
||||
f, wrap = cls.defined_functions[name]
|
||||
cls._wrap_specfun(name, f, wrap)
|
||||
|
||||
self.mpq_1 = self._mpq((1,1))
|
||||
self.mpq_0 = self._mpq((0,1))
|
||||
self.mpq_1_2 = self._mpq((1,2))
|
||||
self.mpq_3_2 = self._mpq((3,2))
|
||||
self.mpq_1_4 = self._mpq((1,4))
|
||||
self.mpq_1_16 = self._mpq((1,16))
|
||||
self.mpq_3_16 = self._mpq((3,16))
|
||||
self.mpq_5_2 = self._mpq((5,2))
|
||||
self.mpq_3_4 = self._mpq((3,4))
|
||||
self.mpq_7_4 = self._mpq((7,4))
|
||||
self.mpq_5_4 = self._mpq((5,4))
|
||||
self.mpq_1_3 = self._mpq((1,3))
|
||||
self.mpq_2_3 = self._mpq((2,3))
|
||||
self.mpq_4_3 = self._mpq((4,3))
|
||||
self.mpq_1_6 = self._mpq((1,6))
|
||||
self.mpq_5_6 = self._mpq((5,6))
|
||||
self.mpq_5_3 = self._mpq((5,3))
|
||||
|
||||
self._misc_const_cache = {}
|
||||
|
||||
self._aliases.update({
|
||||
'phase' : 'arg',
|
||||
'conjugate' : 'conj',
|
||||
'nthroot' : 'root',
|
||||
'polygamma' : 'psi',
|
||||
'hurwitz' : 'zeta',
|
||||
#'digamma' : 'psi0',
|
||||
#'trigamma' : 'psi1',
|
||||
#'tetragamma' : 'psi2',
|
||||
#'pentagamma' : 'psi3',
|
||||
'fibonacci' : 'fib',
|
||||
'factorial' : 'fac',
|
||||
})
|
||||
|
||||
self.zetazero_memoized = self.memoize(self.zetazero)
|
||||
|
||||
# Default -- do nothing
|
||||
@classmethod
|
||||
def _wrap_specfun(cls, name, f, wrap):
|
||||
setattr(cls, name, f)
|
||||
|
||||
# Optional fast versions of common functions in common cases.
|
||||
# If not overridden, default (generic hypergeometric series)
|
||||
# implementations will be used
|
||||
def _besselj(ctx, n, z): raise NotImplementedError
|
||||
def _erf(ctx, z): raise NotImplementedError
|
||||
def _erfc(ctx, z): raise NotImplementedError
|
||||
def _gamma_upper_int(ctx, z, a): raise NotImplementedError
|
||||
def _expint_int(ctx, n, z): raise NotImplementedError
|
||||
def _zeta(ctx, s): raise NotImplementedError
|
||||
def _zetasum_fast(ctx, s, a, n, derivatives, reflect): raise NotImplementedError
|
||||
def _ei(ctx, z): raise NotImplementedError
|
||||
def _e1(ctx, z): raise NotImplementedError
|
||||
def _ci(ctx, z): raise NotImplementedError
|
||||
def _si(ctx, z): raise NotImplementedError
|
||||
def _altzeta(ctx, s): raise NotImplementedError
|
||||
|
||||
def defun_wrapped(f):
|
||||
SpecialFunctions.defined_functions[f.__name__] = f, True
|
||||
return f
|
||||
|
||||
def defun(f):
|
||||
SpecialFunctions.defined_functions[f.__name__] = f, False
|
||||
return f
|
||||
|
||||
def defun_static(f):
|
||||
setattr(SpecialFunctions, f.__name__, f)
|
||||
return f
|
||||
|
||||
@defun_wrapped
|
||||
def cot(ctx, z): return ctx.one / ctx.tan(z)
|
||||
|
||||
@defun_wrapped
|
||||
def sec(ctx, z): return ctx.one / ctx.cos(z)
|
||||
|
||||
@defun_wrapped
|
||||
def csc(ctx, z): return ctx.one / ctx.sin(z)
|
||||
|
||||
@defun_wrapped
|
||||
def coth(ctx, z): return ctx.one / ctx.tanh(z)
|
||||
|
||||
@defun_wrapped
|
||||
def sech(ctx, z): return ctx.one / ctx.cosh(z)
|
||||
|
||||
@defun_wrapped
|
||||
def csch(ctx, z): return ctx.one / ctx.sinh(z)
|
||||
|
||||
@defun_wrapped
|
||||
def acot(ctx, z):
|
||||
if not z:
|
||||
return ctx.pi * 0.5
|
||||
else:
|
||||
return ctx.atan(ctx.one / z)
|
||||
|
||||
@defun_wrapped
|
||||
def asec(ctx, z): return ctx.acos(ctx.one / z)
|
||||
|
||||
@defun_wrapped
|
||||
def acsc(ctx, z): return ctx.asin(ctx.one / z)
|
||||
|
||||
@defun_wrapped
|
||||
def acoth(ctx, z):
|
||||
if not z:
|
||||
return ctx.pi * 0.5j
|
||||
else:
|
||||
return ctx.atanh(ctx.one / z)
|
||||
|
||||
|
||||
@defun_wrapped
|
||||
def asech(ctx, z): return ctx.acosh(ctx.one / z)
|
||||
|
||||
@defun_wrapped
|
||||
def acsch(ctx, z): return ctx.asinh(ctx.one / z)
|
||||
|
||||
@defun
|
||||
def sign(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
if not x or ctx.isnan(x):
|
||||
return x
|
||||
if ctx._is_real_type(x):
|
||||
if x > 0:
|
||||
return ctx.one
|
||||
else:
|
||||
return -ctx.one
|
||||
return x / abs(x)
|
||||
|
||||
@defun
|
||||
def agm(ctx, a, b=1):
|
||||
if b == 1:
|
||||
return ctx.agm1(a)
|
||||
a = ctx.convert(a)
|
||||
b = ctx.convert(b)
|
||||
return ctx._agm(a, b)
|
||||
|
||||
@defun_wrapped
|
||||
def sinc(ctx, x):
|
||||
if ctx.isinf(x):
|
||||
return 1/x
|
||||
if not x:
|
||||
return x+1
|
||||
return ctx.sin(x)/x
|
||||
|
||||
@defun_wrapped
|
||||
def sincpi(ctx, x):
|
||||
if ctx.isinf(x):
|
||||
return 1/x
|
||||
if not x:
|
||||
return x+1
|
||||
return ctx.sinpi(x)/(ctx.pi*x)
|
||||
|
||||
# TODO: tests; improve implementation
|
||||
@defun_wrapped
|
||||
def expm1(ctx, x):
|
||||
if not x:
|
||||
return ctx.zero
|
||||
# exp(x) - 1 ~ x
|
||||
if ctx.mag(x) < -ctx.prec:
|
||||
return x + 0.5*x**2
|
||||
# TODO: accurately eval the smaller of the real/imag parts
|
||||
return ctx.sum_accurately(lambda: iter([ctx.exp(x),-1]),1)
|
||||
|
||||
@defun_wrapped
|
||||
def log1p(ctx, x):
|
||||
if not x:
|
||||
return ctx.zero
|
||||
if ctx.mag(x) < -ctx.prec:
|
||||
return x - 0.5*x**2
|
||||
return ctx.log(ctx.fadd(1, x, prec=2*ctx.prec))
|
||||
|
||||
@defun_wrapped
|
||||
def powm1(ctx, x, y):
|
||||
mag = ctx.mag
|
||||
one = ctx.one
|
||||
w = x**y - one
|
||||
M = mag(w)
|
||||
# Only moderate cancellation
|
||||
if M > -8:
|
||||
return w
|
||||
# Check for the only possible exact cases
|
||||
if not w:
|
||||
if (not y) or (x in (1, -1, 1j, -1j) and ctx.isint(y)):
|
||||
return w
|
||||
x1 = x - one
|
||||
magy = mag(y)
|
||||
lnx = ctx.ln(x)
|
||||
# Small y: x^y - 1 ~ log(x)*y + O(log(x)^2 * y^2)
|
||||
if magy + mag(lnx) < -ctx.prec:
|
||||
return lnx*y + (lnx*y)**2/2
|
||||
# TODO: accurately eval the smaller of the real/imag part
|
||||
return ctx.sum_accurately(lambda: iter([x**y, -1]), 1)
|
||||
|
||||
@defun
|
||||
def _rootof1(ctx, k, n):
|
||||
k = int(k)
|
||||
n = int(n)
|
||||
k %= n
|
||||
if not k:
|
||||
return ctx.one
|
||||
elif 2*k == n:
|
||||
return -ctx.one
|
||||
elif 4*k == n:
|
||||
return ctx.j
|
||||
elif 4*k == 3*n:
|
||||
return -ctx.j
|
||||
return ctx.expjpi(2*ctx.mpf(k)/n)
|
||||
|
||||
@defun
|
||||
def root(ctx, x, n, k=0):
|
||||
n = int(n)
|
||||
x = ctx.convert(x)
|
||||
if k:
|
||||
# Special case: there is an exact real root
|
||||
if (n & 1 and 2*k == n-1) and (not ctx.im(x)) and (ctx.re(x) < 0):
|
||||
return -ctx.root(-x, n)
|
||||
# Multiply by root of unity
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
v = ctx.root(x, n, 0) * ctx._rootof1(k, n)
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return +v
|
||||
return ctx._nthroot(x, n)
|
||||
|
||||
@defun
|
||||
def unitroots(ctx, n, primitive=False):
|
||||
gcd = ctx._gcd
|
||||
prec = ctx.prec
|
||||
try:
|
||||
ctx.prec += 10
|
||||
if primitive:
|
||||
v = [ctx._rootof1(k,n) for k in range(n) if gcd(k,n) == 1]
|
||||
else:
|
||||
# TODO: this can be done *much* faster
|
||||
v = [ctx._rootof1(k,n) for k in range(n)]
|
||||
finally:
|
||||
ctx.prec = prec
|
||||
return [+x for x in v]
|
||||
|
||||
@defun
|
||||
def arg(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
re = ctx._re(x)
|
||||
im = ctx._im(x)
|
||||
return ctx.atan2(im, re)
|
||||
|
||||
@defun
|
||||
def fabs(ctx, x):
|
||||
return abs(ctx.convert(x))
|
||||
|
||||
@defun
|
||||
def re(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
if hasattr(x, "real"): # py2.5 doesn't have .real/.imag for all numbers
|
||||
return x.real
|
||||
return x
|
||||
|
||||
@defun
|
||||
def im(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
if hasattr(x, "imag"): # py2.5 doesn't have .real/.imag for all numbers
|
||||
return x.imag
|
||||
return ctx.zero
|
||||
|
||||
@defun
|
||||
def conj(ctx, x):
|
||||
x = ctx.convert(x)
|
||||
try:
|
||||
return x.conjugate()
|
||||
except AttributeError:
|
||||
return x
|
||||
|
||||
@defun
|
||||
def polar(ctx, z):
|
||||
return (ctx.fabs(z), ctx.arg(z))
|
||||
|
||||
@defun_wrapped
|
||||
def rect(ctx, r, phi):
|
||||
return r * ctx.mpc(*ctx.cos_sin(phi))
|
||||
|
||||
@defun
|
||||
def log(ctx, x, b=None):
|
||||
if b is None:
|
||||
return ctx.ln(x)
|
||||
wp = ctx.prec + 20
|
||||
return ctx.ln(x, prec=wp) / ctx.ln(b, prec=wp)
|
||||
|
||||
@defun
|
||||
def log10(ctx, x):
|
||||
return ctx.log(x, 10)
|
||||
|
||||
@defun
|
||||
def fmod(ctx, x, y):
|
||||
return ctx.convert(x) % ctx.convert(y)
|
||||
|
||||
@defun
|
||||
def degrees(ctx, x):
|
||||
return x / ctx.degree
|
||||
|
||||
@defun
|
||||
def radians(ctx, x):
|
||||
return x * ctx.degree
|
||||
|
||||
def _lambertw_special(ctx, z, k):
|
||||
# W(0,0) = 0; all other branches are singular
|
||||
if not z:
|
||||
if not k:
|
||||
return z
|
||||
return ctx.ninf + z
|
||||
if z == ctx.inf:
|
||||
if k == 0:
|
||||
return z
|
||||
else:
|
||||
return z + 2*k*ctx.pi*ctx.j
|
||||
if z == ctx.ninf:
|
||||
return (-z) + (2*k+1)*ctx.pi*ctx.j
|
||||
# Some kind of nan or complex inf/nan?
|
||||
return ctx.ln(z)
|
||||
|
||||
import math
|
||||
import cmath
|
||||
|
||||
def _lambertw_approx_hybrid(z, k):
|
||||
imag_sign = 0
|
||||
if hasattr(z, "imag"):
|
||||
x = float(z.real)
|
||||
y = z.imag
|
||||
if y:
|
||||
imag_sign = (-1) ** (y < 0)
|
||||
y = float(y)
|
||||
else:
|
||||
x = float(z)
|
||||
y = 0.0
|
||||
imag_sign = 0
|
||||
# hack to work regardless of whether Python supports -0.0
|
||||
if not y:
|
||||
y = 0.0
|
||||
z = complex(x,y)
|
||||
if k == 0:
|
||||
if -4.0 < y < 4.0 and -1.0 < x < 2.5:
|
||||
if imag_sign:
|
||||
# Taylor series in upper/lower half-plane
|
||||
if y > 1.00: return (0.876+0.645j) + (0.118-0.174j)*(z-(0.75+2.5j))
|
||||
if y > 0.25: return (0.505+0.204j) + (0.375-0.132j)*(z-(0.75+0.5j))
|
||||
if y < -1.00: return (0.876-0.645j) + (0.118+0.174j)*(z-(0.75-2.5j))
|
||||
if y < -0.25: return (0.505-0.204j) + (0.375+0.132j)*(z-(0.75-0.5j))
|
||||
# Taylor series near -1
|
||||
if x < -0.5:
|
||||
if imag_sign >= 0:
|
||||
return (-0.318+1.34j) + (-0.697-0.593j)*(z+1)
|
||||
else:
|
||||
return (-0.318-1.34j) + (-0.697+0.593j)*(z+1)
|
||||
# return real type
|
||||
r = -0.367879441171442
|
||||
if (not imag_sign) and x > r:
|
||||
z = x
|
||||
# Singularity near -1/e
|
||||
if x < -0.2:
|
||||
return -1 + 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r)
|
||||
# Taylor series near 0
|
||||
if x < 0.5: return z
|
||||
# Simple linear approximation
|
||||
return 0.2 + 0.3*z
|
||||
if (not imag_sign) and x > 0.0:
|
||||
L1 = math.log(x); L2 = math.log(L1)
|
||||
else:
|
||||
L1 = cmath.log(z); L2 = cmath.log(L1)
|
||||
elif k == -1:
|
||||
# return real type
|
||||
r = -0.367879441171442
|
||||
if (not imag_sign) and r < x < 0.0:
|
||||
z = x
|
||||
if (imag_sign >= 0) and y < 0.1 and -0.6 < x < -0.2:
|
||||
return -1 - 2.33164398159712*(z-r)**0.5 - 1.81218788563936*(z-r)
|
||||
if (not imag_sign) and -0.2 <= x < 0.0:
|
||||
L1 = math.log(-x)
|
||||
return L1 - math.log(-L1)
|
||||
else:
|
||||
if imag_sign == -1 and (not y) and x < 0.0:
|
||||
L1 = cmath.log(z) - 3.1415926535897932j
|
||||
else:
|
||||
L1 = cmath.log(z) - 6.2831853071795865j
|
||||
L2 = cmath.log(L1)
|
||||
return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2)
|
||||
|
||||
def _lambertw_series(ctx, z, k, tol):
|
||||
"""
|
||||
Return rough approximation for W_k(z) from an asymptotic series,
|
||||
sufficiently accurate for the Halley iteration to converge to
|
||||
the correct value.
|
||||
"""
|
||||
magz = ctx.mag(z)
|
||||
if (-10 < magz < 900) and (-1000 < k < 1000):
|
||||
# Near the branch point at -1/e
|
||||
if magz < 1 and abs(z+0.36787944117144) < 0.05:
|
||||
if k == 0 or (k == -1 and ctx._im(z) >= 0) or \
|
||||
(k == 1 and ctx._im(z) < 0):
|
||||
delta = ctx.sum_accurately(lambda: [z, ctx.exp(-1)])
|
||||
cancellation = -ctx.mag(delta)
|
||||
ctx.prec += cancellation
|
||||
# Use series given in Corless et al.
|
||||
p = ctx.sqrt(2*(ctx.e*z+1))
|
||||
ctx.prec -= cancellation
|
||||
u = {0:ctx.mpf(-1), 1:ctx.mpf(1)}
|
||||
a = {0:ctx.mpf(2), 1:ctx.mpf(-1)}
|
||||
if k != 0:
|
||||
p = -p
|
||||
s = ctx.zero
|
||||
# The series converges, so we could use it directly, but unless
|
||||
# *extremely* close, it is better to just use the first few
|
||||
# terms to get a good approximation for the iteration
|
||||
for l in xrange(max(2,cancellation)):
|
||||
if l not in u:
|
||||
a[l] = ctx.fsum(u[j]*u[l+1-j] for j in xrange(2,l))
|
||||
u[l] = (l-1)*(u[l-2]/2+a[l-2]/4)/(l+1)-a[l]/2-u[l-1]/(l+1)
|
||||
term = u[l] * p**l
|
||||
s += term
|
||||
if ctx.mag(term) < -tol:
|
||||
return s, True
|
||||
l += 1
|
||||
ctx.prec += cancellation//2
|
||||
return s, False
|
||||
if k == 0 or k == -1:
|
||||
return _lambertw_approx_hybrid(z, k), False
|
||||
if k == 0:
|
||||
if magz < -1:
|
||||
return z*(1-z), False
|
||||
L1 = ctx.ln(z)
|
||||
L2 = ctx.ln(L1)
|
||||
elif k == -1 and (not ctx._im(z)) and (-0.36787944117144 < ctx._re(z) < 0):
|
||||
L1 = ctx.ln(-z)
|
||||
return L1 - ctx.ln(-L1), False
|
||||
else:
|
||||
# This holds both as z -> 0 and z -> inf.
|
||||
# Relative error is O(1/log(z)).
|
||||
L1 = ctx.ln(z) + 2j*ctx.pi*k
|
||||
L2 = ctx.ln(L1)
|
||||
return L1 - L2 + L2/L1 + L2*(L2-2)/(2*L1**2), False
|
||||
|
||||
@defun
|
||||
def lambertw(ctx, z, k=0):
|
||||
z = ctx.convert(z)
|
||||
k = int(k)
|
||||
if not ctx.isnormal(z):
|
||||
return _lambertw_special(ctx, z, k)
|
||||
prec = ctx.prec
|
||||
ctx.prec += 20 + ctx.mag(k or 1)
|
||||
wp = ctx.prec
|
||||
tol = wp - 5
|
||||
w, done = _lambertw_series(ctx, z, k, tol)
|
||||
if not done:
|
||||
# Use Halley iteration to solve w*exp(w) = z
|
||||
two = ctx.mpf(2)
|
||||
for i in xrange(100):
|
||||
ew = ctx.exp(w)
|
||||
wew = w*ew
|
||||
wewz = wew-z
|
||||
wn = w - wewz/(wew+ew-(w+two)*wewz/(two*w+two))
|
||||
if ctx.mag(wn-w) <= ctx.mag(wn) - tol:
|
||||
w = wn
|
||||
break
|
||||
else:
|
||||
w = wn
|
||||
if i == 100:
|
||||
ctx.warn("Lambert W iteration failed to converge for z = %s" % z)
|
||||
ctx.prec = prec
|
||||
return +w
|
||||
|
||||
@defun_wrapped
|
||||
def bell(ctx, n, x=1):
|
||||
x = ctx.convert(x)
|
||||
if not n:
|
||||
if ctx.isnan(x):
|
||||
return x
|
||||
return type(x)(1)
|
||||
if ctx.isinf(x) or ctx.isinf(n) or ctx.isnan(x) or ctx.isnan(n):
|
||||
return x**n
|
||||
if n == 1: return x
|
||||
if n == 2: return x*(x+1)
|
||||
if x == 0: return ctx.sincpi(n)
|
||||
return _polyexp(ctx, n, x, True) / ctx.exp(x)
|
||||
|
||||
def _polyexp(ctx, n, x, extra=False):
|
||||
def _terms():
|
||||
if extra:
|
||||
yield ctx.sincpi(n)
|
||||
t = x
|
||||
k = 1
|
||||
while 1:
|
||||
yield k**n * t
|
||||
k += 1
|
||||
t = t*x/k
|
||||
return ctx.sum_accurately(_terms, check_step=4)
|
||||
|
||||
@defun_wrapped
|
||||
def polyexp(ctx, s, z):
|
||||
if ctx.isinf(z) or ctx.isinf(s) or ctx.isnan(z) or ctx.isnan(s):
|
||||
return z**s
|
||||
if z == 0: return z*s
|
||||
if s == 0: return ctx.expm1(z)
|
||||
if s == 1: return ctx.exp(z)*z
|
||||
if s == 2: return ctx.exp(z)*z*(z+1)
|
||||
return _polyexp(ctx, s, z)
|
||||
|
||||
@defun_wrapped
|
||||
def cyclotomic(ctx, n, z):
|
||||
n = int(n)
|
||||
if n < 0:
|
||||
raise ValueError("n cannot be negative")
|
||||
p = ctx.one
|
||||
if n == 0:
|
||||
return p
|
||||
if n == 1:
|
||||
return z - p
|
||||
if n == 2:
|
||||
return z + p
|
||||
# Use divisor product representation. Unfortunately, this sometimes
|
||||
# includes singularities for roots of unity, which we have to cancel out.
|
||||
# Matching zeros/poles pairwise, we have (1-z^a)/(1-z^b) ~ a/b + O(z-1).
|
||||
a_prod = 1
|
||||
b_prod = 1
|
||||
num_zeros = 0
|
||||
num_poles = 0
|
||||
for d in range(1,n+1):
|
||||
if not n % d:
|
||||
w = ctx.moebius(n//d)
|
||||
# Use powm1 because it is important that we get 0 only
|
||||
# if it really is exactly 0
|
||||
b = -ctx.powm1(z, d)
|
||||
if b:
|
||||
p *= b**w
|
||||
else:
|
||||
if w == 1:
|
||||
a_prod *= d
|
||||
num_zeros += 1
|
||||
elif w == -1:
|
||||
b_prod *= d
|
||||
num_poles += 1
|
||||
#print n, num_zeros, num_poles
|
||||
if num_zeros:
|
||||
if num_zeros > num_poles:
|
||||
p *= 0
|
||||
else:
|
||||
p *= a_prod
|
||||
p /= b_prod
|
||||
return p
|
||||
|
||||
@defun
|
||||
def mangoldt(ctx, n):
|
||||
r"""
|
||||
Evaluates the von Mangoldt function `\Lambda(n) = \log p`
|
||||
if `n = p^k` a power of a prime, and `\Lambda(n) = 0` otherwise.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> [mangoldt(n) for n in range(-2,3)]
|
||||
[0.0, 0.0, 0.0, 0.0, 0.6931471805599453094172321]
|
||||
>>> mangoldt(6)
|
||||
0.0
|
||||
>>> mangoldt(7)
|
||||
1.945910149055313305105353
|
||||
>>> mangoldt(8)
|
||||
0.6931471805599453094172321
|
||||
>>> fsum(mangoldt(n) for n in range(101))
|
||||
94.04531122935739224600493
|
||||
>>> fsum(mangoldt(n) for n in range(10001))
|
||||
10013.39669326311478372032
|
||||
|
||||
"""
|
||||
n = int(n)
|
||||
if n < 2:
|
||||
return ctx.zero
|
||||
if n % 2 == 0:
|
||||
# Must be a power of two
|
||||
if n & (n-1) == 0:
|
||||
return +ctx.ln2
|
||||
else:
|
||||
return ctx.zero
|
||||
# TODO: the following could be generalized into a perfect
|
||||
# power testing function
|
||||
# ---
|
||||
# Look for a small factor
|
||||
for p in (3,5,7,11,13,17,19,23,29,31):
|
||||
if not n % p:
|
||||
q, r = n // p, 0
|
||||
while q > 1:
|
||||
q, r = divmod(q, p)
|
||||
if r:
|
||||
return ctx.zero
|
||||
return ctx.ln(p)
|
||||
if ctx.isprime(n):
|
||||
return ctx.ln(n)
|
||||
# Obviously, we could use arbitrary-precision arithmetic for this...
|
||||
if n > 10**30:
|
||||
raise NotImplementedError
|
||||
k = 2
|
||||
while 1:
|
||||
p = int(n**(1./k) + 0.5)
|
||||
if p < 2:
|
||||
return ctx.zero
|
||||
if p ** k == n:
|
||||
if ctx.isprime(p):
|
||||
return ctx.ln(p)
|
||||
k += 1
|
||||
|
||||
@defun
|
||||
def stirling1(ctx, n, k, exact=False):
|
||||
v = ctx._stirling1(int(n), int(k))
|
||||
if exact:
|
||||
return int(v)
|
||||
else:
|
||||
return ctx.mpf(v)
|
||||
|
||||
@defun
|
||||
def stirling2(ctx, n, k, exact=False):
|
||||
v = ctx._stirling2(int(n), int(k))
|
||||
if exact:
|
||||
return int(v)
|
||||
else:
|
||||
return ctx.mpf(v)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,493 @@
|
||||
from .functions import defun, defun_wrapped
|
||||
|
||||
def _hermite_param(ctx, n, z, parabolic_cylinder):
|
||||
"""
|
||||
Combined calculation of the Hermite polynomial H_n(z) (and its
|
||||
generalization to complex n) and the parabolic cylinder
|
||||
function D.
|
||||
"""
|
||||
n, ntyp = ctx._convert_param(n)
|
||||
z = ctx.convert(z)
|
||||
q = -ctx.mpq_1_2
|
||||
# For re(z) > 0, 2F0 -- http://functions.wolfram.com/
|
||||
# HypergeometricFunctions/HermiteHGeneral/06/02/0009/
|
||||
# Otherwise, there is a reflection formula
|
||||
# 2F0 + http://functions.wolfram.com/HypergeometricFunctions/
|
||||
# HermiteHGeneral/16/01/01/0006/
|
||||
#
|
||||
# TODO:
|
||||
# An alternative would be to use
|
||||
# http://functions.wolfram.com/HypergeometricFunctions/
|
||||
# HermiteHGeneral/06/02/0006/
|
||||
#
|
||||
# Also, the 1F1 expansion
|
||||
# http://functions.wolfram.com/HypergeometricFunctions/
|
||||
# HermiteHGeneral/26/01/02/0001/
|
||||
# should probably be used for tiny z
|
||||
if not z:
|
||||
T1 = [2, ctx.pi], [n, 0.5], [], [q*(n-1)], [], [], 0
|
||||
if parabolic_cylinder:
|
||||
T1[1][0] += q*n
|
||||
return T1,
|
||||
can_use_2f0 = ctx.isnpint(-n) or ctx.re(z) > 0 or \
|
||||
(ctx.re(z) == 0 and ctx.im(z) > 0)
|
||||
expprec = ctx.prec*4 + 20
|
||||
if parabolic_cylinder:
|
||||
u = ctx.fmul(ctx.fmul(z,z,prec=expprec), -0.25, exact=True)
|
||||
w = ctx.fmul(z, ctx.sqrt(0.5,prec=expprec), prec=expprec)
|
||||
else:
|
||||
w = z
|
||||
w2 = ctx.fmul(w, w, prec=expprec)
|
||||
rw2 = ctx.fdiv(1, w2, prec=expprec)
|
||||
nrw2 = ctx.fneg(rw2, exact=True)
|
||||
nw = ctx.fneg(w, exact=True)
|
||||
if can_use_2f0:
|
||||
T1 = [2, w], [n, n], [], [], [q*n, q*(n-1)], [], nrw2
|
||||
terms = [T1]
|
||||
else:
|
||||
T1 = [2, nw], [n, n], [], [], [q*n, q*(n-1)], [], nrw2
|
||||
T2 = [2, ctx.pi, nw], [n+2, 0.5, 1], [], [q*n], [q*(n-1)], [1-q], w2
|
||||
terms = [T1,T2]
|
||||
# Multiply by prefactor for D_n
|
||||
if parabolic_cylinder:
|
||||
expu = ctx.exp(u)
|
||||
for i in range(len(terms)):
|
||||
terms[i][1][0] += q*n
|
||||
terms[i][0].append(expu)
|
||||
terms[i][1].append(1)
|
||||
return tuple(terms)
|
||||
|
||||
@defun
|
||||
def hermite(ctx, n, z, **kwargs):
|
||||
return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 0), [], **kwargs)
|
||||
|
||||
@defun
|
||||
def pcfd(ctx, n, z, **kwargs):
|
||||
r"""
|
||||
Gives the parabolic cylinder function in Whittaker's notation
|
||||
`D_n(z) = U(-n-1/2, z)` (see :func:`~mpmath.pcfu`).
|
||||
It solves the differential equation
|
||||
|
||||
.. math ::
|
||||
|
||||
y'' + \left(n + \frac{1}{2} - \frac{1}{4} z^2\right) y = 0.
|
||||
|
||||
and can be represented in terms of Hermite polynomials
|
||||
(see :func:`~mpmath.hermite`) as
|
||||
|
||||
.. math ::
|
||||
|
||||
D_n(z) = 2^{-n/2} e^{-z^2/4} H_n\left(\frac{z}{\sqrt{2}}\right).
|
||||
|
||||
**Plots**
|
||||
|
||||
.. literalinclude :: /plots/pcfd.py
|
||||
.. image :: /plots/pcfd.png
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> pcfd(0,0); pcfd(1,0); pcfd(2,0); pcfd(3,0)
|
||||
1.0
|
||||
0.0
|
||||
-1.0
|
||||
0.0
|
||||
>>> pcfd(4,0); pcfd(-3,0)
|
||||
3.0
|
||||
0.6266570686577501256039413
|
||||
>>> pcfd('1/2', 2+3j)
|
||||
(-5.363331161232920734849056 - 3.858877821790010714163487j)
|
||||
>>> pcfd(2, -10)
|
||||
1.374906442631438038871515e-9
|
||||
|
||||
Verifying the differential equation::
|
||||
|
||||
>>> n = mpf(2.5)
|
||||
>>> y = lambda z: pcfd(n,z)
|
||||
>>> z = 1.75
|
||||
>>> chop(diff(y,z,2) + (n+0.5-0.25*z**2)*y(z))
|
||||
0.0
|
||||
|
||||
Rational Taylor series expansion when `n` is an integer::
|
||||
|
||||
>>> taylor(lambda z: pcfd(5,z), 0, 7)
|
||||
[0.0, 15.0, 0.0, -13.75, 0.0, 3.96875, 0.0, -0.6015625]
|
||||
|
||||
"""
|
||||
return ctx.hypercomb(lambda: _hermite_param(ctx, n, z, 1), [], **kwargs)
|
||||
|
||||
@defun
|
||||
def pcfu(ctx, a, z, **kwargs):
|
||||
r"""
|
||||
Gives the parabolic cylinder function `U(a,z)`, which may be
|
||||
defined for `\Re(z) > 0` in terms of the confluent
|
||||
U-function (see :func:`~mpmath.hyperu`) by
|
||||
|
||||
.. math ::
|
||||
|
||||
U(a,z) = 2^{-\frac{1}{4}-\frac{a}{2}} e^{-\frac{1}{4} z^2}
|
||||
U\left(\frac{a}{2}+\frac{1}{4},
|
||||
\frac{1}{2}, \frac{1}{2}z^2\right)
|
||||
|
||||
or, for arbitrary `z`,
|
||||
|
||||
.. math ::
|
||||
|
||||
e^{-\frac{1}{4}z^2} U(a,z) =
|
||||
U(a,0) \,_1F_1\left(-\tfrac{a}{2}+\tfrac{1}{4};
|
||||
\tfrac{1}{2}; -\tfrac{1}{2}z^2\right) +
|
||||
U'(a,0) z \,_1F_1\left(-\tfrac{a}{2}+\tfrac{3}{4};
|
||||
\tfrac{3}{2}; -\tfrac{1}{2}z^2\right).
|
||||
|
||||
**Examples**
|
||||
|
||||
Connection to other functions::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> z = mpf(3)
|
||||
>>> pcfu(0.5,z)
|
||||
0.03210358129311151450551963
|
||||
>>> sqrt(pi/2)*exp(z**2/4)*erfc(z/sqrt(2))
|
||||
0.03210358129311151450551963
|
||||
>>> pcfu(0.5,-z)
|
||||
23.75012332835297233711255
|
||||
>>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2))
|
||||
23.75012332835297233711255
|
||||
>>> pcfu(0.5,-z)
|
||||
23.75012332835297233711255
|
||||
>>> sqrt(pi/2)*exp(z**2/4)*erfc(-z/sqrt(2))
|
||||
23.75012332835297233711255
|
||||
|
||||
"""
|
||||
n, _ = ctx._convert_param(a)
|
||||
return ctx.pcfd(-n-ctx.mpq_1_2, z)
|
||||
|
||||
@defun
|
||||
def pcfv(ctx, a, z, **kwargs):
|
||||
r"""
|
||||
Gives the parabolic cylinder function `V(a,z)`, which can be
|
||||
represented in terms of :func:`~mpmath.pcfu` as
|
||||
|
||||
.. math ::
|
||||
|
||||
V(a,z) = \frac{\Gamma(a+\tfrac{1}{2}) (U(a,-z)-\sin(\pi a) U(a,z)}{\pi}.
|
||||
|
||||
**Examples**
|
||||
|
||||
Wronskian relation between `U` and `V`::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> a, z = 2, 3
|
||||
>>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)
|
||||
0.7978845608028653558798921
|
||||
>>> sqrt(2/pi)
|
||||
0.7978845608028653558798921
|
||||
>>> a, z = 2.5, 3
|
||||
>>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)
|
||||
0.7978845608028653558798921
|
||||
>>> a, z = 0.25, -1
|
||||
>>> pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z)
|
||||
0.7978845608028653558798921
|
||||
>>> a, z = 2+1j, 2+3j
|
||||
>>> chop(pcfu(a,z)*diff(pcfv,(a,z),(0,1))-diff(pcfu,(a,z),(0,1))*pcfv(a,z))
|
||||
0.7978845608028653558798921
|
||||
|
||||
"""
|
||||
n, ntype = ctx._convert_param(a)
|
||||
z = ctx.convert(z)
|
||||
q = ctx.mpq_1_2
|
||||
r = ctx.mpq_1_4
|
||||
if ntype == 'Q' and ctx.isint(n*2):
|
||||
# Faster for half-integers
|
||||
def h():
|
||||
jz = ctx.fmul(z, -1j, exact=True)
|
||||
T1terms = _hermite_param(ctx, -n-q, z, 1)
|
||||
T2terms = _hermite_param(ctx, n-q, jz, 1)
|
||||
for T in T1terms:
|
||||
T[0].append(1j)
|
||||
T[1].append(1)
|
||||
T[3].append(q-n)
|
||||
u = ctx.expjpi((q*n-r)) * ctx.sqrt(2/ctx.pi)
|
||||
for T in T2terms:
|
||||
T[0].append(u)
|
||||
T[1].append(1)
|
||||
return T1terms + T2terms
|
||||
v = ctx.hypercomb(h, [], **kwargs)
|
||||
if ctx._is_real_type(n) and ctx._is_real_type(z):
|
||||
v = ctx._re(v)
|
||||
return v
|
||||
else:
|
||||
def h(n):
|
||||
w = ctx.square_exp_arg(z, -0.25)
|
||||
u = ctx.square_exp_arg(z, 0.5)
|
||||
e = ctx.exp(w)
|
||||
l = [ctx.pi, q, ctx.exp(w)]
|
||||
Y1 = l, [-q, n*q+r, 1], [r-q*n], [], [q*n+r], [q], u
|
||||
Y2 = l + [z], [-q, n*q-r, 1, 1], [1-r-q*n], [], [q*n+1-r], [1+q], u
|
||||
c, s = ctx.cospi_sinpi(r+q*n)
|
||||
Y1[0].append(s)
|
||||
Y2[0].append(c)
|
||||
for Y in (Y1, Y2):
|
||||
Y[1].append(1)
|
||||
Y[3].append(q-n)
|
||||
return Y1, Y2
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
|
||||
@defun
|
||||
def pcfw(ctx, a, z, **kwargs):
|
||||
r"""
|
||||
Gives the parabolic cylinder function `W(a,z)` defined in (DLMF 12.14).
|
||||
|
||||
**Examples**
|
||||
|
||||
Value at the origin::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> a = mpf(0.25)
|
||||
>>> pcfw(a,0)
|
||||
0.9722833245718180765617104
|
||||
>>> power(2,-0.75)*sqrt(abs(gamma(0.25+0.5j*a)/gamma(0.75+0.5j*a)))
|
||||
0.9722833245718180765617104
|
||||
>>> diff(pcfw,(a,0),(0,1))
|
||||
-0.5142533944210078966003624
|
||||
>>> -power(2,-0.25)*sqrt(abs(gamma(0.75+0.5j*a)/gamma(0.25+0.5j*a)))
|
||||
-0.5142533944210078966003624
|
||||
|
||||
"""
|
||||
n, _ = ctx._convert_param(a)
|
||||
z = ctx.convert(z)
|
||||
def terms():
|
||||
phi2 = ctx.arg(ctx.gamma(0.5 + ctx.j*n))
|
||||
phi2 = (ctx.loggamma(0.5+ctx.j*n) - ctx.loggamma(0.5-ctx.j*n))/2j
|
||||
rho = ctx.pi/8 + 0.5*phi2
|
||||
# XXX: cancellation computing k
|
||||
k = ctx.sqrt(1 + ctx.exp(2*ctx.pi*n)) - ctx.exp(ctx.pi*n)
|
||||
C = ctx.sqrt(k/2) * ctx.exp(0.25*ctx.pi*n)
|
||||
yield C * ctx.expj(rho) * ctx.pcfu(ctx.j*n, z*ctx.expjpi(-0.25))
|
||||
yield C * ctx.expj(-rho) * ctx.pcfu(-ctx.j*n, z*ctx.expjpi(0.25))
|
||||
v = ctx.sum_accurately(terms)
|
||||
if ctx._is_real_type(n) and ctx._is_real_type(z):
|
||||
v = ctx._re(v)
|
||||
return v
|
||||
|
||||
"""
|
||||
Even/odd PCFs. Useful?
|
||||
|
||||
@defun
|
||||
def pcfy1(ctx, a, z, **kwargs):
|
||||
a, _ = ctx._convert_param(n)
|
||||
z = ctx.convert(z)
|
||||
def h():
|
||||
w = ctx.square_exp_arg(z)
|
||||
w1 = ctx.fmul(w, -0.25, exact=True)
|
||||
w2 = ctx.fmul(w, 0.5, exact=True)
|
||||
e = ctx.exp(w1)
|
||||
return [e], [1], [], [], [ctx.mpq_1_2*a+ctx.mpq_1_4], [ctx.mpq_1_2], w2
|
||||
return ctx.hypercomb(h, [], **kwargs)
|
||||
|
||||
@defun
|
||||
def pcfy2(ctx, a, z, **kwargs):
|
||||
a, _ = ctx._convert_param(n)
|
||||
z = ctx.convert(z)
|
||||
def h():
|
||||
w = ctx.square_exp_arg(z)
|
||||
w1 = ctx.fmul(w, -0.25, exact=True)
|
||||
w2 = ctx.fmul(w, 0.5, exact=True)
|
||||
e = ctx.exp(w1)
|
||||
return [e, z], [1, 1], [], [], [ctx.mpq_1_2*a+ctx.mpq_3_4], \
|
||||
[ctx.mpq_3_2], w2
|
||||
return ctx.hypercomb(h, [], **kwargs)
|
||||
"""
|
||||
|
||||
@defun_wrapped
|
||||
def gegenbauer(ctx, n, a, z, **kwargs):
|
||||
# Special cases: a+0.5, a*2 poles
|
||||
if ctx.isnpint(a):
|
||||
return 0*(z+n)
|
||||
if ctx.isnpint(a+0.5):
|
||||
# TODO: something else is required here
|
||||
# E.g.: gegenbauer(-2, -0.5, 3) == -12
|
||||
if ctx.isnpint(n+1):
|
||||
raise NotImplementedError("Gegenbauer function with two limits")
|
||||
def h(a):
|
||||
a2 = 2*a
|
||||
T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z)
|
||||
return [T]
|
||||
return ctx.hypercomb(h, [a], **kwargs)
|
||||
def h(n):
|
||||
a2 = 2*a
|
||||
T = [], [], [n+a2], [n+1, a2], [-n, n+a2], [a+0.5], 0.5*(1-z)
|
||||
return [T]
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def jacobi(ctx, n, a, b, x, **kwargs):
|
||||
if not ctx.isnpint(a):
|
||||
def h(n):
|
||||
return (([], [], [a+n+1], [n+1, a+1], [-n, a+b+n+1], [a+1], (1-x)*0.5),)
|
||||
return ctx.hypercomb(h, [n], **kwargs)
|
||||
if not ctx.isint(b):
|
||||
def h(n, a):
|
||||
return (([], [], [-b], [n+1, -b-n], [-n, a+b+n+1], [b+1], (x+1)*0.5),)
|
||||
return ctx.hypercomb(h, [n, a], **kwargs)
|
||||
# XXX: determine appropriate limit
|
||||
return ctx.binomial(n+a,n) * ctx.hyp2f1(-n,1+n+a+b,a+1,(1-x)/2, **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def laguerre(ctx, n, a, z, **kwargs):
|
||||
# XXX: limits, poles
|
||||
#if ctx.isnpint(n):
|
||||
# return 0*(a+z)
|
||||
def h(a):
|
||||
return (([], [], [a+n+1], [a+1, n+1], [-n], [a+1], z),)
|
||||
return ctx.hypercomb(h, [a], **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def legendre(ctx, n, x, **kwargs):
|
||||
if ctx.isint(n):
|
||||
n = int(n)
|
||||
# Accuracy near zeros
|
||||
if (n + (n < 0)) & 1:
|
||||
if not x:
|
||||
return x
|
||||
mag = ctx.mag(x)
|
||||
if mag < -2*ctx.prec-10:
|
||||
return x
|
||||
if mag < -5:
|
||||
ctx.prec += -mag
|
||||
return ctx.hyp2f1(-n,n+1,1,(1-x)/2, **kwargs)
|
||||
|
||||
@defun
|
||||
def legenp(ctx, n, m, z, type=2, **kwargs):
|
||||
# Legendre function, 1st kind
|
||||
n = ctx.convert(n)
|
||||
m = ctx.convert(m)
|
||||
# Faster
|
||||
if not m:
|
||||
return ctx.legendre(n, z, **kwargs)
|
||||
# TODO: correct evaluation at singularities
|
||||
if type == 2:
|
||||
def h(n,m):
|
||||
g = m*0.5
|
||||
T = [1+z, 1-z], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z)
|
||||
return (T,)
|
||||
return ctx.hypercomb(h, [n,m], **kwargs)
|
||||
if type == 3:
|
||||
def h(n,m):
|
||||
g = m*0.5
|
||||
T = [z+1, z-1], [g, -g], [], [1-m], [-n, n+1], [1-m], 0.5*(1-z)
|
||||
return (T,)
|
||||
return ctx.hypercomb(h, [n,m], **kwargs)
|
||||
raise ValueError("requires type=2 or type=3")
|
||||
|
||||
@defun
|
||||
def legenq(ctx, n, m, z, type=2, **kwargs):
|
||||
# Legendre function, 2nd kind
|
||||
n = ctx.convert(n)
|
||||
m = ctx.convert(m)
|
||||
z = ctx.convert(z)
|
||||
if z in (1, -1):
|
||||
#if ctx.isint(m):
|
||||
# return ctx.nan
|
||||
#return ctx.inf # unsigned
|
||||
return ctx.nan
|
||||
if type == 2:
|
||||
def h(n, m):
|
||||
cos, sin = ctx.cospi_sinpi(m)
|
||||
s = 2 * sin / ctx.pi
|
||||
c = cos
|
||||
a = 1+z
|
||||
b = 1-z
|
||||
u = m/2
|
||||
w = (1-z)/2
|
||||
T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \
|
||||
[-n, n+1], [1-m], w
|
||||
T2 = [-s, a, b], [-1, -u, u], [n+m+1], [n-m+1, m+1], \
|
||||
[-n, n+1], [m+1], w
|
||||
return T1, T2
|
||||
return ctx.hypercomb(h, [n, m], **kwargs)
|
||||
if type == 3:
|
||||
# The following is faster when there only is a single series
|
||||
# Note: not valid for -1 < z < 0 (?)
|
||||
if abs(z) > 1:
|
||||
def h(n, m):
|
||||
T1 = [ctx.expjpi(m), 2, ctx.pi, z, z-1, z+1], \
|
||||
[1, -n-1, 0.5, -n-m-1, 0.5*m, 0.5*m], \
|
||||
[n+m+1], [n+1.5], \
|
||||
[0.5*(2+n+m), 0.5*(1+n+m)], [n+1.5], z**(-2)
|
||||
return [T1]
|
||||
return ctx.hypercomb(h, [n, m], **kwargs)
|
||||
else:
|
||||
# not valid for 1 < z < inf ?
|
||||
def h(n, m):
|
||||
s = 2 * ctx.sinpi(m) / ctx.pi
|
||||
c = ctx.expjpi(m)
|
||||
a = 1+z
|
||||
b = z-1
|
||||
u = m/2
|
||||
w = (1-z)/2
|
||||
T1 = [s, c, a, b], [-1, 1, u, -u], [], [1-m], \
|
||||
[-n, n+1], [1-m], w
|
||||
T2 = [-s, c, a, b], [-1, 1, -u, u], [n+m+1], [n-m+1, m+1], \
|
||||
[-n, n+1], [m+1], w
|
||||
return T1, T2
|
||||
return ctx.hypercomb(h, [n, m], **kwargs)
|
||||
raise ValueError("requires type=2 or type=3")
|
||||
|
||||
@defun_wrapped
|
||||
def chebyt(ctx, n, x, **kwargs):
|
||||
if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1:
|
||||
return x * 0
|
||||
return ctx.hyp2f1(-n,n,(1,2),(1-x)/2, **kwargs)
|
||||
|
||||
@defun_wrapped
|
||||
def chebyu(ctx, n, x, **kwargs):
|
||||
if (not x) and ctx.isint(n) and int(ctx._re(n)) % 2 == 1:
|
||||
return x * 0
|
||||
return (n+1) * ctx.hyp2f1(-n, n+2, (3,2), (1-x)/2, **kwargs)
|
||||
|
||||
@defun
|
||||
def spherharm(ctx, l, m, theta, phi, **kwargs):
|
||||
l = ctx.convert(l)
|
||||
m = ctx.convert(m)
|
||||
theta = ctx.convert(theta)
|
||||
phi = ctx.convert(phi)
|
||||
l_isint = ctx.isint(l)
|
||||
l_natural = l_isint and l >= 0
|
||||
m_isint = ctx.isint(m)
|
||||
if l_isint and l < 0 and m_isint:
|
||||
return ctx.spherharm(-(l+1), m, theta, phi, **kwargs)
|
||||
if theta == 0 and m_isint and m < 0:
|
||||
return ctx.zero * 1j
|
||||
if l_natural and m_isint:
|
||||
if abs(m) > l:
|
||||
return ctx.zero * 1j
|
||||
# http://functions.wolfram.com/Polynomials/
|
||||
# SphericalHarmonicY/26/01/02/0004/
|
||||
def h(l,m):
|
||||
absm = abs(m)
|
||||
C = [-1, ctx.expj(m*phi),
|
||||
(2*l+1)*ctx.fac(l+absm)/ctx.pi/ctx.fac(l-absm),
|
||||
ctx.sin(theta)**2,
|
||||
ctx.fac(absm), 2]
|
||||
P = [0.5*m*(ctx.sign(m)+1), 1, 0.5, 0.5*absm, -1, -absm-1]
|
||||
return ((C, P, [], [], [absm-l, l+absm+1], [absm+1],
|
||||
ctx.sin(0.5*theta)**2),)
|
||||
else:
|
||||
# http://functions.wolfram.com/HypergeometricFunctions/
|
||||
# SphericalHarmonicYGeneral/26/01/02/0001/
|
||||
def h(l,m):
|
||||
if ctx.isnpint(l-m+1) or ctx.isnpint(l+m+1) or ctx.isnpint(1-m):
|
||||
return (([0], [-1], [], [], [], [], 0),)
|
||||
cos, sin = ctx.cos_sin(0.5*theta)
|
||||
C = [0.5*ctx.expj(m*phi), (2*l+1)/ctx.pi,
|
||||
ctx.gamma(l-m+1), ctx.gamma(l+m+1),
|
||||
cos**2, sin**2]
|
||||
P = [1, 0.5, 0.5, -0.5, 0.5*m, -0.5*m]
|
||||
return ((C, P, [], [1-m], [-l,l+1], [1-m], sin**2),)
|
||||
return ctx.hypercomb(h, [l,m], **kwargs)
|
||||
@@ -0,0 +1,280 @@
|
||||
from .functions import defun, defun_wrapped
|
||||
|
||||
@defun
|
||||
def qp(ctx, a, q=None, n=None, **kwargs):
|
||||
r"""
|
||||
Evaluates the q-Pochhammer symbol (or q-rising factorial)
|
||||
|
||||
.. math ::
|
||||
|
||||
(a; q)_n = \prod_{k=0}^{n-1} (1-a q^k)
|
||||
|
||||
where `n = \infty` is permitted if `|q| < 1`. Called with two arguments,
|
||||
``qp(a,q)`` computes `(a;q)_{\infty}`; with a single argument, ``qp(q)``
|
||||
computes `(q;q)_{\infty}`. The special case
|
||||
|
||||
.. math ::
|
||||
|
||||
\phi(q) = (q; q)_{\infty} = \prod_{k=1}^{\infty} (1-q^k) =
|
||||
\sum_{k=-\infty}^{\infty} (-1)^k q^{(3k^2-k)/2}
|
||||
|
||||
is also known as the Euler function, or (up to a factor `q^{-1/24}`)
|
||||
the Dedekind eta function.
|
||||
|
||||
**Examples**
|
||||
|
||||
If `n` is a positive integer, the function amounts to a finite product::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> qp(2,3,5)
|
||||
-725305.0
|
||||
>>> fprod(1-2*3**k for k in range(5))
|
||||
-725305.0
|
||||
>>> qp(2,3,0)
|
||||
1.0
|
||||
|
||||
Complex arguments are allowed::
|
||||
|
||||
>>> qp(2-1j, 0.75j)
|
||||
(0.4628842231660149089976379 + 4.481821753552703090628793j)
|
||||
|
||||
The regular Pochhammer symbol `(a)_n` is obtained in the
|
||||
following limit as `q \to 1`::
|
||||
|
||||
>>> a, n = 4, 7
|
||||
>>> limit(lambda q: qp(q**a,q,n) / (1-q)**n, 1)
|
||||
604800.0
|
||||
>>> rf(a,n)
|
||||
604800.0
|
||||
|
||||
The Taylor series of the reciprocal Euler function gives
|
||||
the partition function `P(n)`, i.e. the number of ways of writing
|
||||
`n` as a sum of positive integers::
|
||||
|
||||
>>> taylor(lambda q: 1/qp(q), 0, 10)
|
||||
[1.0, 1.0, 2.0, 3.0, 5.0, 7.0, 11.0, 15.0, 22.0, 30.0, 42.0]
|
||||
|
||||
Special values include::
|
||||
|
||||
>>> qp(0)
|
||||
1.0
|
||||
>>> findroot(diffun(qp), -0.4) # location of maximum
|
||||
-0.4112484791779547734440257
|
||||
>>> qp(_)
|
||||
1.228348867038575112586878
|
||||
|
||||
The q-Pochhammer symbol is related to the Jacobi theta functions.
|
||||
For example, the following identity holds::
|
||||
|
||||
>>> q = mpf(0.5) # arbitrary
|
||||
>>> qp(q)
|
||||
0.2887880950866024212788997
|
||||
>>> root(3,-2)*root(q,-24)*jtheta(2,pi/6,root(q,6))
|
||||
0.2887880950866024212788997
|
||||
|
||||
"""
|
||||
a = ctx.convert(a)
|
||||
if n is None:
|
||||
n = ctx.inf
|
||||
else:
|
||||
n = ctx.convert(n)
|
||||
if n < 0:
|
||||
raise ValueError("n cannot be negative")
|
||||
if q is None:
|
||||
q = a
|
||||
else:
|
||||
q = ctx.convert(q)
|
||||
if n == 0:
|
||||
return ctx.one + 0*(a+q)
|
||||
infinite = (n == ctx.inf)
|
||||
same = (a == q)
|
||||
if infinite:
|
||||
if abs(q) >= 1:
|
||||
if same and (q == -1 or q == 1):
|
||||
return ctx.zero * q
|
||||
raise ValueError("q-function only defined for |q| < 1")
|
||||
elif q == 0:
|
||||
return ctx.one - a
|
||||
maxterms = kwargs.get('maxterms', 50*ctx.prec)
|
||||
if infinite and same:
|
||||
# Euler's pentagonal theorem
|
||||
def terms():
|
||||
t = 1
|
||||
yield t
|
||||
k = 1
|
||||
x1 = q
|
||||
x2 = q**2
|
||||
while 1:
|
||||
yield (-1)**k * x1
|
||||
yield (-1)**k * x2
|
||||
x1 *= q**(3*k+1)
|
||||
x2 *= q**(3*k+2)
|
||||
k += 1
|
||||
if k > maxterms:
|
||||
raise ctx.NoConvergence
|
||||
return ctx.sum_accurately(terms)
|
||||
# return ctx.nprod(lambda k: 1-a*q**k, [0,n-1])
|
||||
def factors():
|
||||
k = 0
|
||||
r = ctx.one
|
||||
while 1:
|
||||
yield 1 - a*r
|
||||
r *= q
|
||||
k += 1
|
||||
if k >= n:
|
||||
return
|
||||
if k > maxterms:
|
||||
raise ctx.NoConvergence
|
||||
return ctx.mul_accurately(factors)
|
||||
|
||||
@defun_wrapped
|
||||
def qgamma(ctx, z, q, **kwargs):
|
||||
r"""
|
||||
Evaluates the q-gamma function
|
||||
|
||||
.. math ::
|
||||
|
||||
\Gamma_q(z) = \frac{(q; q)_{\infty}}{(q^z; q)_{\infty}} (1-q)^{1-z}.
|
||||
|
||||
|
||||
**Examples**
|
||||
|
||||
Evaluation for real and complex arguments::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> qgamma(4,0.75)
|
||||
4.046875
|
||||
>>> qgamma(6,6)
|
||||
121226245.0
|
||||
>>> qgamma(3+4j, 0.5j)
|
||||
(0.1663082382255199834630088 + 0.01952474576025952984418217j)
|
||||
|
||||
The q-gamma function satisfies a functional equation similar
|
||||
to that of the ordinary gamma function::
|
||||
|
||||
>>> q = mpf(0.25)
|
||||
>>> z = mpf(2.5)
|
||||
>>> qgamma(z+1,q)
|
||||
1.428277424823760954685912
|
||||
>>> (1-q**z)/(1-q)*qgamma(z,q)
|
||||
1.428277424823760954685912
|
||||
|
||||
"""
|
||||
if abs(q) > 1:
|
||||
return ctx.qgamma(z,1/q)*q**((z-2)*(z-1)*0.5)
|
||||
return ctx.qp(q, q, None, **kwargs) / \
|
||||
ctx.qp(q**z, q, None, **kwargs) * (1-q)**(1-z)
|
||||
|
||||
@defun_wrapped
|
||||
def qfac(ctx, z, q, **kwargs):
|
||||
r"""
|
||||
Evaluates the q-factorial,
|
||||
|
||||
.. math ::
|
||||
|
||||
[n]_q! = (1+q)(1+q+q^2)\cdots(1+q+\cdots+q^{n-1})
|
||||
|
||||
or more generally
|
||||
|
||||
.. math ::
|
||||
|
||||
[z]_q! = \frac{(q;q)_z}{(1-q)^z}.
|
||||
|
||||
**Examples**
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> qfac(0,0)
|
||||
1.0
|
||||
>>> qfac(4,3)
|
||||
2080.0
|
||||
>>> qfac(5,6)
|
||||
121226245.0
|
||||
>>> qfac(1+1j, 2+1j)
|
||||
(0.4370556551322672478613695 + 0.2609739839216039203708921j)
|
||||
|
||||
"""
|
||||
if ctx.isint(z) and ctx._re(z) > 0:
|
||||
n = int(ctx._re(z))
|
||||
return ctx.qp(q, q, n, **kwargs) / (1-q)**n
|
||||
return ctx.qgamma(z+1, q, **kwargs)
|
||||
|
||||
@defun
|
||||
def qhyper(ctx, a_s, b_s, q, z, **kwargs):
|
||||
r"""
|
||||
Evaluates the basic hypergeometric series or hypergeometric q-series
|
||||
|
||||
.. math ::
|
||||
|
||||
\,_r\phi_s \left[\begin{matrix}
|
||||
a_1 & a_2 & \ldots & a_r \\
|
||||
b_1 & b_2 & \ldots & b_s
|
||||
\end{matrix} ; q,z \right] =
|
||||
\sum_{n=0}^\infty
|
||||
\frac{(a_1;q)_n, \ldots, (a_r;q)_n}
|
||||
{(b_1;q)_n, \ldots, (b_s;q)_n}
|
||||
\left((-1)^n q^{n\choose 2}\right)^{1+s-r}
|
||||
\frac{z^n}{(q;q)_n}
|
||||
|
||||
where `(a;q)_n` denotes the q-Pochhammer symbol (see :func:`~mpmath.qp`).
|
||||
|
||||
**Examples**
|
||||
|
||||
Evaluation works for real and complex arguments::
|
||||
|
||||
>>> from mpmath import *
|
||||
>>> mp.dps = 25; mp.pretty = True
|
||||
>>> qhyper([0.5], [2.25], 0.25, 4)
|
||||
-0.1975849091263356009534385
|
||||
>>> qhyper([0.5], [2.25], 0.25-0.25j, 4)
|
||||
(2.806330244925716649839237 + 3.568997623337943121769938j)
|
||||
>>> qhyper([1+j], [2,3+0.5j], 0.25, 3+4j)
|
||||
(9.112885171773400017270226 - 1.272756997166375050700388j)
|
||||
|
||||
Comparing with a summation of the defining series, using
|
||||
:func:`~mpmath.nsum`::
|
||||
|
||||
>>> b, q, z = 3, 0.25, 0.5
|
||||
>>> qhyper([], [b], q, z)
|
||||
0.6221136748254495583228324
|
||||
>>> nsum(lambda n: z**n / qp(q,q,n)/qp(b,q,n) * q**(n*(n-1)), [0,inf])
|
||||
0.6221136748254495583228324
|
||||
|
||||
"""
|
||||
#a_s = [ctx._convert_param(a)[0] for a in a_s]
|
||||
#b_s = [ctx._convert_param(b)[0] for b in b_s]
|
||||
#q = ctx._convert_param(q)[0]
|
||||
a_s = [ctx.convert(a) for a in a_s]
|
||||
b_s = [ctx.convert(b) for b in b_s]
|
||||
q = ctx.convert(q)
|
||||
z = ctx.convert(z)
|
||||
r = len(a_s)
|
||||
s = len(b_s)
|
||||
d = 1+s-r
|
||||
maxterms = kwargs.get('maxterms', 50*ctx.prec)
|
||||
def terms():
|
||||
t = ctx.one
|
||||
yield t
|
||||
qk = 1
|
||||
k = 0
|
||||
x = 1
|
||||
while 1:
|
||||
for a in a_s:
|
||||
p = 1 - a*qk
|
||||
t *= p
|
||||
for b in b_s:
|
||||
p = 1 - b*qk
|
||||
if not p:
|
||||
raise ValueError
|
||||
t /= p
|
||||
t *= z
|
||||
x *= (-1)**d * qk ** d
|
||||
qk *= q
|
||||
t /= (1 - qk)
|
||||
k += 1
|
||||
yield t * x
|
||||
if k > maxterms:
|
||||
raise ctx.NoConvergence
|
||||
return ctx.sum_accurately(terms)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,32 @@
|
||||
from .functions import defun_wrapped
|
||||
|
||||
@defun_wrapped
|
||||
def squarew(ctx, t, amplitude=1, period=1):
|
||||
P = period
|
||||
A = amplitude
|
||||
return A*((-1)**ctx.floor(2*t/P))
|
||||
|
||||
@defun_wrapped
|
||||
def trianglew(ctx, t, amplitude=1, period=1):
|
||||
A = amplitude
|
||||
P = period
|
||||
|
||||
return 2*A*(0.5 - ctx.fabs(1 - 2*ctx.frac(t/P + 0.25)))
|
||||
|
||||
@defun_wrapped
|
||||
def sawtoothw(ctx, t, amplitude=1, period=1):
|
||||
A = amplitude
|
||||
P = period
|
||||
return A*ctx.frac(t/P)
|
||||
|
||||
@defun_wrapped
|
||||
def unit_triangle(ctx, t, amplitude=1):
|
||||
A = amplitude
|
||||
if t <= -1 or t >= 1:
|
||||
return ctx.zero
|
||||
return A*(-ctx.fabs(t) + 1)
|
||||
|
||||
@defun_wrapped
|
||||
def sigmoid(ctx, t, amplitude=1):
|
||||
A = amplitude
|
||||
return A / (1 + ctx.exp(-t))
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user