This page contains information about the source codes for computing the central $L$-values and their algebraic parts and integer values of $L$-functions of elliptic curves twisted by a family of primitive Dirichlet characters. More precisely, it contains the sources codes of two main command line programs:

Information about the numerical data produced by the programs is also provided.

1) Introduction

Let \(\mathcal B_k\) be the family of primitive Dirichlet characters of order \(k\) and define

\[ \mathcal B_{k,N}(X) = \lbrace \chi \in \mathcal B_k \mid \mathfrak f_{\chi} \leq X \text{ and } \text{gcd} ( N, \mathfrak{f}_\chi )=1 \rbrace \]

where \(\text{gcd}\) is the greatest common divisor function and \(\mathfrak{f}_\chi\) is the conductor of \(\chi\). Moreover, denote \(\zeta_k := e^{2 \pi i/k}\) and \(Z_k := \big[0, \zeta_k, \zeta_k^2, \ldots, \zeta_k^{k-1}, 1\big]\) for a fixed \(k\).

Let \(E\) be an elliptic curve defined over \(\mathbb{Q}\) of conductor \(N\). Then, the \(L\)-function of an elliptic curve \(E\) twisted by \(\chi\) is defined by the following Dirichlet series for \(\text{Re}(s) > 3/2\):

\[ L(E, s, \chi) := \sum_{n \ge 1}\frac{\chi(n)a_n}{n^s} = \prod_{p \nmid N}\Big(1 - \frac{\chi(p)a_p}{p^s} + \frac{\chi^2(p)}{p^{2s-1}}\Big)^{-1}\prod_{p \mid N}\Big(1 - \frac{\chi(p)a_p}{p^s}\Big)^{-1} \]

where \(a_p\) is the traces of Frobenius of \(E\) if \(p \nmid N\) and \(0, \pm 1\) depending on the reduction type of \(E\) modulo \(p\) otherwise. It is well-known that it can be analytically continued to \(\mathbb{C}\) and satisfies some functional equation which relates \(s\) to \(2-s\), so that the critical strip is \(\{s \in \mathbb{C} \mid 1/2 < \text{Re}(s) < 3/2\}\).

We can compute the values of \(L(E, s, \chi)\) at \(s = 1\) for \(\chi \in \mathcal B_{k,N}(X)\) by the following well-known formula:

\[ L(E, 1, \chi) = \sum_{n \ge 1}(\chi(n) + w_E C_\chi\overline{\chi}(n))\frac{a_n}{n}\text{exp} (-2\pi n/(\mathfrak f_\chi \sqrt{N}) ) \qquad\qquad(1) \]

where \(a_n\) and \(w_E\) are the coefficients and the root number of \(L(E, s)\), respectively, and \(C_{\chi} = \chi(N) \tau^2(\chi)/\mathfrak{f}_\chi\) where \(\tau(\chi)\) is the Gauss sum of \(\chi\). Here \(\overline{\chi}\) is the complex conjugate of \(\chi\) and \(\text{exp}\) is the exponential function.

The algebraic part of \(L(E,1,\chi)\) is defined as

\[ L_E^{\text{alg}}(\chi) = \frac{2\tau(\overline{\chi})}{\Omega_\chi}L(E,1,\chi) \]

where \(\tau(\chi)\) is the Gauss sum of \(\chi\) and \(\Omega_\chi = \Omega^{\pm}\) is a period df \(E\) depending on the signs of \(\chi\). It is known that the algebraic part is an algebraic integer in the cyclotomic field \(\mathbb{Q}(\chi)\) adjoining with the values of \(\chi\).

Denote the maximal real subfield of \(\mathbb{Q}(\chi)\) and its ring of integers by \(\mathbb{Q}^+(\chi)\) and \(\mathcal O_\chi^+\), respectively. Then, from Proposition 2.1 in [1], for each \(L_E^\text{alg}(\chi)\), we can find a real cyclotomic integer \(\alpha_\chi \in \mathcal O_\chi^+\) satisfying \(\sigma(\alpha_\chi) = \alpha_\chi^\sigma\) for all \(\sigma \in \text{G}\), the Galois group of \(\mathbb{Q}(\chi)/\mathbb{Q}\). Lastly, denote

