lcalc - Man Page
Compute zeros and values of L-functions
lcalc is a command-line program for computing the zeros and values of L-functions. Several L-functions are built-in, and the default is the Riemann zeta function. You can also specify your own L-function in the form of a data file that describes it; see the Data File Format section below.
Given an L-function (by default, the Riemann zeta), lcalc can compute its zeros using either the --zeros or --zeros-interval flags. For example,
$ lcalc --zeros 1000
will compute the first thousand zeros of the Riemann zeta function, while checking the (generalized) Riemann hypothesis and making sure that no zeros are missed. The output consists only of the imaginary parts of the zeros, since all of the real parts should be 0.5.
The (generalized) Riemann hypothesis is confirmed by comparing the number of zeros found to the main term in the formula for N(T). The difference, S(T), should be small on average. If not, then missing zeros are detected and the program backtracks looking for sign changes in more refined steps. This is repeated until the missing zeros are found within reasonable time. Otherwise, the program exits.
The found zeros are also verified before being output, using the explicit formula to compare sums over the zeros to sums over primes. The program uses this comparison to dynamically set the output precision, and exits if both sides do not agree to at least two places after the decimal. More precisely, the Riemann-Weil explicit formula is checked repeatedly, as zeros are outputted. Let the non-trivial zeros of L(s) be 1/2 + i gamma. Let f be a function of the form f(x) = exp(-A(x-x_0)^2), where A = log(10)/Digits and Digits is the working number of digits of precision (fifteen for built in doubles), and x_0 varies as explained shortly.
Then, the sums over the zeros
are computed in two ways to working precision: first using the numerically computed zeros, and second using the explicit formula to express the sum over zeros as a sum over primes powers. These two are compared, and the program uses the amount of agreement between the two to help decide the output precision of the zeros. If the two do not agree to at least two digits after the decimal, then the program quits.
The value x_0 is chosen to coincide with a zero of L(s), say gamma_j, but translated by 0.5 so as to desymmetrize the function f (not translating would leave it an even function, and it would be quadratically, rather than linearly, sensitive to inaccuracies in the zeros).
At first, a separate test function f is used for each of the zeros, with x_0 coinciding with those zeros. After a while, however, a new f is taken only once every five zeros, with x_0 coinciding similarly.
You can also start after the Nth zero. For example,
$ lcalc --zeros 5 --N 5
outputs the sixth through tenth zeros. Caution: the --N option evaluates N(T) by using the formula involving the main term (from the Gamma factors) and S(T). S(T) is computed by looking at the change of arg of L(s) as one goes from 1/2 - iT to infinity - iT and then up to infinity + iT and to 1/2 + iT. This can take a long time if T is near the ordinate of a zero. One could improve this by implementing Turing's method for finding the Nth zero (only looking at sign changes on the critical line), or by tweaking the starting point and suppressing a corresponding number of zeros should the program take too long, but for now, only the contour integral is computed.
A variant of Turing's method which only looks on the critical line is used for the --zeros option, to test that all zeros have been found. However, with regards to the --N option, S(T) is initially computed via contour integration to zoom in on the Nth zero.
If you aren't concerned about verifying the (generalized) Riemann hypothesis, or if you don't expect it to hold (for instance, if you plan to study Dirichlet series without Euler products) then you can search for zeros in an "interval" using the --zeros-interval option. For example,
$ lcalc --zeros-interval --x=10 --y=100 --stepsize=0.1
searches for zeros of the Riemann zeta function in the interval from 1/2 + 10i to 1/2 + 100i, checking for sign changes advancing in steps of size 0.1. The first column of the output contains the imaginary part of the zero, and the second column contains a quantity related to S(T) --- it increases roughly by two whenever a sign change, that is, a pair of zeros, is missed. Higher up the critical strip you should use a smaller stepsize so as not to miss zeros. The --zeros-interval options makes sense if you want to output zeros as they are found.
The --zeros option, which does verify the (generalized) Riemann hypothesis, looks for several dozen zeros to make sure none have been missed before outputting any zeros at all; as a result, it takes longer than --zeros-interval to output the first few zeros. For collecting more than just a handful of zeros, or to make certain that no zeros have been missed, one should use the --zeros option.
The lcalc program can also compute the values of an L-function by using the --value flag. For example,
$ lcalc --value --x=0.5 --y=100
will compute the value of the (default) Riemann zeta function at the point x + yi = 0.5 + 100i. Both real and complex parts are output, separated by a space; in this case, the output is approximately 2.692619886 -0.0203860296 which represents zeta(x+yi) = 2.692619886 - 0.0203860296i.
You can also compute values along a line segment at equally spaced points:
$ lcalc --value-line-segment --x=0.5 --X=0.5 --y=0 --Y=10 --number-samples=100
computes the values of the Riemann zeta function from 0.5 + 0i through 0.5 + 100i at 1000 equally-spaced points. The output contains four columns: the real and imaginary parts of the current point (two columns), followed by the real and imaginary parts of the function value (two columns).
Elliptic Curve L-Functions
If lcalc was built with PARI support, the --elliptic-curve option can be combined with the --a1 through --a6 flags to specify an elliptic curve L-function. For example,
$ lcalc --zeros=5 --elliptic-curve --a1=0 --a2=0 --a3=0 --a4=0 --a6=1
computes the first five zeros of the L-function associated with the elliptic curve y^2 = x^3 + 1.
Twists by Dirichlet characters are currently available for all zeta and cusp-form L-functions. For example,
$ lcalc --value --x=0.5 --y=0 --twist-quadratic --start -100 --finish 100
will output L(1/2, chi_d) for d between -100 and 100, inclusive. Other twisting options are available. For example
$ lcalc --zeros=200 --twist-primitive --start 3 --finish 100
gives the first two-hundred zeros of all primitive L(s,chi) with a conductor between 3 and 100, inclusive.
Notice that with the --twist-quadratic option one is specifying the discriminant which can be negative, while with the --twist-primitive option one is specifying the conductor which should be positive.
When using the various twisting options, other than --twist-quadratic, the second output column is a label for the character modulo n. It is an integer between 1 and phi(n). If one is restricting to primitive charcters, then only a subset of these integers appear in the second column.
One can obtain a table of characters using the --output-character option. This doesn't work with the --twist-quadratic option, but does with the other twisting options.
The --output-character option takes an argument of either 1 or 2. An argument of 1, for example, will print a comprehensive table of chi(n) for all gcd(n,conductor) = 1.
The characters are constructed multiplicatively in terms of generators of the cyclic groups mod the prime powers that divide n, and there's no simple formula to go from the label to the character. As a result, --output-character will also output a table for each character before outputting the zeros of the corresponding L-function (but not with the --twist-quadratic option).
These tables contain six columns:
- the value of n
- a label for the character, an integer between 1 and phi(n)
- the conductor of the inducing character (same as the first column, if primitive).
- column 4: m, an integer that is coprime with n
If, instead, --output-character=2 is given, then only the value of chi(-1) (that is, whether chi is even or odd) and whether chi is primitive or not will be printed.
For basic program usage, run
$ lcalc --help
Most of lcalc's options are sufficiently explained by its output. Here we collect some further information about specific options.
- --rank-compute, -r
Compute the analytic rank. Analytic rank works well even for high rank since the method used does not compute derivatives, but rather looks at the behaviour of the L-function near the critical point.
- --derivative=<n>, -d <n>
Compute the nth derivative. Presently the derivative option uses numeric differentiation (taking linear combinations of L(s + mh) for integers m, to pull out the relevant Taylor coefficient of L(s)). To get good results with higher derivatives, one should build libLfunction/lcalc with more precision.
Compute the first thousand zeros of the Riemann zeta function using the --zeros option, and output their imaginary parts (after verifying that their real parts are all one-half):
$ lcalc --zeros 1000
Compute the value of the Riemann zeta function at x + iy using the --value, --x, and --y options:
$ lcalc --value --x=0.5 --y=14.134725141738
Compute the first 10 zeros of the Ramanujan tau L-function using the --tau and --zeros (-z) options:
$ lcalc --tau --zeros=10
Compute the first zero of the real quadratic Dirichlet L-function of conductor 4 after using the --twist-quadratic, --start, and --finish options to specify the L-function:
$ lcalc --twist-quadratic --start=-4 --finish=-4 --zeros=1
Data File Format
The lcalc -F option allows you to load L-function data from a file. Here we explain the format of this file. Basically it must contain the functional equation, Dirichlet coefficients, and some other helpful information.
The first line should contain an integer, either 1, 2, or 3: 1 specifies that the Dirichlet coefficients are to be given as integers (up to thirty-two bits long), 2 that they are floating point numbers, 3 that they are complex numbers. So, for example, the first line would be a 2 for cusp form or Maass form L-function (we normalize the L-functional so that the functional equation is s <-> 1-s, so the normalized Dirichlet coefficients for a cusp form L-function are not integers).
The second line specifies info that the calculator can exploit (or will exploit at some future date). It is an integer that is assigned to different types of L-functions. Currently:
- -2 for L(s,chi) but where the number of coefficients computed is less than the period
- -1 for zeta
- 0 for unknown
- 1 for periodic, including L(s,chi)
- 2 for cusp form (in S_K(Gamma_0(N))
- 3 for Maass form for SL_2(Z)
other integers reserved for future types.
The third line is an integer that specifies how many Dirichlet coefficients to read from the file; there should be at least this many Dirichlet coefficients in the file. This is a useful quantity to specify sincethe data file might have, say, a million coefficients, but one might want to read in just ten thousand of them.
The fourth line is either 0, if the Dirichlet coefficients are not periodic, or a positive integer specifying the period otherwise (this happens in the case of Dirichlet L-functions). For a Maass form it should be 0.
The fifth line is a positive integer, the quasi-degree. This is the number of gamma factors of the form Gamma(gamma s + lambda) in the functional equation, where gamma is either 0.5 or 1, and lambda is a complex number with Re(lambda) >= 0. For example, it is 1 for Dirichlet L-functions, 1 for cusp form L-functions, 2 for Maass form L-functions, etc. Note that the "1" for cusp form L-functions could be a "2" if you wish to split the gamma factor up using the Legendre duplication formula. But it's better not to.
Next come the gamma factors, with two lines for each gamma factor. The first of each pair of lines contains gamma (either 0.5 or 1), and the second line contains a pair of floating point numbers separated by a space specifying the real and imaginary parts of lambda (even if purely real, lambda should be given as a pair of numbers, the second one then being 0).
Next you specify the functional equation. Let
' | |
Lambda(s) = Q^s | | Gamma(gamma_j*s + lambda_j)*L(s)
j = 1
satisfy Lambda(s) = omega*conj(Lambda(1-conj(s))), where Q is a real number, omega is complex,and "conj" denotes the complex conjugate. Notice that the functional equation is s into 1-s; that is, the gamma factors and Dirichlet coefficients of L(s) should be normalized correctly so as to have critical line Re(s) = 1/2.
The next line in the datafile is Q giving as a floating point number, and the one after that gives omega as a pair x y of floating point numbers, specifying the real and imaginary parts of omega (even when omega is unity, it should be given as a pair of floating points, for example 1 0 to indicate 1 + 0i).
We need to allow for the possibility of poles. For example, if L(s) = zeta(s) then Lambda(s) has simple poles with residue 1 at s=1 and residue -1 at s=0. To take into account such possibilities I assume that Lambda(s) has at most simple poles. So, the next line specifies the number of poles (usually 0) and then, for each pole there are two lines. The first line in each pair gives the pole x+iy as a pair x y of floating point numbers. The second line specifies the residue at that pole, also as a pair of floating point numbers.
Finally the Dirichlet coefficients. The remaining lines in the file contain a list of Dirichlet coefficients, at least as many as indicated in line three of the datafile. The coefficients can be integers, real, or complex. If complex they should, as usual, be given as a pair x y of floating point numbers separated by a space. Otherwise they should be given as single column of integers or floating point numbers respectively.
The datafile should only contain numbers and no comments. The comments below are for the sake of understanding the structure of the datafile.
An example data file for the Maass form for SL_2(Z) associated to the eigenvalue with R = 13.779751351891 is given below (beware: the Dirichlet coefficients for this particular L-function are only accurate to a handful of decimal places, especially near the tail of the file).
2 the Dirichlet coefficients are real 3 this is a Maass form L-function 29900 use this many of the Dirichlet coefficients 0 zero since the coefficients are not periodic 2 two gamma factors .5 the first gamma factor 0 6.88987567594535 the first lambda .5 the second gamma factor 0 -6.88987567594535 the second lambda .3183098861837906715 the Q in the functional equation 1 0 the omega 1 + 0i in the functional equation 0 the number of poles of Lambda(s) 1 the first Dirichlet coefficient 1.549304477941 the second Dirichlet coefficient 0.246899772454 the third Dirichlet coefficient 1.400344365369 the fourth Dirichlet coefficient ...
Several such example data files are included with lcalc.
Report bugs to https://gitlab.com/sagemath/lcalc/issues