Skip to content

Commit 6863aeb

Browse files
committed
update power series
1 parent 9f6cb5c commit 6863aeb

File tree

1 file changed

+42
-47
lines changed

1 file changed

+42
-47
lines changed

src/besselk.jl

Lines changed: 42 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,8 @@ function besselk(nu, x::T) where T <: Union{Float32, Float64, BigFloat}
211211

212212
if nu > 25.0 || x > 35.0
213213
return besselk_large_orders(nu, x)
214+
elseif x < 2.0 || nu > 1.6x - 1.0
215+
return besselk_power_series(nu, x)
214216
else
215217
return besselk_continued_fraction(nu, x)
216218
end
@@ -272,51 +274,44 @@ function besselk_ratio_knu_knup1(v, x::T) where T
272274
return xinv * (v + x + 1/2) + xinv * hn
273275
end
274276

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
319317
end
320-
throw(error("$maxit iterations reached without achieving atol $tol."))
321-
end
322-

0 commit comments

Comments
 (0)