Skip to content

Commit 99c92ae

Browse files
authored
replace evalpoly with at-evalpoly (fix #251) (#253)
1 parent 2d5c48e commit 99c92ae

File tree

1 file changed

+32
-64
lines changed

1 file changed

+32
-64
lines changed

src/expint.jl

Lines changed: 32 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ using Base.MathConstants
33
# barebones polynomial type supporting +, -, *
44
struct SimplePoly{T}
55
coeffs::Vector{T}
6-
end
6+
end
77

88
Base.:*(p::SimplePoly, c) = SimplePoly(p.coeffs * c)
99
Base.:*(c, p::SimplePoly) = p * c
@@ -43,7 +43,7 @@ macro E₁_cf64(x, n::Integer)
4343
# consider using BigFloat?
4444
p, q = E₁_cfpoly_approx(n, Float64)
4545
xesc = esc(x)
46-
46+
4747
num_expr = :(@evalpoly $xesc)
4848
append!(num_expr.args, Float64.(p.coeffs))
4949
den_expr = :(@evalpoly $xesc)
@@ -74,54 +74,22 @@ macro E₁_taylor64(z, n::Integer)
7474
:( $taylor - log($zesc) )
7575
end
7676

77-
# minimax rational approximations to E_1(x)/exp(-x)
78-
const num_2_4 = (3.600530862438501481559423277418128014798,
79-
28.73031134165011013783185685393062481126,
80-
46.04314409968065653003548224846863877481,
81-
21.47189493062368074985000918414086604187,
82-
2.719957622921613321844755385973197500235,
83-
1.508750885580864752293599048121678438568e-6)
84-
const denom_2_4 = (1.0,
85-
18.06743589038646654075831055159865459831,
86-
61.19456872238615922515377354442679999566,
87-
64.81772518730116701207299231777089576859,
88-
24.19034591054828198408354214931112970741,
89-
2.720026796991567940556850921390829046015)
90-
91-
const num_4_10 = (3.149019890512432117647119992448352099575,
92-
14.77395058090815888166486507990452627136,
93-
14.78214309058953358717796744960600201013,
94-
4.559401130686434886620102186841739864936,
95-
0.4027858394909585103775445204576054721422,
96-
2.302781920509468929446800686773538387432e-9)
97-
const denom_4_10 = (1.0,
98-
11.65960373479520446458792926669115987821,
99-
26.20023773503894444535165299118172674849,
100-
18.93899893550582921168134979000319186841,
101-
4.962178168140565906794561070524079194193,
102-
0.4027860481050182948737116109665534358805)
103-
104-
const num_10_20 = (2.564801308922428705318296668924369454617,
105-
5.482252510134574167659359513298970499768,
106-
2.379528224853089764405551768869103662657,
107-
0.2523431276282591480166881146593592650031,
108-
1.444719769329975045925053905197199934930e-9,
109-
-8.977332061106791470409502623202677045468e-12)
110-
const denom_10_20 = (1.0,
111-
6.421214405318108272004472721910023284626,
112-
7.609584052290707052877352911548283916247,
113-
2.631866613851763364839413823973711355890,
114-
0.2523432345539146248733539983749722854603)
115-
11677
# adapted from Steven G Johnson's initial implementation: issue #19
11778
function expint_opt(x::Float64)
11879
x < 0 && throw(DomainError(x, "negative argument, convert to complex first"))
11980
x == 0 && return Inf
12081
if x > 2.15
121-
x < 4.0 && return exp(-x) * evalpoly(x, num_2_4) / evalpoly(x, denom_2_4)
122-
x < 10.0 && return exp(-x) * evalpoly(x, num_4_10) / evalpoly(x, denom_4_10)
123-
x < 20.0 && return exp(-x) * evalpoly(x, num_10_20) / evalpoly(x, denom_10_20)
124-
x < 200.0 && return @E₁_cf64(x, 8)
82+
# minimax rational approximations to E_1(x)/exp(-x):
83+
x < 4.0 && return exp(-x) *
84+
@evalpoly(x, 3.600530862438501481559423277418128014798, 28.73031134165011013783185685393062481126, 46.04314409968065653003548224846863877481, 21.47189493062368074985000918414086604187, 2.719957622921613321844755385973197500235, 1.508750885580864752293599048121678438568e-6) /
85+
@evalpoly(x, 1.0, 18.06743589038646654075831055159865459831, 61.19456872238615922515377354442679999566, 64.81772518730116701207299231777089576859, 24.19034591054828198408354214931112970741, 2.720026796991567940556850921390829046015)
86+
x < 10.0 && return exp(-x) *
87+
@evalpoly(x, 3.149019890512432117647119992448352099575, 14.77395058090815888166486507990452627136, 14.78214309058953358717796744960600201013, 4.559401130686434886620102186841739864936, 0.4027858394909585103775445204576054721422, 2.302781920509468929446800686773538387432e-9) /
88+
@evalpoly(x, 1.0, 11.65960373479520446458792926669115987821, 26.20023773503894444535165299118172674849, 18.93899893550582921168134979000319186841, 4.962178168140565906794561070524079194193, 0.4027860481050182948737116109665534358805)
89+
x < 20.0 && return exp(-x) *
90+
@evalpoly(x, 2.564801308922428705318296668924369454617, 5.482252510134574167659359513298970499768, 2.379528224853089764405551768869103662657, 0.2523431276282591480166881146593592650031, 1.444719769329975045925053905197199934930e-9, -8.977332061106791470409502623202677045468e-12) /
91+
@evalpoly(x, 1.0, 6.421214405318108272004472721910023284626, 7.609584052290707052877352911548283916247, 2.631866613851763364839413823973711355890, 0.2523432345539146248733539983749722854603)
92+
x < 200.0 && return @E₁_cf64(x, 8) # fallback continued fraction
12593
return x < 740.0 ? @E₁_cf64(x, 4) : 0.0 # underflow
12694
else
12795
# crossover point to taylor should be tuned more
@@ -178,7 +146,7 @@ function En_cf_nogamma(ν::Number, z::Number, n::Int=1000)
178146
Aprev::typeof(B) = 1
179147
ϵ = 10*eps(real(B))
180148
scale = sqrt(floatmax(real(A)))
181-
149+
182150
# two recurrence steps / loop
183151
iters = 0
184152
for i = 2:n
@@ -190,26 +158,26 @@ function En_cf_nogamma(ν::Number, z::Number, n::Int=1000)
190158
B′ = B
191159
B = z*B + (i-1) * Bprev
192160
Bprev = B′
193-
161+
194162
A′ = A
195163
A = A ++i-1) * Aprev
196164
Aprev = A′
197165
B′ = B
198166
B = B ++i-1) * Bprev
199167
Bprev = B′
200-
168+
201169
conv = abs(Aprev*B - A*Bprev) < ϵ*abs(B*Bprev)
202170
conv && i > 4 && break
203-
204-
# rescale
171+
172+
# rescale
205173
if max(abs(real(A)), abs(imag(A))) > scale
206174
A /= scale
207175
Aprev /= scale
208176
B /= scale
209177
Bprev /= scale
210178
end
211179
end
212-
180+
213181
exppart = exp(-z)
214182
if abs(real(exppart)) == Inf && abs(imag(exppart)) == Inf
215183
return exp(-z + log(A) - log(B)), iters
@@ -232,7 +200,7 @@ function En_cf_gamma(ν::Number, z::Number, n::Int=1000)
232200
Aprev::typeof(A) = 1
233201
ϵ = 10*eps(real(B))
234202
scale = sqrt(floatmax(real(A)))
235-
203+
236204
iters = 0
237205
j = 1
238206
for i = 2:n
@@ -245,7 +213,7 @@ function En_cf_gamma(ν::Number, z::Number, n::Int=1000)
245213
B′ = B
246214
B = (i - ν)*B + term * Bprev
247215
Bprev = B′
248-
216+
249217
conv = abs(Aprev*B - A*Bprev) < ϵ*abs(B*Bprev)
250218
conv && break
251219

