lcalc(1) | General Commands Manual | lcalc(1) |

# NAME¶

lcalc - 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:

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

# 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 = 1satisfy

*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