@@ -145,43 +145,63 @@ function besselk1x(x::T) where T <: Union{Float32, Float64}
145
145
return muladd (evalpoly (inv (x), P3_k1 (T)), inv (evalpoly (inv (x), Q3_k1 (T))), Y2_k1 (T)) / sqrt (x)
146
146
end
147
147
end
148
- #=
149
- If besselk0(x) or besselk1(0) is equal to zero
150
- this will underflow and always return zero even if besselk(x, nu)
151
- is larger than the smallest representing floating point value.
152
- In other words, for large values of x and small to moderate values of nu,
153
- this routine will underflow to zero.
154
- For small to medium values of x and large values of nu this will overflow and return Inf.
155
- =#
156
- #=
148
+
157
149
"""
158
150
besselk(x::T) where T <: Union{Float32, Float64}
159
151
160
152
Modified Bessel function of the second kind of order nu, ``K_{nu}(x)``.
161
153
"""
162
- function besselk(nu, x::T) where T <: Union{Float32, Float64, BigFloat}
163
- T == Float32 ? branch = 20 : branch = 50
164
- if nu < branch
165
- return besselk_up_recurrence(x, besselk1(x), besselk0(x), 1, nu)[1]
166
- else
167
- return besselk_large_orders(nu, x)
168
- end
154
+ function besselk (nu, x:: T ) where T <: Union{Float32, Float64}
155
+ # check to make sure nu isn't zero
156
+ iszero (nu) && return besselk0 (x)
157
+
158
+ # use uniform debye expansion if x or nu is large
159
+ besselik_debye_cutoff (nu, x) && return besselk_large_orders (nu, x)
160
+
161
+ # for integer nu use forward recurrence starting with K_0 and K_1
162
+ isinteger (nu) && return besselk_up_recurrence (x, besselk1 (x), besselk0 (x), 1 , nu)[1 ]
163
+
164
+ # for small x and nu > x use power series
165
+ besselk_power_series_cutoff (nu, x) && return besselk_power_series (nu, x)
166
+
167
+ # for rest of values use the continued fraction approach
168
+ return besselk_continued_fraction (nu, x)
169
169
end
170
- =#
171
170
"""
172
- besselk (x::T) where T <: Union{Float32, Float64}
171
+ besselkx (x::T) where T <: Union{Float32, Float64}
173
172
174
173
Scaled modified Bessel function of the second kind of order nu, ``K_{nu}(x)*e^{x}``.
175
174
"""
176
- function besselkx (nu:: Int , x:: T ) where T <: Union{Float32, Float64}
177
- T == Float32 ? branch = 20 : branch = 50
178
- if nu < branch
179
- return besselk_up_recurrence (x, besselk1x (x), besselk0x (x), 1 , nu)[1 ]
180
- else
181
- return besselk_large_orders_scaled (nu, x)
182
- end
175
+ function besselkx (nu, x:: T ) where T <: Union{Float32, Float64}
176
+ # check to make sure nu isn't zero
177
+ iszero (nu) && return besselk0x (x)
178
+
179
+ # use uniform debye expansion if x or nu is large
180
+ besselik_debye_cutoff (nu, x) && return besselk_large_orders_scaled (nu, x)
181
+
182
+ # for integer nu use forward recurrence starting with K_0 and K_1
183
+ isinteger (nu) && return besselk_up_recurrence (x, besselk1x (x), besselk0x (x), 1 , nu)[1 ]
184
+
185
+ # for small x and nu > x use power series
186
+ besselk_power_series_cutoff (nu, x) && return besselk_power_series (nu, x) * exp (x)
187
+
188
+ # for rest of values use the continued fraction approach
189
+ return besselk_continued_fraction (nu, x) * exp (x)
183
190
end
184
- function besselk_large_orders (v, x:: T ) where T <: Union{Float32, Float64, BigFloat}
191
+
192
+ # ####
193
+ # #### Debye's uniform asymptotic for K_{nu}(x)
194
+ # ####
195
+
196
+ # Implements the uniform asymptotic expansion https://dlmf.nist.gov/10.41
197
+ # In general this is valid when either x or nu is gets large
198
+ # see the file src/U_polynomials.jl for more details
199
+ """
200
+ besselk_large_orders(nu, x::T)
201
+
202
+ Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
203
+ """
204
+ function besselk_large_orders (v, x:: T ) where T
185
205
S = promote_type (T, Float64)
186
206
x = S (x)
187
207
z = x / v
@@ -193,7 +213,7 @@ function besselk_large_orders(v, x::T) where T <: Union{Float32, Float64, BigFlo
193
213
194
214
return T (coef* Uk_poly_Kn (p, v, p2, T))
195
215
end
196
- function besselk_large_orders_scaled (v, x:: T ) where T <: Union{Float32, Float64, BigFloat}
216
+ function besselk_large_orders_scaled (v, x:: T ) where T
197
217
S = promote_type (T, Float64)
198
218
x = S (x)
199
219
z = x / v
@@ -205,42 +225,44 @@ function besselk_large_orders_scaled(v, x::T) where T <: Union{Float32, Float64,
205
225
206
226
return T (coef* Uk_poly_Kn (p, v, p2, T))
207
227
end
228
+ besselik_debye_cutoff (nu, x:: Float64 ) = nu > 25.0 || x > 35.0
229
+ besselik_debye_cutoff (nu, x:: Float32 ) = nu > 15.0 || x > 20.0
208
230
209
- function besselk (nu, x:: T ) where T <: Union{Float32, Float64, BigFloat}
210
- (isinteger (nu) && nu < 250 ) && return besselk_up_recurrence (x, besselk1 (x), besselk0 (x), 1 , nu)[1 ]
211
-
212
- if nu > 25.0 || x > 35.0
213
- return besselk_large_orders (nu, x)
214
- elseif x < 2.0
215
- return besselk_power_series (nu, x)
216
- else
217
- return besselk_continued_fraction (nu, x)
218
- end
219
- end
231
+ # ####
232
+ # #### Continued fraction with Wronskian for K_{nu}(x)
233
+ # ####
220
234
221
- # could also use the continued fraction for inu/inmu1
222
- # but it seems like adapting the besseli_power series
223
- # to give both nu and nu-1 is faster
235
+ # Use the ratio K_{nu+1}/K_{nu} and I_{nu-1}, I_{nu}
236
+ # along with the Wronskian (NIST https://dlmf.nist.gov/10.28.E2) to calculate K_{nu}
237
+ # Inu and Inum1 are generated from the power series form where K_{nu_1}/K_{nu}
238
+ # is calculated with continued fractions.
239
+ # The continued fraction K_{nu_1}/K_{nu} method is a slightly modified form
240
+ # https://github.com/heltonmc/Bessels.jl/issues/17#issuecomment-1195726642 by @cgeoga
241
+ #
242
+ # It is also possible to use continued fraction to calculate inu/inmu1 such as
243
+ # inum1 = besseli_power_series(nu-1, x)
244
+ # H_inu = steed(nu, x)
245
+ # inu = besseli_power_series(nu, x)#inum1 * H_inu
246
+ # but it appears to be faster to modify the series to calculate both Inu and Inum1
224
247
225
- # inum1 = besseli_power_series(nu-1, x)
226
- # H_inu = steed(nu, x)
227
- # inu = besseli_power_series(nu, x)#inum1 * H_inu
228
248
function besselk_continued_fraction (nu, x)
229
249
inu, inum1 = besseli_power_series_inu_inum1 (nu, x)
230
250
H_knu = besselk_ratio_knu_knup1 (nu- 1 , x)
231
251
return 1 / (x * (inum1 + inu / H_knu))
232
252
end
233
253
254
+ # a modified version of the I_{nu} power series to compute both I_{nu} and I_{nu-1}
255
+ # use this along with the continued fractions for besselk
234
256
function besseli_power_series_inu_inum1 (v, x:: T ) where T
235
257
MaxIter = 3000
236
258
out = zero (T)
237
259
out2 = zero (T)
238
260
x2 = x / 2
239
- xs = (x2) ^ v
261
+ xs = x2 ^ v
240
262
gmx = xs / gamma (v)
241
263
a = gmx / v
242
264
b = gmx / x2
243
- t2 = x2* x2
265
+ t2 = x2 * x2
244
266
for i in 0 : MaxIter
245
267
out += a
246
268
out2 += b
@@ -251,20 +273,19 @@ function besseli_power_series_inu_inum1(v, x::T) where T
251
273
return out, out2
252
274
end
253
275
254
-
255
- # slightly modified version of https://github.com/heltonmc/Bessels.jl/issues/17#issuecomment-1195726642 from @cgeoga
276
+ # computes K_{nu+1}/K_{nu} using continued fractions and the modified Lentz method
277
+ # generally slow to converge for small x
256
278
function besselk_ratio_knu_knup1 (v, x:: T ) where T
257
279
MaxIter = 1000
258
- # do the modified Lentz method:
259
280
(hn, Dn, Cn) = (1e-50 , zero (v), 1e-50 )
260
281
261
- jf = one (T)
262
- vv = v* v
282
+ jf = one (T)
283
+ vv = v * v
263
284
for _ in 1 : MaxIter
264
- an = (vv - ((2 * jf - 1 )^ 2 ) * T (0.25 ))
265
- bn = 2 * (x + jf)
266
- Cn = an / Cn + bn
267
- Dn = inv (muladd (an, Dn, bn))
285
+ an = (vv - ((2 * jf - 1 )^ 2 ) * T (0.25 ))
286
+ bn = 2 * (x + jf)
287
+ Cn = an / Cn + bn
288
+ Dn = inv (muladd (an, Dn, bn))
268
289
del = Dn * Cn
269
290
hn *= del
270
291
abs (del - 1 ) < eps (T) && break
@@ -274,7 +295,6 @@ function besselk_ratio_knu_knup1(v, x::T) where T
274
295
return xinv * (v + x + 1 / 2 ) + xinv * hn
275
296
end
276
297
277
-
278
298
# ####
279
299
# #### Power series for K_{nu}(x)
280
300
# ####
@@ -315,15 +335,16 @@ function besselk_power_series(v, x::T) where T
315
335
_t2 = gam_nv * xd2_v * gam_1mnv
316
336
(xd2_pow, fact_k, out) = (one (T), one (T), zero (T))
317
337
for k in 0 : MaxIter
318
- t1 = xd2_pow * T (0.5 )
319
- tmp = muladd (_t1, gam_1mnv, _t2 * gam_1mv)
320
- tmp *= inv (gam_1mv * gam_1mnv * fact_k)
321
- term = t1 * tmp
322
- out += term
323
- abs (term / out) < eps (T) && return out
324
- (gam_1mnv, gam_1mv) = (gam_1mnv* (one (T) + v + k), gam_1mv* (one (T) - v + k))
325
- xd2_pow *= zz
326
- fact_k *= k + one (T)
338
+ t1 = xd2_pow * T (0.5 )
339
+ tmp = muladd (_t1, gam_1mnv, _t2 * gam_1mv)
340
+ tmp *= inv (gam_1mv * gam_1mnv * fact_k)
341
+ term = t1 * tmp
342
+ out += term
343
+ abs (term / out) < eps (T) && return out
344
+ (gam_1mnv, gam_1mv) = (gam_1mnv* (one (T) + v + k), gam_1mv* (one (T) - v + k))
345
+ xd2_pow *= zz
346
+ fact_k *= k + one (T)
327
347
end
328
348
return out
329
349
end
350
+ besselk_power_series_cutoff (nu, x) = x < 2.0 || nu > 1.6 x - 1.0
0 commit comments