@@ -256,7 +224,7 @@ function En_cf_gamma(ν::Number, z::Number, n::Int=1000)
256224
Bprev /= scale
257225
end
258226
end
259-
227+
260228
gammapart = En_safe_gamma_term(ν, z)
261229
cfpart = exp(-z)
262230
if abs(real(cfpart)) == Inf || abs(imag(cfpart)) == Inf
@@ -268,7 +236,7 @@ function En_cf_gamma(ν::Number, z::Number, n::Int=1000)
268236
return gammapart, -cfpart, iters
269237
end
270238

271-
# picks between continued fraction representations in
239+
# picks between continued fraction representations in
272240
# En_cf_nogamma and En_cf_gamma
273241
# returns (evaluated result, # iterations used, whether En_cf_gamma was chosen)
274242
function En_cf::Number, z::Number, niter::Int=1000)
@@ -290,17 +258,17 @@ function En_taylor(ν::Number, start::Number, z₀::Number, Δ::Number)
290258
asum = a
291259
Δ_prod_fact = -Δ
292260
ϵ = 10*eps(real(asum))
293-
261+
294262
for k = 0:100
295263
a_pre = Δ_prod_fact + a*Δ*- k - 1)/(k + 1)
296264
a = a_pre / z₀
297265
asum_prev = asum
298266
asum += a
299-
267+
300268
if abs(asum_prev - asum) < ϵ
301269
break
302270
end
303-
271+
304272
Δ_prod_fact *= -Δ / (k + 2)
305273
end
306274

