General \(L\)-series#
In addition to the built-in \(L\)-series for the standard objects, Magma provides machinery that allows the user to define \(L\)-series for arbitrary objects, provided that they admit a meromorphic continuation to the whole complex plane and satisfy a functional equation of the standard kind. To describe how this may be done, we need to introduce some terminology.
Terminology#
Recall that an \(L\)-series is a sum
where the coefficients \(a_n\) are complex numbers, known as the Dirichlet coefficients of the \(L\)-series. For example, the Dirichlet coefficients of the classical Riemann zeta function are \(a_n=1\) for all \(n\). We assume that, as in the case of Riemann zeta function, every \(L(s)\) has a meromorphic continuation to the complex plane and has a functional equation. Technically, our assumptions are as follows:
Assumption 1. The defining series for \(L(s)\) converges for Re\((s)\) sufficiently large. Equivalently, the coefficients \(a_n\) grow at worst as a polynomial function of \(n\).
Assumption 2. \(L(s)\) admits a meromorphic continuation to the entire complex plane.
Assumption 3. The following exist: real positive weight, complex sign of absolute value 1, real positive conductor and the \(\Gamma\)-factor
of dimension \(d\ge 1\) and rational \(\gamma\)-shifts \(\lambda_1,\ldots \lambda_d\), such that
satisfies the functional equation
Here \(\bar L\) is the dual \(L\)-series with complex conjugate coefficients \(L(s)=\sum_{n=1}^\infty\overline{a_n}/n^s\). Note that the “weight” here is one more than the usual motivic notion of weight.
Assumption 4. The series \(L^*(s)\) has finitely many simple poles and no other singularities.
Assumption 1 will almost certainly be true for any naturally arising \(L\)-function and Assumptions 2–4 are expected (but not proven) to be satisfied for most of the \(L\)-functions arising in geometry and number theory. In fact, there is a large class of so-called motivic \(L\)-functions for which all of the above assumptions are conjectured to be true. Essentially this class consists of \(L\)-functions associated to cohomology groups of varieties over number fields. This is an extremely large class of \(L\)-functions that includes all of the standard examples.
Constructing a General \(L\)-Series#
When computing the values \(L(s)\) for a complex number \(s\), Magma relies heavily on the functional equation. This means that the emphasized parameters in Assumptions 1–4 (coefficients, weight, conductor, poles, etc.) must be known before the computations can be carried out.
The generic LSeries function allows the user to construct an \(L\)-series for a new object by specifying values for these parameters. In fact, this function is used to construct all of the built-in \(L\)-series in Magma and some of these will be used as examples to illustrate its use (see the advanced examples section).
- LSeries({weight, gamma, conductor, }{cffun}): FldReElt, [FldRatElt], FldReElt, Any -> LSer#
Sign : FldComElt Default: 0 Poles : SeqEnum Default: [] Residues : SeqEnum Default: [] Parent : Any Default: CoefficientGrowth: UserProgram Default: Precision : RngIntElt Default: ImS : FldReElt Default: 0 Asymptotics : BoolElt Default: true
The function takes four arguments: real positive \(weight\), sequence of rational numbers \(gamma\), real positive \(conductor\) and a coefficient function cffun together with a number of optional parameters. It constructs an \(L\)-series with specified coefficients and the form of the functional equation.
Compulsory arguments (see Section Terminology for terminology):
\(weight\) — weight of the \(L\)-series.
Specifies the functional equation; for instance, it is
1for the Riemann zeta function,2for curves over the rationals andWeight(f)for modular forms \(f\).Note that the “weight” in this sense is the \(w\) such that \(s\rightarrow w-s\) satisfies a functional equation. The usual motivic notion of weight is thus one less than this – for instance, a weight \(k\) modular form has weight \(k\) in Magma, but weight \(k-1\) for motives, as its coefficients are typically of size \((\sqrt p)^{k-1}\).
\(gamma\) — gamma shifts/parameters.
List (of type
SeqEnum) of rational numbers that specify the gamma factor. For instance, this is[0]for the Riemann zeta function,[0,1]for an elliptic curve over \({\mathbb{Q}}\) and[0,...,0,1,...,1]with \(r_1+r_2\) zeros and \(r_2\) ones for the Dedekind zeta function of a number field \(K\) with \(r_1\) real and \(r_2\) pairs of complex embeddings.\(conductor\) — conductor of the \(L\)-series.
Part of the exponential factor \(({{\rm conductor}\over\pi^d})^{s/2}\) in the functional equation. It is usually an integer and is 1 for the Riemann zeta function, the level for a modular form, \(|\)Discriminant\((K)|\) for the Dedekind zeta of a number field \(K\) and the conductor of the Jacobian for an algebraic curve over the rationals.
cffun — specifies the coefficients \(a_n\). The following possibilities are allowed:
\(\qquad\) A finite sequence \([a_1,...,a_n]\);
\(\qquad\) A function \(f(n)\) that returns \(a_n\);
\(\qquad\) A function \(f(p,d)\) that computes the inverse of the local factor at \(p\) up to degree \(d\);
\(\qquad\) The value
0, signifying that the coefficients will be provided later with a call toLSetCoefficients.
See Section Setting the Coefficients for a detailed explanation and examples.
Optional parameters for
Lseries::Sign— the sign appearing in the functional equation.This is the sign that enters the functional equation and is usually \(\pm 1\). It must be a complex number of absolute value 1. Alternatively, this can be set to 0 (default), in which case Magma will determine it numerically from the functional equation.
Instead of providing the actual sign, the user may also use an option
Sign:=swhere \(s\) is the name of a function \(s(p)\) or \(s(L,p)\) that computes the sign to precision \(p\).Poles— the poles \(z\) of \(L^*(s)\) with Re \(z\ge\)weight/2 andResidues— the residues at these polesIf \(L^*(s)\) happens to have poles, there are only finitely many of them and each must be simple by Assumption 4 of the previous subsection. The poles have to be known and specified here when the \(L\)-function is created. The poles are symmetric about the point \(s=\)weight/2 by the functional equation and only the “right half” of the set of them has to be supplied in the form of a sequence as the value of the parameter
Poles. The default setting is that \(L^*(s)\) has no poles.The residues at the poles of \(L^*(s)\) are also required and they can be specified using the
Residuesparameter, which also takes a sequence as its value. It can be either a sequence of the same length asPolesor left to its default setting[]. In that case, Magma will attempt to determine the residues numerically using the functional equation. However, this is only possible ifSignis known and will not work to full precision if there is more than one pair of poles.As an example, the Riemann zeta function has weight 1 and the modified function \(\zeta^*(s)\) has poles at \(s=0\) and \(s=1\) with residues \(1\) and \(-1\) respectively. So the parameter assignments
Poles:=[1], Residues:=[-1]would be used in its definition.As in the case of
Sign, instead of providing the actual residues, the user may give the name of a function \(r(p)\) or \(r(L,p)\) that computes the residues up to precision \(p\) and returns them as a sequence.Parent— any Magma object.This is only used for printing purposes. When a variable type
LSeris printed, Magma prints “L-series of” and then prints the parent object.Precision— the precision to which \(L\)-values are to be computed (default is 0, use current precision).CoefficientGrowth— name \(f\) of a function \(f(x)\) or \(f(L,x)\) such that \(|a_n|<f(n)\).ImS— the largest imaginary part of \(s\) for which \(L(s)\) will be evaluated.Asymptotics— whether to use asymptotic expansions (default is yes).These four settings are related to the precision to which values are calculated in \(L\)-series computations. They are the same as for the
SetPrecisionfunction and are described in detail in Section Precision.As an illustration, the following code creates the Riemann zeta function from its invariants. This is essentially what
RiemannZeta()does:> Z := LSeries(1, [0], 1, func<n|1> > : Sign:=1, Poles:=[1], Residues:=[-1]); > CFENew(Z); 0.0000000000000000000000000000000
- LSeries(HS, N, cffun): HodgeStruc, RngIntElt, Any -> LSer#
This is an alternative way to specify a generic \(L\)-series, with all the varargs of before. Instead of giving a real number for the functional equation reflection together with \(\gamma\)-shifts, one passes a Hodge structure to the LSeries intrinsic instead.
For more examples, see the “Advanced Examples” Section Examples.
It is strongly advised that whenever a generic \(L\)-series is defined the user apply one of the functions
CFENeworCheckFunctionalEquation. If one of the specified parameters (conductor, sign, etc.) happens to be incorrect, the resulting \(L\)-series will not have a functional equation and the values returned by the evaluation functions will be nonsense. Only by checking the functional equation will the user have an indication that something is wrong.
- CheckFunctionalEquation(L): LSer -> FldComElt#
- CFENew(L): LSer -> FldReElt#
t: FldReElt Default: 1.2
Given an \(L\)-series \(L\), this intrinsic tests the functional equation numerically and should ideally return 0 (to the current precision), meaning that the test was passed.
The new preferred version is
CFENew, which in many cases of reasonable interest can often require signficantly fewer terms (using a dynamic criterion, rather than a static one), sometimes by a factor of 5 or more. It also is much superior at keeping track of internal precision. However, it does not completely replicate the old machinery (particularly when poles/residues are present), and soCheckFunctionalEquationis also still available.If the value returned is a significant distance from 0, either the \(L\)-function does not have a functional equation or some of the defining parameters (conductor, poles etc.) have been incorrectly specified or not enough coefficients have been given. In the latter case,
CFENeworCheckFunctionalEquationis likely to return a number reasonably close to 0 that measures the accuracy of computations.As already mentioned, Magma can only work with \(L\)-functions that satisfy the functional equation as in Section Terminology. Whenever an \(L\)-function is constructed, its functional equation is implicitly used in all the \(L\)-series evaluations, even when \(L\) is evaluated in the region where the original Dirichlet series is absolutely convergent.
If either the sign in the functional equation or the residues of \(L^*(s)\) have not yet been computed, this function will first compute them. If the sign was undefined, it returns \(|\)
Sign\(|-1\) which, again, must be 0.The optional technical parameter
tis a point on the real line where Magma evaluates the two Theta functions associated to the \(L\)-series and subtracts one from the other to get the value returned byCFENeworCheckFunctionalEquation.
- Example: Lseries Checkfun (ex-37a053)#
We attempt to define a truncated \(L\)-series of a quadratic character mod 3 (so \(\chi(n) =0,1,-1\) if \(n\) is 0, 1 or 2 mod 3 respectively.)
> L := LSeries(1, [0], 3, [1,-1,0,1,-1,0] : Sign:=-1); > CFENew(L); 0.222346257403989419245646558069
This does not look right. In fact, the \(\gamma\)-shifts are \(0\) for even quadratic characters, but \(1\) for odd ones,
> L := LSeries(1, [1], 3, [1,-1,0,1,-1,0] : Sign:=-1); > CheckFunctionalEquation(L); 1.06372623587922089871296822624 > CFENew(L); 7.47942058598558334301138589732E-18
This still does not look right (though
CFENewwill ignoreSign). We gave the wrong sign in the functional equation.> L := LSeries(1, [1], 3, [1,-1,0,1,-1,0] : Sign:=1); > CFENew(L); 7.47942058598558334301138589732E-18
This certainly looks better but it indicates that we did not give enough coefficients to our \(L\)-series. We determine how many coefficients are needed to do computations with default precision (30 digits),
> LCfRequired(L); // approx for old CheckFunctionalEquation 11
So 11 coefficients are needed and we provide them in the code below.
> L := LSeries(1, [1], 3, [1,-1,0,1,-1,0,1,-1,0,1] : Sign:=1); > CFENew(L); 0.000000000000000000000000000000
This a correct way to define our \(L\)-series. Even better is the following variant:
> L := LSeries(1, [1], 3, func<n|((n+1) mod 3)-1> : Sign:=1); > CFENew(L); 0.000000000000000000000000000000
This allows Magma to calculate as many \(a_n\) as it deems necessary using the provided function.
Setting the Coefficients#
- LSetCoefficients(L, cffun): LSer, Any -> {}#
This function defines the coefficients \(a_n\) of the \(L\)-series \(L\). The argument cffun can be one of the following:
A sequence \([a_1,...,a_n]\);
A function \(f(n)\) that returns \(a_n\);
A function \(f(p,d)\) that computes the inverse of the local factor at \(p\) up to degree \(d\).
When a user-defined \(L\)-series is constructed by invoking the function
> L := LSeries(weight,gamma,conductor,cffun: <optional parameters>);
this is actually equivalent to invoking the pair of functions
> L := LSeries(weight,gamma,conductor,0: <optional parameters>); > LSetCoefficients(L,cffun);
where the first line indicates that the coefficients will be supplied later and the second line actually specifies the coefficients. So the following description of the cffun argument for the function
LSetCoefficients(L,cffun)applies to the mainLSeriessignature as well.The first two ways to specify the \(a_n\) are either to give a pre-computed sequence of coefficients up to a certain bound or to give the name of a function \(f(n)\) that computes the \(a_n\). (The other two ways are described in the two subsections that follow.) For instance, the following both define the Riemann zeta function with weight 1, one \(\gamma\)-shift 0, conductor 1, sign 1, pole at \(s=1\) with residue \(-1\), all \(a_n=1\):
> V := [ 1 : k in [1..100] ]; > L := LSeries(1, [0], 1, V : Sign:=1, Poles:=[1], Residues:=[-1]);
or
> f := func<n|1>; > L := LSeries(1, [0], 1, f : Sign:=1, Poles:=[1], Residues:=[-1]);
Of the two possibilities, the second one is safer to use in a sense that it lets Magma decide how many coefficients it needs in order to perform the calculations to the required precision. However, if the computation of \(a_n\) is costly and it involves previous coefficients, then it is probably better to write a function that computes \(a_n\) recursively, storing them in a sequence and then passing it to
LSeries.
Specifying the Coefficients Later#
When specifying a finite list of coefficients it is necessary to know in advance how many coefficients have to be computed. For this, Magma provides a function LCfRequired(L) (see Section Accessing the Invariants).
- Example: Lseries Lcfrequired (ex-147e8a)#
We define \(L\) to be our own version of the Riemann zeta function (weight 1, one \(\gamma\)-shift 0, conductor 1, sign 1, pole at \(s=1\) with residue \(-1\)), but tell Magma that we will specify the coefficients later with the 4th parameter set to 0. Then we ask how many coefficients it needs to perform computations:
> L := LSeries(1, [0], 1, 0: Sign:=1, Poles:=[1], Residues:=[-1]); > N := LCfRequired(L); N; 6
Now we compute the coefficient vector \([a_1,...,a_N]\).
> vec := [1, 1, 1, 1, 1, 1]; > LSetCoefficients(L,vec);
Now we can evaluate our \(\zeta\)-function
> Evaluate(L,2); 1.64493406684822643647241516665 > Pi(RealField())^2/6; 1.64493406684822643647241516665
If we provide fewer coefficients, the computations will not have full precision but we can use
CFENeworCheckFunctionalEquationto get an indication of the resulting accuracy.> LSetCoefficients(L, [1,1]); > CFENew(L); 1.49504837351354351195383363489E-10 > Evaluate(L, 2); 1.64493406707833102002503055804 > $1 - Pi(RealField(28))^2/6; 2.301045835526153913058838556E-10
Note that such good accuracy with just two coefficients is a singular phenomenon that only happens for \(L\)-functions with very small conductors. Normally, at least hundreds of coefficients are needed to compute \(L\)-values to this precision.
Note also that the last value
Evaluate(L,2)in the example is much more precise than what one would get with the truncated version of the original defining series \(\zeta_{trunc}(s)=1+1/2^s\) at \(s=2\). The reason for this is that even in the region where the original Dirichlet series converges, the use of the functional equation usually speeds up the convergence.
Generating the Coefficients from Local Factors#
The final and, in many cases, mathematically the most natural way to supply the coefficients is by specifying the so-called local factors. Recall that the coefficients for most \(L\)-series are weakly multiplicative, meaning that \(a_{mn}=a_ma_n\) for \(m\) coprime to \(n\). For such \(L\)-series, \(L(s)\) admits a product formula
where \(F_p(x)\) is a formal power series in \(x\) with complex coefficients. For example, if \(L(s)\) arises from a variety over a number field (and this, as we already mentioned, essentially covers everything), then such a product formula holds. In this case the \(F_p(x)\) are polynomials of degree \(d\) for those primes \(p\) not dividing the conductor and are of smaller degree otherwise. Moreover, their coefficients lie in the ring of integers of the field of definition of the variety.
For \(L\)-functions with weakly multiplicative coefficients, Magma allows the coefficients to be defined by specifying the local factors, using the function LSetCoefficients(L,f) where \(f\) is a user-defined function with two arguments \(p\) and \(d\) that computes \(F_p(x)\), either as a full polynomial in \(x\) or as a power series in \(x\) of precision \(O(x^{d+1})\). For example, the Riemann zeta function has \(F_p(x)=1-x\) for all \(p\), so we can define it as follows,
> Z := LSeries(1, [0], 1, 0 : Poles:=[1], Residues:=[-1], Sign:=1);
> P<x> := PolynomialRing(Integers());
> LSetCoefficients(Z, func<p,d | 1-x> );
or as
> P<x> := PowerSeriesRing(Integers());
> Z := LSeries(1, [0], 1, func<p,d|1-x+O(x^(d+1))> :
Poles:=[1], Residues:=[-1], Sign:=1);