@@ -130,26 +130,91 @@ function besseli1x(x::T) where T <: Union{Float32, Float64}
130
130
return z
131
131
end
132
132
133
+ # Modified Bessel functions of the first kind of order nu
134
+ # besseli(nu, x)
135
+ #
136
+ # A numerical routine to compute the modified Bessel function of the first kind I_{ν}(x) [1]
137
+ # for real orders and arguments of positive or negative value. The routine is based on several
138
+ # publications [2-6] that calculate I_{ν}(x) for positive arguments and orders where
139
+ # reflection identities are used to compute negative arguments and orders.
140
+ #
141
+ # In particular, the reflectance identities for negative noninteger orders I_{−ν}(x) = I_{ν}(x) + 2 / πsin(πν)*Kν(x)
142
+ # and for negative integer orders I_{−n}(x) = I_n(x) are used.
143
+ # For negative arguments of integer order, In(−x) = (−1)^n In(x) is used and for
144
+ # noninteger orders, Iν(−x) = exp(iπν) Iν(x) is used. For negative orders and arguments the previous identities are combined.
145
+ #
146
+ # The identities are computed by calling the `besseli_positive_args(nu, x)` function which computes I_{ν}(x)
147
+ # for positive arguments and orders. For large orders, Debye's uniform asymptotic expansions are used where large arguments (x>>nu)
148
+ # a large argument expansion is used. The rest of the values are computed using the power series.
149
+
150
+ # [1] https://dlmf.nist.gov/10.40.E1
151
+ # [2] Amos, Donald E. "Computation of modified Bessel functions and their ratios." Mathematics of computation 28.125 (1974): 239-251.
152
+ # [3] Gatto, M. A., and J. B. Seery. "Numerical evaluation of the modified Bessel functions I and K."
153
+ # Computers & Mathematics with Applications 7.3 (1981): 203-209.
154
+ # [4] Temme, Nico M. "On the numerical evaluation of the modified Bessel function of the third kind."
155
+ # Journal of Computational Physics 19.3 (1975): 324-337.
156
+ # [5] Amos, DEv. "Algorithm 644: A portable package for Bessel functions of a complex argument and nonnegative order."
157
+ # ACM Transactions on Mathematical Software (TOMS) 12.3 (1986): 265-273.
158
+ # [6] Segura, Javier, P. Fernández de Córdoba, and Yu L. Ratis. "A code to evaluate modified bessel functions based on thecontinued fraction method."
159
+ # Computer physics communications 105.2-3 (1997): 263-272.
160
+ #
161
+
133
162
"""
134
- besseli(nu, x::T) where T <: Union{Float32, Float64}
163
+ besseli(x::T) where T <: Union{Float32, Float64}
135
164
136
- Modified Bessel function of the first kind of order nu, ``I_{nu}(x)``.
137
- Nu must be real.
165
+ Modified Bessel function of the second kind of order nu, ``I_{nu}(x)``.
138
166
"""
139
- function besseli (nu, x:: T ) where T <: Union{Float32, Float64}
140
- nu == 0 && return besseli0 (x)
141
- nu == 1 && return besseli1 (x)
142
-
143
- if x > maximum ((T (30 ), nu^ 2 / 4 ))
144
- return T (besseli_large_argument (nu, x))
145
- elseif x <= 2 * sqrt (nu + 1 )
146
- return T (besseli_small_arguments (nu, x))
147
- elseif nu < 100
148
- return T (_besseli_continued_fractions (nu, x))
167
+ function besseli (nu:: Real , x:: T ) where T
168
+ isinteger (nu) && return besseli (Int (nu), x)
169
+ abs_nu = abs (nu)
170
+ abs_x = abs (x)
171
+
172
+ if nu >= 0
173
+ if x >= 0
174
+ return besseli_positive_args (abs_nu, abs_x)
175
+ else
176
+ return cispi (abs_nu) * besseli_positive_args (abs_nu, abs_x)
177
+ end
149
178
else
150
- return T (besseli_large_orders (nu, x))
179
+ if x >= 0
180
+ return besseli_positive_args (abs_nu, abs_x) + 2 / π * sinpi (abs_nu) * besselk_positive_args (abs_nu, abs_x)
181
+ else
182
+ Iv = besseli_positive_args (abs_nu, abs_x)
183
+ Kv = besselk_positive_args (abs_nu, abs_x)
184
+ return cispi (abs_nu) * Iv + 2 / π * sinpi (abs_nu) * (cispi (- abs_nu) * Kv - im * π * Iv)
185
+ end
151
186
end
152
187
end
188
+ function besseli (nu:: Integer , x:: T ) where T
189
+ abs_nu = abs (nu)
190
+ abs_x = abs (x)
191
+ sg = iseven (abs_nu) ? 1 : - 1
192
+
193
+ if x >= 0
194
+ return besseli_positive_args (abs_nu, abs_x)
195
+ else
196
+ return sg * besseli_positive_args (abs_nu, abs_x)
197
+ end
198
+ end
199
+
200
+ """
201
+ besseli_positive_args(nu, x::T) where T <: Union{Float32, Float64}
202
+
203
+ Modified Bessel function of the first kind of order nu, ``I_{nu}(x)`` for positive arguments.
204
+ """
205
+ function besseli_positive_args (nu, x:: T ) where T <: Union{Float32, Float64}
206
+ iszero (nu) && return besseli0 (x)
207
+ isone (nu) && return besseli1 (x)
208
+
209
+ # use large argument expansion if x >> nu
210
+ besseli_large_argument_cutoff (nu, x) && return besseli_large_argument (nu, x)
211
+
212
+ # use uniform debye expansion if x or nu is large
213
+ besselik_debye_cutoff (nu, x) && return T (besseli_large_orders (nu, x))
214
+
215
+ # for rest of values use the power series
216
+ return besseli_power_series (nu, x)
217
+ end
153
218
154
219
"""
155
220
besselix(nu, x::T) where T <: Union{Float32, Float64}
@@ -158,19 +223,31 @@ Scaled modified Bessel function of the first kind of order nu, ``I_{nu}(x)*e^{-x
158
223
Nu must be real.
159
224
"""
160
225
function besselix (nu, x:: T ) where T <: Union{Float32, Float64}
161
- nu == 0 && return besseli0x (x)
162
- nu == 1 && return besseli1x (x)
163
-
164
- if x > maximum ((T (30 ), nu^ 2 / 4 ))
165
- return T (besseli_large_argument_scaled (nu, x))
166
- elseif x <= 2 * sqrt (nu + 1 )
167
- return T (besseli_small_arguments (nu, x)) * exp (- x)
168
- elseif nu < 100
169
- return T (_besseli_continued_fractions_scaled (nu, x))
170
- else
171
- return besseli_large_orders_scaled (nu, x)
172
- end
226
+ iszero (nu) && return besseli0x (x)
227
+ isone (nu) && return besseli1x (x)
228
+
229
+ # use large argument expansion if x >> nu
230
+ besseli_large_argument_cutoff (nu, x) && return besseli_large_argument_scaled (nu, x)
231
+
232
+ # use uniform debye expansion if x or nu is large
233
+ besselik_debye_cutoff (nu, x) && return T (besseli_large_orders_scaled (nu, x))
234
+
235
+ # for rest of values use the power series
236
+ return besseli_power_series (nu, x) * exp (- x)
173
237
end
238
+
239
+ # ####
240
+ # #### Debye's uniform asymptotic for I_{nu}(x)
241
+ # ####
242
+
243
+ # Implements the uniform asymptotic expansion https://dlmf.nist.gov/10.41
244
+ # In general this is valid when either x or nu is gets large
245
+ # see the file src/U_polynomials.jl for more details
246
+ """
247
+ besseli_large_orders(nu, x::T)
248
+
249
+ Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
250
+ """
174
251
function besseli_large_orders (v, x:: T ) where T <: Union{Float32, Float64}
175
252
S = promote_type (T, Float64)
176
253
x = S (x)
@@ -183,6 +260,7 @@ function besseli_large_orders(v, x::T) where T <: Union{Float32, Float64}
183
260
184
261
return coef* Uk_poly_In (p, v, p2, T)
185
262
end
263
+
186
264
function besseli_large_orders_scaled (v, x:: T ) where T <: Union{Float32, Float64}
187
265
S = promote_type (T, Float64)
188
266
x = S (x)
@@ -195,39 +273,18 @@ function besseli_large_orders_scaled(v, x::T) where T <: Union{Float32, Float64}
195
273
196
274
return T (coef* Uk_poly_In (p, v, p2, T))
197
275
end
198
- function _besseli_continued_fractions (nu, x:: T ) where T
199
- S = promote_type (T, Float64)
200
- xx = S (x)
201
- knum1, knu = besselk_up_recurrence (xx, besselk1 (xx), besselk0 (xx), 1 , nu- 1 )
202
- # if knu or knum1 is zero then besseli will likely overflow
203
- (iszero (knu) || iszero (knum1)) && return throw (DomainError (x, " Overflow error" ))
204
- return 1 / (x * (knum1 + knu / steed (nu, x)))
205
- end
206
- function _besseli_continued_fractions_scaled (nu, x:: T ) where T
207
- S = promote_type (T, Float64)
208
- xx = S (x)
209
- knum1, knu = besselk_up_recurrence (xx, besselk1x (xx), besselk0x (xx), 1 , nu- 1 )
210
- # if knu or knum1 is zero then besseli will likely overflow
211
- (iszero (knu) || iszero (knum1)) && return throw (DomainError (x, " Overflow error" ))
212
- return 1 / (x * (knum1 + knu / steed (nu, x)))
213
- end
214
- function steed (n, x:: T ) where T
215
- MaxIter = 1000
216
- xinv = inv (x)
217
- xinv2 = 2 * xinv
218
- d = x / (n + n)
219
- a = d
220
- h = a
221
- b = muladd (2 , n, 2 ) * xinv
222
- for _ in 1 : MaxIter
223
- d = inv (b + d)
224
- a *= muladd (b, d, - 1 )
225
- h = h + a
226
- b = b + xinv2
227
- abs (a / h) <= eps (T) && break
228
- end
229
- return h
230
- end
276
+
277
+ # ####
278
+ # #### Large argument expansion (x>>nu) for I_{nu}(x)
279
+ # ####
280
+
281
+ # Implements the uniform asymptotic expansion https://dlmf.nist.gov/10.40.E1
282
+ # In general this is valid when x > nu^2
283
+ """
284
+ besseli_large_orders(nu, x::T)
285
+
286
+ Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
287
+ """
231
288
function besseli_large_argument (v, z:: T ) where T
232
289
MaxIter = 1000
233
290
a = exp (z / 2 )
@@ -263,26 +320,71 @@ function besseli_large_argument_scaled(v, z::T) where T
263
320
end
264
321
return res * coef
265
322
end
323
+ besseli_large_argument_cutoff (nu, x) = x > maximum ((30.0 , nu^ 2 / 6 ))
266
324
267
- function besseli_small_arguments (v, z:: T ) where T
268
- S = promote_type (T, Float64)
269
- x = S (z)
270
- if v < 20
271
- coef = (x / 2 )^ v / factorial (v)
272
- else
273
- vinv = inv (v)
274
- coef = sqrt (vinv / (2 * π)) * MathConstants. e^ (v * (log (x / (2 * v)) + 1 ))
275
- coef *= evalpoly (vinv, (1 , - 1 / 12 , 1 / 288 , 139 / 51840 , - 571 / 2488320 , - 163879 / 209018880 , 5246819 / 75246796800 , 534703531 / 902961561600 ))
325
+ # ####
326
+ # #### Power series for I_{nu}(x)
327
+ # ####
328
+
329
+ # Use power series form of I_v(x) which is generally accurate across all values though slower for larger x
330
+ # https://dlmf.nist.gov/10.25.E2
331
+ """
332
+ besseli_power_series(nu, x::T) where T <: Float64
333
+
334
+ Computes ``I_{nu}(x)`` using the power series for any value of nu.
335
+ """
336
+ function besseli_power_series (v, x:: T ) where T
337
+ MaxIter = 3000
338
+ out = zero (T)
339
+ xs = (x/ 2 )^ v
340
+ a = xs / gamma (v + one (T))
341
+ t2 = (x/ 2 )^ 2
342
+ for i in 0 : MaxIter
343
+ out += a
344
+ abs (a) < eps (T) * abs (out) && break
345
+ a *= inv ((v + i + one (T)) * (i + one (T))) * t2
276
346
end
347
+ return out
348
+ end
349
+
350
+ #=
351
+ # the following is a deprecated version of the continued fraction approach
352
+ # using K0 and K1 as starting values then forward recurrence up till nu
353
+ # then using the wronskian to getting I_{nu}
354
+ # in general this method is slow and depends on starting values of K0 and K1
355
+ # which is not very flexible for arbitary orders
277
356
357
+ function _besseli_continued_fractions(nu, x::T) where T
358
+ S = promote_type(T, Float64)
359
+ xx = S(x)
360
+ knum1, knu = besselk_up_recurrence(xx, besselk1(xx), besselk0(xx), 1, nu-1)
361
+ # if knu or knum1 is zero then besseli will likely overflow
362
+ (iszero(knu) || iszero(knum1)) && return throw(DomainError(x, "Overflow error"))
363
+ return 1 / (x * (knum1 + knu / steed(nu, x)))
364
+ end
365
+ function _besseli_continued_fractions_scaled(nu, x::T) where T
366
+ S = promote_type(T, Float64)
367
+ xx = S(x)
368
+ knum1, knu = besselk_up_recurrence(xx, besselk1x(xx), besselk0x(xx), 1, nu-1)
369
+ # if knu or knum1 is zero then besseli will likely overflow
370
+ (iszero(knu) || iszero(knum1)) && return throw(DomainError(x, "Overflow error"))
371
+ return 1 / (x * (knum1 + knu / steed(nu, x)))
372
+ end
373
+ function steed(n, x::T) where T
278
374
MaxIter = 1000
279
- out = one (S)
280
- zz = x^ 2 / 4
281
- a = one (S)
282
- for k in 1 : MaxIter
283
- a *= zz / (k * (k + v))
284
- out += a
285
- a <= eps (T) && break
375
+ xinv = inv(x)
376
+ xinv2 = 2 * xinv
377
+ d = x / (n + n)
378
+ a = d
379
+ h = a
380
+ b = muladd(2, n, 2) * xinv
381
+ for _ in 1:MaxIter
382
+ d = inv(b + d)
383
+ a *= muladd(b, d, -1)
384
+ h = h + a
385
+ b = b + xinv2
386
+ abs(a / h) <= eps(T) && break
286
387
end
287
- return coef * out
388
+ return h
288
389
end
390
+ =#
0 commit comments