Skip to content

Commit 84038c2

Browse files
committed
update besselk, besseli
1 parent 7ef33ac commit 84038c2

File tree

5 files changed

+50
-43
lines changed

5 files changed

+50
-43
lines changed

src/besseli.jl

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ function besseli_positive_args(nu, x::T) where T <: Union{Float32, Float64}
217217
besseli_large_argument_cutoff(nu, x) && return besseli_large_argument(nu, x)
218218

219219
# use uniform debye expansion if x or nu is large
220-
besselik_debye_cutoff(nu, x) && return T(besseli_large_orders(nu, x))
220+
besselik_debye_cutoff(nu, x) && return besseli_large_orders(nu, x)
221221

222222
# for rest of values use the power series
223223
return besseli_power_series(nu, x)
@@ -236,12 +236,13 @@ _besselix(nu, x::Float16) = Float16(_besselix(nu, Float32(x)))
236236
function _besselix(nu, x::T) where T <: Union{Float32, Float64}
237237
iszero(nu) && return besseli0x(x)
238238
isone(nu) && return besseli1x(x)
239+
isinf(x) && return T(Inf)
239240

240241
# use large argument expansion if x >> nu
241242
besseli_large_argument_cutoff(nu, x) && return besseli_large_argument_scaled(nu, x)
242243

243244
# use uniform debye expansion if x or nu is large
244-
besselik_debye_cutoff(nu, x) && return T(besseli_large_orders_scaled(nu, x))
245+
besselik_debye_cutoff(nu, x) && return besseli_large_orders_scaled(nu, x)
245246

246247
# for rest of values use the power series
247248
return besseli_power_series(nu, x) * exp(-x)
@@ -259,7 +260,7 @@ end
259260
260261
Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
261262
"""
262-
function besseli_large_orders(v, x::T) where T <: Union{Float32, Float64}
263+
function besseli_large_orders(v, x::T) where T
263264
S = promote_type(T, Float64)
264265
x = S(x)
265266
z = x / v
@@ -269,10 +270,10 @@ function besseli_large_orders(v, x::T) where T <: Union{Float32, Float64}
269270
p = inv(zs)
270271
p2 = v^2/fma(max(v,x), max(v,x), min(v,x)^2)
271272

272-
return coef*Uk_poly_In(p, v, p2, T)
273+
return T(coef*Uk_poly_In(p, v, p2, T))
273274
end
274275

275-
function besseli_large_orders_scaled(v, x::T) where T <: Union{Float32, Float64}
276+
function besseli_large_orders_scaled(v, x::T) where T
276277
S = promote_type(T, Float64)
277278
x = S(x)
278279
z = x / v
@@ -296,42 +297,35 @@ end
296297
297298
Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
298299
"""
299-
function besseli_large_argument(v, z::T) where T
300-
MaxIter = 1000
301-
a = exp(z / 2)
302-
coef = a / sqrt(2 * T(pi) * z)
303-
fv2 = 4 * v^2
304-
term = one(T)
305-
res = term
306-
s = -term
307-
for i in 1:MaxIter
308-
i = T(i)
309-
offset = muladd(2, i, -1)
310-
term *= T(0.125) * muladd(offset, -offset, fv2) / (z * i)
311-
res = muladd(term, s, res)
312-
s = -s
313-
abs(term) <= eps(T) && break
314-
end
315-
return res * coef * a
300+
function besseli_large_argument(v, x::T) where T
301+
a = exp(x / 2)
302+
coef = a / sqrt(2 ** x))
303+
return T(_besseli_large_argument(v, x) * coef * a)
316304
end
317-
function besseli_large_argument_scaled(v, z::T) where T
318-
MaxIter = 1000
319-
coef = inv(sqrt(2 * T(pi) * z))
305+
306+
besseli_large_argument_scaled(v, x::T) where T = T(_besseli_large_argument(v, x) / sqrt(2 ** x)))
307+
308+
function _besseli_large_argument(v, x::T) where T
309+
MaxIter = 5000
310+
S = promote_type(T, Float64)
311+
v, x = S(v), S(x)
312+
320313
fv2 = 4 * v^2
321-
term = one(T)
314+
term = one(S)
322315
res = term
323316
s = -term
324317
for i in 1:MaxIter
325-
i = T(i)
326318
offset = muladd(2, i, -1)
327-
term *= T(0.125) * muladd(offset, -offset, fv2) / (z * i)
319+
term *= muladd(offset, -offset, fv2) / (8 * x * i)
328320
res = muladd(term, s, res)
329321
s = -s
330322
abs(term) <= eps(T) && break
331323
end
332-
return res * coef
324+
return res
333325
end
334-
besseli_large_argument_cutoff(nu, x) = x > maximum((30.0, nu^2 / 6))
326+
327+
besseli_large_argument_cutoff(nu, x::Float64) = x > 23.0 && x > nu^2 / 1.8 + 23.0
328+
besseli_large_argument_cutoff(nu, x::Float32) = x > 18.0f0 && x > nu^2 / 19.5f0 + 18.0f0
335329

