Skip to content

Commit b15874f

Browse files
authored
Derivative zeros and fast Float64 lookup tables (#23)
* Add functions for zeros of derivatives of J and Y functions Implement lookup tables (for Float64 outputs) for all four exported functions (fixes Issue #7) Improve docstrings, remove unused keyword argument order Add additional unit tests Update README.md Eliminate order keyword which is ignored by Roots.find_zero Fix Issue #21 * Update README.md and correct typo * Update README.md and correct typo
1 parent d8fd669 commit b15874f

File tree

3 files changed

+545
-24
lines changed

3 files changed

+545
-24
lines changed

README.md

Lines changed: 65 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,96 @@
11
# FunctionZeros
2-
*Zeros of the Bessel J and Y functions*
2+
*Zeros of the Bessel J and Y functions and their derivatives*
33

44
[![Build Status](https://github.com/JuliaMath/FunctionZeros.jl/actions/workflows/CI.yml/badge.svg?branch=master)](https://github.com/JuliaMath/FunctionZeros.jl/actions/workflows/CI.yml?query=branch%3Amaster)
55
[![Codecov](https://codecov.io/gh/JuliaMath/ILog2.jl/branch/master/graph/badge.svg)](https://codecov.io/gh/JuliaMath/FunctionZeros.jl)
66
[![Aqua QA](https://raw.githubusercontent.com/JuliaTesting/Aqua.jl/master/badge.svg)](https://github.com/JuliaTesting/Aqua.jl)
77
[![JET QA](https://img.shields.io/badge/JET.jl-%E2%9C%88%EF%B8%8F-%23aa4444)](https://github.com/aviatesk/JET.jl)
88

9-
This module provides a function to compute the zeros of the Bessel J and K functions,
10-
that is Bessel functions of the first and second kind.
9+
This package provides functions to compute the zeros of the J and Y functions,
10+
and the zeros of their derivatives, where J and Y are Bessel functions of the first and second kind, respectively.
11+
12+
For all functions described below, the order `nu::Real` is a finite number and `n::Integer` is a positive integer.
13+
When `nu isa AbstractFloat`, the returned value has the same type as `nu`. When `nu isa Integer`, the usual
14+
promotion rules apply, so that for most builtin integer types the output type will be `Float64`. However,
15+
when `nu isa BigInt` the output type will be `BigFloat`.
16+
17+
When the output type is `Float64`, the exported functions (`besselj_zero`,
18+
`bessely_zero`, `besselj_deriv_zero`, and `bessely_deriv_zero`) will use lookup tables to rapidly
19+
return function zeros if the order `nu` is one of the first few values of `0, 1, ...` and the enumerator
20+
`n` is one of the first values of `1, 2, 3, ...`. See the individual function docstrings for the actual
21+
extents of the lookup tables.
22+
23+
### Exported Functions
1124

1225
#### besselj_zero(nu, n)
1326

1427
```julia
1528
besselj_zero(nu, n)
1629
```
1730

18-
Return the `n`th zero of the the Bessel J function of order `nu`. The returned
19-
value has the same type as `nu`.
31+
Return the `n`th zero of the Bessel J function of order `nu`.
2032

21-
#### FunctionZeros.besselj_zero_asymptotic(nu, n)
33+
#### bessely_zero(nu, n)
34+
35+
```julia
36+
bessely_zero(nu, n)
37+
```
2238

23-
Asymptotic formula for the `n`th zero fo the the Bessel J function of order `nu`.
39+
Return the `n`th zero of the Bessel Y function of order `nu`.
40+
41+
#### besselj_deriv_zero(nu, n)
2442

2543
```julia
26-
besselj_zero_asymptotic(nu, n)
44+
besselj_deriv_zero(nu, n)
2745
```
2846

47+
Return the `n`th nonvanishing zero of the derivative of the Bessel J
48+
function of order `nu`.
2949

30-
#### bessely_zero(nu, n)
50+
#### bessely_deriv_zero(nu, n)
3151

3252
```julia
33-
bessely_zero(nu, n)
53+
bessely_deriv_zero(nu, n)
54+
```
55+
56+
Return the `n`th zero of the derivative of the Bessel Y function of order `nu`.
57+
58+
### Non-exported But Useful Functions
59+
60+
#### FunctionZeros.besselj_zero_asymptotic(nu, n)
61+
62+
```julia
63+
FunctionZeros.besselj_zero_asymptotic(nu, n)
3464
```
3565

36-
Return the `n`th zero of the the Bessel Y function of order `nu`. The returned
37-
value has the same type as `nu`.
66+
Asymptotic formula for the `n`th zero for the Bessel J function of order `nu`.
67+
3868

3969
#### FunctionZeros.bessely_zero_asymptotic(nu, n)
4070

41-
Asymptotic formula for the `n`th zero fo the the Bessel Y function of order `nu`.
71+
```julia
72+
FunctionZeros.bessely_zero_asymptotic(nu, n)
73+
```
74+
75+
Asymptotic formula for the `n`th zero for the Bessel Y function of order `nu`.
76+
77+
78+
#### FunctionZeros.besselj_deriv_zero_asymptotic(nu, n)
4279

4380
```julia
44-
bessely_zero_asymptotic(nu, n)
81+
FunctionZeros.besselj_deriv_zero_asymptotic(nu, n)
4582
```
83+
84+
Asymptotic formula for the `n`th nonvanishing zero of the derivative of the
85+
Bessel J function of order `nu`.
86+
87+
88+
#### FunctionZeros.bessely_deriv_zero_asymptotic(nu, n)
89+
90+
```julia
91+
FunctionZeros.bessely_deriv_zero_asymptotic(nu, n)
92+
```
93+
94+
Asymptotic formula for the `n`th zero of the derivative of the Bessel Y function of order `nu`.
95+
96+

src/FunctionZeros.jl

Lines changed: 240 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,51 @@
1+
2+
"""
3+
FunctionZeros
4+
This module provides functions to compute the zeros of the J and Y functions,
5+
and the zeros of their derivatives, where J and Y are Bessel functions of the first and second kind, respectively.
6+
"""
17
module FunctionZeros
28
import SpecialFunctions
39
import Roots
410

5-
export besselj_zero, bessely_zero
11+
export besselj_zero, bessely_zero, besselj_deriv_zero, bessely_deriv_zero
12+
13+
# Set max order and index of zeros to precompute and tabulate
14+
const nupre_max = 1
15+
const npre_max = 500
16+
17+
# Strings used in multiple function docstrings:
18+
const speeddocstr = """For greater speed, table lookup is used for `Float64` outputs when
19+
`nu ∈ 0:$nupre_max` and `n ∈ 1:$(npre_max)`."""
20+
const argstr = """## Arguments
21+
- `nu::Real`: The order of the Bessel function.
22+
- `n::Integer`: Enumerates the zero to be found.
23+
24+
## Return Value
25+
The return value type is determined by `nu`.
26+
When `nu isa AbstractFloat`, the returned value has the same type as `nu`.
27+
When `nu isa Integer`, the usual promotion rules apply.
28+
"""
29+
30+
"""
31+
besselj_zero_asymptotic(nu, n)
632
33+
Asymptotic formula for the `n`th zero of the the Bessel function of the first kind J of order `nu`.
734
35+
$argstr
36+
"""
837
besselj_zero_asymptotic(nu, n) = bessel_zero_asymptotic(nu, n, 1)
938

39+
"""
40+
bessely_zero_asymptotic(nu, n)
41+
42+
Asymptotic formula for the `n`th zero of the the Bessel function of the second kind Y of order `nu`.
43+
44+
$argstr
45+
"""
46+
bessely_zero_asymptotic(nu, n) = bessel_zero_asymptotic(nu, n, 2)
47+
48+
1049
"""
1150
bessel_zero_asymptotic(nu, n, kind=1)
1251
@@ -42,25 +81,216 @@ end
4281
# Order 0 is 6 times slower and 50-100 times less accurate
4382
# than higher orders, with other parameters constant.
4483
"""
45-
besselj_zero(nu, n; order=2)
84+
_besselj_zero(nu, n)
4685
4786
`n`th zero of the Bessel J function of order `nu`,
48-
for `n` = `1,2,...`.
87+
for `n` = `1,2,...`.
4988
50-
`order` is passed to the function `Roots.fzero`.
89+
$argstr
5190
"""
52-
besselj_zero(nu, n; order=2) = Roots.find_zero((x) -> SpecialFunctions.besselj(nu, x),
53-
bessel_zero_asymptotic(nu, n, 1); order=order)
91+
function _besselj_zero(nu::Real, n::Integer)
92+
return Roots.find_zero(bessel_zero_asymptotic(nu, n, 1)) do x
93+
return SpecialFunctions.besselj(nu, x)
94+
end
95+
end
96+
97+
# Float64 tabulation of selected besselj_zero values
98+
const jzero_pre = [_besselj_zero(nu, n) for nu in 0:nupre_max, n in 1:npre_max]
5499

55100
"""
56-
bessely_zero(nu, n; order=2)
101+
besselj_zero(nu, n)
102+
103+
Return the `n`th zero of the Bessel J function of order `nu`,
104+
for `n` = `1,2,...`.
105+
106+
$argstr
107+
$speeddocstr
108+
"""
109+
besselj_zero(nu::Real, n::Integer) = _besselj_zero(nu, n)
110+
111+
besselj_zero(nu::BigInt, n::Integer) = _besselj_zero(nu, n)
112+
113+
function besselj_zero(nu::Union{Integer,Float64}, n::Integer)
114+
if nu in 0:nupre_max && n in 1:npre_max
115+
return jzero_pre[Int(nu) + 1, Int(n)]
116+
else
117+
return _besselj_zero(nu, n)
118+
end
119+
end
120+
121+
122+
"""
123+
_bessely_zero(nu, n)
57124
58125
`n`th zero of the Bessel Y function of order `nu`,
59126
for `n` = `1,2,...`.
60127
61-
`order` is passed to the function `Roots.fzero`.
128+
$argstr
129+
"""
130+
function _bessely_zero(nu, n)
131+
if isone(n) && abs(nu) < 0.1587 # See Issue 21
132+
return Roots.find_zero((nu + besselj_zero(nu, n)) / 2) do x
133+
SpecialFunctions.bessely(nu, x)
134+
end
135+
else
136+
return Roots.find_zero(bessel_zero_asymptotic(nu, n, 2)) do x
137+
SpecialFunctions.bessely(nu, x)
138+
end
139+
end
140+
end
141+
142+
# tabulation of selected bessely_zero values in Float64
143+
const yzero_pre = [_bessely_zero(nu, n) for nu in 0:nupre_max, n in 1:npre_max]
144+
145+
"""
146+
bessely_zero(nu, n)
147+
148+
Return the `n`th zero of the Bessel Y function of order `nu`,
149+
for `n` = `1,2,...`.
150+
151+
$argstr
152+
$speeddocstr
153+
"""
154+
bessely_zero(nu::Real, n::Integer) = _bessely_zero(nu, n)
155+
156+
bessely_zero(nu::BigInt, n::Integer) = _bessely_zero(nu, n)
157+
158+
function bessely_zero(nu::Union{Integer,Float64}, n::Integer)
159+
if nu in 0:nupre_max && n in 1:npre_max
160+
return yzero_pre[Int(nu) + 1, Int(n)]
161+
else
162+
return _bessely_zero(nu, n)
163+
end
164+
end
165+
62166
"""
63-
bessely_zero(nu, n; order=2) = Roots.find_zero((x) -> SpecialFunctions.bessely(nu, x),
64-
bessel_zero_asymptotic(nu, n, 2); order=order)
167+
besselj_deriv_zero_asymptotic(nu, n)
168+
169+
Asymptotic formula for the `n`th zero of the derivative of the Bessel function of the first kind J of order `nu`.
170+
171+
$argstr
172+
"""
173+
besselj_deriv_zero_asymptotic(nu, n) = bessel_deriv_zero_asymptotic(nu, n, 1)
174+
175+
"""
176+
bessely_deriv_zero_asymptotic(nu, n)
177+
178+
Asymptotic formula for the `n`th zero of the derivative of the Bessel function of the second kind Y of order `nu`.
179+
180+
$argstr
181+
"""
182+
bessely_deriv_zero_asymptotic(nu, n) = bessel_deriv_zero_asymptotic(nu, n, 2)
183+
184+
185+
"""
186+
bessel_deriv_zero_asymptotic(nu, n, kind=1)
187+
188+
Asymptotic formula for the `n`th zero of the the derivative of Bessel J (Y) function
189+
of order `nu`. `kind == 1 (2)` for Bessel function of the first (second) kind, J (Y).
190+
"""
191+
function bessel_deriv_zero_asymptotic(nu_in::Real, n::Integer, kind=1)
192+
# Reference: https://dlmf.nist.gov/10.21.E20
193+
nu = abs(nu_in)
194+
if kind == 1
195+
beta = MathConstants.pi * (n + nu / 2 - 3//4)
196+
else # kind == 2
197+
beta = MathConstants.pi * (n + nu / 2 - 1//4)
198+
end
199+
delta = 8 * beta
200+
mu = 4 * nu^2
201+
mup2 = mu * mu
202+
mup3 = mup2 * mu
203+
mup4 = mup3 * mu
204+
deltap2 = delta * delta
205+
deltap3 = deltap2 * delta
206+
deltap5 = deltap3 * deltap2
207+
deltap7 = deltap5 * deltap2
208+
t1 = (mu + 3) / delta
209+
t2 = 4 * (7 * mup2 + 82 * mu - 9) / (3 * deltap3)
210+
t3 = 32 * (83 * mup3 + 2075 * mup2 - 3039 * mu + 3537) / (15 * deltap5)
211+
t4 = 64 * (6949 * mup4 + 296492 * mup3 - 1248002 * mup2 + 7414380 * mu - 5853627) /
212+
(105 * deltap7)
213+
zero_asymp = beta - (t1 + t2 + t3 + t4)
214+
return zero_asymp
215+
end
216+
217+
"""
218+
_besselj_deriv_zero(nu, n)
219+
220+
Return the `n`th nonvanishing zero of the derivative of Bessel J function of order `nu`,
221+
for `n` = `1,2,...`.
222+
223+
$argstr
224+
"""
225+
function _besselj_deriv_zero(nu::Real, n::Integer)
226+
# Ref: https://dlmf.nist.gov/10.6.E1
227+
iszero(nu) && (n += 1) # Skip the zero occuring at zero
228+
return Roots.find_zero(bessel_deriv_zero_asymptotic(nu, n, 1)) do x
229+
SpecialFunctions.besselj(nu - 1, x) - SpecialFunctions.besselj(nu + 1, x)
230+
end
231+
end
232+
233+
# tabulation of selected besselj_deriv_zero values in Float64
234+
const jderivzero_pre = [_besselj_deriv_zero(nu, n) for nu in 0:nupre_max, n in 1:npre_max]
235+
236+
"""
237+
besselj_deriv_zero(nu, n)
238+
239+
Return the `n`th nonvanishing zero of the derivative of the Bessel J function of order `nu`,
240+
for `n` = `1,2,...`.
241+
242+
$argstr
243+
$speeddocstr
244+
"""
245+
besselj_deriv_zero(nu::Real, n::Integer) = _besselj_deriv_zero(nu, n)
246+
247+
besselj_deriv_zero(nu::BigInt, n::Integer) = _besselj_deriv_zero(nu, n)
248+
249+
function besselj_deriv_zero(nu::Union{Integer,Float64}, n::Integer)
250+
if nu in 0:nupre_max && n in 1:npre_max
251+
return jderivzero_pre[Int(nu) + 1, Int(n)]
252+
else
253+
return _besselj_deriv_zero(nu, n)
254+
end
255+
end
256+
257+
"""
258+
_bessely_deriv_zero(nu, n)
259+
260+
Return the `n`th zero of the derivative of the Bessel Y function of order `nu`,
261+
for `n` = `1,2,...`.
262+
263+
$argstr
264+
"""
265+
function _bessely_deriv_zero(nu::Real, n::Integer)
266+
# Ref: https://dlmf.nist.gov/10.6.E1
267+
return Roots.find_zero(bessel_deriv_zero_asymptotic(nu, n, 2)) do x
268+
SpecialFunctions.bessely(nu - 1, x) - SpecialFunctions.bessely(nu + 1, x)
269+
end
270+
end
271+
272+
# tabulation of selected bessely_deriv_zero values in Float64
273+
const yderivzero_pre = [_bessely_deriv_zero(nu, n) for nu in 0:nupre_max, n in 1:npre_max]
274+
275+
"""
276+
bessely_deriv_zero(nu, n)
277+
278+
Return the `n`th zero of the derivative of the Bessel Y function of order `nu`,
279+
for `n` = `1,2,...`.
280+
281+
$argstr
282+
$speeddocstr
283+
"""
284+
bessely_deriv_zero(nu::Real, n::Integer) = _bessely_deriv_zero(nu, n)
285+
286+
bessely_deriv_zero(nu::BigInt, n::Integer) = _bessely_deriv_zero(nu, n)
287+
288+
function bessely_deriv_zero(nu::Union{Integer,Float64}, n::Integer)
289+
if nu in 0:nupre_max && n in 1:npre_max
290+
return yderivzero_pre[Int(nu) + 1, Int(n)]
291+
else
292+
return _bessely_deriv_zero(nu, n)
293+
end
294+
end
65295

66296
end # module FunctionZeros

0 commit comments

Comments
 (0)