@@ -186,59 +186,65 @@ function besselj(nu::Integer, x::T) where T
186
186
end
187
187
end
188
188
189
+ """
190
+ besselj_positive_args(nu, x::T) where T <: Float64
191
+
192
+ Bessel function of the first kind of order nu, ``J_{nu}(x)``.
193
+ nu and x must be real and nu and x must be positive.
194
+
195
+ No checks on arguments are performed and should only be called if certain nu, x >= 0.
196
+ """
189
197
function besselj_positive_args (nu:: Real , x:: T ) where T
190
198
nu == 0 && return besselj0 (x)
191
199
nu == 1 && return besselj1 (x)
192
200
193
- x < 4.0 && return besselj_small_arguments_orders (nu, x)
201
+ # x < ~nu branch see src/U_polynomials.jl
202
+ besseljy_debye_cutoff (nu, x) && return besseljy_debye (nu, x)[1 ]
194
203
195
- large_arg_cutoff = 1.65 * nu
196
- (x > large_arg_cutoff && x > 20.0 ) && return besseljy_large_argument (nu, x)[1 ]
204
+ # large argument branch see src/asymptotics.jl
205
+ besseljy_large_argument_cutoff (nu, x ) && return besseljy_large_argument (nu, x)[1 ]
197
206
207
+ # x > ~nu branch see src/U_polynomials.jl on computing Hankel function
208
+ hankel_debye_cutoff (nu, x) && return real (hankel_debye (nu, x))
198
209
199
- debye_cutoff = 2.0 + 1.00035 * x + Base. Math. _approx_cbrt (302.681 * Float64 (x))
200
- nu > debye_cutoff && return besseljy_debye (nu, x)[1 ]
201
-
202
- if nu >= x
203
- nu_shift = ceil (Int, debye_cutoff - nu)
204
- v = nu + nu_shift
205
- jnu = besseljy_debye (v, x)[1 ]
206
- jnup1 = besseljy_debye (v+ 1 , x)[1 ]
207
- return besselj_down_recurrence (x, jnu, jnup1, v, nu)[1 ]
208
- end
209
-
210
- # at this point x > nu and x < nu * 1.65
211
- # in this region forward recurrence is stable
212
- # we must decide if we should do backward recurrence if we are closer to debye accuracy
213
- # or if we should do forward recurrence if we are closer to large argument expansion
214
- debye_cutoff = 5.0 + 1.00033 * x + Base. Math. _approx_cbrt (1427.61 * Float64 (x))
210
+ # use power series for small x and for when nu > x
211
+ besselj_series_cutoff (nu, x) && return besselj_power_series (nu, x)
215
212
216
- debye_diff = debye_cutoff - nu
217
- large_arg_diff = nu - x / 2.0
213
+ # At this point we must fill the region when x ≈ v with recurrence
214
+ # Backward recurrence is always stable and forward recurrence is stable when x > nu
215
+ # However, we only use backward recurrence by shifting the order up and using `besseljy_debye` to generate start values
216
+ # Both `besseljy_debye` and `hankel_debye` get more accurate for large orders,
217
+ # however `besseljy_debye` is slightly more efficient (no complex variables) and we need no branches if only consider one direction.
218
+ # On the other hand, shifting the order down avoids any concern about underflow for large orders
219
+ # Shifting the order too high while keeping x fixed could result in numerical underflow
220
+ # Therefore we need to shift up only until the `besseljy_debye` is accurate and need to test that no underflow occurs
221
+ # Shifting the order up decreases the value substantially for high orders and results in a stable forward recurrence
222
+ # as the values rapidly increase
218
223
219
- if (debye_diff > large_arg_diff && x > 20.0 )
220
- nu_shift = ceil (Int, large_arg_diff)
221
- v2 = nu - nu_shift
222
- jnu = besseljy_large_argument (v2, x)[1 ]
223
- jnum1 = besseljy_large_argument (v2 - 1 , x)[1 ]
224
- return besselj_up_recurrence (x, jnu, jnum1, v2, nu)[1 ]
225
- else
226
- nu_shift = ceil (Int, debye_diff)
227
- v = nu + nu_shift
228
- jnu = besseljy_debye (v, x)[1 ]
229
- jnup1 = besseljy_debye (v+ 1 , x)[1 ]
230
- return besselj_down_recurrence (x, jnu, jnup1, v, nu)[1 ]
231
- end
224
+ debye_cutoff = 2.0 + 1.00035 * x + Base. Math. _approx_cbrt (302.681 * Float64 (x))
225
+ nu_shift = ceil (Int, debye_cutoff - nu)
226
+ v = nu + nu_shift
227
+ jnu = besseljy_debye (v, x)[1 ]
228
+ jnup1 = besseljy_debye (v+ 1 , x)[1 ]
229
+ return besselj_down_recurrence (x, jnu, jnup1, v, nu)[1 ]
232
230
end
233
231
234
- # generally can only use for x < 4.0
235
- # this needs a better way to sum these as it produces large errors
232
+ # ####
233
+ # #### Power series for J_{nu}(x)
234
+ # ####
235
+
236
+ # accurate for x < 7.0 or nu > 2+ 0.109x + 0.062x^2 for Float64
237
+ # accurate for x < 20.0 or nu > 14.4 - 0.455x + 0.027x^2 for Float32 (when using F64 precision)
236
238
# only valid in non-oscillatory regime (v>1/2, 0<t<sqrt(v^2 - 0.25))
237
- # power series has premature underflow for large orders
238
- function besselj_small_arguments_orders (v, x :: T ) where T
239
- v > 60 && return log_besselj_small_arguments_orders (v , x)
239
+ # power series has premature underflow for large orders though use besseljy_debye for large orders
240
+ """
241
+ besselj_power_series(nu , x::T) where T <: Float64
240
242
241
- MaxIter = 2000
243
+ Computes ``J_{nu}(x)`` using the power series.
244
+ In general, this is most accurate for small arguments and when nu > x.
245
+ """
246
+ function besselj_power_series (v, x:: T ) where T
247
+ MaxIter = 3000
242
248
out = zero (T)
243
249
a = (x/ 2 )^ v / gamma (v + one (T))
244
250
t2 = (x/ 2 )^ 2
@@ -250,11 +256,16 @@ function besselj_small_arguments_orders(v, x::T) where T
250
256
return out
251
257
end
252
258
259
+ besselj_series_cutoff (v, x:: Float64 ) = (x < 7.0 ) || v > (2 + x* (0.109 + 0.062 x))
260
+ besselj_series_cutoff (v, x:: Float32 ) = (x < 20.0 ) || v > (14.4 + x* (- 0.455 + 0.027 x))
261
+
262
+ #=
253
263
# this needs a better way to sum these as it produces large errors
254
264
# use when v is large and x is small
255
- # need for bessely
265
+ # though when v is large we should use the debye expansion instead
266
+ # also do not have a julia implementation of loggamma so will not use for now
256
267
function log_besselj_small_arguments_orders(v, x::T) where T
257
- MaxIter = 2000
268
+ MaxIter = 3000
258
269
out = zero(T)
259
270
a = one(T)
260
271
x2 = (x/2)^2
@@ -266,3 +277,4 @@ function log_besselj_small_arguments_orders(v, x::T) where T
266
277
logout = -loggamma(v + 1) + fma(v, log(x/2), log(out))
267
278
return exp(logout)
268
279
end
280
+ =#
0 commit comments