@@ -211,6 +211,8 @@ function besselk(nu, x::T) where T <: Union{Float32, Float64, BigFloat}
211
211
212
212
if nu > 25.0 || x > 35.0
213
213
return besselk_large_orders (nu, x)
214
+ elseif x < 2.0 || nu > 1.6 x - 1.0
215
+ return besselk_power_series (nu, x)
214
216
else
215
217
return besselk_continued_fraction (nu, x)
216
218
end
@@ -272,51 +274,44 @@ function besselk_ratio_knu_knup1(v, x::T) where T
272
274
return xinv * (v + x + 1 / 2 ) + xinv * hn
273
275
end
274
276
275
- # originally contribued by @cgeoga https://github.com/cgeoga/BesselK.jl/blob/main/src/besk_ser.jl
276
- function _besselk_ser (v, x, maxit, tol, modify)
277
- T = promote_type (typeof (x), typeof (v))
278
- out = zero (T)
279
- oneT = one (T)
280
- twoT = oneT+ oneT
281
- # precompute a handful of things:
282
- xd2 = x/ twoT
283
- xd22 = xd2* xd2
284
- half = oneT/ twoT
285
- if modify
286
- e2v = exp2 (v)
287
- xd2_v = (x^ (2 * v))/ e2v
288
- xd2_nv = e2v
289
- else
290
- lxd2 = log (xd2)
291
- xd2_v = exp (v* lxd2)
292
- xd2_nv = exp (- v* lxd2)
293
- end
294
- gam_v = gamma (v)
295
- gam_nv = gamma (- v)
296
- gam_1mv = - gam_nv* v # == gamma(one(T)-v)
297
- gam_1mnv = gam_v* v # == gamma(one(T)+v)
298
- xd2_pow = oneT
299
- fact_k = oneT
300
- floatk = convert (T, 0 )
301
- (gpv, gmv) = (gam_1mnv, gam_1mv)
302
- # One final re-compression of a few things:
303
- _t1 = gam_v* xd2_nv* gam_1mv
304
- _t2 = gam_nv* xd2_v* gam_1mnv
305
- # now the loop using Oana's series expansion, with term function manually
306
- # inlined for max speed:
307
- for _j in 0 : maxit
308
- t1 = half* xd2_pow
309
- tmp = _t1/ (gmv* fact_k)
310
- tmp += _t2/ (gpv* fact_k)
311
- term = t1* tmp
312
- out += term
313
- ((abs (term) < tol) && _j> 5 ) && return out
314
- # Use the trick that gamma(1+k+1+v) == gamma(1+k+v)*(1+k+v) to skip gamma calls:
315
- (gpv, gmv) = (gpv* (oneT+ v+ floatk), gmv* (oneT- v+ floatk))
316
- xd2_pow *= xd22
317
- fact_k *= (floatk+ 1 )
318
- floatk += 1.0
277
+ # originally contributed by @cgeoga https://github.com/cgeoga/BesselK.jl/blob/main/src/besk_ser.jl
278
+ # Equation 3.2 from Geoga, Christopher J., et al. "Fitting Mat\'ern Smoothness Parameters Using Automatic Differentiation."
279
+ # arXiv preprint arXiv:2201.00090 (2022).
280
+ function besselk_power_series (v, x:: T ) where T
281
+ MaxIter = 1000
282
+ # precompute a handful of things:
283
+ xd2 = x / 2
284
+ xd22 = xd2 * xd2
285
+ half = one (T) / 2
286
+ # (x/2)^(±v). Writing the literal power doesn't seem to change anything here,
287
+ # and I think this is faster:
288
+ lxd2 = log (xd2)
289
+ xd2_v = exp (v* lxd2)
290
+ xd2_nv = exp (- v* lxd2)
291
+ # use the gamma function a couple times to start:
292
+ gam_v = gamma (v)
293
+ xp1 = abs (v) + one (T)
294
+ gam_nv = π / sinpi (xp1) / _gamma (xp1)
295
+ gam_1mv = - gam_nv* v # == gamma(one(T)-v)
296
+ gam_1mnv = gam_v* v # == gamma(one(T)+v)
297
+ (gpv, gmv) = (gam_1mnv, gam_1mv)
298
+ # One final re-compression of a few things:
299
+ _t1 = gam_v* xd2_nv* gam_1mv
300
+ _t2 = gam_nv* xd2_v* gam_1mnv
301
+ # A couple series-specific accumulators:
302
+ (xd2_pow, fact_k, floatk, out) = (one (T), one (T), zero (T), zero (T))
303
+ for _ in 0 : MaxIter
304
+ t1 = half* xd2_pow
305
+ tmp = _t1/ (gmv* fact_k)
306
+ tmp += _t2/ (gpv* fact_k)
307
+ term = t1* tmp
308
+ out += term
309
+ abs (term/ out) < eps (T) && return out
310
+ # Use the trick that gamma(1+k+1+v) == gamma(1+k+v)*(1+k+v) to skip gamma calls:
311
+ (gpv, gmv) = (gpv* (one (T)+ v+ floatk), gmv* (one (T)- v+ floatk))
312
+ xd2_pow *= xd22
313
+ fact_k *= (floatk+ 1 )
314
+ floatk += one (T)
315
+ end
316
+ return out
319
317
end
320
- throw (error (" $maxit iterations reached without achieving atol $tol ." ))
321
- end
322
-
0 commit comments