Skip to content

Commit f5728a8

Browse files
committed
MH's besselk asymptotic expansion.
1 parent 64f4cb5 commit f5728a8

File tree

1 file changed

+23
-50
lines changed

1 file changed

+23
-50
lines changed

src/besselk.jl

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ function besselk_positive_args(nu, x::T) where T <: Union{Float32, Float64}
233233
(isinteger(nu-1/2) && !debye_cut) && return sphericalbesselk(nu-1/2, x)*SQRT_PID2(T)*sqrt(x)
234234

235235
# check if the standard asymptotic expansion can be used:
236-
besselk_asexp_cutoff(nu, x) && return besselk_asymptoticexp(nu, x, 10)
236+
besselk_asexp_cutoff(nu, x) && return besselk_large_argument(nu, x)
237237

238238
# use uniform debye expansion if x or nu is large
239239
debye_cut && return besselk_large_orders(nu, x)
@@ -442,55 +442,28 @@ Computes the asymptotic expansion of K_ν w.r.t. argument. Accurate for large x,
442442
"""
443443
besselk_asexp_cutoff(nu, x::T) where T = (nu < 20.0) && (x > ASEXP_CUTOFF(T))
444444

445-
# Compute the function for (_v, _v+1), where _v = v-floor(v), and then do
446-
# forward recursion. If the argument is really large, this should be faster than
447-
# the uniform asymptotic expansion and plenty accurate.
448-
function besselk_asymptoticexp(v, x::T, order) where T
449-
_v = v - floor(v)
450-
(kv, kvp1) = _besselk_as_pair(_v, x, order)
451-
v == _v && return kv
452-
_v += one(v)
453-
twodx = T(2)*inv(x)
454-
while _v < v
455-
(kv, kvp1) = (kvp1, muladd(kvp1, twodx*_v, kv))
456-
_v += one(_v)
457-
end
458-
kvp1
445+
function besselk_large_argument(v, x::T) where T
446+
a = exp(-x / 2)
447+
coef = a * sqrt(pi / 2x)
448+
return T(_besselk_large_argument(v, x) * coef * a)
459449
end
460450

461-
# For now, no exponential improvement. It requires the exponential integral
462-
# function, which would either need to be lifted from SpecialFunctions.jl or
463-
# re-implemented. And with an order of, like, 10, this seems to be pretty
464-
# accurate and still faster than the uniform asymptotic expansion.
465-
function _besselk_as_pair(v, x::T, order) where T
466-
fv = 4*v*v
467-
fvp1 = 4*(v+one(v))^2
468-
_z = x
469-
ser_v = one(T)
470-
ser_vp1 = one(T)
471-
floatj = one(T)
472-
ak_numv = fv - floatj
473-
ak_numvp1 = fvp1 - floatj
474-
factj = one(T)
475-
twofloatj = one(T)
476-
eightj = T(8)
477-
for _ in 1:order
478-
# add to the series:
479-
term_v = ak_numv/(factj*_z*eightj)
480-
ser_v += term_v
481-
term_vp1 = ak_numvp1/(factj*_z*eightj)
482-
ser_vp1 += term_vp1
483-
# update ak and _z:
484-
floatj += one(T)
485-
twofloatj += T(2)
486-
factj *= floatj
487-
fourfloatj = twofloatj*twofloatj
488-
ak_numv *= (fv - fourfloatj)
489-
ak_numvp1 *= (fvp1 - fourfloatj)
490-
_z *= x
491-
eightj *= T(8)
492-
end
493-
pre_multiply = SQRT_PID2(T)*exp(-x)/sqrt(x)
494-
(pre_multiply*ser_v, pre_multiply*ser_vp1)
451+
besselk_large_argument_scaled(v, x::T) where T = T(_besselk_large_argument(v, x) * sqrt(pi / 2x))
452+
453+
function _besselk_large_argument(v, x::T) where T
454+
MaxIter = 5000
455+
S = promote_type(T, Float64)
456+
v, x = S(v), S(x)
457+
458+
fv2 = 4 * v^2
459+
term = one(S)
460+
res = term
461+
s = term
462+
for i in 1:MaxIter
463+
offset = muladd(2, i, -1)
464+
term *= muladd(offset, -offset, fv2) / (8 * x * i)
465+
res = muladd(term, s, res)
466+
abs(term) <= eps(T) && break
467+
end
468+
return res
495469
end
496-

0 commit comments

Comments
 (0)