@@ -329,15 +297,15 @@ function En_expand_origin(ν::Number, z::Number)
329297
end
330298
k += 1
331299
end
332-
300+
333301
return gammaterm - sumterm
334302
end
335303

336304
# series about the origin, special case for integer n
337305
# https://functions.wolfram.com/GammaBetaErf/ExpIntegralE/06/01/04/01/02/0005/
338306
function En_expand_origin(n::Integer, z::Number)
339307
gammaterm = 1 # (-z)^(n-1) / (n-1)!
340-
for i = 1:n-1
308+
for i = 1:n-1
341309
gammaterm *= -z / i
342310
end
343311

@@ -428,7 +396,7 @@ function expint(ν::Number, z::Number, niter::Int=1000)
428396
rez, imz = real(z), abs(imag(z))
429397
z = doconj ? conj(z) : z
430398
ν = doconj ? conj(ν) : ν
431-
399+
432400
quick_niter, nmax = 50, 45
433401
# start with small imaginary part if exactly on negative real axis
434402
imstart = (imz == 0) ? abs(z)*sqrt(eps(typeof(real(z)))) : imz
@@ -444,7 +412,7 @@ function expint(ν::Number, z::Number, niter::Int=1000)
444412
z₀ = rez + imstart*im
445413
E_start, i, _ = En_cf(ν, z₀, quick_niter)
446414
end
447-
415+
448416
# nsteps chosen so |Δ| ≤ 0.5
449417
nsteps = ceil(2 * (imstart - imz))
450418
Δ = (imz - imstart)*im / nsteps
@@ -454,12 +422,12 @@ function expint(ν::Number, z::Number, niter::Int=1000)
454422
E_start = En_taylor(ν, E_start, z₀, Δ)
455423
z₀ += Δ
456424
end
457-
425+
458426
# more exact imaginary part available for non-integer ν
459427
if imz == 0
460428
E_start = real(E_start) + En_imagbranchcut(ν, z)
461429
end
462-
430+
463431
return doconj ? conj(E_start) : E_start
464432
end
465433
throw("unreachable")

0 commit comments

Comments
 (0)