Skip to content

Commit 919c449

Browse files
committed
add tests and gamma
1 parent f5cf89e commit 919c449

File tree

5 files changed

+117
-8
lines changed

5 files changed

+117
-8
lines changed

Project.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ uuid = "0e736298-9ec6-45e8-9647-e4fc86a2fe38"
33
authors = ["Michael Helton <[email protected]> and contributors"]
44
version = "0.1.0"
55

6+
[deps]
7+
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
8+
69
[compat]
710
julia = "1.5"
811

src/Bessels.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
module Bessels
22

3+
using SpecialFunctions: loggamma
4+
35
export besselj0
46
export besselj1
57

src/besselj.jl

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,29 @@ function besselj1(x::Float32)
150150
return p * s
151151
end
152152
end
153+
"""
154+
besselj(nu, x::T) where T <: Union{Float32, Float64}
155+
156+
Bessel function of the first kind of order nu, ``J_{nu}(x)``.
157+
Nu must be real.
158+
"""
159+
function _besselj(nu, x)
160+
nu == 0 && return besselj0(x)
161+
nu == 1 && return besselj1(x)
162+
if x < 30.0
163+
if nu > 60.0
164+
return log_besselj_small_arguments_orders(nu, x)
165+
else
166+
return besselj_small_arguments_orders(nu, x)
167+
end
168+
elseif 2*x < nu
169+
return besselj_debye(nu, x)
170+
elseif x > 2*nu
171+
return besselj_large_argument(nu, x)
172+
else
173+
return nothing
174+
end
175+
end
153176

154177
function _α_αp_asymptotic(v, x::T) where T
155178
μ = 4 * v^2
@@ -190,8 +213,9 @@ function bessely_large_argument(v, x::T) where T
190213
return sin(α)*b
191214
end
192215

193-
216+
# this needs a better way to sum these as it produces large errors
194217
# only valid in non-oscillatory regime (v>1/2, 0<t<sqrt(v^2 - 0.25))
218+
# power series has premature underflow for large orders
195219
function besselj_small_arguments_orders(v, x::T) where T
196220
MaxIter = 1_000
197221
out = zero(T)
@@ -200,33 +224,35 @@ function besselj_small_arguments_orders(v, x::T) where T
200224
for i in 0:MaxIter
201225
out += a
202226
abs(a) < eps(T) * abs(out) && break
203-
a = -a * inv((v + i + one(T)) * (i + one(T))) * t2
227+
a *= -inv((v + i + one(T)) * (i + one(T))) * t2
204228
end
205229
return out
206230
end
207231

232+
# this needs a better way to sum these as it produces large errors
208233
# perhaps use when v is small i believe v also has to be positive for this to work
209234
# need for bessely
210235
function log_besselj_small_arguments_orders(v, x::T) where T
211-
MaxIter = 200
236+
MaxIter = 500
212237
out = zero(T)
213238
a = one(T)
214239
x2 = (x/2)^2
215240
for i in 0:MaxIter
216241
out += a
217242
a = -a * x2 * inv((i + one(T)) * (v + i + one(T)))
218-
#(abs(a) < eps(T) * abs(out)) && break
243+
(abs(a) < eps(T) * abs(out)) && break
219244
end
220-
logout = -loggamma(v + 1) + v * log(x/2) + log(out)
221-
return logout
245+
@show out
246+
logout = -loggamma(v + 1) + fma(v, log(x/2), log(out))
247+
return exp(logout)
222248
end
223249

224250
# valid when x < v (uniform asymptotic expansions)
225251
function besselj_debye(v, x)
226252
T = eltype(x)
227253
S = promote_type(T, Float64)
228254
x = S(x)
229-
255+
230256
vmx = fma(v,v, -x^2)
231257
vdx = v/x
232258
b = sqrt(vmx)
@@ -238,3 +264,71 @@ function besselj_debye(v, x)
238264

