1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108 |
- from .functions import defun, defun_wrapped
- @defun
- def j0(ctx, x):
- """Computes the Bessel function `J_0(x)`. See :func:`~mpmath.besselj`."""
- return ctx.besselj(0, x)
- @defun
- def j1(ctx, x):
- """Computes the Bessel function `J_1(x)`. See :func:`~mpmath.besselj`."""
- return ctx.besselj(1, x)
- @defun
- def besselj(ctx, n, z, derivative=0, **kwargs):
- if type(n) is int:
- n_isint = True
- else:
- n = ctx.convert(n)
- n_isint = ctx.isint(n)
- if n_isint:
- n = int(ctx._re(n))
- if n_isint and n < 0:
- return (-1)**n * ctx.besselj(-n, z, derivative, **kwargs)
- z = ctx.convert(z)
- M = ctx.mag(z)
- if derivative:
- d = ctx.convert(derivative)
- # TODO: the integer special-casing shouldn't be necessary.
- # However, the hypergeometric series gets inaccurate for large d
- # because of inaccurate pole cancellation at a pole far from
- # zero (needs to be fixed in hypercomb or hypsum)
- if ctx.isint(d) and d >= 0:
- d = int(d)
- orig = ctx.prec
- try:
- ctx.prec += 15
- v = ctx.fsum((-1)**k * ctx.binomial(d,k) * ctx.besselj(2*k+n-d,z)
- for k in range(d+1))
- finally:
- ctx.prec = orig
- v *= ctx.mpf(2)**(-d)
- else:
- def h(n,d):
- r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), -0.25, exact=True)
- B = [0.5*(n-d+1), 0.5*(n-d+2)]
- T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[],B,[(n+1)*0.5,(n+2)*0.5],B+[n+1],r)]
- return T
- v = ctx.hypercomb(h, [n,d], **kwargs)
- else:
- # Fast case: J_n(x), n int, appropriate magnitude for fixed-point calculation
- if (not derivative) and n_isint and abs(M) < 10 and abs(n) < 20:
- try:
- return ctx._besselj(n, z)
- except NotImplementedError:
- pass
- if not z:
- if not n:
- v = ctx.one + n+z
- elif ctx.re(n) > 0:
- v = n*z
- else:
- v = ctx.inf + z + n
- else:
- #v = 0
- orig = ctx.prec
- try:
- # XXX: workaround for accuracy in low level hypergeometric series
- # when alternating, large arguments
- ctx.prec += min(3*abs(M), ctx.prec)
- w = ctx.fmul(z, 0.5, exact=True)
- def h(n):
- r = ctx.fneg(ctx.fmul(w, w, prec=max(0,ctx.prec+M)), exact=True)
- return [([w], [n], [], [n+1], [], [n+1], r)]
- v = ctx.hypercomb(h, [n], **kwargs)
- finally:
- ctx.prec = orig
- v = +v
- return v
- @defun
- def besseli(ctx, n, z, derivative=0, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- if not z:
- if derivative:
- raise ValueError
- if not n:
- # I(0,0) = 1
- return 1+n+z
- if ctx.isint(n):
- return 0*(n+z)
- r = ctx.re(n)
- if r == 0:
- return ctx.nan*(n+z)
- elif r > 0:
- return 0*(n+z)
- else:
- return ctx.inf+(n+z)
- M = ctx.mag(z)
- if derivative:
- d = ctx.convert(derivative)
- def h(n,d):
- r = ctx.fmul(ctx.fmul(z, z, prec=ctx.prec+M), 0.25, exact=True)
- B = [0.5*(n-d+1), 0.5*(n-d+2), n+1]
- T = [([2,ctx.pi,z],[d-2*n,0.5,n-d],[n+1],B,[(n+1)*0.5,(n+2)*0.5],B,r)]
- return T
- v = ctx.hypercomb(h, [n,d], **kwargs)
- else:
- def h(n):
- w = ctx.fmul(z, 0.5, exact=True)
- r = ctx.fmul(w, w, prec=max(0,ctx.prec+M))
- return [([w], [n], [], [n+1], [], [n+1], r)]
- v = ctx.hypercomb(h, [n], **kwargs)
- return v
- @defun_wrapped
- def bessely(ctx, n, z, derivative=0, **kwargs):
- if not z:
- if derivative:
- # Not implemented
- raise ValueError
- if not n:
- # ~ log(z/2)
- return -ctx.inf + (n+z)
- if ctx.im(n):
- return ctx.nan * (n+z)
- r = ctx.re(n)
- q = n+0.5
- if ctx.isint(q):
- if n > 0:
- return -ctx.inf + (n+z)
- else:
- return 0 * (n+z)
- if r < 0 and int(ctx.floor(q)) % 2:
- return ctx.inf + (n+z)
- else:
- return ctx.ninf + (n+z)
- # XXX: use hypercomb
- ctx.prec += 10
- m, d = ctx.nint_distance(n)
- if d < -ctx.prec:
- h = +ctx.eps
- ctx.prec *= 2
- n += h
- elif d < 0:
- ctx.prec -= d
- # TODO: avoid cancellation for imaginary arguments
- cos, sin = ctx.cospi_sinpi(n)
- return (ctx.besselj(n,z,derivative,**kwargs)*cos - \
- ctx.besselj(-n,z,derivative,**kwargs))/sin
- @defun_wrapped
- def besselk(ctx, n, z, **kwargs):
- if not z:
- return ctx.inf
- M = ctx.mag(z)
- if M < 1:
- # Represent as limit definition
- def h(n):
- r = (z/2)**2
- T1 = [z, 2], [-n, n-1], [n], [], [], [1-n], r
- T2 = [z, 2], [n, -n-1], [-n], [], [], [1+n], r
- return T1, T2
- # We could use the limit definition always, but it leads
- # to very bad cancellation (of exponentially large terms)
- # for large real z
- # Instead represent in terms of 2F0
- else:
- ctx.prec += M
- def h(n):
- return [([ctx.pi/2, z, ctx.exp(-z)], [0.5,-0.5,1], [], [], \
- [n+0.5, 0.5-n], [], -1/(2*z))]
- return ctx.hypercomb(h, [n], **kwargs)
- @defun_wrapped
- def hankel1(ctx,n,x,**kwargs):
- return ctx.besselj(n,x,**kwargs) + ctx.j*ctx.bessely(n,x,**kwargs)
- @defun_wrapped
- def hankel2(ctx,n,x,**kwargs):
- return ctx.besselj(n,x,**kwargs) - ctx.j*ctx.bessely(n,x,**kwargs)
- @defun_wrapped
- def whitm(ctx,k,m,z,**kwargs):
- if z == 0:
- # M(k,m,z) = 0^(1/2+m)
- if ctx.re(m) > -0.5:
- return z
- elif ctx.re(m) < -0.5:
- return ctx.inf + z
- else:
- return ctx.nan * z
- x = ctx.fmul(-0.5, z, exact=True)
- y = 0.5+m
- return ctx.exp(x) * z**y * ctx.hyp1f1(y-k, 1+2*m, z, **kwargs)
- @defun_wrapped
- def whitw(ctx,k,m,z,**kwargs):
- if z == 0:
- g = abs(ctx.re(m))
- if g < 0.5:
- return z
- elif g > 0.5:
- return ctx.inf + z
- else:
- return ctx.nan * z
- x = ctx.fmul(-0.5, z, exact=True)
- y = 0.5+m
- return ctx.exp(x) * z**y * ctx.hyperu(y-k, 1+2*m, z, **kwargs)
- @defun
- def hyperu(ctx, a, b, z, **kwargs):
- a, atype = ctx._convert_param(a)
- b, btype = ctx._convert_param(b)
- z = ctx.convert(z)
- if not z:
- if ctx.re(b) <= 1:
- return ctx.gammaprod([1-b],[a-b+1])
- else:
- return ctx.inf + z
- bb = 1+a-b
- bb, bbtype = ctx._convert_param(bb)
- try:
- orig = ctx.prec
- try:
- ctx.prec += 10
- v = ctx.hypsum(2, 0, (atype, bbtype), [a, bb], -1/z, maxterms=ctx.prec)
- return v / z**a
- finally:
- ctx.prec = orig
- except ctx.NoConvergence:
- pass
- def h(a,b):
- w = ctx.sinpi(b)
- T1 = ([ctx.pi,w],[1,-1],[],[a-b+1,b],[a],[b],z)
- T2 = ([-ctx.pi,w,z],[1,-1,1-b],[],[a,2-b],[a-b+1],[2-b],z)
- return T1, T2
- return ctx.hypercomb(h, [a,b], **kwargs)
- @defun
- def struveh(ctx,n,z, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- # http://functions.wolfram.com/Bessel-TypeFunctions/StruveH/26/01/02/
- def h(n):
- return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], -(z/2)**2)]
- return ctx.hypercomb(h, [n], **kwargs)
- @defun
- def struvel(ctx,n,z, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- # http://functions.wolfram.com/Bessel-TypeFunctions/StruveL/26/01/02/
- def h(n):
- return [([z/2, 0.5*ctx.sqrt(ctx.pi)], [n+1, -1], [], [n+1.5], [1], [1.5, n+1.5], (z/2)**2)]
- return ctx.hypercomb(h, [n], **kwargs)
- def _anger(ctx,which,v,z,**kwargs):
- v = ctx._convert_param(v)[0]
- z = ctx.convert(z)
- def h(v):
- b = ctx.mpq_1_2
- u = v*b
- m = b*3
- a1,a2,b1,b2 = m-u, m+u, 1-u, 1+u
- c, s = ctx.cospi_sinpi(u)
- if which == 0:
- A, B = [b*z, s], [c]
- if which == 1:
- A, B = [b*z, -c], [s]
- w = ctx.square_exp_arg(z, mult=-0.25)
- T1 = A, [1, 1], [], [a1,a2], [1], [a1,a2], w
- T2 = B, [1], [], [b1,b2], [1], [b1,b2], w
- return T1, T2
- return ctx.hypercomb(h, [v], **kwargs)
- @defun
- def angerj(ctx, v, z, **kwargs):
- return _anger(ctx, 0, v, z, **kwargs)
- @defun
- def webere(ctx, v, z, **kwargs):
- return _anger(ctx, 1, v, z, **kwargs)
- @defun
- def lommels1(ctx, u, v, z, **kwargs):
- u = ctx._convert_param(u)[0]
- v = ctx._convert_param(v)[0]
- z = ctx.convert(z)
- def h(u,v):
- b = ctx.mpq_1_2
- w = ctx.square_exp_arg(z, mult=-0.25)
- return ([u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], \
- [b*(u-v+3),b*(u+v+3)], w),
- return ctx.hypercomb(h, [u,v], **kwargs)
- @defun
- def lommels2(ctx, u, v, z, **kwargs):
- u = ctx._convert_param(u)[0]
- v = ctx._convert_param(v)[0]
- z = ctx.convert(z)
- # Asymptotic expansion (GR p. 947) -- need to be careful
- # not to use for small arguments
- # def h(u,v):
- # b = ctx.mpq_1_2
- # w = -(z/2)**(-2)
- # return ([z], [u-1], [], [], [b*(1-u+v)], [b*(1-u-v)], w),
- def h(u,v):
- b = ctx.mpq_1_2
- w = ctx.square_exp_arg(z, mult=-0.25)
- T1 = [u-v+1, u+v+1, z], [-1, -1, u+1], [], [], [1], [b*(u-v+3),b*(u+v+3)], w
- T2 = [2, z], [u+v-1, -v], [v, b*(u+v+1)], [b*(v-u+1)], [], [1-v], w
- T3 = [2, z], [u-v-1, v], [-v, b*(u-v+1)], [b*(1-u-v)], [], [1+v], w
- #c1 = ctx.cospi((u-v)*b)
- #c2 = ctx.cospi((u+v)*b)
- #s = ctx.sinpi(v)
- #r1 = (u-v+1)*b
- #r2 = (u+v+1)*b
- #T2 = [c1, s, z, 2], [1, -1, -v, v], [], [-v+1], [], [-v+1], w
- #T3 = [-c2, s, z, 2], [1, -1, v, -v], [], [v+1], [], [v+1], w
- #T2 = [c1, s, z, 2], [1, -1, -v, v+u-1], [r1, r2], [-v+1], [], [-v+1], w
- #T3 = [-c2, s, z, 2], [1, -1, v, -v+u-1], [r1, r2], [v+1], [], [v+1], w
- return T1, T2, T3
- return ctx.hypercomb(h, [u,v], **kwargs)
- @defun
- def ber(ctx, n, z, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBer2/26/01/02/0001/
- def h(n):
- r = -(z/4)**4
- cos, sin = ctx.cospi_sinpi(-0.75*n)
- T1 = [cos, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r
- T2 = [sin, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r
- return T1, T2
- return ctx.hypercomb(h, [n], **kwargs)
- @defun
- def bei(ctx, n, z, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinBei2/26/01/02/0001/
- def h(n):
- r = -(z/4)**4
- cos, sin = ctx.cospi_sinpi(0.75*n)
- T1 = [cos, z/2], [1, n+2], [], [n+2], [], [1.5, 0.5*(n+3), 0.5*n+1], r
- T2 = [sin, z/2], [1, n], [], [n+1], [], [0.5, 0.5*(n+1), 0.5*n+1], r
- return T1, T2
- return ctx.hypercomb(h, [n], **kwargs)
- @defun
- def ker(ctx, n, z, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKer2/26/01/02/0001/
- def h(n):
- r = -(z/4)**4
- cos1, sin1 = ctx.cospi_sinpi(0.25*n)
- cos2, sin2 = ctx.cospi_sinpi(0.75*n)
- T1 = [2, z, 4*cos1], [-n-3, n, 1], [-n], [], [], [0.5, 0.5*(1+n), 0.5*(n+2)], r
- T2 = [2, z, -sin1], [-n-3, 2+n, 1], [-n-1], [], [], [1.5, 0.5*(3+n), 0.5*(n+2)], r
- T3 = [2, z, 4*cos2], [n-3, -n, 1], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r
- T4 = [2, z, -sin2], [n-3, 2-n, 1], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r
- return T1, T2, T3, T4
- return ctx.hypercomb(h, [n], **kwargs)
- @defun
- def kei(ctx, n, z, **kwargs):
- n = ctx.convert(n)
- z = ctx.convert(z)
- # http://functions.wolfram.com/Bessel-TypeFunctions/KelvinKei2/26/01/02/0001/
- def h(n):
- r = -(z/4)**4
- cos1, sin1 = ctx.cospi_sinpi(0.75*n)
- cos2, sin2 = ctx.cospi_sinpi(0.25*n)
- T1 = [-cos1, 2, z], [1, n-3, 2-n], [n-1], [], [], [1.5, 0.5*(3-n), 1-0.5*n], r
- T2 = [-sin1, 2, z], [1, n-1, -n], [n], [], [], [0.5, 0.5*(1-n), 1-0.5*n], r
- T3 = [-sin2, 2, z], [1, -n-1, n], [-n], [], [], [0.5, 0.5*(n+1), 0.5*(n+2)], r
- T4 = [-cos2, 2, z], [1, -n-3, n+2], [-n-1], [], [], [1.5, 0.5*(n+3), 0.5*(n+2)], r
- return T1, T2, T3, T4
- return ctx.hypercomb(h, [n], **kwargs)
- # TODO: do this more generically?
- def c_memo(f):
- name = f.__name__
- def f_wrapped(ctx):
- cache = ctx._misc_const_cache
- prec = ctx.prec
- p,v = cache.get(name, (-1,0))
- if p >= prec:
- return +v
- else:
- cache[name] = (prec, f(ctx))
- return cache[name][1]
- return f_wrapped
- @c_memo
- def _airyai_C1(ctx):
- return 1 / (ctx.cbrt(9) * ctx.gamma(ctx.mpf(2)/3))
- @c_memo
- def _airyai_C2(ctx):
- return -1 / (ctx.cbrt(3) * ctx.gamma(ctx.mpf(1)/3))
- @c_memo
- def _airybi_C1(ctx):
- return 1 / (ctx.nthroot(3,6) * ctx.gamma(ctx.mpf(2)/3))
- @c_memo
- def _airybi_C2(ctx):
- return ctx.nthroot(3,6) / ctx.gamma(ctx.mpf(1)/3)
- def _airybi_n2_inf(ctx):
- prec = ctx.prec
- try:
- v = ctx.power(3,'2/3')*ctx.gamma('2/3')/(2*ctx.pi)
- finally:
- ctx.prec = prec
- return +v
- # Derivatives at z = 0
- # TODO: could be expressed more elegantly using triple factorials
- def _airyderiv_0(ctx, z, n, ntype, which):
- if ntype == 'Z':
- if n < 0:
- return z
- r = ctx.mpq_1_3
- prec = ctx.prec
- try:
- ctx.prec += 10
- v = ctx.gamma((n+1)*r) * ctx.power(3,n*r) / ctx.pi
- if which == 0:
- v *= ctx.sinpi(2*(n+1)*r)
- v /= ctx.power(3,'2/3')
- else:
- v *= abs(ctx.sinpi(2*(n+1)*r))
- v /= ctx.power(3,'1/6')
- finally:
- ctx.prec = prec
- return +v + z
- else:
- # singular (does the limit exist?)
- raise NotImplementedError
- @defun
- def airyai(ctx, z, derivative=0, **kwargs):
- z = ctx.convert(z)
- if derivative:
- n, ntype = ctx._convert_param(derivative)
- else:
- n = 0
- # Values at infinities
- if not ctx.isnormal(z) and z:
- if n and ntype == 'Z':
- if n == -1:
- if z == ctx.inf:
- return ctx.mpf(1)/3 + 1/z
- if z == ctx.ninf:
- return ctx.mpf(-2)/3 + 1/z
- if n < -1:
- if z == ctx.inf:
- return z
- if z == ctx.ninf:
- return (-1)**n * (-z)
- if (not n) and z == ctx.inf or z == ctx.ninf:
- return 1/z
- # TODO: limits
- raise ValueError("essential singularity of Ai(z)")
- # Account for exponential scaling
- if z:
- extraprec = max(0, int(1.5*ctx.mag(z)))
- else:
- extraprec = 0
- if n:
- if n == 1:
- def h():
- # http://functions.wolfram.com/03.07.06.0005.01
- if ctx._re(z) > 4:
- ctx.prec += extraprec
- w = z**1.5; r = -0.75/w; u = -2*w/3
- ctx.prec -= extraprec
- C = -ctx.exp(u)/(2*ctx.sqrt(ctx.pi))*ctx.nthroot(z,4)
- return ([C],[1],[],[],[(-1,6),(7,6)],[],r),
- # http://functions.wolfram.com/03.07.26.0001.01
- else:
- ctx.prec += extraprec
- w = z**3 / 9
- ctx.prec -= extraprec
- C1 = _airyai_C1(ctx) * 0.5
- C2 = _airyai_C2(ctx)
- T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w
- T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w
- return T1, T2
- return ctx.hypercomb(h, [], **kwargs)
- else:
- if z == 0:
- return _airyderiv_0(ctx, z, n, ntype, 0)
- # http://functions.wolfram.com/03.05.20.0004.01
- def h(n):
- ctx.prec += extraprec
- w = z**3/9
- ctx.prec -= extraprec
- q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3
- a1=q13; a2=1; b1=(1-n)*q13; b2=(2-n)*q13; b3=1-n*q13
- T1 = [3, z], [n-q23, -n], [a1], [b1,b2,b3], \
- [a1,a2], [b1,b2,b3], w
- a1=q23; b1=(2-n)*q13; b2=1-n*q13; b3=(4-n)*q13
- T2 = [3, z, -z], [n-q43, -n, 1], [a1], [b1,b2,b3], \
- [a1,a2], [b1,b2,b3], w
- return T1, T2
- v = ctx.hypercomb(h, [n], **kwargs)
- if ctx._is_real_type(z) and ctx.isint(n):
- v = ctx._re(v)
- return v
- else:
- def h():
- if ctx._re(z) > 4:
- # We could use 1F1, but it results in huge cancellation;
- # the following expansion is better.
- # TODO: asymptotic series for derivatives
- ctx.prec += extraprec
- w = z**1.5; r = -0.75/w; u = -2*w/3
- ctx.prec -= extraprec
- C = ctx.exp(u)/(2*ctx.sqrt(ctx.pi)*ctx.nthroot(z,4))
- return ([C],[1],[],[],[(1,6),(5,6)],[],r),
- else:
- ctx.prec += extraprec
- w = z**3 / 9
- ctx.prec -= extraprec
- C1 = _airyai_C1(ctx)
- C2 = _airyai_C2(ctx)
- T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w
- T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w
- return T1, T2
- return ctx.hypercomb(h, [], **kwargs)
- @defun
- def airybi(ctx, z, derivative=0, **kwargs):
- z = ctx.convert(z)
- if derivative:
- n, ntype = ctx._convert_param(derivative)
- else:
- n = 0
- # Values at infinities
- if not ctx.isnormal(z) and z:
- if n and ntype == 'Z':
- if z == ctx.inf:
- return z
- if z == ctx.ninf:
- if n == -1:
- return 1/z
- if n == -2:
- return _airybi_n2_inf(ctx)
- if n < -2:
- return (-1)**n * (-z)
- if not n:
- if z == ctx.inf:
- return z
- if z == ctx.ninf:
- return 1/z
- # TODO: limits
- raise ValueError("essential singularity of Bi(z)")
- if z:
- extraprec = max(0, int(1.5*ctx.mag(z)))
- else:
- extraprec = 0
- if n:
- if n == 1:
- # http://functions.wolfram.com/03.08.26.0001.01
- def h():
- ctx.prec += extraprec
- w = z**3 / 9
- ctx.prec -= extraprec
- C1 = _airybi_C1(ctx)*0.5
- C2 = _airybi_C2(ctx)
- T1 = [C1,z],[1,2],[],[],[],[ctx.mpq_5_3],w
- T2 = [C2],[1],[],[],[],[ctx.mpq_1_3],w
- return T1, T2
- return ctx.hypercomb(h, [], **kwargs)
- else:
- if z == 0:
- return _airyderiv_0(ctx, z, n, ntype, 1)
- def h(n):
- ctx.prec += extraprec
- w = z**3/9
- ctx.prec -= extraprec
- q13,q23,q43 = ctx.mpq_1_3, ctx.mpq_2_3, ctx.mpq_4_3
- q16 = ctx.mpq_1_6
- q56 = ctx.mpq_5_6
- a1=q13; a2=1; b1=(1-n)*q13; b2=(2-n)*q13; b3=1-n*q13
- T1 = [3, z], [n-q16, -n], [a1], [b1,b2,b3], \
- [a1,a2], [b1,b2,b3], w
- a1=q23; b1=(2-n)*q13; b2=1-n*q13; b3=(4-n)*q13
- T2 = [3, z], [n-q56, 1-n], [a1], [b1,b2,b3], \
- [a1,a2], [b1,b2,b3], w
- return T1, T2
- v = ctx.hypercomb(h, [n], **kwargs)
- if ctx._is_real_type(z) and ctx.isint(n):
- v = ctx._re(v)
- return v
- else:
- def h():
- ctx.prec += extraprec
- w = z**3 / 9
- ctx.prec -= extraprec
- C1 = _airybi_C1(ctx)
- C2 = _airybi_C2(ctx)
- T1 = [C1],[1],[],[],[],[ctx.mpq_2_3],w
- T2 = [z*C2],[1],[],[],[],[ctx.mpq_4_3],w
- return T1, T2
- return ctx.hypercomb(h, [], **kwargs)
- def _airy_zero(ctx, which, k, derivative, complex=False):
- # Asymptotic formulas are given in DLMF section 9.9
- def U(t): return t**(2/3.)*(1-7/(t**2*48))
- def T(t): return t**(2/3.)*(1+5/(t**2*48))
- k = int(k)
- if k < 1:
- raise ValueError("k cannot be less than 1")
- if not derivative in (0,1):
- raise ValueError("Derivative should lie between 0 and 1")
- if which == 0:
- if derivative:
- return ctx.findroot(lambda z: ctx.airyai(z,1),
- -U(3*ctx.pi*(4*k-3)/8))
- return ctx.findroot(ctx.airyai, -T(3*ctx.pi*(4*k-1)/8))
- if which == 1 and complex == False:
- if derivative:
- return ctx.findroot(lambda z: ctx.airybi(z,1),
- -U(3*ctx.pi*(4*k-1)/8))
- return ctx.findroot(ctx.airybi, -T(3*ctx.pi*(4*k-3)/8))
- if which == 1 and complex == True:
- if derivative:
- t = 3*ctx.pi*(4*k-3)/8 + 0.75j*ctx.ln2
- s = ctx.expjpi(ctx.mpf(1)/3) * T(t)
- return ctx.findroot(lambda z: ctx.airybi(z,1), s)
- t = 3*ctx.pi*(4*k-1)/8 + 0.75j*ctx.ln2
- s = ctx.expjpi(ctx.mpf(1)/3) * U(t)
- return ctx.findroot(ctx.airybi, s)
- @defun
- def airyaizero(ctx, k, derivative=0):
- return _airy_zero(ctx, 0, k, derivative, False)
- @defun
- def airybizero(ctx, k, derivative=0, complex=False):
- return _airy_zero(ctx, 1, k, derivative, complex)
- def _scorer(ctx, z, which, kwargs):
- z = ctx.convert(z)
- if ctx.isinf(z):
- if z == ctx.inf:
- if which == 0: return 1/z
- if which == 1: return z
- if z == ctx.ninf:
- return 1/z
- raise ValueError("essential singularity")
- if z:
- extraprec = max(0, int(1.5*ctx.mag(z)))
- else:
- extraprec = 0
- if kwargs.get('derivative'):
- raise NotImplementedError
- # Direct asymptotic expansions, to avoid
- # exponentially large cancellation
- try:
- if ctx.mag(z) > 3:
- if which == 0 and abs(ctx.arg(z)) < ctx.pi/3 * 0.999:
- def h():
- return (([ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),)
- return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True)
- if which == 1 and abs(ctx.arg(-z)) < 2*ctx.pi/3 * 0.999:
- def h():
- return (([-ctx.pi,z],[-1,-1],[],[],[(1,3),(2,3),1],[],9/z**3),)
- return ctx.hypercomb(h, [], maxterms=ctx.prec, force_series=True)
- except ctx.NoConvergence:
- pass
- def h():
- A = ctx.airybi(z, **kwargs)/3
- B = -2*ctx.pi
- if which == 1:
- A *= 2
- B *= -1
- ctx.prec += extraprec
- w = z**3/9
- ctx.prec -= extraprec
- T1 = [A], [1], [], [], [], [], 0
- T2 = [B,z], [-1,2], [], [], [1], [ctx.mpq_4_3,ctx.mpq_5_3], w
- return T1, T2
- return ctx.hypercomb(h, [], **kwargs)
- @defun
- def scorergi(ctx, z, **kwargs):
- return _scorer(ctx, z, 0, kwargs)
- @defun
- def scorerhi(ctx, z, **kwargs):
- return _scorer(ctx, z, 1, kwargs)
- @defun_wrapped
- def coulombc(ctx, l, eta, _cache={}):
- if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec:
- return +_cache[l,eta][1]
- G3 = ctx.loggamma(2*l+2)
- G1 = ctx.loggamma(1+l+ctx.j*eta)
- G2 = ctx.loggamma(1+l-ctx.j*eta)
- v = 2**l * ctx.exp((-ctx.pi*eta+G1+G2)/2 - G3)
- if not (ctx.im(l) or ctx.im(eta)):
- v = ctx.re(v)
- _cache[l,eta] = (ctx.prec, v)
- return v
- @defun_wrapped
- def coulombf(ctx, l, eta, z, w=1, chop=True, **kwargs):
- # Regular Coulomb wave function
- # Note: w can be either 1 or -1; the other may be better in some cases
- # TODO: check that chop=True chops when and only when it should
- #ctx.prec += 10
- def h(l, eta):
- try:
- jw = ctx.j*w
- jwz = ctx.fmul(jw, z, exact=True)
- jwz2 = ctx.fmul(jwz, -2, exact=True)
- C = ctx.coulombc(l, eta)
- T1 = [C, z, ctx.exp(jwz)], [1, l+1, 1], [], [], [1+l+jw*eta], \
- [2*l+2], jwz2
- except ValueError:
- T1 = [0], [-1], [], [], [], [], 0
- return (T1,)
- v = ctx.hypercomb(h, [l,eta], **kwargs)
- if chop and (not ctx.im(l)) and (not ctx.im(eta)) and (not ctx.im(z)) and \
- (ctx.re(z) >= 0):
- v = ctx.re(v)
- return v
- @defun_wrapped
- def _coulomb_chi(ctx, l, eta, _cache={}):
- if (l, eta) in _cache and _cache[l,eta][0] >= ctx.prec:
- return _cache[l,eta][1]
- def terms():
- l2 = -l-1
- jeta = ctx.j*eta
- return [ctx.loggamma(1+l+jeta) * (-0.5j),
- ctx.loggamma(1+l-jeta) * (0.5j),
- ctx.loggamma(1+l2+jeta) * (0.5j),
- ctx.loggamma(1+l2-jeta) * (-0.5j),
- -(l+0.5)*ctx.pi]
- v = ctx.sum_accurately(terms, 1)
- _cache[l,eta] = (ctx.prec, v)
- return v
- @defun_wrapped
- def coulombg(ctx, l, eta, z, w=1, chop=True, **kwargs):
- # Irregular Coulomb wave function
- # Note: w can be either 1 or -1; the other may be better in some cases
- # TODO: check that chop=True chops when and only when it should
- if not ctx._im(l):
- l = ctx._re(l) # XXX: for isint
- def h(l, eta):
- # Force perturbation for integers and half-integers
- if ctx.isint(l*2):
- T1 = [0], [-1], [], [], [], [], 0
- return (T1,)
- l2 = -l-1
- try:
- chi = ctx._coulomb_chi(l, eta)
- jw = ctx.j*w
- s = ctx.sin(chi); c = ctx.cos(chi)
- C1 = ctx.coulombc(l,eta)
- C2 = ctx.coulombc(l2,eta)
- u = ctx.exp(jw*z)
- x = -2*jw*z
- T1 = [s, C1, z, u, c], [-1, 1, l+1, 1, 1], [], [], \
- [1+l+jw*eta], [2*l+2], x
- T2 = [-s, C2, z, u], [-1, 1, l2+1, 1], [], [], \
- [1+l2+jw*eta], [2*l2+2], x
- return T1, T2
- except ValueError:
- T1 = [0], [-1], [], [], [], [], 0
- return (T1,)
- v = ctx.hypercomb(h, [l,eta], **kwargs)
- if chop and (not ctx._im(l)) and (not ctx._im(eta)) and (not ctx._im(z)) and \
- (ctx._re(z) >= 0):
- v = ctx._re(v)
- return v
- def mcmahon(ctx,kind,prime,v,m):
- """
- Computes an estimate for the location of the Bessel function zero
- j_{v,m}, y_{v,m}, j'_{v,m} or y'_{v,m} using McMahon's asymptotic
- expansion (Abramowitz & Stegun 9.5.12-13, DLMF 20.21(vi)).
- Returns (r,err) where r is the estimated location of the root
- and err is a positive number estimating the error of the
- asymptotic expansion.
- """
- u = 4*v**2
- if kind == 1 and not prime: b = (4*m+2*v-1)*ctx.pi/4
- if kind == 2 and not prime: b = (4*m+2*v-3)*ctx.pi/4
- if kind == 1 and prime: b = (4*m+2*v-3)*ctx.pi/4
- if kind == 2 and prime: b = (4*m+2*v-1)*ctx.pi/4
- if not prime:
- s1 = b
- s2 = -(u-1)/(8*b)
- s3 = -4*(u-1)*(7*u-31)/(3*(8*b)**3)
- s4 = -32*(u-1)*(83*u**2-982*u+3779)/(15*(8*b)**5)
- s5 = -64*(u-1)*(6949*u**3-153855*u**2+1585743*u-6277237)/(105*(8*b)**7)
- if prime:
- s1 = b
- s2 = -(u+3)/(8*b)
- s3 = -4*(7*u**2+82*u-9)/(3*(8*b)**3)
- s4 = -32*(83*u**3+2075*u**2-3039*u+3537)/(15*(8*b)**5)
- s5 = -64*(6949*u**4+296492*u**3-1248002*u**2+7414380*u-5853627)/(105*(8*b)**7)
- terms = [s1,s2,s3,s4,s5]
- s = s1
- err = 0.0
- for i in range(1,len(terms)):
- if abs(terms[i]) < abs(terms[i-1]):
- s += terms[i]
- else:
- err = abs(terms[i])
- if i == len(terms)-1:
- err = abs(terms[-1])
- return s, err
- def generalized_bisection(ctx,f,a,b,n):
- """
- Given f known to have exactly n simple roots within [a,b],
- return a list of n intervals isolating the roots
- and having opposite signs at the endpoints.
- TODO: this can be optimized, e.g. by reusing evaluation points.
- """
- if n < 1:
- raise ValueError("n cannot be less than 1")
- N = n+1
- points = []
- signs = []
- while 1:
- points = ctx.linspace(a,b,N)
- signs = [ctx.sign(f(x)) for x in points]
- ok_intervals = [(points[i],points[i+1]) for i in range(N-1) \
- if signs[i]*signs[i+1] == -1]
- if len(ok_intervals) == n:
- return ok_intervals
- N = N*2
- def find_in_interval(ctx, f, ab):
- return ctx.findroot(f, ab, solver='illinois', verify=False)
- def bessel_zero(ctx, kind, prime, v, m, isoltol=0.01, _interval_cache={}):
- prec = ctx.prec
- workprec = max(prec, ctx.mag(v), ctx.mag(m))+10
- try:
- ctx.prec = workprec
- v = ctx.mpf(v)
- m = int(m)
- prime = int(prime)
- if v < 0:
- raise ValueError("v cannot be negative")
- if m < 1:
- raise ValueError("m cannot be less than 1")
- if not prime in (0,1):
- raise ValueError("prime should lie between 0 and 1")
- if kind == 1:
- if prime: f = lambda x: ctx.besselj(v,x,derivative=1)
- else: f = lambda x: ctx.besselj(v,x)
- if kind == 2:
- if prime: f = lambda x: ctx.bessely(v,x,derivative=1)
- else: f = lambda x: ctx.bessely(v,x)
- # The first root of J' is very close to 0 for small
- # orders, and this needs to be special-cased
- if kind == 1 and prime and m == 1:
- if v == 0:
- return ctx.zero
- if v <= 1:
- # TODO: use v <= j'_{v,1} < y_{v,1}?
- r = 2*ctx.sqrt(v*(1+v)/(v+2))
- return find_in_interval(ctx, f, (r/10, 2*r))
- if (kind,prime,v,m) in _interval_cache:
- return find_in_interval(ctx, f, _interval_cache[kind,prime,v,m])
- r, err = mcmahon(ctx, kind, prime, v, m)
- if err < isoltol:
- return find_in_interval(ctx, f, (r-isoltol, r+isoltol))
- # An x such that 0 < x < r_{v,1}
- if kind == 1 and not prime: low = 2.4
- if kind == 1 and prime: low = 1.8
- if kind == 2 and not prime: low = 0.8
- if kind == 2 and prime: low = 2.0
- n = m+1
- while 1:
- r1, err = mcmahon(ctx, kind, prime, v, n)
- if err < isoltol:
- r2, err2 = mcmahon(ctx, kind, prime, v, n+1)
- intervals = generalized_bisection(ctx, f, low, 0.5*(r1+r2), n)
- for k, ab in enumerate(intervals):
- _interval_cache[kind,prime,v,k+1] = ab
- return find_in_interval(ctx, f, intervals[m-1])
- else:
- n = n*2
- finally:
- ctx.prec = prec
- @defun
- def besseljzero(ctx, v, m, derivative=0):
- r"""
- For a real order `\nu \ge 0` and a positive integer `m`, returns
- `j_{\nu,m}`, the `m`-th positive zero of the Bessel function of the
- first kind `J_{\nu}(z)` (see :func:`~mpmath.besselj`). Alternatively,
- with *derivative=1*, gives the first nonnegative simple zero
- `j'_{\nu,m}` of `J'_{\nu}(z)`.
- The indexing convention is that used by Abramowitz & Stegun
- and the DLMF. Note the special case `j'_{0,1} = 0`, while all other
- zeros are positive. In effect, only simple zeros are counted
- (all zeros of Bessel functions are simple except possibly `z = 0`)
- and `j_{\nu,m}` becomes a monotonic function of both `\nu`
- and `m`.
- The zeros are interlaced according to the inequalities
- .. math ::
- j'_{\nu,k} < j_{\nu,k} < j'_{\nu,k+1}
- j_{\nu,1} < j_{\nu+1,2} < j_{\nu,2} < j_{\nu+1,2} < j_{\nu,3} < \cdots
- **Examples**
- Initial zeros of the Bessel functions `J_0(z), J_1(z), J_2(z)`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> besseljzero(0,1); besseljzero(0,2); besseljzero(0,3)
- 2.404825557695772768621632
- 5.520078110286310649596604
- 8.653727912911012216954199
- >>> besseljzero(1,1); besseljzero(1,2); besseljzero(1,3)
- 3.831705970207512315614436
- 7.01558666981561875353705
- 10.17346813506272207718571
- >>> besseljzero(2,1); besseljzero(2,2); besseljzero(2,3)
- 5.135622301840682556301402
- 8.417244140399864857783614
- 11.61984117214905942709415
- Initial zeros of `J'_0(z), J'_1(z), J'_2(z)`::
- 0.0
- 3.831705970207512315614436
- 7.01558666981561875353705
- >>> besseljzero(1,1,1); besseljzero(1,2,1); besseljzero(1,3,1)
- 1.84118378134065930264363
- 5.331442773525032636884016
- 8.536316366346285834358961
- >>> besseljzero(2,1,1); besseljzero(2,2,1); besseljzero(2,3,1)
- 3.054236928227140322755932
- 6.706133194158459146634394
- 9.969467823087595793179143
- Zeros with large index::
- >>> besseljzero(0,100); besseljzero(0,1000); besseljzero(0,10000)
- 313.3742660775278447196902
- 3140.807295225078628895545
- 31415.14114171350798533666
- >>> besseljzero(5,100); besseljzero(5,1000); besseljzero(5,10000)
- 321.1893195676003157339222
- 3148.657306813047523500494
- 31422.9947255486291798943
- >>> besseljzero(0,100,1); besseljzero(0,1000,1); besseljzero(0,10000,1)
- 311.8018681873704508125112
- 3139.236339643802482833973
- 31413.57032947022399485808
- Zeros of functions with large order::
- >>> besseljzero(50,1)
- 57.11689916011917411936228
- >>> besseljzero(50,2)
- 62.80769876483536093435393
- >>> besseljzero(50,100)
- 388.6936600656058834640981
- >>> besseljzero(50,1,1)
- 52.99764038731665010944037
- >>> besseljzero(50,2,1)
- 60.02631933279942589882363
- >>> besseljzero(50,100,1)
- 387.1083151608726181086283
- Zeros of functions with fractional order::
- >>> besseljzero(0.5,1); besseljzero(1.5,1); besseljzero(2.25,4)
- 3.141592653589793238462643
- 4.493409457909064175307881
- 15.15657692957458622921634
- Both `J_{\nu}(z)` and `J'_{\nu}(z)` can be expressed as infinite
- products over their zeros::
- >>> v,z = 2, mpf(1)
- >>> (z/2)**v/gamma(v+1) * \
- ... nprod(lambda k: 1-(z/besseljzero(v,k))**2, [1,inf])
- ...
- 0.1149034849319004804696469
- >>> besselj(v,z)
- 0.1149034849319004804696469
- >>> (z/2)**(v-1)/2/gamma(v) * \
- ... nprod(lambda k: 1-(z/besseljzero(v,k,1))**2, [1,inf])
- ...
- 0.2102436158811325550203884
- >>> besselj(v,z,1)
- 0.2102436158811325550203884
- """
- return +bessel_zero(ctx, 1, derivative, v, m)
- @defun
- def besselyzero(ctx, v, m, derivative=0):
- r"""
- For a real order `\nu \ge 0` and a positive integer `m`, returns
- `y_{\nu,m}`, the `m`-th positive zero of the Bessel function of the
- second kind `Y_{\nu}(z)` (see :func:`~mpmath.bessely`). Alternatively,
- with *derivative=1*, gives the first positive zero `y'_{\nu,m}` of
- `Y'_{\nu}(z)`.
- The zeros are interlaced according to the inequalities
- .. math ::
- y_{\nu,k} < y'_{\nu,k} < y_{\nu,k+1}
- y_{\nu,1} < y_{\nu+1,2} < y_{\nu,2} < y_{\nu+1,2} < y_{\nu,3} < \cdots
- **Examples**
- Initial zeros of the Bessel functions `Y_0(z), Y_1(z), Y_2(z)`::
- >>> from mpmath import *
- >>> mp.dps = 25; mp.pretty = True
- >>> besselyzero(0,1); besselyzero(0,2); besselyzero(0,3)
- 0.8935769662791675215848871
- 3.957678419314857868375677
- 7.086051060301772697623625
- >>> besselyzero(1,1); besselyzero(1,2); besselyzero(1,3)
- 2.197141326031017035149034
- 5.429681040794135132772005
- 8.596005868331168926429606
- >>> besselyzero(2,1); besselyzero(2,2); besselyzero(2,3)
- 3.384241767149593472701426
- 6.793807513268267538291167
- 10.02347797936003797850539
- Initial zeros of `Y'_0(z), Y'_1(z), Y'_2(z)`::
- >>> besselyzero(0,1,1); besselyzero(0,2,1); besselyzero(0,3,1)
- 2.197141326031017035149034
- 5.429681040794135132772005
- 8.596005868331168926429606
- >>> besselyzero(1,1,1); besselyzero(1,2,1); besselyzero(1,3,1)
- 3.683022856585177699898967
- 6.941499953654175655751944
- 10.12340465543661307978775
- >>> besselyzero(2,1,1); besselyzero(2,2,1); besselyzero(2,3,1)
- 5.002582931446063945200176
- 8.350724701413079526349714
- 11.57419546521764654624265
- Zeros with large index::
- >>> besselyzero(0,100); besselyzero(0,1000); besselyzero(0,10000)
- 311.8034717601871549333419
- 3139.236498918198006794026
- 31413.57034538691205229188
- >>> besselyzero(5,100); besselyzero(5,1000); besselyzero(5,10000)
- 319.6183338562782156235062
- 3147.086508524556404473186
- 31421.42392920214673402828
- >>> besselyzero(0,100,1); besselyzero(0,1000,1); besselyzero(0,10000,1)
- 313.3726705426359345050449
- 3140.807136030340213610065
- 31415.14112579761578220175
- Zeros of functions with large order::
- >>> besselyzero(50,1)
- 53.50285882040036394680237
- >>> besselyzero(50,2)
- 60.11244442774058114686022
- >>> besselyzero(50,100)
- 387.1096509824943957706835
- >>> besselyzero(50,1,1)
- 56.96290427516751320063605
- >>> besselyzero(50,2,1)
- 62.74888166945933944036623
- >>> besselyzero(50,100,1)
- 388.6923300548309258355475
- Zeros of functions with fractional order::
- >>> besselyzero(0.5,1); besselyzero(1.5,1); besselyzero(2.25,4)
- 1.570796326794896619231322
- 2.798386045783887136720249
- 13.56721208770735123376018
- """
- return +bessel_zero(ctx, 2, derivative, v, m)
|