@@ -137,37 +137,51 @@ Modified Bessel function of the first kind of order nu, ``I_{nu}(x)``.
137
137
Nu must be real.
138
138
"""
139
139
function besseli (nu, x:: T ) where T <: Union{Float32, Float64}
140
- nu == 0 && return besseli0 (x)
141
- nu == 1 && return besseli1 (x)
140
+ iszero (nu) && return besseli0 (x)
141
+ isone (nu) && return besseli1 (x)
142
142
143
- if x > maximum ((T (30 ), nu^ 2 / 6 ))
144
- return T (besseli_large_argument (nu, x))
145
- elseif nu > 25.0 || x > 35.0
146
- return T (besseli_large_orders (nu, x))
147
- else
148
- return T (besseli_power_series (nu, x))
149
- end
143
+ # use large argument expansion if x >> nu
144
+ besseli_large_argument_cutoff (nu, x) && return besseli_large_argument (nu, x)
145
+
146
+ # use uniform debye expansion if x or nu is large
147
+ besselik_debye_cutoff (nu, x) && return T (besseli_large_orders (nu, x))
148
+
149
+ # for rest of values use the power series
150
+ return besseli_power_series (nu, x)
150
151
end
152
+
151
153
"""
152
154
besselix(nu, x::T) where T <: Union{Float32, Float64}
153
155
154
156
Scaled modified Bessel function of the first kind of order nu, ``I_{nu}(x)*e^{-x}``.
155
157
Nu must be real.
156
158
"""
157
159
function besselix (nu, x:: T ) where T <: Union{Float32, Float64}
158
- nu == 0 && return besseli0x (x)
159
- nu == 1 && return besseli1x (x)
160
-
161
- if x > maximum ((T (30 ), nu^ 2 / 4 ))
162
- return T (besseli_large_argument_scaled (nu, x))
163
- elseif x <= 2 * sqrt (nu + 1 )
164
- return T (besseli_power_series (nu, x)) * exp (- x)
165
- elseif nu < 100
166
- return T (_besseli_continued_fractions_scaled (nu, x))
167
- else
168
- return besseli_large_orders_scaled (nu, x)
169
- end
160
+ iszero (nu) && return besseli0x (x)
161
+ isone (nu) && return besseli1x (x)
162
+
163
+ # use large argument expansion if x >> nu
164
+ besseli_large_argument_cutoff (nu, x) && return besseli_large_argument_scaled (nu, x)
165
+
166
+ # use uniform debye expansion if x or nu is large
167
+ besselik_debye_cutoff (nu, x) && return T (besseli_large_orders_scaled (nu, x))
168
+
169
+ # for rest of values use the power series
170
+ return besseli_power_series (nu, x) * exp (- x)
170
171
end
172
+
173
+ # ####
174
+ # #### Debye's uniform asymptotic for I_{nu}(x)
175
+ # ####
176
+
177
+ # Implements the uniform asymptotic expansion https://dlmf.nist.gov/10.41
178
+ # In general this is valid when either x or nu is gets large
179
+ # see the file src/U_polynomials.jl for more details
180
+ """
181
+ besseli_large_orders(nu, x::T)
182
+
183
+ Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
184
+ """
171
185
function besseli_large_orders (v, x:: T ) where T <: Union{Float32, Float64}
172
186
S = promote_type (T, Float64)
173
187
x = S (x)
@@ -180,6 +194,7 @@ function besseli_large_orders(v, x::T) where T <: Union{Float32, Float64}
180
194
181
195
return coef* Uk_poly_In (p, v, p2, T)
182
196
end
197
+
183
198
function besseli_large_orders_scaled (v, x:: T ) where T <: Union{Float32, Float64}
184
199
S = promote_type (T, Float64)
185
200
x = S (x)
@@ -192,39 +207,18 @@ function besseli_large_orders_scaled(v, x::T) where T <: Union{Float32, Float64}
192
207
193
208
return T (coef* Uk_poly_In (p, v, p2, T))
194
209
end
195
- function _besseli_continued_fractions (nu, x:: T ) where T
196
- S = promote_type (T, Float64)
197
- xx = S (x)
198
- knum1, knu = besselk_up_recurrence (xx, besselk1 (xx), besselk0 (xx), 1 , nu- 1 )
199
- # if knu or knum1 is zero then besseli will likely overflow
200
- (iszero (knu) || iszero (knum1)) && return throw (DomainError (x, " Overflow error" ))
201
- return 1 / (x * (knum1 + knu / steed (nu, x)))
202
- end
203
- function _besseli_continued_fractions_scaled (nu, x:: T ) where T
204
- S = promote_type (T, Float64)
205
- xx = S (x)
206
- knum1, knu = besselk_up_recurrence (xx, besselk1x (xx), besselk0x (xx), 1 , nu- 1 )
207
- # if knu or knum1 is zero then besseli will likely overflow
208
- (iszero (knu) || iszero (knum1)) && return throw (DomainError (x, " Overflow error" ))
209
- return 1 / (x * (knum1 + knu / steed (nu, x)))
210
- end
211
- function steed (n, x:: T ) where T
212
- MaxIter = 1000
213
- xinv = inv (x)
214
- xinv2 = 2 * xinv
215
- d = x / (n + n)
216
- a = d
217
- h = a
218
- b = muladd (2 , n, 2 ) * xinv
219
- for _ in 1 : MaxIter
220
- d = inv (b + d)
221
- a *= muladd (b, d, - 1 )
222
- h = h + a
223
- b = b + xinv2
224
- abs (a / h) <= eps (T) && break
225
- end
226
- return h
227
- end
210
+
211
+ # ####
212
+ # #### Large argument expansion (x>>nu) for I_{nu}(x)
213
+ # ####
214
+
215
+ # Implements the uniform asymptotic expansion https://dlmf.nist.gov/10.40.E1
216
+ # In general this is valid when x > nu^2
217
+ """
218
+ besseli_large_orders(nu, x::T)
219
+
220
+ Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
221
+ """
228
222
function besseli_large_argument (v, z:: T ) where T
229
223
MaxIter = 1000
230
224
a = exp (z / 2 )
@@ -260,7 +254,19 @@ function besseli_large_argument_scaled(v, z::T) where T
260
254
end
261
255
return res * coef
262
256
end
257
+ besseli_large_argument_cutoff (nu, x) = x > maximum ((30.0 , nu^ 2 / 6 ))
258
+
259
+ # ####
260
+ # #### Power series for I_{nu}(x)
261
+ # ####
262
+
263
+ # Use power series form of I_v(x) which is generally accurate across all values though slower for larger x
264
+ # https://dlmf.nist.gov/10.25.E2
265
+ """
266
+ besseli_power_series(nu, x::T) where T <: Float64
263
267
268
+ Computes ``I_{nu}(x)`` using the power series for any value of nu.
269
+ """
264
270
function besseli_power_series (v, x:: T ) where T
265
271
MaxIter = 3000
266
272
out = zero (T)
@@ -274,3 +280,45 @@ function besseli_power_series(v, x::T) where T
274
280
end
275
281
return out
276
282
end
283
+
284
+ #=
285
+ # the following is a deprecated version of the continued fraction approach
286
+ # using K0 and K1 as starting values then forward recurrence up till nu
287
+ # then using the wronskian to getting I_{nu}
288
+ # in general this method is slow and depends on starting values of K0 and K1
289
+ # which is not very flexible for arbitary orders
290
+
291
+ function _besseli_continued_fractions(nu, x::T) where T
292
+ S = promote_type(T, Float64)
293
+ xx = S(x)
294
+ knum1, knu = besselk_up_recurrence(xx, besselk1(xx), besselk0(xx), 1, nu-1)
295
+ # if knu or knum1 is zero then besseli will likely overflow
296
+ (iszero(knu) || iszero(knum1)) && return throw(DomainError(x, "Overflow error"))
297
+ return 1 / (x * (knum1 + knu / steed(nu, x)))
298
+ end
299
+ function _besseli_continued_fractions_scaled(nu, x::T) where T
300
+ S = promote_type(T, Float64)
301
+ xx = S(x)
302
+ knum1, knu = besselk_up_recurrence(xx, besselk1x(xx), besselk0x(xx), 1, nu-1)
303
+ # if knu or knum1 is zero then besseli will likely overflow
304
+ (iszero(knu) || iszero(knum1)) && return throw(DomainError(x, "Overflow error"))
305
+ return 1 / (x * (knum1 + knu / steed(nu, x)))
306
+ end
307
+ function steed(n, x::T) where T
308
+ MaxIter = 1000
309
+ xinv = inv(x)
310
+ xinv2 = 2 * xinv
311
+ d = x / (n + n)
312
+ a = d
313
+ h = a
314
+ b = muladd(2, n, 2) * xinv
315
+ for _ in 1:MaxIter
316
+ d = inv(b + d)
317
+ a *= muladd(b, d, -1)
318
+ h = h + a
319
+ b = b + xinv2
320
+ abs(a / h) <= eps(T) && break
321
+ end
322
+ return h
323
+ end
324
+ =#
0 commit comments