lcalc - Man Page

Compute zeros and values of L-functions


lcalc [Options]


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.

Computing Zeros

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

          )    f(gamma)

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.

Computing Values

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:

  1. the value of n
  2. a label for the character, an integer between 1 and phi(n)
  3. the conductor of the inducing character (same as the first column, if primitive).
  4. column 4: m, an integer that is coprime with n
  5. Re(chi(m))
  6. Im(chi(m))

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.


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.


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:


Report bugs to