336330
#####
337331
##### Power series for I_{nu}(x)

src/besselk.jl

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ function besselk_large_orders_scaled(v, x::T) where T
301301
return T(coef*Uk_poly_Kn(p, v, p2, T))
302302
end
303303
besselik_debye_cutoff(nu, x::Float64) = nu > 25.0 || x > 35.0
304-
besselik_debye_cutoff(nu, x::Float32) = nu > 15.0 || x > 20.0
304+
besselik_debye_cutoff(nu, x::Float32) = nu > 15.0f0 || x > 20.0f0
305305

306306
#####
307307
##### Continued fraction with Wronskian for K_{nu}(x)
@@ -389,35 +389,38 @@ No checks are performed on nu so this is not accurate when nu is an integer.
389389
"""
390390
function besselk_power_series(v, x::T) where T
391391
MaxIter = 1000
392+
S = promote_type(T, Float64)
393+
v, x = S(v), S(x)
394+
392395
z = x / 2
393396
zz = z * z
394-
395397
logz = log(z)
396398
xd2_v = exp(v*logz)
397399
xd2_nv = inv(xd2_v)
398400

399401
# use the reflection identify to calculate gamma(-v)
400402
# use relation gamma(v)*v = gamma(v+1) to avoid two gamma calls
401403
gam_v = gamma(v)
402-
xp1 = abs(v) + one(T)
404+
xp1 = abs(v) + one(S)
403405
gam_nv = π / (sinpi(xp1) * gam_v * v)
404406
gam_1mv = -gam_nv * v
405407
gam_1mnv = gam_v * v
406408

407409
_t1 = gam_v * xd2_nv * gam_1mv
408410
_t2 = gam_nv * xd2_v * gam_1mnv
409-
(xd2_pow, fact_k, out) = (one(T), one(T), zero(T))
411+
(xd2_pow, fact_k, out) = (one(S), one(S), zero(S))
410412
for k in 0:MaxIter
411413
t1 = xd2_pow * T(0.5)
412414
tmp = muladd(_t1, gam_1mnv, _t2 * gam_1mv)
413415
tmp *= inv(gam_1mv * gam_1mnv * fact_k)
414416
term = t1 * tmp
415417
out += term
416418
abs(term / out) < eps(T) && break
417-
(gam_1mnv, gam_1mv) = (gam_1mnv*(one(T) + v + k), gam_1mv*(one(T) - v + k))
419+
(gam_1mnv, gam_1mv) = (gam_1mnv*(one(S) + v + k), gam_1mv*(one(S) - v + k))
418420
xd2_pow *= zz
419-
fact_k *= k + one(T)
421+
fact_k *= k + one(S)
420422
end
421-
return out
423+
return T(out)
422424
end
423-
besselk_power_series_cutoff(nu, x) = x < 2.0 || nu > 1.6x - 1.0
425+
besselk_power_series_cutoff(nu, x::Float64) = x < 2.0 || nu > 1.6x - 1.0
426+
besselk_power_series_cutoff(nu, x::Float32) = x < 10.0f0 || nu > 1.65f0*x - 8.0f0

src/bessely.jl

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,11 @@
3434
3535
Bessel function of the second kind of order zero, ``Y_0(x)``.
3636
"""
37-
function bessely0(x::T) where T <: Union{Float32, Float64}
37+
bessely0(x::Real) = _bessely0(float(x))
38+
39+
_bessely0(x::Float16) = Float16(_bessely0(Float32(x)))
40+
41+
function _bessely0(x::T) where T <: Union{Float32, Float64}
3842
if x <= zero(x)
3943
if iszero(x)
4044
return T(-Inf)
@@ -110,7 +114,11 @@ end
110114
111115
Bessel function of the second kind of order one, ``Y_1(x)``.
112116
"""
113-
function bessely1(x::T) where T <: Union{Float32, Float64}
117+
bessely1(x::Real) = _bessely1(float(x))
118+
119+
_bessely1(x::Float16) = Float16(_bessely1(Float32(x)))
120+
121+
function _bessely1(x::T) where T <: Union{Float32, Float64}
114122
if x <= zero(x)
115123
if iszero(x)
116124
return T(-Inf)
@@ -318,6 +326,7 @@ function bessely_positive_args(nu, x::T) where T
318326
# use power series for small x and for when nu > x
319327
bessely_series_cutoff(nu, x) && return bessely_power_series(nu, x)[1]
320328

329+
# shift nu down and use upward recurrence starting from either chebyshev approx or hankel expansion
321330
return bessely_fallback(nu, x)
322331
end
323332

@@ -369,7 +378,7 @@ function bessely_power_series(v, x::T) where T
369378
return (out*c - out2) / s, out
370379
end
371380
bessely_series_cutoff(v, x::Float64) = (x < 7.0) || v > 1.35*x - 4.5
372-
bessely_series_cutoff(v, x::Float32) = (x < 21.0) || v > 1.38*x - 12.5
381+
bessely_series_cutoff(v, x::Float32) = (x < 21.0f0) || v > 1.38f0*x - 12.5f0
373382

374383
#####
375384
##### Fallback for Y_{nu}(x)
@@ -382,7 +391,7 @@ function bessely_fallback(nu, x)
382391
else
383392
# at this point x > 19.0 (for Float64) and fairly close to nu
384393
# shift nu down and use the debye expansion for Hankel function (valid x > nu) then use forward recurrence
385-
nu_shift = ceil(nu) - floor(Int, hankel_debye_fit(x)) + 4
394+
nu_shift = ceil(Int, nu) - floor(Int, hankel_debye_fit(x)) + 4
386395
v2 = maximum((nu - nu_shift, modf(nu)[1] + 1))
387396
return besselj_up_recurrence(x, imag(hankel_debye(v2, x)), imag(hankel_debye(v2 - 1, x)), v2, nu)[1]
388397
end

test/besseli_test.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ for v in nu, xx in x
103103
xx *= v
104104
sf = SpecialFunctions.besseli(v, xx)
105105
@test isapprox(besseli(v, xx), Float64(sf), rtol=2e-13)
106+
@test isapprox(besseli(Float32(v), Float32(xx)), Float32(sf))
106107
end
107108

108109
# test Inf

test/besselk_test.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ t = [besselk(m, x) for m in m, x in x]
108108
@test besselkx(3.2, 1.1) SpecialFunctions.besselk(3.2, 1.1)*exp(1.1)
109109
@test besselkx(8.2, 9.1) SpecialFunctions.besselk(8.2, 9.1)*exp(9.1)
110110

111-
112111
## Tests for besselk
113112

114113
## test all numbers and orders for 0<nu<100
@@ -118,6 +117,7 @@ for v in nu, xx in x
118117
xx *= v
119118
sf = SpecialFunctions.besselk(v, xx)
120119
@test isapprox(besselk(v, xx), Float64(sf), rtol=2e-13)
120+
@test isapprox(besselk(Float32(v), Float32(xx)), Float32(sf))
121121
end
122122

123123
# test Float16

0 commit comments

Comments
 (0)