# lcalc - Man Page

Compute zeros and values of L-functions

## Synopsis

**lcalc** [Options]

## Description

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)*

*/*

*-----*

*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

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* *Re(chi(m))**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.

## Options

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

*n*th 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.

## Examples

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

*a*

*--------'*

*' | |*

*| |*

*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.

## Bugs

Report bugs to https://gitlab.com/sagemath/lcalc/issues