\[ A_\chi = \text{Nm}_{\mathbb{Q}^+(\chi)/\mathbb{Q}}(\alpha_\chi) \in \mathbb{Z} \]

where \(\text{Nm}_{\mathbb{Q}^+(\chi)/\mathbb{Q}}\) is the field norm from \(\mathbb Q^+(\chi)\) to \(\mathbb Q\).

Notes: - The twists package uses the label of \(E\) as Cremona’s elliptic curve label. - In computing \(L_E^{\text{alg}}(\chi)\), the period lattice \(\Omega^\pm\) is computed such that \(\Omega^+ \in \mathbb R\) and \(\Omega^- \in \mathbb R i\).

[1] Hershy Kisilevsky and Jungbae Nam. Small Algebraic Central Values of Twists of Elliptic L-Functions, 2022 (Preprint)

2) Data Conversion and Sample Data Archived

Considering the cloud storage limit of Zenodo, the raw output data obtained by twists_clve are converted into Python-compatible data format using Numpy and stored in Zenodo. Thus, one is recommended to use twists_ailve on SageMath to read these sample data in Zenodo. The sample data can be downloaded from the author’s Zenodo dataset archive.

The hardware systems for obtaining these sample data are

For the sample data in Zenodo, we compute \(L(E, 1, \chi)\) with a massive amount of precomputed \(a_n\)’s (as will be mentioned below in more detail) so that the errors of its real and imaginary part are at most \(10^{-10}\). It implies that the values of \(\alpha_\chi \in \mathbb R\) have at least the correct first 4 digits.

Data file naming conventions for the sample data are - Raw data file generated by twists_clve: E_k_X_raw.dat where - E - The Cremona label of \(E\) - k - The order of \(\chi \in \mathcal{B}_{k,N}(X)\) - X - 3m and 1m for \(k = 3, 5, 7, 13\) and \(k = 6\), respectively - Python data file equivalently converted from the raw one: E_k_X_central_l_values.npz
where E, k, and X are same as above. - Python data file for algebraic and integer \(L\)-values:
where E, k, and X are same as above.

Note: If a data file contains X = 3m (or 1m) in its name, it means the data contains the \(L\)-values for \(\chi\) of conductor less than or equal to \(3\cdot 10^6\) (or \(10^6\), respectively).

In Zenodo DOI:

One can find two zip files: (17 GB) and (13 GB). and contain the E_k_X_central_l_values.npz files and files, respectively, with some natural directory structure for k = \(3, 5, 6, 7, 13\) and the following elliptic curves E:

11a1, 14a1, 15a1, 17a1, 19a1, 20a1, 21a1, 24a1, 26a1, 26b1, 27a1, 30a1, 32a1, 33a1, 34a1, 35a1, 
36a1, 37a1, 37b1, 38a1, 38b1, 39a1, 40a1, 42a1, 43a1, 44a1, 45a1, 46a1, 48a1, 49a1, 50a1, 50b1, 
51a1, 52a1, 53a1, 54a1, 54b1, 55a1, 56a1, 57a1, 57b1, 57c1, 58a1, 58b1, 61a1, 62a1, 63a1, 64a1, 
65a1, 66a1, 66b1, 66c1, 67a1, 69a1, 70a1, 72a1, 73a1, 75a1, 75b1, 75c1, 76a1, 77a1, 77b1, 77c1, 
78a1, 79a1, 80a1, 80b1, 82a1, 83a1, 84a1, 84b1, 85a1, 88a1, 89a1, 89b1, 90a1, 90b1, 90c1, 91a1, 
91b1, 92a1, 92b1, 94a1, 96a1, 96b1, 98a1, 99a1, 99b1, 99c1, 99d1

One can unzip them on your local system and read the data directly with twists_ailve on SageMath.

3) twists_clve

twists_clve is a command line program written in C/C++ and CUDA for computing and storing the values of \(L(E, 1, \chi)\) for \(\mathcal B_{k,N}(X)\) and some other number theoretic values related with them. For a fixed \(E\) and \(k\), when \(X\) gets large, the computations for obtaining the values of \(L(E, 1, \chi)\) demand massive computational power.

