Skip to content

Commit cd255e3

Browse files
committed
Some fixes and simplications to the beta function(s). The handling
of large magnitude differences was only applied to logabsbeta but not logbeta. They now share code so the special handling applies to both functions. The cutoff for special handling has also been lowered to avoid loss of precision for intermediate argument size differences. Finally, helper functions for the beta function now have assertions to ensure that they are only applied for the ranges they are written for.
1 parent feccbf4 commit cd255e3

File tree

4 files changed

+132
-117
lines changed

4 files changed

+132
-117
lines changed

src/beta_inc.jl

Lines changed: 65 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ loggammadiv(a::Number, b::Number) = _loggammadiv(float(a), float(b))
1313

1414
_loggammadiv(a::T, b::T) where {T<:Base.IEEEFloat} = T(_loggammadiv(Float64(a), Float64(b))) # handle Float16, Float32
1515
function _loggammadiv(a::Float64, b::Float64)
16+
@assert b >= 8
1617
if a > b
1718
h = b/a
1819
c = 1.0/(1.0 + h)
@@ -46,11 +47,13 @@ end
4647
stirling_corr(a0,b0)
4748
4849
Compute stirling(a0) + stirling(b0) - stirling(a0 + b0)
49-
for a0,b0 >= 8
50+
for a0, b0 >= 8
5051
"""
5152
function stirling_corr(a0::Float64, b0::Float64)
52-
a = min(a0,b0)
53-
b = max(a0,b0)
53+
@assert a0 >= 0
54+
@assert b0 >= 0
55+
a = min(a0, b0)
56+
b = max(a0, b0)
5457

5558
h = a/b
5659
c = h/(1.0 + h)
@@ -90,11 +93,11 @@ function esum(mu::Float64, x::Float64)
9093
end
9194

9295
"""
93-
beta_integrand(a,b,x,y,mu=0.0)
96+
beta_integrand(a, b, x, y, mu=0.0)
9497
9598
Compute ``e^{mu} * x^{a}y^{b}/B(a,b)``
9699
"""
97-
function beta_integrand(a::Float64,b::Float64,x::Float64,y::Float64,mu::Float64=0.0)
100+
function beta_integrand(a::Float64, b::Float64, x::Float64, y::Float64, mu::Float64=0.0)
98101
a0, b0 = minmax(a,b)
99102
if a0 >= 8.0
100103
if a > b
@@ -175,7 +178,7 @@ end
175178
"""
176179
beta_inc_cont_fraction(a,b,x,y,lambda,epps)
177180
178-
Compute ``I_{x}(a,b)`` using continued fraction expansion when `a,b > 1`.
181+
Compute ``I_{x}(a,b)`` using continued fraction expansion when `a, b > 1`.
179182
It is assumed that ``\\lambda = (a+b)*y - b``
180183
181184
External links: [DLMF](https://dlmf.nist.gov/8.17.22), [Wikipedia](https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function)
@@ -186,6 +189,8 @@ See also: [`beta_inc`](@ref)
186189
`BFRAC(A,B,X,Y,LAMBDA,EPS)` from Didonato and Morris (1982)
187190
"""
188191
function beta_inc_cont_fraction(a::Float64, b::Float64, x::Float64, y::Float64, lambda::Float64, epps::Float64)
192+
@assert a > 1
193+
@assert b > 1
189194
ans = beta_integrand(a,b,x,y)
190195
if ans == 0.0
191196
return 0.0
@@ -241,7 +246,7 @@ end
241246
"""
242247
beta_inc_asymptotic_symmetric(a,b,lambda,epps)
243248
244-
Compute ``I_{x}(a,b)`` using asymptotic expansion for `a,b >= 15`.
249+
Compute ``I_{x}(a,b)`` using asymptotic expansion for `a, b >= 15`.
245250
It is assumed that ``\\lambda = (a+b)*y - b``.
246251
247252
External links: [DLMF](https://dlmf.nist.gov/8.17.22), [Wikipedia](https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function)
@@ -252,6 +257,8 @@ See also: [`beta_inc`](@ref)
252257
`BASYM(A,B,LAMBDA,EPS)` from Didonato and Morris (1982)
253258
"""
254259
function beta_inc_asymptotic_symmetric(a::Float64, b::Float64, lambda::Float64, epps::Float64)
260+
@assert a >= 15
261+
@assert b >= 15
255262
a0 =zeros(22)
256263
b0 = zeros(22)
257264
c = zeros(22)
@@ -338,16 +345,18 @@ function beta_inc_asymptotic_symmetric(a::Float64, b::Float64, lambda::Float64,
338345
end
339346

340347
"""
341-
beta_inc_asymptotic_asymmetric(a,b,x,y,w,epps)
348+
beta_inc_asymptotic_asymmetric(a, b, x, y, w, epps)
342349
343-
Evaluation of ``I_{x}(a,b)`` when `b < min(epps,epps*a)` and `x <= 0.5` using asymptotic expansion.
350+
Evaluation of ``I_{x}(a,b)`` using asymptotic expansion.
344351
It is assumed `a >= 15` and `b <= 1`, and epps is tolerance used.
345352
346353
External links: [DLMF](https://dlmf.nist.gov/8.17.22), [Wikipedia](https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function)
347354
348355
See also: [`beta_inc`](@ref)
349356
"""
350357
function beta_inc_asymptotic_asymmetric(a::Float64, b::Float64, x::Float64, y::Float64, w::Float64, epps::Float64)
358+
@assert a >= 15
359+
@assert b <= 1
351360
c = zeros(31)
352361
d = zeros(31)
353362
bm1 = b - 1.0
@@ -424,6 +433,8 @@ See also: [`beta_inc`](@ref)
424433
`FPSER(A,B,X,EPS)` from Didonato and Morris (1982)
425434
"""
426435
function beta_inc_power_series2(a::Float64, b::Float64, x::Float64, epps::Float64)
436+
@assert b < eps(Float64)*min(1.0, a)
437+
@assert x <= 0.5
427438
ans = 1.0
428439
if a > 1.0e-3 * epps
429440
ans = 0.0
@@ -466,6 +477,9 @@ See also: [`beta_inc`](@ref)
466477
`APSER(A,B,X,EPS)` from Didonato and Morris (1982)
467478
"""
468479
function beta_inc_power_series1(a::Float64, b::Float64, x::Float64, epps::Float64)
480+
@assert a <= eps(Float64)*min(1.0, b)
481+
@assert b*x <= 1
482+
@assert x <= 0.5
469483
g = Base.MathConstants.eulergamma
470484
bx = b*x
471485
t = x - bx
@@ -492,7 +506,7 @@ end
492506

493507
#B .LE. 1 OR B*X .LE. 0.7
494508
"""
495-
beta_inc_power_series(a,b,x,epps)
509+
beta_inc_power_series(a, b, x, epps)
496510
497511
Computes ``I_x(a,b)`` using power series :
498512
```math
@@ -506,6 +520,7 @@ See also: [`beta_inc`](@ref)
506520
`BPSER(A,B,X,EPS)` from Didonato and Morris (1982)
507521
"""
508522
function beta_inc_power_series(a::Float64, b::Float64, x::Float64, epps::Float64)
523+
@assert b <= 1 || b*x <= 0.7
509524
ans = 0.0
510525
if x == 0.0
511526
return 0.0
@@ -592,7 +607,7 @@ function beta_inc_power_series(a::Float64, b::Float64, x::Float64, epps::Float64
592607
end
593608

594609
"""
595-
beta_inc_diff(a,b,x,y,n,epps)
610+
beta_inc_diff(a, b, x, y, n, epps)
596611
597612
Compute ``I_{x}(a,b) - I_{x}(a+n,b)`` where `n` is positive integer and epps is tolerance.
598613
A generalised version of [DLMF](https://dlmf.nist.gov/8.17.20).
@@ -694,7 +709,7 @@ end
694709
#Wikipedia : https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function
695710

696711
"""
697-
beta_inc(a,b,x)
712+
beta_inc(a, b, x[, y])
698713
699714
Returns a tuple ``(I_{x}(a,b),1.0-I_{x}(a,b))`` where the Regularized Incomplete Beta Function is given by:
700715
```math
@@ -704,50 +719,50 @@ where ``B(a,b) = \\Gamma(a)\\Gamma(b)/\\Gamma(a+b)``.
704719
705720
External links: [DLMF](https://dlmf.nist.gov/8.17.1), [Wikipedia](https://en.wikipedia.org/wiki/Beta_function#Incomplete_beta_function)
706721
707-
See also: [`beta_inc_inv(a,b,p,q)`](@ref SpecialFunctions.beta_inc_inv)
722+
See also: [`beta_inc_inv(a, b, p, q)`](@ref SpecialFunctions.beta_inc_inv)
708723
"""
709724
function beta_inc(a::Float64, b::Float64, x::Float64, y::Float64)
710725
p = 0.0
711726
q = 0.0
712727
# lambda = a - (a+b)*x
713728
if a < 0.0 || b < 0.0
714-
return error("a or b is negative")
729+
return throw(DomainError((a, b), "a or b is negative"))
715730
elseif a == 0.0 && b == 0.0
716-
return error("a and b are 0.0")
731+
return throw(DomainError((a, b), "a and b are 0.0"))
717732
elseif x < 0.0 || x > 1.0
718-
return error("x < 0 or x > 1")
733+
return throw(DomainError(x, "x < 0 or x > 1"))
719734
elseif y < 0.0 || y > 1.0
720-
return error("y < 0 or y > 1")
735+
return throw(DomainError(y, "y < 0 or y > 1"))
721736
else
722737
z = x + y - 1.0
723738
if abs(z) > 3.0*eps()
724-
return error("x + y != 1.0") # ERROR HANDLING
739+
return throw(DomainError((x, y), "x + y != 1.0")) # ERROR HANDLING
725740
end
726741
end
727742

728743
if x == 0.0
729-
return (0.0,1.0)
744+
return (0.0, 1.0)
730745
elseif y == 0.0
731-
return (1.0,0.0)
746+
return (1.0, 0.0)
732747
elseif a == 0.0
733-
return (1.0,0.0)
748+
return (1.0, 0.0)
734749
elseif b == 0.0
735-
return (0.0,1.0)
750+
return (0.0, 1.0)
736751
end
737752
#EVALUATION OF ALGOS FOR PROPER SUB-DOMAINS ABOVE
738753
epps = max(eps(), 1.0e-15)
739-
if max(a,b) < 1.0E-3 * epps
740-
return (b/(a+b), a/(a+b))
754+
if max(a, b) < 1.0E-3 * epps
755+
return (b/(a + b), a/(a + b))
741756
end
742757
ind = false
743758
a0 = a
744759
b0 = b
745760
x0 = x
746761
y0 = y
747762

748-
if min(a0,b0) > 1.0
763+
if min(a0, b0) > 1.0
749764
#PROCEDURE FOR A0>1 AND B0>1
750-
lambda = a > b ? (a+b)*y - b : a - (a+b)*x
765+
lambda = a > b ? (a + b)*y - b : a - (a + b)*x
751766
if lambda < 0.0
752767
ind = true
753768
a0 = b
@@ -757,41 +772,41 @@ function beta_inc(a::Float64, b::Float64, x::Float64, y::Float64)
757772
lambda = abs(lambda)
758773
end
759774
if b0 < 40.0 && b0*x0 <= 0.7
760-
p = beta_inc_power_series(a0,b0,x0,epps)
775+
p = beta_inc_power_series(a0, b0, x0, epps)
761776
q = 1.0 - p
762777
elseif b0 < 40.0
763-
n = trunc(Int, b0)
764-
b0 -= float(n)
765-
if b0 == 0.0
766-
n-=1
767-
b0=1.0
768-
end
769-
p = beta_inc_diff(b0,a0,y0,x0,n,epps)
770-
if x0 <= 0.7
771-
p += beta_inc_power_series(a0,b0,x0,epps)
772-
q = 1.0 - p
773-
else
774-
if a0 <= 15.0
775-
n = 20
776-
p += beta_inc_diff(a0, b0, x0, y0, n, epps)
777-
a0 += n
778-
end
779-
p = beta_inc_asymptotic_asymmetric(a0,b0,x0,y0,p,15.0*eps())
780-
q = 1.0 - p
778+
n = trunc(Int, b0)
779+
b0 -= n
780+
if b0 == 0.0
781+
n -= 1
782+
b0 = 1.0
783+
end
784+
p = beta_inc_diff(b0, a0, y0, x0, n, epps)
785+
if x0 <= 0.7
786+
p += beta_inc_power_series(a0, b0, x0, epps)
787+
q = 1.0 - p
788+
else
789+
if a0 <= 15.0
790+
n = 20
791+
p += beta_inc_diff(a0, b0, x0, y0, n, epps)
792+
a0 += n
781793
end
794+
p = beta_inc_asymptotic_asymmetric(a0, b0, x0, y0, p, 15.0*eps())
795+
q = 1.0 - p
796+
end
782797
elseif a0 > b0
783798
if b0 <= 100.0 || lambda > 0.03*b0
784-
p = beta_inc_cont_fraction(a0,b0,x0,y0,lambda,15.0*eps())
799+
p = beta_inc_cont_fraction(a0, b0, x0, y0, lambda, 15.0*eps())
785800
q = 1.0 - p
786801
else
787-
p = beta_inc_asymptotic_symmetric(a0,b0,lambda,100.0*eps())
802+
p = beta_inc_asymptotic_symmetric(a0, b0, lambda, 100.0*eps())
788803
q = 1.0 - p
789804
end
790805
elseif a0 <= 100.0 || lambda > 0.03*a0
791-
p = beta_inc_cont_fraction(a0,b0,x0,y0,lambda,15.0*eps())
806+
p = beta_inc_cont_fraction(a0, b0, x0, y0, lambda, 15.0*eps())
792807
q = 1.0 - p
793808
else
794-
p = beta_inc_asymptotic_symmetric(a0,b0,lambda,100.0*eps())
809+
p = beta_inc_asymptotic_symmetric(a0, b0, lambda, 100.0*eps())
795810
q = 1.0 - p
796811
end
797812
return ind ? (q, p) : (p, q)
@@ -872,7 +887,7 @@ beta_inc(a::T, b::T, x::T, y::T) where {T<:AbstractFloat} = throw(MethodError(be
872887
#Volume 26, Number 1, 1977, pages 111-114.
873888

874889
"""
875-
beta_inc_inv(a,b,p,q,lb=logbeta(a,b)[1])
890+
beta_inc_inv(a, b, p, q, lb=logbeta(a,b)[1])
876891
877892
Computes inverse of incomplete beta function. Given `a`,`b` and ``I_{x}(a,b) = p`` find `x` and return tuple `(x,y)`.
878893

0 commit comments

Comments
 (0)