Skip to content

Commit 86da54f

Browse files
authored
Merge pull request #46 from cgeoga/master
Special half-integer branch for `besselk`
2 parents 0302204 + a3bfb4c commit 86da54f

File tree

6 files changed

+64
-1
lines changed

6 files changed

+64
-1
lines changed

src/Bessels.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ include("bessely.jl")
4141
include("hankel.jl")
4242
include("airy.jl")
4343
include("sphericalbessel.jl")
44+
include("modifiedsphericalbessel.jl")
4445

4546
include("Float128/besseli.jl")
4647
include("Float128/besselj.jl")

src/besselk.jl

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,15 @@ function besselk_positive_args(nu, x::T) where T <: Union{Float32, Float64}
225225

226226
# dispatch to avoid uniform expansion when nu = 0
227227
iszero(nu) && return besselk0(x)
228+
229+
# pre-compute the uniform asymptotic expansion cutoff:
230+
debye_cut = besselik_debye_cutoff(nu, x)
231+
232+
# check if nu is a half-integer:
233+
(isinteger(nu-1/2) && !debye_cut) && return sphericalbesselk(nu-1/2, x)*SQRT_PID2(T)*sqrt(x)
228234

229235
# use uniform debye expansion if x or nu is large
230-
besselik_debye_cutoff(nu, x) && return besselk_large_orders(nu, x)
236+
debye_cut && return besselk_large_orders(nu, x)
231237

232238
# for integer nu use forward recurrence starting with K_0 and K_1
233239
isinteger(nu) && return besselk_up_recurrence(x, besselk1(x), besselk0(x), 1, nu)[1]
@@ -424,3 +430,4 @@ function besselk_power_series(v, x::T) where T
424430
end
425431
besselk_power_series_cutoff(nu, x::Float64) = x < 2.0 || nu > 1.6x - 1.0
426432
besselk_power_series_cutoff(nu, x::Float32) = x < 10.0f0 || nu > 1.65f0*x - 8.0f0
433+

src/constants.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,3 +284,6 @@ const Q3_k1(::Type{Float64}) = (
284284
2.88448064302447607e1, 2.27912927104139732e0,
285285
2.50358186953478678e-2
286286
)
287+
288+
const SQRT_PID2(::Type{Float64}) = 1.2533141373155003
289+
const SQRT_PID2(::Type{Float32}) = 1.2533141f0

src/modifiedsphericalbessel.jl

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
2+
"""
3+
sphericalbesselk(nu, x::T) where T <: {Float32, Float64}
4+
5+
Computes `k_{ν}(x)`, the modified second-kind spherical Bessel function, and offers special branches for integer orders.
6+
"""
7+
sphericalbesselk(nu, x) = _sphericalbesselk(nu, float(x))
8+
9+
function _sphericalbesselk(nu, x::T) where T
10+
isnan(x) && return NaN
11+
if isinteger(nu) && nu < 41.5
12+
if x < zero(x)
13+
return throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
14+
end
15+
# using ifelse here to hopefully cut out a branch on nu < 0 or not. The
16+
# symmetry here is that
17+
# k_{-n} = (...)*K_{-n + 1/2}
18+
# = (...)*K_{|n| - 1/2}
19+
# = (...)*K_{|n|-1 + 1/2}
20+
# = k_{|n|-1}
21+
_nu = ifelse(nu<zero(nu), -one(nu)-nu, nu)
22+
return sphericalbesselk_int(Int(_nu), x)
23+
else
24+
return inv(SQRT_PID2(T)*sqrt(x))*besselk(nu+1/2, x)
25+
end
26+
end
27+
28+
function sphericalbesselk_int(v::Int, x)
29+
b0 = inv(x)
30+
b1 = (x+one(x))/(x*x)
31+
iszero(v) && return b0*exp(-x)
32+
_v = one(v)
33+
invx = inv(x)
34+
while _v < v
35+
_v += one(_v)
36+
b0, b1 = b1, b0 + (2*_v - one(_v))*b1*invx
37+
end
38+
exp(-x)*b1
39+
end
40+

test/besselk_test.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,12 @@ k1x_32 = besselk1x.(Float32.(x))
6767
@test besselk(100, 3.9) SpecialFunctions.besselk(100, 3.9)
6868
@test besselk(100, 234.0) SpecialFunctions.besselk(100, 234.0)
6969

70+
# test half-integer orders:
71+
for (v,x) in Iterators.product(-35/2:1.0:81/2, range(0.0, 30.0, length=51)[2:end])
72+
@test besselk(v, x) SpecialFunctions.besselk(v, x)
73+
@test besselk(Float32(v), Float32(x)) SpecialFunctions.besselk(Float32(v), Float32(x))
74+
end
75+
7076
# test small arguments and order
7177
m = 0:40; x = [1e-6; 1e-4; 1e-3; 1e-2; 0.1; 1.0:2.0:500.0]
7278
for m in m, x in x

test/sphericalbessel_test.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,9 @@ v, x = -4.0, 5.6
5858
v, x = -6.8, 15.6
5959
@test isapprox(Bessels.sphericalbesselj(v, x), 0.04386355397884301866595, rtol=3e-12)
6060
@test isapprox(Bessels.sphericalbessely(v, x), 0.05061013363904335437354, rtol=3e-12)
61+
62+
# test for negative order of spherical modified besselk in the special integer
63+
# routine:
64+
for v in 1:10
65+
@test Bessels.sphericalbesselk(-v, 1.1) Bessels.sphericalbesselk(v-1, 1.1)
66+
end

0 commit comments

Comments
 (0)