239265
return coef * Uk_poly_Jn(p, v, p2, T)
240266
end
267+
268+
# For 0.0 <= x < 171.5
269+
# Mean ULP = 0.55
270+
# Max ULP = 2.4
271+
# Adapted from Cephes Mathematical Library (MIT license https://en.smath.com/view/CephesMathLibrary/license) by Stephen L. Moshier
272+
function gamma(x)
273+
if x > 11.5
274+
return large_gamma(x)
275+
elseif x < 0.0
276+
#p = floor(x)
277+
#isequal(p, abs(x)) && return throw(DomainError(x, "NaN result for non-NaN input."))
278+
# need reflection formula
279+
return throw(DomainError(x, "Negative numbers are currently not implemented"))
280+
elseif x <= 11.5
281+
return small_gamma(x)
282+
elseif isnan(x)
283+
return x
284+
end
285+
end
286+
function large_gamma(x)
287+
T = Float64
288+
w = inv(x)
289+
s = (
290+
8.333333333333331800504e-2, 3.472222222230075327854e-3, -2.681327161876304418288e-3, -2.294719747873185405699e-4,
291+
7.840334842744753003862e-4, 6.989332260623193171870e-5, -5.950237554056330156018e-4, -2.363848809501759061727e-5,
292+
7.147391378143610789273e-4
293+
)
294+
w = w * evalpoly(w, s) + one(T)
295+
# lose precision on following block
296+
y = exp((x))
297+
if x > 143.01608
298+
isinf(x) && return x
299+
# avoid overflow
300+
v = x^(0.5 * x - 0.25)
301+
y = v * (v / y)
302+
else
303+
y = x^(x - 0.5) / y
304+
# (x - 0.5) * log(x) - x
305+
end
306+
return SQ2PI(T) * y * w
307+
end
308+
function small_gamma(x)
309+
T = Float64
310+
P = (
311+
1.000000000000000000009e0, 8.378004301573126728826e-1, 3.629515436640239168939e-1, 1.113062816019361559013e-1,
312+
2.385363243461108252554e-2, 4.092666828394035500949e-3, 4.542931960608009155600e-4, 4.212760487471622013093e-5
313+
)
314+
Q = (
315+
9.999999999999999999908e-1, 4.150160950588455434583e-1, -2.243510905670329164562e-1, -4.633887671244534213831e-2,
316+
2.773706565840072979165e-2, -7.955933682494738320586e-4, -1.237799246653152231188e-3, 2.346584059160635244282e-4,
317+
-1.397148517476170440917e-5
318+
)
319+
320+
z = one(T)
321+
while x >= 3.0
322+
x -= one(T)
323+
z *= x
324+
end
325+
while x < 2.0
326+
z /= x
327+
x += one(T)
328+
end
329+
330+
x -= T(2)
331+
p = evalpoly(x, P)
332+
q = evalpoly(x, Q)
333+
return z * p / q
334+
end

src/math_constants.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const SQ2OPI(::Type{Float64}) = .79788456080286535588
1111
const TWOOPI(::Type{Float64}) = 0.6366197723675814
1212
const SQPIO2(::Type{Float64}) = 1.25331413731550025
1313
const SQ1O2PI(::Type{Float64}) = 0.3989422804014327
14-
14+
const SQ2PI(::Type{Float64}) = 2.5066282746310007
1515

1616
const PIO4(::Type{Float32}) = 0.78539816339744830962f0
1717
const TWOOPI(::Type{Float32}) = 0.636619772367581343075535f0

test/besselj_test.jl

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,3 +76,13 @@ j1_32 = besselj1.(Float32.(x))
7676

7777
#@test besselj(3, 1.0) ≈ SpecialFunctions.besselj(3, 1.0)
7878
#@test besselj(-5, 6.1) ≈ SpecialFunctions.besselj(-5, 6.1)
79+
80+
## test the small value approximation using power series
81+
82+
nu = [0.5, 1.5, 3.0, 10.0, 22.2, 35.0, 52.1, 100.0, 250.2, 500.0, 1000.0]
83+
x = [0.001, 1.0, 5.0, 15.0, 29.9]
84+
85+
for v in nu, x in x
86+
@show v, x
87+
@test Bessels._besselj(v, x) SpecialFunctions.besselj(v, x)
88+
end

0 commit comments

Comments
 (0)