Skip to content

Commit 0af9c4d

Browse files
committed
Iv ad
1 parent 6acfecc commit 0af9c4d

File tree

1 file changed

+34
-54
lines changed

1 file changed

+34
-54
lines changed

src/BesselFunctions/besseli.jl

Lines changed: 34 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -444,9 +444,9 @@ function _besseli(nu::Integer, x::T) where T <: Union{Float32, Float64}
444444
sg = iseven(abs_nu) ? 1 : -1
445445

446446
if x >= 0
447-
return besseli_positive_args(abs_nu, abs_x)
447+
return besseli_positive_args(T(abs_nu), abs_x)
448448
else
449-
return sg * besseli_positive_args(abs_nu, abs_x)
449+
return sg * besseli_positive_args(T(abs_nu), abs_x)
450450
end
451451
end
452452

@@ -507,17 +507,16 @@ besseli_underflow_check(nu, x::T) where T = nu > 140 + T(1.45)*x + 53*Base.Math.
507507
Modified Bessel function of the first kind of order nu, ``I_{nu}(x)`` for positive arguments.
508508
"""
509509
function besseli_positive_args(nu, x::T) where T <: Union{Float32, Float64}
510-
iszero(nu) && return besseli0(x)
511-
isone(nu) && return besseli1(x)
512-
513-
# use large argument expansion if x >> nu
514-
besseli_large_argument_cutoff(nu, x) && return besseli_large_argument(nu, x)
515-
516-
# use uniform debye expansion if x or nu is large
517-
besselik_debye_cutoff(nu, x) && return besseli_large_orders(nu, x)
518-
519-
# for rest of values use the power series
520-
return besseli_power_series(nu, x)
510+
if besseli_large_argument_cutoff(nu, x)
511+
# large argument expansion if x >> nu
512+
return besseli_large_args(nu, x)
513+
elseif besselik_debye_cutoff(nu, x)
514+
# uniform debye expansion if x or nu is large
515+
return besseli_large_orders(nu, x)
516+
else
517+
# power series
518+
return besseli_power_series(nu, x)
519+
end
521520
end
522521

523522
"""
@@ -536,7 +535,7 @@ function _besselix(nu, x::T) where T <: Union{Float32, Float64}
536535
isinf(x) && return T(Inf)
537536

538537
# use large argument expansion if x >> nu
539-
besseli_large_argument_cutoff(nu, x) && return besseli_large_argument_scaled(nu, x)
538+
besseli_large_argument_cutoff(nu, x) && return besselix_large_args(nu, x)
540539

541540
# use uniform debye expansion if x or nu is large
542541
besselik_debye_cutoff(nu, x) && return besseli_large_orders_scaled(nu, x)
@@ -589,65 +588,46 @@ end
589588

590589
# Implements the uniform asymptotic expansion https://dlmf.nist.gov/10.40.E1
591590
# In general this is valid when x > nu^2
592-
"""
593-
besseli_large_orders(nu, x::T)
594591

595-
Debey's uniform asymptotic expansion for large order valid when v-> ∞ or x -> ∞
596-
"""
597-
function besseli_large_argument(v, x::T) where T
598-
a = exp(x / 2)
599-
coef = a / sqrt(2 ** x))
600-
return T(_besseli_large_argument(v, x) * coef * a)
592+
function besseli_large_args(v, x::T) where T
593+
e = exp(x / 2)
594+
return (e * besselix_large_args(v, x)) * e
601595
end
602596

603-
besseli_large_argument_scaled(v, x::T) where T = T(_besseli_large_argument(v, x) / sqrt(2 ** x)))
604-
605-
function _besseli_large_argument(v, x::T) where T
606-
MaxIter = 5000
607-
S = promote_type(T, Float64)
608-
v, x = S(v), S(x)
609-
610-
fv2 = 4 * v^2
611-
term = one(S)
612-
res = term
613-
s = -term
597+
function besselix_large_args(v, x::ComplexOrReal{T}) where T
598+
MaxIter = 1000
599+
t = one(x)
600+
invx = inv(8 * x)
601+
s = t
614602
for i in 1:MaxIter
615-
offset = muladd(2, i, -1)
616-
term *= muladd(offset, -offset, fv2) / (8 * x * i)
617-
res = muladd(term, s, res)
618-
s = -s
619-
abs(term) <= eps(T) && break
603+
t *= -invx * ((4*v^2 - (2i - 1)^2) / i)
604+
s += t
605+
abs(t) <= eps(T) && break
620606
end
621-
return res
607+
return s / sqrt(2 ** x))
622608
end
623609

624-
besseli_large_argument_cutoff(nu, x::Float64) = x > 23.0 && x > nu^2 / 1.8 + 23.0
625-
besseli_large_argument_cutoff(nu, x::Float32) = x > 18.0f0 && x > nu^2 / 19.5f0 + 18.0f0
610+
besseli_large_argument_cutoff(nu, x::Float64) = x > nu^2 / 2 + 19.0
611+
besseli_large_argument_cutoff(nu, x::Float32) = x > nu^2 / 2 + 9.0f0
626612

627613
#####
628614
##### Power series for I_{nu}(x)
629615
#####
630616

631617
# Use power series form of I_v(x) which is generally accurate across all values though slower for larger x
632618
# https://dlmf.nist.gov/10.25.E2
633-
"""
634-
besseli_power_series(nu, x::T) where T <: Float64
635619

636-
Computes ``I_{nu}(x)`` using the power series for any value of nu.
637-
"""
638620
function besseli_power_series(v, x::ComplexOrReal{T}) where T
639621
MaxIter = 3000
640-
S = eltype(x)
641-
out = zero(S)
642-
xs = (x/2)^v
643-
a = xs / gamma(v + one(T))
644-
t2 = (x/2)^2
622+
s = zero(x)
623+
t = one(x)
624+
xx = x * x * T(0.25)
645625
for i in 0:MaxIter
646-
out += a
647-
abs(a) < eps(T) * abs(out) && break
648-
a *= inv((v + i + one(T)) * (i + one(T))) * t2
626+
s += t
627+
abs(t) < eps(T) * abs(s) && break
628+
t *= xx / ((v + i + 1) * (i + 1))
649629
end
650-
return out
630+
return s * ((x/2)^v / gamma(v + 1))
651631
end
652632

653633
#=

0 commit comments

Comments
 (0)