@@ -274,46 +274,56 @@ function besselk_ratio_knu_knup1(v, x::T) where T
274
274
return xinv * (v + x + 1 / 2 ) + xinv * hn
275
275
end
276
276
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).
277
+
278
+ # ####
279
+ # #### Power series for K_{nu}(x)
280
+ # ####
281
+
282
+ # Use power series form of K_v(x) which is accurate for small x (x<2) or when nu > X
283
+ # We use the form as described by Equation 3.2 from reference [7].
284
+ # This method was originally contributed by @cgeoga https://github.com/cgeoga/BesselK.jl/blob/main/src/besk_ser.jl
285
+ # A modified form appears below. See more discussion at https://github.com/heltonmc/Bessels.jl/pull/29
286
+ # This is only accurate for noninteger orders (nu) and no checks are performed.
287
+ #
288
+ # [7] Geoga, Christopher J., et al. "Fitting Mat\'ern Smoothness Parameters Using Automatic Differentiation."
289
+ # arXiv preprint arXiv:2201.00090 (2022).
290
+ """
291
+ besselk_power_series(nu, x::T) where T <: Float64
292
+
293
+ Computes ``K_{nu}(x)`` using the power series when nu is not an integer.
294
+ In general, this is most accurate for small arguments and when nu > x.
295
+ No checks are performed on nu so this is not accurate when nu is an integer.
296
+ """
280
297
function besselk_power_series (v, x:: T ) where T
281
298
MaxIter = 1000
282
- xd2 = x / 2
283
- xd22 = xd2 * xd2
284
- half = one (T) / 2
285
- # (x/2)^(±v). Writing the literal power doesn't seem to change anything here,
286
- # and I think this is faster:
287
- lxd2 = log (xd2)
288
- xd2_v = exp (v* lxd2)
289
- xd2_nv = exp (- v* lxd2)
299
+ z = x / 2
300
+ zz = z * z
301
+
302
+ logz = log (z)
303
+ xd2_v = exp (v* logz)
304
+ xd2_nv = inv (xd2_v)
290
305
291
- gam_v = gamma (v)
292
306
# use the reflection identify to calculate gamma(-v)
293
- # use relation gamma(v)*v = gamma(v+1)
307
+ # use relation gamma(v)*v = gamma(v+1) to avoid two gamma calls
308
+ gam_v = gamma (v)
294
309
xp1 = abs (v) + one (T)
295
- gam_nv = π / (sinpi (xp1) * gam_v* v)
296
-
297
- gam_1mv = - gam_nv* v
298
- gam_1mnv = gam_v* v
310
+ gam_nv = π / (sinpi (xp1) * gam_v * v)
311
+ gam_1mv = - gam_nv * v
312
+ gam_1mnv = gam_v * v
299
313
300
- # One final re-compression of a few things:
301
- _t1 = gam_v* xd2_nv* gam_1mv
302
- _t2 = gam_nv* xd2_v* gam_1mnv
303
- # A couple series-specific accumulators:
304
- (xd2_pow, fact_k, floatk, out) = (one (T), one (T), zero (T), zero (T))
305
- for _ in 0 : MaxIter
306
- t1 = half* xd2_pow
307
- tmp = _t1/ (gam_1mv* fact_k)
308
- tmp += _t2/ (gam_1mnv* fact_k)
309
- term = t1* tmp
314
+ _t1 = gam_v * xd2_nv * gam_1mv
315
+ _t2 = gam_nv * xd2_v * gam_1mnv
316
+ (xd2_pow, fact_k, out) = (one (T), one (T), zero (T))
317
+ 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
310
322
out += term
311
- abs (term/ out) < eps (T) && return out
312
- # Use the trick that gamma(1+k+1+v) == gamma(1+k+v)*(1+k+v) to skip gamma calls:
313
- (gam_1mnv, gam_1mv) = (gam_1mnv* (one (T)+ v+ floatk), gam_1mv* (one (T)- v+ floatk))
314
- xd2_pow *= xd22
315
- fact_k *= (floatk+ 1 )
316
- floatk += one (T)
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)
317
327
end
318
328
return out
319
- end
329
+ end
0 commit comments