Interestingly, one of the ways to achieve this goal is to use General Purpose Graphic Processing Units (GPGPU). CUDA is one of those in the present time. For a practical example, for \(X\) is a couple of millions and CUDA GPU with around 3000 cores, the total computational time can be reduced by a couple of thousand times faster than using one core of CPU.

System and Libraries Requirements

Hardware and Operating System:
Compilers for Building:

External Libraries:

Other Tools:

Note: For more detailed requirements of compilers and external libraries above, consult their websites.

Instructions for Configuring and Building twists_clve

  1. Download the twists package and unzip it in your working directory.
  2. Check the requirements above for your systems:
    • One can check his/her GPU hardware specifications by building and running “/Samples/1_Utilities/deviceQuery” of the CUDA samples package installed. Refer to deviceQuery_output.txt in the twists package as an example.
    • The Makefile is written under the assumption that FLINT and GMP are installed as shared libraries.
    • Make sure that helper_cuda.h and helper_string.h, originally located under the directory of /common/ of the CUDA samples package, can be found in an implementation-defined directory by nvcc.
  3. Run Makefile in twists_clve directory by “make” or “make all”.

Instructions for Running twists_clve

  1. Compute and save the coefficients \(a_n\) starting from \(n = 1\) of \(L(E, s, \chi)\) using Pari/GP as shown the following example GP code: for \(E\): 11a1 and \(n \le 10^6\) as an example,

    ? default(parisizemax,"20000M")
    ***   Warning: new maximum stack size = 20000002048 (19073.488 Mbytes).
    ?default(primelimit, "200000000")
    ? le = ["11a1"]
    %2 = ["11a1"]
    ? {for(j=1,length(le),E=ellinit(le[j]);van=ellan(E,10^8);
    *** ellan: Warning: increasing stack size to 16000000.
    *** ellan: Warning: increasing stack size to 32000000.
    *** ellan: Warning: increasing stack size to 64000000.
    *** ellan: Warning: increasing stack size to 128000000.
    *** ellan: Warning: increasing stack size to 256000000.
    *** ellan: Warning: increasing stack size to 512000000.
    *** ellan: Warning: increasing stack size to 1024000000.

    Note: For computing \(L(E, 1, \chi)\) within a desired precision, firstly one needs to compute the number of \(a_n\)’s depending on \(N\) and \(\mathfrak{f}_\chi\). The formula to compute it can be easily derived from Equation (1) and can be found in the definition of function twists_num_terms in source/twists_dirichlet_character/twists_dirichlet_character.c.

  2. Assume that with the data “A” of \(a_n\) for \(E\) of conductor “N” and root number “W” one wants to compute \(L(E, 1, \chi)\) for the primitive Dirichlet characters of order “K” and conductor between “C1” and “C2” and save those in the output data “L”.

    Then, run twists_clve with the following arguments as: twists_clve N W K C1 C2 A L

    For \(E\): 11a1 as an example,

    [@twists_clve]$ ./twists_clve 11 1 3 2 10000 ./ ./output.dat
    The length of a_n is 100000001
    a_n are loaded into device!
    Twists for N = 11, k = 3, kinv = 0.33333333333333331, zeta_k = -0.50000000000000022 + 0.86602540378443849*I
    Done for f < 99. tw.num_chi: 32
    Done for f < 198. tw.num_chi: 62
    Done for f < 9999. tw.num_chi: 3184
    The computations are all done. Max stream idx = 31
    cpu = 5547 ms wall = 5566 ms

Output Data

The output data consist of the tuples of the following 13 entries:

\[ [ N, k, \mathfrak f_\chi, r_\chi, \Re(L), \Im(L), \Re(\tau(\chi)), \Im(\tau(\chi)), e_\chi(N), c, e_\chi(c), e_\chi(-1), T_\chi ] \]


Note: Even though FLINT and GMP support arbitrary precision integer and float computations, the output float data are of double precision type at most due to the limited support of CUDA.

For \(E\): 11a1 as an example,

[@twists_clve]$ cat output.dat 
11, 3, 7, 1, 1.9971068270600856, 1.3284392937855753, 2.3704694055761992, -1.1751062918847859, 1, 2, 2, 3, 6825
11, 3, 7, 2, 1.9971068270600865, -1.3284392937855733, 2.3704694055761992, 1.1751062918847872, 2, 2, 1, 3, 6825
11, 3, 9997, 3, 0.84765090208809601, -9.1002858592966529, 9.273015315297048, -99.554061629637928, 3, 2, 1, 3, 461435
11, 3, 9997, 4, 3.96079186956215e-14, -2.7321877339158544e-14, 58.849069303070934, 80.831844233314186, 1, 7, 1, 3, 461435

4) twists_ailve

twists_ailve is a SageMath command line program to convert the raw twists_clve data archived in Zenodo and compute the algebraic and integer \(L\)-values.


SageMath ver. 9.0 or later (

Instructions for Running twists_ailve

  1. Open SageMath command line (Optional): any interactive SageMath shell will work.

  2. Load twists_ailve.sage on SageMath by typing: load(‘./twists_ailve.sage’) for example.

  3. From a E_k_X_raw.dat, computed by twists_clve, one can create a tw_central_l_values Python class object as

    sage: load('./twists_ailve.sage')
    sage: L = tw_central_l_values.load_from_dat('11a1', 3, './'); print(L[0])
    [11, 3, 7, 1, (1.9971068270600854+1.3284392937855751j), (2.3704694055761992-1.1751062918847859j), 1, 2, 2, 3, 6825]
  4. Once a tw_central_l_values class object is created, one can save it as a npz (Numpy compressed) file into a path. Then, the npz file is saved in

    sage: L.save_to_npz('./')
  5. Similar to Step 3. one can also create a tw_central_l_values Python class object from a E_k_X_central_l_values.npz as below:

    sage: L = tw_central_l_values.load_from_npz('11a1', 3, './'); print(L[0])
    [11, 3, 7, 1, (1.9971068270600854+1.3284392937855751j), (2.3704694055761992-1.1751062918847859j), 1, 2, 2, 3, 6825]
  6. Compute the algebraic parts and integer values of \(L(E, 1, \chi)\) from a E_k_X_central_l_values.npz as below:

    sage: %time A = tw_alg_int_l_values.load_from_central_l_values_npz('11a1',3,'./')
    CPU times: user 28.8 s, sys: 31.7 ms, total: 28.9 s
    Wall time: 28.9 s
    sage: print(A[0])
    [11, 3, 10, 7, 1, 1, (4.999999999999993+8.660254037844375j), 9.999999999999988, 3, 1]

    Note: It takes significantly more time as k increases for each E and X.

  7. Save the algebraic parts and integer values as a zip file into a path as below:

    sage: A.save_to_zip('./')
  8. One can also load the tw_alg_int_l_values class object from a as below:

    sage: A = tw_alg_int_l_values.load_from_zip('11a1', 3, './'); print(A[0])
    [11, 3, 10, 7, 1, 1, (4.999999999999993+8.660254037844375j), 9.999999999999988, 3, 1]

Output Data

The output data consist of the tuples of the following 13 entries:

\[ [ N, k, g, \mathfrak{f}_\chi, r_\chi, A_\chi/g, L_E^\text{alg}(\chi), \alpha_\chi, e_\chi(-1), e_\chi(-N)] \]


For \(E\): 11a1, \(k = 3\), and \(\mathfrak{f}_\chi = 7\), as an example,

sage: A = tw_alg_int_l_values.load_from_zip('11a1', 3, './'); print(A[0])
[11, 3, 10, 7, 1, 1, (4.999999999999993+8.660254037844375j), 9.999999999999988, 3, 1]

Note: For better loading procedure and storage saving, the tw_central_l_values and tw_alg_int_l_values classes use Numpy for each array element except the A_chi_div_g list of tw_alg_int_l_values class. It is because the absolute value of an integer element in that list can easily be greater than the maximum allowed for a 64-bit integer (one can find those integer elements for \(k = 13\) in the sample data).

Class object tw_central_l_values for E_k_X_central_l_values.npz

Class tw_central_l_values consists of the following members:

Class object of Algebraic and Integer \(L\)-values for

Class tw_alg_int_l_values consists of the following members: