Skip to content

Commit 4dde04e

Browse files
committed
Asymptotic expansion for large arg and small-ish order.
1 parent 86da54f commit 4dde04e

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

src/besselk.jl

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,9 @@ function besselk_positive_args(nu, x::T) where T <: Union{Float32, Float64}
232232
# check if nu is a half-integer:
233233
(isinteger(nu-1/2) && !debye_cut) && return sphericalbesselk(nu-1/2, x)*SQRT_PID2(T)*sqrt(x)
234234

235+
# check if the standard asymptotic expansion can be used:
236+
besselk_asexp_cutoff(nu, x) && return besselk_asymptoticexp(nu, x)
237+
235238
# use uniform debye expansion if x or nu is large
236239
debye_cut && return besselk_large_orders(nu, x)
237240

@@ -431,3 +434,68 @@ end
431434
besselk_power_series_cutoff(nu, x::Float64) = x < 2.0 || nu > 1.6x - 1.0
432435
besselk_power_series_cutoff(nu, x::Float32) = x < 10.0f0 || nu > 1.65f0*x - 8.0f0
433436

437+
438+
"""
439+
besselk_asymptoticexp(v, x, order)
440+
441+
Computes the asymptotic expansion of K_ν w.r.t. argument. Accurate for large x, and faster than uniform asymptotic expansion for small to small-ish orders. The default order of the expansion is 10.
442+
"""
443+
function besselk_asymptoticexp(v, x::T) where T
444+
_besselk_asymptoticexp(v, float(x), 10)
445+
end
446+
besselk_asexp_cutoff(nu, x) = (nu < 20.0) && (x > 25.0)
447+
448+
# Compute the function for (_v, _v+1), where _v = v-floor(v), and then do
449+
# forward recursion. If the argument is really large, this should be faster than
450+
# the uniform asymptotic expansion and plenty accurate.
451+
function _besselk_asymptoticexp(v, x::T, order) where T
452+
_v = v - floor(v)
453+
(kv, kvp1) = _besselk_as_pair(_v, x, order)
454+
v == _v && return kv
455+
_v += one(v)
456+
twodx = T(2)*inv(x)
457+
while _v < v
458+
(kv, kvp1) = (kvp1, muladd(kvp1, twodx*_v, kv))
459+
_v += one(_v)
460+
end
461+
kvp1
462+
end
463+
464+
# For now, no exponential improvement. It requires the exponential integral
465+
# function, which would either need to be lifted from SpecialFunctions.jl or
466+
# re-implemented. And with an order of, like, 10, this seems to be pretty
467+
# accurate and still faster than the uniform asymptotic expansion.
468+
function _besselk_as_pair(v, x::T, order) where{T}
469+
twox = T(2)
470+
fv = 4*v*v
471+
fvp1 = 4*(v+one(v))^2
472+
_z = x
473+
ser = zero(T)
474+
ser_v = one(T)
475+
ser_vp1 = one(T)
476+
floatj = one(T)
477+
ak_numv = fv - floatj
478+
ak_numvp1 = fvp1 - floatj
479+
factj = one(T)
480+
twofloatj = one(T)
481+
eightj = T(8)
482+
for _ in 1:order
483+
# add to the series:
484+
term_v = ak_numv/(factj*_z*eightj)
485+
ser_v += term_v
486+
term_vp1 = ak_numvp1/(factj*_z*eightj)
487+
ser_vp1 += term_vp1
488+
# update ak and _z:
489+
floatj += one(T)
490+
twofloatj += T(2)
491+
factj *= floatj
492+
fourfloatj = twofloatj*twofloatj
493+
ak_numv *= (fv - fourfloatj)
494+
ak_numvp1 *= (fvp1 - fourfloatj)
495+
_z *= x
496+
eightj *= T(8)
497+
end
498+
pre_multiply = SQRT_PID2(T)*exp(-x)/sqrt(x)
499+
(pre_multiply*ser_v, pre_multiply*ser_vp1)
500+
end
501+

0 commit comments

Comments
 (0)