-
Notifications
You must be signed in to change notification settings - Fork 26
Description
I am interested in implementing a version of the Riemann-Siegel formula for calculating non-trivial zeros of the Riemann zeta function in Julia. I can "successfully" get "manually finding roots by stepping through an interval and looking for sign changes" to work. I can also "successfully" get find_zeros from the Roots.jl package to work (although, this is a "bad" approach for solving this problem and appears to auto-default to the Bisection method). This method might work better for this problem if one had access to other root-finding methods such as Brent's method.
However, I cannot seem to get roots.jl (from IntervalRootFinding) to work.The issue appears to be the way I pass through rtsArray = roots(f, a, b) on line 40 of the code below. Through looking at the example here, one can write
f(x) = sin(x) - 0.1*x^2 + 1
and then
roots(f, -10..10)
which returns
4-element Array{Root{Interval{Float64}},1}:
Root([3.14959, 3.1496], :unique)
Root([-4.42654, -4.42653], :unique)
Root([-1.08205, -1.08204], :unique)
Root([-3.10682, -3.10681], :unique)
I don't understand why I cannot do the same thing with RiemennZ (as is done in findRootsSlow and findRoots in my code below). The second version of my code is pasted below.
#=**
**
** Riemann-Siegel Formula for roots of Zeta(s) on the critical line.
**
**************************************************************************
** Axion004
** 10/29/2022
**
** This program finds roots of Zeta(s) using the Riemann-Siegel
** formula. The Riemann–Siegel theta function is approximated by
** Stirling's approximation. It also uses an interpolation method to
** locate zeros. The coefficients for R(t) are handled by the Taylor
** Series approximation originally listed by Haselgrove in the 1960s.
** It is necessary to approximate these coefficients in order to
** increase computational speed.
Future Work
1. Implement a cleaner version of findRoots(). Investigate interval methods.
2. Use GPU and parallel processing capability. A lot of good ideas are discussed in
https://www.youtube.com/watch?v=8E0qdO_jRZ0.
3. Calculate all of the Riemann-Siegel coefficients explicitly.
4. Implement the Odlyzko–Schönhage algorithm.
**************************************************************************=#
using Roots
using IntervalArithmetic, IntervalRootFinding
"""
* Search for zeros of the Riemann-Siegel Z function in the interval [a,b] by
the roots function in IntervalRootFinding.jl. See
https://github.com/JuliaIntervals/IntervalRootFinding.jl and
https://juliaintervals.github.io/pages/packages/.
* @param a - the starting value of the interval.
* @param b - the ending value of the interval.
* @return an array of zeros of zeta(s) on the critical line.
"""
function findRootsIntervalArithmetic(a::Float64, b::Float64)
f(x) = RiemennZ(x, 4);
rtsArray = roots(f, a, b) # I don't know why this doesn't work.
println(size(rtsArray))
#for root in rtsArray
# println(root)
#end
end
"""
* Search for zeros of the Riemann-Siegel Z function in the interval [a,b] by
the find_zeros function in Roots.jl. See
https://juliamath.github.io/Roots.jl/dev/roots/#Searching-for-all-zeros-in-an-interval.
* @param a - the starting value of the interval.
* @param b - the ending value of the interval.
* @return an array of zeros of zeta(s) on the critical line.
"""
function findRoots(a::Float64, b::Float64)
f(x) = RiemennZ(x, 4);
rtsArray = find_zeros(f, a, b) # This isn't accurate when b > 1000.
# rtsArray = find_zeros(f, a, b, Roots.Brent()) # I don't know why this doesn't work.
println(size(rtsArray))
#for root in rtsArray
#println(root)
#end
end
"""
* Extremely inefficient way of calculating roots manually.
"""
function findRootsSlow(a::Float64, b::Float64)
f(x) = RiemennZ(x, 4);
step_size = 0.01
while a < b
a += step_size
# This is a very inefficient way of looking for roots
if sign(f(a)) != sign(f(a + step_size))
x = fzero(f, [a, a + step_size])
# Print the roots to the console
println(x)
#io = open("roots.txt","a")
# println(io,x)
#close(io)
end
end
end
"""
* Riemann-Siegel theta function using the approximation by the
Stirling series.
* @param t - the value of t inside the Z(t) function.
* @return Stirling's approximation for theta(t).
"""
function theta(t::Float64)
return (t/2.0 * log(t/(2.0*pi)) - t/2.0 - pi/8.0
+ 1.0/(48.0*t) + 7.0/(5760*t^3))
end
"""
* Computes the floor of the absolute value term passed in as t.
* @param t - the value of t inside the Z(t) function.
* @return floor of the absolute value of t.
"""
function fAbs(t::Float64)
return floor(abs(t))
end
"""
* Riemann-Siegel Z(t) function implemented per the Riemenn Siegel
formula. See http://mathworld.wolfram.com/Riemann-SiegelFormula.html
for details.
* @param t - the value of t inside the Z(t) function.
@param r - referenced for calculating the remainder terms by the
Taylor series approximations.
* @return the approximate value of Z(t) through the Riemann-Siegel
formula
"""
function RiemennZ(t::Float64, r::Int)
twopi = pi * 2.0;
val = sqrt(t/twopi)
n = fAbs(val)
sum = 0.0
for i in 1:n
sum += (cos(theta(t) - t * log(i))) / sqrt(i)
end
sum = 2.0 * sum
frac = val - n
k = 0
R = 0.0
# Calculate every remainder term through the Taylor Series coefficients.
# The coefficients are defined in the Riemann-Siegel C function.
while k <= r
R = R + C(k, 2.0*frac-1.0) * (t / twopi)^(k * (-0.5))
k += 1
end
remainder = (-1)^(n-1) * (t / twopi)^(-0.25) * R
return sum + remainder
end
"""
* C terms for the Riemann-Siegel formula. See
Haselgrove (1960): Tables of the Riemann Zeta Function,
doi:10.2307/3614148 for details.
Calculates the Taylor Series coefficients for C0, C1, C2, C3,
and C4.
* @param n - the number of coefficient terms to use.
* @param z - referenced per the Taylor series calculations.
* @return the Taylor series approximation of the remainder terms.
"""
function C(n::Int, z::Float64)
if n==0
return (.38268343236508977173 * z^0.0
+.43724046807752044936 * z^2.0
+.13237657548034352332 * z^4.0
-.01360502604767418865 * z^6.0
-.01356762197010358089 * z^8.0
-.00162372532314446528 * z^10.0
+.00029705353733379691 * z^12.0
+.00007943300879521470 * z^14.0
+.00000046556124614505 * z^16.0
-.00000143272516309551 * z^18.0
-.00000010354847112313 * z^20.0
+.00000001235792708386 * z^22.0
+.00000000178810838580 * z^24.0
-.00000000003391414390 * z^26.0
-.00000000001632663390 * z^28.0
-.00000000000037851093 * z^30.0
+.00000000000009327423 * z^32.0
+.00000000000000522184 * z^34.0
-.00000000000000033507 * z^36.0
-.00000000000000003412 * z^38.0
+.00000000000000000058 * z^40.0
+.00000000000000000015 * z^42.0)
elseif n==1
return (-.02682510262837534703 * z^1.0
+.01378477342635185305 * z^3.0
+.03849125048223508223 * z^5.0
+.00987106629906207647 * z^7.0
-.00331075976085840433 * z^9.0
-.00146478085779541508 * z^11.0
-.00001320794062487696 * z^13.0
+.00005922748701847141 * z^15.0
+.00000598024258537345 * z^17.0
-.00000096413224561698 * z^19.0
-.00000018334733722714 * z^21.0
+.00000000446708756272 * z^23.0
+.00000000270963508218 * z^25.0
+.00000000007785288654 * z^27.0
-.00000000002343762601 * z^29.0
-.00000000000158301728 * z^31.0
+.00000000000012119942 * z^33.0
+.00000000000001458378 * z^35.0
-.00000000000000028786 * z^37.0
-.00000000000000008663 * z^39.0
-.00000000000000000084 * z^41.0
+.00000000000000000036 * z^43.0
+.00000000000000000001 * z^45.0)
elseif n==2
return(+.00518854283029316849 * z^0.0
+.00030946583880634746 * z^2.0
-.01133594107822937338 * z^4.0
+.00223304574195814477 * z^6.0
+.00519663740886233021 * z^8.0
+.00034399144076208337 * z^10.0
-.00059106484274705828 * z^12.0
-.00010229972547935857 * z^14.0
+.00002088839221699276 * z^16.0
+.00000592766549309654 * z^18.0
-.00000016423838362436 * z^20.0
-.00000015161199700941 * z^22.0
-.00000000590780369821 * z^24.0
+.00000000209115148595 * z^26.0
+.00000000017815649583 * z^28.0
-.00000000001616407246 * z^30.0
-.00000000000238069625 * z^32.0
+.00000000000005398265 * z^34.0
+.00000000000001975014 * z^36.0
+.00000000000000023333 * z^38.0
-.00000000000000011188 * z^40.0
-.00000000000000000416 * z^42.0
+.00000000000000000044 * z^44.0
+.00000000000000000003 * z^46.0)
elseif n==3
return (-.00133971609071945690 * z^1.0
+.00374421513637939370 * z^3.0
-.00133031789193214681 * z^5.0
-.00226546607654717871 * z^7.0
+.00095484999985067304 * z^9.0
+.00060100384589636039 * z^11.0
-.00010128858286776622 * z^13.0
-.00006865733449299826 * z^15.0
+.00000059853667915386 * z^17.0
+.00000333165985123995 * z^19.0
+.00000021919289102435 * z^21.0
-.00000007890884245681 * z^23.0
-.00000000941468508130 * z^25.0
+.00000000095701162109 * z^27.0
+.00000000018763137453 * z^29.0
-.00000000000443783768 * z^31.0
-.00000000000224267385 * z^33.0
-.00000000000003627687 * z^35.0
+.00000000000001763981 * z^37.0
+.00000000000000079608 * z^39.0
-.00000000000000009420 * z^41.0
-.00000000000000000713 * z^43.0
+.00000000000000000033 * z^45.0
+.00000000000000000004 * z^47.0)
else
return (+.00046483389361763382 * z^0.0
-.00100566073653404708 * z^2.0
+.00024044856573725793 * z^4.0
+.00102830861497023219 * z^6.0
-.00076578610717556442 * z^8.0
-.00020365286803084818 * z^10.0
+.00023212290491068728 * z^12.0
+.00003260214424386520 * z^14.0
-.00002557906251794953 * z^16.0
-.00000410746443891574 * z^18.0
+.00000117811136403713 * z^20.0
+.00000024456561422485 * z^22.0
-.00000002391582476734 * z^24.0
-.00000000750521420704 * z^26.0
+.00000000013312279416 * z^28.0
+.00000000013440626754 * z^30.0
+.00000000000351377004 * z^32.0
-.00000000000151915445 * z^34.0
-.00000000000008915418 * z^36.0
+.00000000000001119589 * z^38.0
+.00000000000000105160 * z^40.0
-.00000000000000005179 * z^42.0
-.00000000000000000807 * z^44.0
+.00000000000000000011 * z^46.0
+.00000000000000000004 * z^48.0)
end
end
# Main
function main()
println("Zeroes on the critical line Zeta(1/2 + it).")
a = 1.0
b = 100.0
#findRootsSlow(a, b)
findRoots(a, b)
#findRootsIntervalArithmetic(a, b)
end
main()
Uncommenting findRootsIntervalArithmetic(a, b)on line 288 returns
ERROR: MethodError: no method matching roots(::var"#f#72", ::Float64, ::Float64)
Closest candidates are:
roots(::Function, ::Function, ::Any) at ~/.julia/packages/IntervalRootFinding/OdQlQ/src/roots.jl:126
roots(::Function, ::Function, ::Any, ::Type{C}) where C<:Contractor at ~/.julia/packages/IntervalRootFinding/OdQlQ/src/roots.jl:126
roots(::Function, ::Function, ::Any, ::Type{C}, ::Float64) where C<:Contractor at ~/.julia/packages/IntervalRootFinding/OdQlQ/src/roots.jl:139
...
Stacktrace:
[1] findRootsIntervalArithmetic(a::Float64, b::Float64)
@ Main ~/RiemannSiegel/RiemannSiegel.jl:40
[2] main()
@ Main ~/RiemannSiegel/RiemannSiegel.jl:288
[3] top-level scope
@ ~/RiemannSiegel/RiemannSiegel.jl:291
So, I am likely doing something wrong on line 40 when I write rtsArray = roots(f, a, b). I think that I have incorrectly called roots and this is expected to error out.