Skip to content

Commit c8f019f

Browse files
authored
Merge pull request #53 from JuliaMath/sequence
Return sequence of Bessel functions
2 parents cc97c8c + 4a59336 commit c8f019f

16 files changed

+249
-32
lines changed

NEWS.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ For bug fixes, performance enhancements, or fixes to unexported functions we wil
1010

1111
# Unreleased
1212

13+
# Version 0.2.3
14+
15+
### Added
16+
- Add support for nu isa AbstractRange (i.e., `besselj(0:10, 1.0)`) to allow for fast computation of Bessel functions at many orders ([PR #53](https://github.com/JuliaMath/Bessels.jl/pull/53)).
17+
1318
# Version 0.2.2
1419

1520
### Added

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "Bessels"
22
uuid = "0e736298-9ec6-45e8-9647-e4fc86a2fe38"
33
authors = ["Michael Helton <[email protected]> and contributors"]
4-
version = "0.2.2"
4+
version = "0.2.3"
55

66
[compat]
77
julia = "1.6"

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,32 @@ julia> besselk0(x)
120120
julia> besselk1(x)
121121
1.6750295538365835e-6
122122
```
123+
## Support for sequence of orders
124+
125+
We also provide support for `besselj(nu::M, x::T)`, `bessely(nu::M, x::T)`, `besseli(nu::M, x::T)`, `besselk(nu::M, x::T)`, `besseli(nu::M, x::T)`, `besselh(nu::M, k, x::T)` when `M` is some `AbstractRange` and `T` is some float.
126+
127+
```julia
128+
julia> besselj(0:10, 1.0)
129+
11-element Vector{Float64}:
130+
0.7651976865579666
131+
0.44005058574493355
132+
0.11490348493190049
133+
0.019563353982668407
134+
0.0024766389641099553
135+
0.00024975773021123444
136+
2.0938338002389273e-5
137+
1.5023258174368085e-6
138+
9.422344172604502e-8
139+
5.249250179911876e-9
140+
2.630615123687453e-10
141+
```
142+
143+
In general, this provides a fast way to generate a sequence of Bessel functions for many orders.
144+
```julia
145+
julia> @btime besselj(0:100, 50.0)
146+
443.328 ns (2 allocations: 1.75 KiB)
147+
```
148+
This function will allocate so it is recommended that you calculate the Bessel functions at the top level of your function outside any hot loop.
123149

124150
### Support for negative arguments
125151

src/besseli.jl

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -164,11 +164,12 @@ end
164164
165165
Modified Bessel function of the second kind of order nu, ``I_{nu}(x)``.
166166
"""
167-
besseli(nu::Real, x::Real) = _besseli(nu, float(x))
167+
# perhaps have two besseli(nu::Real, x::Real) and besseli(nu::AbstractRange, x::Real)
168+
besseli(nu, x::Real) = _besseli(nu, float(x))
168169

169-
_besseli(nu, x::Float16) = Float16(_besseli(nu, Float32(x)))
170+
_besseli(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_besseli(Float32(nu), Float32(x)))
170171

171-
function _besseli(nu, x::T) where T <: Union{Float32, Float64}
172+
function _besseli(nu::T, x::T) where T <: Union{Float32, Float64}
172173
isinteger(nu) && return _besseli(Int(nu), x)
173174
~isfinite(x) && return x
174175
abs_nu = abs(nu)
@@ -205,6 +206,35 @@ function _besseli(nu::Integer, x::T) where T <: Union{Float32, Float64}
205206
end
206207
end
207208

209+
function _besseli(nu::AbstractRange, x::T) where T
210+
(nu[1] >= 0 && step(nu) == 1) || throw(ArgumentError("nu must be >= 0 with step(nu)=1"))
211+
len = length(nu)
212+
isone(len) && return [besseli(nu[1], x)]
213+
out = zeros(T, len)
214+
k = len
215+
inu = zero(T)
216+
while abs(inu) < floatmin(T)
217+
if besseli_underflow_check(nu[k], x)
218+
inu = zero(T)
219+
else
220+
inu = _besseli(nu[k], x)
221+
end
222+
out[k] = inu
223+
k -= 1
224+
k < 1 && break
225+
end
226+
if k > 1
227+
out[k] = _besseli(nu[k], x)
228+
tmp = @view out[1:k+1]
229+
out[1:k+1] = besselk_down_recurrence!(tmp, x, nu[1:k+1])
230+
return out
231+
else
232+
return out
233+
end
234+
end
235+
236+
besseli_underflow_check(nu, x::T) where T = nu > 140 + T(1.45)*x + 53*Base.Math._approx_cbrt(x)
237+
208238
"""
209239
besseli_positive_args(nu, x::T) where T <: Union{Float32, Float64}
210240
@@ -232,7 +262,7 @@ Nu must be real.
232262
"""
233263
besselix(nu::Real, x::Real) = _besselix(nu, float(x))
234264

235-
_besselix(nu, x::Float16) = Float16(_besselix(nu, Float32(x)))
265+
_besselix(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_besselix(Float32(nu), Float32(x)))
236266

237267
function _besselix(nu, x::T) where T <: Union{Float32, Float64}
238268
iszero(nu) && return besseli0x(x)

src/besselj.jl

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -206,11 +206,11 @@ end
206206
Bessel function of the first kind of order nu, ``J_{nu}(x)``.
207207
nu and x must be real where nu and x can be positive or negative.
208208
"""
209-
besselj(nu::Real, x::Real) = _besselj(nu, float(x))
209+
besselj(nu, x::Real) = _besselj(nu, float(x))
210210

211-
_besselj(nu, x::Float16) = Float16(_besselj(nu, Float32(x)))
211+
_besselj(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_besselj(Float32(nu), Float32(x)))
212212

213-
function _besselj(nu, x::T) where T <: Union{Float32, Float64}
213+
function _besselj(nu::T, x::T) where T <: Union{Float32, Float64}
214214
isinteger(nu) && return _besselj(Int(nu), x)
215215
abs_nu = abs(nu)
216216
abs_x = abs(x)
@@ -255,6 +255,41 @@ function _besselj(nu::Integer, x::T) where T <: Union{Float32, Float64}
255255
end
256256
end
257257

258+
function _besselj(nu::AbstractRange, x::T) where T
259+
(nu[1] >= 0 && step(nu) == 1) || throw(ArgumentError("nu must be >= 0 with step(nu)=1"))
260+
len = length(nu)
261+
isone(len) && return [besselj(nu[1], x)]
262+
263+
out = zeros(T, len)
264+
if nu[end] < x
265+
out[1], out[2] = _besselj(nu[1], x), _besselj(nu[2], x)
266+
return besselj_up_recurrence!(out, x, nu)
267+
else
268+
k = len
269+
jn = zero(T)
270+
while abs(jn) < floatmin(T)
271+
if besselj_underflow_check(nu[k], x)
272+
jn = zero(T)
273+
else
274+
jn = _besselj(nu[k], x)
275+
end
276+
out[k] = jn
277+
k -= 1
278+
k < 1 && break
279+
end
280+
if k > 1
281+
out[k] = _besselj(nu[k], x)
282+
tmp = @view out[1:k+1]
283+
besselj_down_recurrence!(tmp, x, nu[1:k+1])
284+
return out
285+
else
286+
return out
287+
end
288+
end
289+
end
290+
291+
besselj_underflow_check(nu, x::T) where T = nu > 100 + T(1.01)*x + 85*Base.Math._approx_cbrt(x)
292+
258293
"""
259294
besselj_positive_args(nu, x::T) where T <: Float64
260295

src/besselk.jl

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,11 +187,11 @@ end
187187
188188
Modified Bessel function of the second kind of order nu, ``K_{nu}(x)``.
189189
"""
190-
besselk(nu::Real, x::Real) = _besselk(nu, float(x))
190+
besselk(nu, x::Real) = _besselk(nu, float(x))
191191

192-
_besselk(nu, x::Float16) = Float16(_besselk(nu, Float32(x)))
192+
_besselk(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_besselk(Float32(nu), Float32(x)))
193193

194-
function _besselk(nu, x::T) where T <: Union{Float32, Float64}
194+
function _besselk(nu::T, x::T) where T <: Union{Float32, Float64}
195195
isinteger(nu) && return _besselk(Int(nu), x)
196196
abs_nu = abs(nu)
197197
abs_x = abs(x)
@@ -216,6 +216,35 @@ function _besselk(nu::Integer, x::T) where T <: Union{Float32, Float64}
216216
end
217217
end
218218

219+
function _besselk(nu::AbstractRange, x::T) where T
220+
(nu[1] >= 0 && step(nu) == 1) || throw(ArgumentError("nu must be >= 0 with step(nu)=1"))
221+
len = length(nu)
222+
isone(len) && return [besselk(nu[1], x)]
223+
out = zeros(T, len)
224+
k = 1
225+
knu = zero(T)
226+
while abs(knu) < floatmin(T)
227+
if besselk_underflow_check(nu[k], x)
228+
knu = zero(T)
229+
else
230+
knu = _besselk(nu[k], x)
231+
end
232+
out[k] = knu
233+
k += 1
234+
k == len && break
235+
end
236+
if k < len
237+
out[k] = _besselk(nu[k], x)
238+
tmp = @view out[k-1:end]
239+
out[k-1:end] = besselk_up_recurrence!(tmp, x, nu[k-1:end])
240+
return out
241+
else
242+
return out
243+
end
244+
end
245+
246+
besselk_underflow_check(nu, x::T) where T = nu < T(1.45)*(x - 780) + 45*Base.Math._approx_cbrt(x - 780)
247+
219248
"""
220249
besselk_positive_args(x::T) where T <: Union{Float32, Float64}
221250

src/bessely.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -243,11 +243,11 @@ end
243243
Bessel function of the second kind of order nu, ``Y_{nu}(x)``.
244244
nu and x must be real where nu and x can be positive or negative.
245245
"""
246-
bessely(nu::Real, x::Real) = _bessely(nu, float(x))
246+
bessely(nu, x::Real) = _bessely(nu, float(x))
247247

248-
_bessely(nu, x::Float16) = Float16(_bessely(nu, Float32(x)))
248+
_bessely(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_bessely(Float32(nu), Float32(x)))
249249

250-
function _bessely(nu, x::T) where T <: Union{Float32, Float64}
250+
function _bessely(nu::T, x::T) where T <: Union{Float32, Float64}
251251
isnan(nu) || isnan(x) && return NaN
252252
isinteger(nu) && return _bessely(Int(nu), x)
253253
abs_nu = abs(nu)
@@ -296,6 +296,13 @@ function _bessely(nu::Integer, x::T) where T <: Union{Float32, Float64}
296296
end
297297
end
298298

299+
function _bessely(nu::AbstractRange, x::T) where T
300+
(nu[1] >= 0 && step(nu) == 1) || throw(ArgumentError("nu must be >= 0 with step(nu)=1"))
301+
out = Vector{T}(undef, length(nu))
302+
out[1], out[2] = _bessely(nu[1], x), _bessely(nu[2], x)
303+
return besselj_up_recurrence!(out, x, nu)
304+
end
305+
299306
#####
300307
##### `bessely` for positive arguments and orders
301308
#####

src/hankel.jl

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,25 @@ function besselh(nu::Real, k::Integer, x)
194194
end
195195
end
196196

197+
function besselh(nu::AbstractRange, k::Integer, x::T) where T
198+
(nu[1] >= 0 && step(nu) == 1) || throw(ArgumentError("nu must be >= 0 with step(nu)=1"))
199+
if nu[end] < x
200+
out = Vector{Complex{T}}(undef, length(nu))
201+
out[1], out[2] = besselh(nu[1], k, x), besselh(nu[2], k, x)
202+
return besselj_up_recurrence!(out, x, nu)
203+
else
204+
Jn = besselj(nu, x)
205+
Yn = bessely(nu, x)
206+
if k == 1
207+
return complex.(Jn, Yn)
208+
elseif k == 2
209+
return complex.(Jn, -Yn)
210+
else
211+
throw(ArgumentError("k must be 1 or 2"))
212+
end
213+
end
214+
end
215+
197216
"""
198217
hankelh1(nu, x)
199218
Bessel function of the third kind of order `nu`, ``H^{(1)}_\\nu(x)``.

src/modifiedsphericalbessel.jl

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Computes `k_{ν}(x)`, the modified second-kind spherical Bessel function, and of
1919
"""
2020
sphericalbesselk(nu::Real, x::Real) = _sphericalbesselk(nu, float(x))
2121

22-
_sphericalbesselk(nu, x::Float16) = Float16(_sphericalbesselk(nu, Float32(x)))
22+
_sphericalbesselk(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_sphericalbesselk(Float32(nu), Float32(x)))
2323

2424
function _sphericalbesselk(nu, x::T) where T <: Union{Float32, Float64}
2525
if ~isfinite(x)
@@ -39,7 +39,7 @@ function _sphericalbesselk(nu, x::T) where T <: Union{Float32, Float64}
3939
_nu = ifelse(nu<zero(nu), -one(nu)-nu, nu)
4040
return sphericalbesselk_int(Int(_nu), x)
4141
else
42-
return inv(SQPIO2(T)*sqrt(x))*besselk(nu+1/2, x)
42+
return inv(SQPIO2(T)*sqrt(x))*besselk(nu+T(1)/2, x)
4343
end
4444
end
4545
sphericalbesselk_cutoff(nu) = nu < 41.5
@@ -65,15 +65,15 @@ Computes `i_{ν}(x)`, the modified first-kind spherical Bessel function.
6565
"""
6666
sphericalbesseli(nu::Real, x::Real) = _sphericalbesseli(nu, float(x))
6767

68-
_sphericalbesseli(nu, x::Float16) = Float16(_sphericalbesseli(nu, Float32(x)))
68+
_sphericalbesseli(nu::Union{Int16, Float16}, x::Union{Int16, Float16}) = Float16(_sphericalbesseli(Float32(nu), Float32(x)))
6969

7070
function _sphericalbesseli(nu, x::T) where T <: Union{Float32, Float64}
7171
isinf(x) && return x
7272
x < zero(x) && throw(DomainError(x, "Complex result returned for real arguments. Complex arguments are currently not supported"))
7373

7474
sphericalbesselj_small_args_cutoff(nu, x::T) && return sphericalbesseli_small_args(nu, x)
7575
isinteger(nu) && return _sphericalbesseli_small_orders(Int(nu), x)
76-
return SQPIO2(T)*besseli(nu+1/2, x) / sqrt(x)
76+
return SQPIO2(T)*besseli(nu+T(1)/2, x) / sqrt(x)
7777
end
7878

7979
function _sphericalbesseli_small_orders(nu::Integer, x::T) where T
@@ -86,7 +86,7 @@ function _sphericalbesseli_small_orders(nu::Integer, x::T) where T
8686
nu_abs == 0 && return sinhx / x
8787
nu_abs == 1 && return (x*coshx - sinhx) / x2
8888
nu_abs == 2 && return (x2*sinhx + 3*(sinhx - x*coshx)) / (x2*x)
89-
return SQPIO2(T)*besseli(nu+1/2, x) / sqrt(x)
89+
return SQPIO2(T)*besseli(nu+T(1)/2, x) / sqrt(x)
9090
end
9191

9292
function sphericalbesseli_small_args(nu, x::T) where T

0 commit comments

Comments
 (0)