Skip to content

Commit d83f5d0

Browse files
committed
add neg orders and args
1 parent 9eed89e commit d83f5d0

File tree

5 files changed

+109
-13
lines changed

5 files changed

+109
-13
lines changed

src/Bessels.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ using SpecialFunctions: loggamma
44

55
export besselj0
66
export besselj1
7+
export besselj
78

89
export bessely0
910
export bessely1

src/besselj.jl

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -150,13 +150,40 @@ function besselj1(x::Float32)
150150
return p * s
151151
end
152152
end
153-
"""
154-
besselj(nu, x::T) where T <: Union{Float32, Float64}
155153

156-
Bessel function of the first kind of order nu, ``J_{nu}(x)``.
157-
Nu must be real.
158-
"""
159-
function _besselj(nu, x)
154+
function besselj(nu::Real, x::T) where T
155+
abs_nu = abs(nu)
156+
abs_x = abs(x)
157+
158+
Jnu = besselj_positive_args(abs_nu, abs_x)
159+
160+
if nu >= zero(T)
161+
if x >= zero(T)
162+
return Jnu
163+
else
164+
if isinteger(abs_nu)
165+
return (-1)^Int(abs_nu) * Jnu
166+
else
167+
return cispi(abs_nu)*Jnu
168+
end
169+
end
170+
else
171+
if x >= zero(T)
172+
isinteger(nu) && return (-1)^Int(abs_nu) * Jnu
173+
Ynu = bessely_positive_args(abs_nu, abs_x)
174+
spi, cpi = sincospi(abs_nu)
175+
return cpi*Jnu - spi*Ynu
176+
else
177+
Ynu = bessely_positive_args(abs_nu, abs_x)
178+
spi, cpi = sincospi(abs_nu)
179+
out = cpi*Jnu - spi*Ynu
180+
isinteger(nu) && return (-1)^Int(abs_nu) * out
181+
return cispi(nu)*out
182+
end
183+
end
184+
end
185+
186+
function besselj_positive_args(nu::Real, x::T) where T
160187
nu == 0 && return besselj0(x)
161188
nu == 1 && return besselj1(x)
162189

