Skip to content

Commit 5268c7c

Browse files
committed
Fix premature truncation of the recurrence sum in zeta(s,z).
Skipping the recurrence sum or truncating early causes the function to break inside a region determined by shape of the cutoff.
1 parent f8fd782 commit 5268c7c

File tree

2 files changed

+57
-49
lines changed

2 files changed

+57
-49
lines changed

src/gamma.jl

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ function _zeta(s::T, z::T) where {T<:ComplexOrReal{Float64}}
262262
end
263263

264264
m = s - 1
265+
minus_s = -s
265266
ζ = zero(T)
266267

267268
# Algorithm is just the m-th derivative of digamma formula above,
@@ -270,67 +271,69 @@ function _zeta(s::T, z::T) where {T<:ComplexOrReal{Float64}}
270271
# Note: we multiply by -(-1)^m m! in polygamma below, so this factor is
271272
# pulled out of all of our derivatives.
272273

273-
cutoff = 7 + real(m) + abs(imag(m)) # TODO: this cutoff is too conservative?
274-
if x < cutoff
275-
# shift using recurrence formula
276-
xf = floor(x)
277-
nx = Int(xf)
278-
n = ceil(Int, cutoff - nx)
279-
minus_s = -s
280-
if nx < 0 # x < 0
281-
# need to use (-z)^(-s) recurrence to be correct for real z < 0
282-
# [the general form of the recurrence term is (z^2)^(-s/2)]
283-
minus_z = -z
284-
ζ += pow_oftype(ζ, minus_z, minus_s) # ν = 0 term
285-
if xf != z
286-
ζ += pow_oftype(ζ, z - nx, minus_s)
287-
# real(z - nx) > 0, so use correct branch cut
288-
# otherwise, if xf==z, then the definition skips this term
289-
end
290-
# do loop in different order, depending on the sign of s,
291-
# so that we are looping from largest to smallest summands and
292-
# can halt the loop early if possible; see issue #15946
293-
# FIXME: still slow for small m, large Im(z)
294-
if real(s) > 0
295-
for ν in -nx-1:-1:1
296-
ζₒ= ζ
297-
ζ += pow_oftype(ζ, minus_z - ν, minus_s)
298-
ζ == ζₒ && break # prevent long loop for large -x > 0
299-
end
300-
else
301-
for ν in 1:-nx-1
302-
ζₒ= ζ
303-
ζ += pow_oftype(ζ, minus_z - ν, minus_s)
304-
ζ == ζₒ && break # prevent long loop for large -x > 0
305-
end
306-
end
307-
else # x ≥ 0 && z != 0
308-
ζ += pow_oftype(ζ, z, minus_s)
309-
end
310-
# loop order depends on sign of s, as above
274+
# If x < 0 the series begins with p=-⌊x⌋ terms where real(z+k) < 0.
275+
# Since we're using the recurrence ((z+k)^2)^(s/2), we reflect
276+
# these to (-z-k)^(-s).
277+
nx = floor(Int, x)
278+
p = max(0, -nx)
279+
if p > 0
280+
# do loop in different order, depending on the sign of s,
281+
# so that we are looping from largest to smallest summands and
282+
# can halt the loop early if possible; see issue #15946
283+
# FIXME: still slow for small m, large Im(z)
284+
minus_z = -z
311285
if real(s) > 0
312-
for ν in max(1,1-nx):n-1
313-
ζₒ= ζ
314-
ζ += pow_oftype(ζ, z + ν, minus_s)
315-
ζ == ζₒ && break # prevent long loop for large m
286+
for k in p-1:-1:0
287+
ζₒ = ζ
288+
ζ += pow_oftype(ζ, minus_z - k, minus_s)
289+
ζ == ζₒ && break # prevent long loop for large -x > 0
316290
end
317291
else
318-
for ν in n-1:-1:max(1,1-nx)
319-
ζₒ= ζ
320-
ζ += pow_oftype(ζ, z + ν, minus_s)
321-
ζ == ζₒ && break # prevent long loop for large m
292+
for k in 0:1:p-1
293+
ζₒ = ζ
294+
ζ += pow_oftype(ζ, minus_z - k, minus_s)
295+
ζ == ζₒ && break # prevent long loop for large -x > 0
322296
end
323297
end
324-
z += n
298+
z += p # Shift according to recurrence
299+
end
300+
301+
# real(z) ≥ 0 is guaranteed here.
302+
# If any term must be dropped, it will be this one.
303+
if z != 0
304+
ζ += pow_oftype(ζ, z, minus_s)
325305
end
326306

307+
# Add all terms where ⌊real(z+k)⌋ < 7 + real(s) + |imag(s)|, using
308+
# at least enough terms to balance any reflected terms added
309+
# previously. The loop order depends on sign of s, as above.
310+
n = max(1+p, ceil(Int, 7 + real(m) + abs(imag(m)) - (nx+p)))
311+
if real(s) > 0
312+
for k in 1:1:n-1
313+
ζₒ = ζ
314+
ζ += pow_oftype(ζ, z + k, minus_s)
315+
ζ == ζₒ && break # prevent long loop for large m
316+
end
317+
else
318+
for k in n-1:-1:1
319+
ζₒ = ζ
320+
ζ += pow_oftype(ζ, z + k, minus_s)
321+
ζ == ζₒ && break # prevent long loop for large m
322+
end
323+
end
324+
z += n
325+
326+
# Euler-Maclaurin integral and tail sum
327327
t = inv(z)
328328
w = isa(t, Real) ? conj(oftype(ζ, t))^m : oftype(ζ, t)^m
329329
ζ += w * (inv(m) + 0.5*t)
330330

331331
t *= t # 1/z^2
332-
ζ += w*t * @pg_horner(t,m,0.08333333333333333,-0.008333333333333333,0.003968253968253968,-0.004166666666666667,0.007575757575757576,-0.021092796092796094,0.08333333333333333,-0.4432598039215686,3.0539543302701198)
333-
332+
ζ += w * t * @pg_horner(t, m, 0.08333333333333333, -0.008333333333333333,
333+
0.003968253968253968, -0.004166666666666667,
334+
0.007575757575757576, -0.021092796092796094,
335+
0.08333333333333333, -0.4432598039215686,
336+
3.0539543302701198)
334337
return ζ
335338
end
336339

test/gamma.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,11 @@ end
277277
# issue #450
278278
@test SpecialFunctions.cotderiv(0, 2.0) == Inf
279279
@test_throws DomainError SpecialFunctions.cotderiv(-1, 2.0)
280+
281+
# issue #488
282+
# TODO: Perhaps these bounds can be tightened in the future.
283+
@test 3e-5 > relerr(zeta(-5.75, 0.5), 0.00140748175562420497363476203726333231826481355014969602507003223784179195)
284+
@test 5e-5 > relerr(zeta(-8-1im, 0.5), -0.00345211118818533736386710113396098188185995501107179962865430034343404+0.0109171284162538012544319013865107198958348806377836496833453787991565im)
280285
end
281286

282287
@testset "logabsbinomial" begin

0 commit comments

Comments
 (0)