Skip to content

Commit b4a8832

Browse files
committed
update docs for power series
1 parent 277bd35 commit b4a8832

File tree

1 file changed

+44
-34
lines changed

1 file changed

+44
-34
lines changed

src/besselk.jl

Lines changed: 44 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -274,46 +274,56 @@ function besselk_ratio_knu_knup1(v, x::T) where T
274274
return xinv * (v + x + 1/2) + xinv * hn
275275
end
276276

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+
"""
280297
function besselk_power_series(v, x::T) where T
281298
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)
290305

291-
gam_v = gamma(v)
292306
# 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)
294309
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
299313

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
310322
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)
317327
end
318328
return out
319-
end
329+
end

0 commit comments

Comments
 (0)