Skip to content

Commit 1b7a377

Browse files
authored
Use IrrationalConstants and LogExpFunctions.log1mexp (#345)
* Use IrrationalConstants * Use `log1p` * Use `LogExpFunctions.log1mexp` * Use `reim` * Bump version * Update src/beta_inc.jl * Load constants instead of only IrrationalConstants
1 parent d4f6d7e commit 1b7a377

File tree

8 files changed

+47
-40
lines changed

8 files changed

+47
-40
lines changed

Project.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
name = "SpecialFunctions"
22
uuid = "276daf66-3868-5448-9aa4-cd146d93841b"
3-
version = "1.6.2"
3+
version = "1.7.0"
44

55
[deps]
66
ChainRulesCore = "d360d2e6-b24c-11e9-a2a3-2a2ae2dbcce4"
7+
IrrationalConstants = "92d709cd-6900-40b7-9082-c6be49f344b6"
78
LogExpFunctions = "2ab3a3ac-af41-5b50-aa03-7779005ae688"
89
OpenLibm_jll = "05823500-19ac-5b8b-9628-191a04bc5112"
910
OpenSpecFun_jll = "efe28fd5-8261-553b-a9e1-b2916fc3738e"
1011

1112
[compat]
1213
ChainRulesCore = "0.9.44, 0.10, 1"
1314
ChainRulesTestUtils = "0.6.8, 0.7, 1"
14-
LogExpFunctions = "0.2, 0.3"
15+
IrrationalConstants = "0.1"
16+
LogExpFunctions = "0.3"
1517
OpenLibm_jll = "0.7, 0.8"
1618
OpenSpecFun_jll = "0.5"
1719
julia = "1.3"

src/SpecialFunctions.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
module SpecialFunctions
22

3+
using IrrationalConstants:
4+
twoπ,
5+
halfπ,
6+
sqrtπ,
7+
sqrt2π,
8+
invπ,
9+
inv2π,
10+
invsqrt2,
11+
invsqrt2π,
12+
logπ,
13+
log2π
14+
315
import ChainRulesCore
416
import LogExpFunctions
517

src/beta_inc.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function beta_integrand(a::Float64, b::Float64, x::Float64, y::Float64, mu::Floa
117117
e = lambda/b
118118
v = abs(e) > 0.6 ? e - log(y/y0) : - LogExpFunctions.log1pmx(e)
119119
z = esum(mu, -(a*u + b*v))
120-
return (1.0/sqrt(2*pi))*sqrt(b*x0)*z*exp(-stirling_corr(a,b))
120+
return sqrt(inv2π*b*x0)*z*exp(-stirling_corr(a,b))
121121
elseif x > 0.375
122122
if y > 0.375
123123
lnx = log(x)
@@ -266,7 +266,7 @@ function beta_inc_asymptotic_symmetric(a::Float64, b::Float64, lambda::Float64,
266266
b0 = zeros(22)
267267
c = zeros(22)
268268
d = zeros(22)
269-
e0 = 2/sqrt(pi)
269+
e0 = 2/sqrtπ
270270
e1 = 2^(-1.5)
271271
sm = 0.0
272272
ans = 0.0

src/betanc.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ function ncbeta_poisson(a::Float64, b::Float64, lambda::Float64, x::Float64)
104104
end
105105

106106
t0 = logabsgamma(a+b)[1] - logabsgamma(a+1.0)[1] - logabsgamma(b)[1]
107-
s0 = a*log(x) + b*log(1.0-x)
107+
s0 = a*log(x) + b*log1p(-x)
108108

109109
s = 0.0
110110
for j = 0:iter1-1

src/ellip.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ function ellipk(m::Float64)
5050
end
5151

5252
if x == 0.0
53-
return pi/2
53+
return Float64(halfπ)
5454

5555
elseif x == 1.0
5656
return Inf
@@ -165,7 +165,7 @@ function ellipk(m::Float64)
165165
0.179481482914906162 , 0.144556057087555150 , 0.123200993312427711 ,
166166
0.108938811574293531 , 0.098853409871592910 , 0.091439629201749751 ,
167167
0.085842591595413900 , 0.081541118718303215)
168-
km = -Base.log(qd) * (kdm/pi)
168+
km = -Base.log(qd) * (kdm * invπ)
169169
t = km
170170
end
171171

@@ -225,7 +225,7 @@ function ellipe(m::Float64)
225225
end
226226

227227
if x == 0.0
228-
return pi/2
228+
return Float64(halfπ)
229229
elseif x == 1.0
230230
return 1.0
231231

@@ -336,7 +336,7 @@ function ellipe(m::Float64)
336336
hdm = kdm - edm
337337
km = ellipk(Float64(x))
338338
#em = km + (pi/2 - km*edm)/kdm
339-
em = (pi/2 + km*hdm) / kdm #to avoid precision loss near 1
339+
em = (halfπ + km*hdm) / kdm #to avoid precision loss near 1
340340
t = em
341341
end
342342
if flag_is_m_neg

src/erf.jl

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -248,7 +248,7 @@ function erfinv(x::Float64)
248248
-0.10014_37634_97830_70835e2,
249249
0.1e1)
250250
else # Table 57 in Blair et al.
251-
t = 1.0 / sqrt(-log(1.0 - a))
251+
t = inv(sqrt(-log1p(-a)))
252252
return @horner(t, 0.10501_31152_37334_38116e-3,
253253
0.10532_61131_42333_38164_25e-1,
254254
0.26987_80273_62432_83544_516,
@@ -301,7 +301,7 @@ function erfinv(x::Float32)
301301
-0.21757_03119_6f1,
302302
0.1f1)
303303
else # Table 50 in Blair et al.
304-
t = 1.0f0 / sqrt(-log(1.0f0 - a))
304+
t = inv(sqrt(-log1p(-a)))
305305
return @horner(t, 0.15504_70003_116f0,
306306
0.13827_19649_631f1,
307307
0.69096_93488_87f0,
@@ -417,7 +417,6 @@ end
417417

418418
function erfinv(y::BigFloat)
419419
xfloat = erfinv(Float64(y))
420-
sqrtπ = sqrt(big(pi))
421420
if isfinite(xfloat)
422421
x = BigFloat(xfloat)
423422
else
@@ -426,7 +425,7 @@ function erfinv(y::BigFloat)
426425
x = copysign(sqrt(-log((1-abs(y))*sqrtπ)), y)
427426
isfinite(x) || return x
428427
end
429-
sqrtπhalf = sqrtπ * 0.5
428+
sqrtπhalf = sqrtπ * big(0.5)
430429
tol = 2eps(abs(x))
431430
while true # Newton iterations
432431
Δx = sqrtπhalf * (erf(x) - y) * exp(x^2)
@@ -439,7 +438,6 @@ end
439438
function erfcinv(y::BigFloat)
440439
yfloat = Float64(y)
441440
xfloat = erfcinv(yfloat)
442-
sqrtπ = sqrt(big(pi))
443441
if isfinite(xfloat)
444442
x = BigFloat(xfloat)
445443
else
@@ -453,7 +451,7 @@ function erfcinv(y::BigFloat)
453451
# TODO: Newton convergence is slow near y=0 singularity; accelerate?
454452
isfinite(x) || return x
455453
end
456-
sqrtπhalf = sqrtπ * 0.5
454+
sqrtπhalf = sqrtπ * big(0.5)
457455
tol = 2eps(abs(x))
458456
while true # Newton iterations
459457
Δx = sqrtπhalf * (erfc(x) - y) * exp(x^2)
@@ -489,7 +487,7 @@ function erfcx(x::BigFloat)
489487
w *= -k*v
490488
s += w
491489
end
492-
return (1+s)/(x*sqrt(oftype(x,pi)))
490+
return (1+s)/(x*sqrtπ)
493491
end
494492
end
495493

@@ -568,15 +566,13 @@ External links: [Wikipedia](https://en.wikipedia.org/wiki/Error_function).
568566
See also: [`erf(x,y)`](@ref erf).
569567
"""
570568
function logerf(a::Real, b::Real)
571-
if abs(a) 1/√2 && abs(b) 1/√2
569+
if abs(a) invsqrt2 && abs(b) invsqrt2
572570
return log(erf(a, b))
573571
elseif b > a > 0
574-
return logerfc(a) + log1mexp(logerfc(b) - logerfc(a))
572+
return logerfc(a) + LogExpFunctions.log1mexp(logerfc(b) - logerfc(a))
575573
elseif a < b < 0
576-
return logerfc(-b) + log1mexp(logerfc(-a) - logerfc(-b))
574+
return logerfc(-b) + LogExpFunctions.log1mexp(logerfc(-a) - logerfc(-b))
577575
else
578576
return log(erf(a, b))
579577
end
580578
end
581-
582-
log1mexp(x::Real) = x < -log(oftype(x, 2)) ? log1p(-exp(x)) : log(-expm1(x))

src/gamma.jl

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -428,12 +428,12 @@ function zeta(s::ComplexOrReal{Float64})
428428
if absim > 12 # amplitude of sinpi(s/2) ≈ exp(imag(s)*π/2)
429429
# avoid overflow/underflow (issue #128)
430430
lg = loggamma(1 - s)
431-
ln2pi = 1.83787706640934548356 # log(2pi) to double precision
432431
rehalf = real(s)*0.5
433-
return zeta(1 - s) * exp(lg + absim*(pi/2) + s*ln2pi) * (0.5/π) *
434-
Complex(sinpi(rehalf), copysign(cospi(rehalf), imag(s)))
432+
return zeta(1 - s) * exp(lg + absim*halfπ + s*log2π) * inv2π * Complex(
433+
sinpi(rehalf), copysign(cospi(rehalf), imag(s))
434+
)
435435
else
436-
return zeta(1 - s) * gamma(1 - s) * sinpi(s*0.5) * (2π)^s / π
436+
return zeta(1 - s) * gamma(1 - s) * sinpi(s*0.5) * twoπ^s * invπ
437437
end
438438
end
439439

@@ -692,8 +692,7 @@ end
692692
# SciPy loggamma function. The key identities are also described
693693
# at http://functions.wolfram.com/GammaBetaErf/LogGamma/
694694
function loggamma(z::Complex{Float64})
695-
x = real(z)
696-
y = imag(z)
695+
x, y = reim(z)
697696
yabs = abs(y)
698697
if !isfinite(x) || !isfinite(y) # Inf or NaN
699698
if isinf(x) && isfinite(y)
@@ -707,13 +706,11 @@ function loggamma(z::Complex{Float64})
707706
return loggamma_asymptotic(z)
708707
elseif x < 0.1 # use reflection formula to transform to x > 0
709708
if x == 0 && y == 0 # return Inf with the correct imaginary part for z == 0
710-
return Complex(Inf, signbit(x) ? copysign(oftype(x, pi), -y) : -y)
709+
return Complex(Inf, signbit(x) ? copysign(Float64), -y) : -y)
711710
end
712711
# the 2pi * floor(...) stuff is to choose the correct branch cut for log(sinpi(z))
713-
return Complex(1.1447298858494001741434262, # log(pi)
714-
copysign(6.2831853071795864769252842, y) # 2pi
715-
* floor(0.5*x+0.25)) -
716-
log(sinpi(z)) - loggamma(1-z)
712+
return Complex(Float64(logπ), copysign(Float64(twoπ), y) * floor(0.5*x+0.25)) -
713+
log(sinpi(z)) - loggamma(1-z)
717714
elseif abs(x - 1) + yabs < 0.1
718715
# taylor series at z=1
719716
# ... coefficients are [-eulergamma; [(-1)^k * zeta(k)/k for k in 2:15]]

src/gamma_inc.jl

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ const big1 = [25.0, 14.0, 10.0]
66
const e0 = [0.25e-3, 0.25e-1, 0.14]
77
const x0 = [31.0, 17.0, 9.7]
88
const alog10 = log(10)
9-
const rt2pin = 1.0/sqrt(2*pi)
10-
const rtpi = sqrt(pi)
9+
const rt2pin = Float64(invsqrt2π)
10+
const rtpi = Float64(sqrtπ)
1111
const stirling_coef = [1.996379051590076518221, -0.17971032528832887213e-2, 0.131292857963846713e-4, -0.2340875228178749e-6, 0.72291210671127e-8, -0.3280997607821e-9, 0.198750709010e-10, -0.15092141830e-11, 0.1375340084e-12, -0.145728923e-13, 0.17532367e-14, -0.2351465e-15, 0.346551e-16, -0.55471e-17, 0.9548e-18, -0.1748e-18, 0.332e-19, -0.58e-20]
1212
const auxgam_coef = [-1.013609258009865776949, 0.784903531024782283535e-1, 0.67588668743258315530e-2, -0.12790434869623468120e-2, 0.462939838642739585e-4, 0.43381681744740352e-5, -0.5326872422618006e-6, 0.172233457410539e-7, 0.8300542107118e-9, -0.10553994239968e-9, 0.39415842851e-11, 0.362068537e-13, -0.107440229e-13, 0.5000413e-15, -0.62452e-17, -0.5185e-18, 0.347e-19, -0.9e-21]
1313

@@ -139,11 +139,11 @@ function stirling_error(x::Float64)
139139
if x < floatmin(Float64)*1000.0
140140
return floatmax(Float64)/1000.0
141141
elseif x < 1
142-
return loggamma1p(x) - (x + 0.5)*log(x) + x - log((2*pi))/2.0
142+
return loggamma1p(x) - (x + 0.5)*log(x) + x - log2π/2.0
143143
elseif x < 2
144-
return loggamma1p(x - 1) - (x - 0.5)*log(x) + x - log((2*pi))/2.0
144+
return loggamma1p(x - 1) - (x - 0.5)*log(x) + x - log2π/2.0
145145
elseif x < 3
146-
return loggamma1p(x - 2) - (x - 0.5)*log(x) + x - log((2*pi))/2.0 + log(x - 1)
146+
return loggamma1p(x - 2) - (x - 0.5)*log(x) + x - log2π/2.0 + log(x - 1)
147147
elseif x < 12
148148
z=18.0/(x*x)-1.0
149149
return chepolsum(z, stirling_coef)/(12.0*x)
@@ -170,7 +170,7 @@ function gammax(x::Float64)
170170
if x >= 3
171171
return exp(stirling_error(x))
172172
elseif x > 0
173-
return gamma(x)/(exp(-x + (x - 0.5)*log(x))*sqrt(2*pi))
173+
return gamma(x)/(exp(-x + (x - 0.5)*log(x))*sqrt2π)
174174
else
175175
return floatmax(Float64)/1000.0
176176
end
@@ -705,7 +705,7 @@ where ``b = 1-a``, ``L = \\ln{x_0}``.
705705
"""
706706
function gamma_inc_inv_qsmall(a::Float64, q::Float64)
707707
b = 1.0 - a
708-
eta = sqrt(-2/a*log(q*gammax(a)*sqrt(2*pi)/sqrt(a)))
708+
eta = sqrt(-2/a*log(q*gammax(a)*sqrt(twoπ/a)))
709709
x0 = a*lambdaeta(eta)
710710
l = log(x0)
711711

@@ -758,7 +758,7 @@ function gamma_inc_inv_alarge(a::Float64, porq::Float64, s::Integer)
758758
eta = s*r/sqrt(a*0.5)
759759
eta += (coeff1(eta) + (coeff2(eta) + coeff3(eta)/a)/a)/a
760760
x0 = a*lambdaeta(eta)
761-
fp = -sqrt(a/(2*pi))*exp(-0.5*a*eta*eta)/gammax(a)
761+
fp = -sqrt(inv2π*a)*exp(-0.5*a*eta*eta)/gammax(a)
762762
return (x0, fp)
763763
end
764764
# Reference : 'Computation of the incomplete gamma function ratios and their inverse' by Armido R DiDonato, Alfred H Morris.

0 commit comments

Comments
 (0)