src/bessely.jl

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,40 @@ end
187187
Bessel function of the first kind of order nu, ``J_{nu}(x)``.
188188
Nu must be real.
189189
"""
190-
function bessely(nu, x::T) where T
190+
191+
192+
function bessely(nu::Real, x::T) where T
193+
abs_nu = abs(nu)
194+
abs_x = abs(x)
195+
196+
Ynu = bessely_positive_args(abs_nu, abs_x)
197+
198+
if nu > zero(T)
199+
if x > zero(T)
200+
return Ynu
201+
else
202+
Jnu = besselj_positive_args(abs_nu, abs_x)
203+
return cispi(-nu)*Ynu + 2im*cospi(nu)*Jnu
204+
end
205+
elseif nu < zero(T)
206+
if x > zero(T)
207+
isinteger(nu) && return (-1)^Int(abs_nu) * Ynu
208+
Jnu = besselj_positive_args(abs_nu, abs_x)
209+
spi, cpi = sincospi(abs_nu)
210+
return cpi*Ynu + spi*Jnu
211+
else
212+
Jnu = besselj_positive_args(abs_nu, abs_x)
213+
isinteger(nu) && return Ynu + 2im * Jnu
214+
spi, cpi = sincospi(abs_nu)
215+
out = cpi * (cispi(nu)*Ynu + 2im * cpi * Jnu)
216+
return out + spi * cispi(abs_nu) * Jnu
217+
end
218+
else
219+
return -T(Inf)
220+
end
221+
end
222+
223+
function bessely_positive_args(nu, x::T) where T
191224

192225
# use forward recurrence if nu is an integer up until it becomes inefficient
193226
(isinteger(nu) && nu < 250) && return besselj_up_recurrence(x, bessely1(x), bessely0(x), 1, nu)[1]

test/besselj_test.jl

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ nu = [2, 4, 6, 10, 15, 20, 25, 30, 40, 50, 60, 70, 80, 90, 100]
8484
for v in nu, xx in x
8585
xx *= v
8686
sf = SpecialFunctions.besselj(BigFloat(v), BigFloat(xx))
87-
@test isapprox(Bessels._besselj(v, xx), Float64(sf), rtol=5e-14)
87+
@test isapprox(besselj(v, xx), Float64(sf), rtol=5e-14)
8888
end
8989

9090
# test half orders (SpecialFunctions does not give big float precision)
@@ -94,20 +94,38 @@ x = [0.05, 0.1, 0.2, 0.4, 0.5, 0.6, 0.7, 0.75, 0.8, 0.85, 0.9, 0.92, 0.95, 0.97,
9494
nu = [0.1, 0.4567, 0.8123, 1.5, 2.5, 4.1234, 6.8, 12.3, 18.9, 28.2345, 38.1235, 51.23, 72.23435, 80.5, 98.5, 104.2]
9595
for v in nu, xx in x
9696
xx *= v
97-
@test isapprox(Bessels._besselj(v, xx), SpecialFunctions.besselj(v, xx), rtol=1e-12)
97+
@test isapprox(besselj(v, xx), SpecialFunctions.besselj(v, xx), rtol=1e-12)
9898
end
9999

100100
## test large orders
101101
nu = [150, 165.2, 200.0, 300.0, 500.0, 1000.0, 5000.2, 10000.0, 50000.0]
102102
x = [0.2, 0.4, 0.5, 0.6, 0.7, 0.75, 0.8, 0.85, 0.9, 0.92,0.93, 0.94, 0.95, 0.96, 0.97, 0.98, 0.99,0.995, 0.999, 1.0, 1.01, 1.05, 1.08, 1.1, 1.2]
103103
for v in nu, xx in x
104104
xx *= v
105-
@test isapprox(Bessels._besselj(v, xx), SpecialFunctions.besselj(v, xx), rtol=5e-11)
105+
@test isapprox(besselj(v, xx), SpecialFunctions.besselj(v, xx), rtol=5e-11)
106106
end
107107

108108
## test large arguments
109-
@test isapprox(Bessels._besselj(10.0, 150.0), SpecialFunctions.besselj(10.0, 150.0), rtol=1e-12)
109+
@test isapprox(besselj(10.0, 150.0), SpecialFunctions.besselj(10.0, 150.0), rtol=1e-12)
110110

111111
# test BigFloat for single point
112-
@test isapprox(Bessels._besselj(big"2000", big"1500.0"), SpecialFunctions.besselj(big"2000", big"1500"), rtol=5e-20)
113-
@test isapprox(Bessels._besselj(big"20", big"1500.0"), SpecialFunctions.besselj(big"20", big"1500"), rtol=5e-20)
112+
@test isapprox(besselj(big"2000", big"1500.0"), SpecialFunctions.besselj(big"2000", big"1500"), rtol=5e-20)
113+
@test isapprox(besselj(big"20", big"1500.0"), SpecialFunctions.besselj(big"20", big"1500"), rtol=5e-20)
114+
115+
116+
# need to test accuracy of negative orders and negative arguments and all combinations within
117+
# SpecialFunctions.jl doesn't provide these so will hand check against hard values
118+
# values taken from https://keisan.casio.com/exec/system/1180573474 which match mathematica
119+
# need to also account for different branches when nu isa integer
120+
nu = -9.102; x = -12.48
121+
@test isapprox(besselj(nu, x), 0.09842356047575545808128 -0.03266486217437818486161im, rtol=8e-15)
122+
nu = -5.0; x = -5.1
123+
@test isapprox(besselj(nu, x), 0.2740038554704588327387, rtol=8e-15)
124+
nu = -7.3; x = 19.1
125+
@test isapprox(besselj(nu, x), 0.1848055978553359009813, rtol=8e-15)
126+
nu = -14.0; x = 21.3
127+
@test isapprox(besselj(nu, x), -0.1962844898264965120021, rtol=8e-15)
128+
nu = 13.0; x = -8.5
129+
@test isapprox(besselj(nu, x), -0.006128034621313167000171, rtol=8e-15)
130+
nu = 17.45; x = -16.23
131+
@test isapprox(besselj(nu, x), -0.01607335977752705869797 -0.1014831996412783806255im, rtol=8e-15)

test/bessely_test.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,20 @@ for v in nu, xx in x
8686
xx *= v
8787
@test isapprox(bessely(v, xx), SpecialFunctions.bessely(v, xx), rtol=5e-12)
8888
end
89+
90+
# need to test accuracy of negative orders and negative arguments and all combinations within
91+
# SpecialFunctions.jl doesn't provide these so will hand check against hard values
92+
# values taken from https://keisan.casio.com/exec/system/1180573474 which match mathematica
93+
# need to also account for different branches when nu isa integer
94+
nu = -2.3; x = -2.4
95+
@test isapprox(bessely(nu, x), -0.0179769671833457636186 + 0.7394120337538928700168im, rtol=8e-15)
96+
nu = -4.0; x = -12.6
97+
@test isapprox(bessely(nu, x), -0.02845106816742465563357 +0.4577922229605476882792im, rtol=8e-15)
98+
nu = -6.2; x = 18.6
99+
@test isapprox(bessely(nu, x), -0.05880321550673669650027, rtol=8e-15)
100+
nu = -8.0; x = 23.2
101+
@test isapprox(bessely(nu, x),-0.166071277370329242677, rtol=8e-15)
102+
nu = 11.0; x = -8.2
103+
@test isapprox(bessely(nu, x),1.438494049708244558901 -0.06222860017222637350092im, rtol=8e-15)
104+
nu = 13.678; x = -12.98
105+
@test isapprox(bessely(nu, x),-0.2227392320508850571009 -0.2085585256158188848322im, rtol=8e-15)

0 commit comments

Comments
 (0)