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)