A library for computing Hecke matrices for positive definite rational quinary quadratic forms. It also supports other ranks - n = 3,4,5 are fully supported at the moment.
The code in this repository is based on the work of many, among them:
- Jeffery P. Hein - his repository of Ternary Birch is the basis for this code (https://github.com/jefferyphein/ternary-birch)
- Mathieu Dutour Sikiric - we use his code for computing canonical forms, which allows us to perform computations for arbitrary ranks (https://github.com/MathieuDutSik/polyhedral_common)
- Matthew Greenberg and John Voight - their magma code for p-neighbors is the algorithm on which this code is based. (https://github.com/assaferan/ModFrmAlg)
-
sage is only needed for usage with the sage interface
-
This code has only been tested so far on:
- linux #73-Ubuntu, compiled with gcc 9.3.0 on AMD Ryzen Threadripper 2970WX 24-Core Processor.
- macOS BigSur 11.4, compiled with gcc 4.2.1 on Intel i9 8-Core Processor.
The main (and develop) branch require the following libraries:
The standalone branch does not use any of them. The flint branch only requires ANTIC and FLINT.
This can be installed either as a C++ library (libbirch.so) or compiled directly for use within a Sage session.
At the moment, usage in Sage does not have all the features of the package.
To build as a C++ library on a standard Linux system with autotools:
./autogen.sh
./configure --prefix=DIR
make
make install
See Usage.
While this library can compiled into a C++ library and used within applications, it was specifically designed to be integrated into Sage.
To use in Sage, you will need to install this package to your local Sage distribution. Within the src
directory, run the following command:
sage setup.py install --user
Once this finishes, from within Sage:
sage: from quinary_birch import BirchGenus
A genus for the desired level can be generated by instantiating a BirchGenus
object.
sage: g = BirchGenus(11*13*17*19*23)
By default, this will construct a quinary quadratic form with the specified discriminant. The quadratic form is constructed so that as many of its primes are ramified in its associated quaternion order. If there are an odd number of prime factors, all primes will be ramified; if there are an even number of prime factors, all but the largest prime will ramify.
You can override the default ramification behavior by specifying the ramified_primes
keyword argument:
sage: h = BirchGenus(11*13*17*19*23, ramified_primes=[11,13,17])
With a genus object constructed, Hecke matrices at primes not dividing the discriminant can be computed for each conductor, a squarefree divisor of the discriminant, using the hecke_matrix
member function. These are Hecke matrices with weight associated to a twist by a Kronecker character whose conductor is a squarefree divisor of the discriminant.
sage: A = g.hecke_matrix(101, 17*19)
This returns the Hecke matrix for conductor 323 = 17*19
at the prime 101
. Despite only returning a single matrix, Hecke matrices for each squarefree conductor have also been computed and stored in memory. These matrices are accessible by varying the conductor parameter.
sage: B = g.hecke_matrix(101, 11*17)
By default, the matrices returned by hecke_matrix
are either sparse (scipy.csr_matrix
objects) or dense (numpy.ndarray
objects). This behavior is determined by an internal density estimate, and can be overridden with the sparse
keyword argument.
sage: C = g.hecke_matrix(71, 11*17, sparse=False)
Additionally, the underlying arithmetic within hecke_matrix
is done using multi-precision arithmetic. For sufficiently small discriminants and primes, it may be advantageous to use native 64-bit arithmetic in computing Hecke matrices. This behavior can be overriden with the precise
keyword argument.
sage: D = g.hecke_matrix(73, 11*17, precise=False)
There is a mechanism in place to catch overflow issues when they occur, namely when computing p-neighbors, if the discriminants do not match an OverflowError
exception is thrown.
With a genus object constructed, you can recover its rational eigenvectors as follows:
sage: g = BirchGenus(5 * 7 * 11 * 13 * 17)
sage: evecs = g.rational_eigenvectors()
This will return a list of dictionaries corresponding to each rational eigenvector. Each dictionary contains three keywords: aps
, vector
, and conductor
.
- The
aps
keyword contains the vector's known eigenvalues, this is a dictionary indexed by the corresponding prime. - The
vector
keyword contains the column vector for the associated Hecke matrices. - The
conductor
keyword contains an integer corresponding to the conductor of the character from which it arose.
The rational eigenvectors are stored internally so that additional calls to rational_eigenvectors
do nothing. You can force recomputation of the rational eigenvectors with the force=True
parameter.
As with computing Hecke matrices, you can pass the precise
keyword to compute eigenvectors for reasonably small levels. Note: This only affects computing Hecke matrices and not the underlying linear algebra.
sage: evecs = g.rational_eigenvectors(precise=False)
Once rational eigenvectors have been computed, you are ready to compute Hecke eigenvalues.
sage: g.compute_eigenvalues(101)
This function has no return value, but instead updates the internal state of the eigenvectors stored within the genus object. By printing the contents of evecs
again, you can see that the eigenvalues are now present.
sage: print(evecs)
You may also compute all eigenvalues up to a specific bound.
sage: g.compute_eigenvalues_upto(1000)
Additionally, the compute_eigenvalues
and compute_eigenvalues_upto
functions accept a precise=False
keyword argument to allow for even faster eigenvalue computations for reasonaly small level and primes.
With a genus object constructed, isometries used within the hecke_matrix
functions can be directly accessed via the isometry_sequence
member function. This data can then be used to manually compute custom Hecke operators with a user-defined representation. For example, in Sage:
def trivial_representation(isometry):
return 1
import numpy as np
g = BirchGenus(11)
dim = g.dimensions()[1]
mat = np.zeros((dim, dim), dtype=np.int32)
for entry in g.isometry_sequence(31):
row = entry['src']
col = entry['dst']
mat[row][col] += trivial_representation(entry)
Compare this to:
sage: mat == g.hecke_matrix(31, 1)
This feature is still under development.
By default, when BirchGenus
is constructed, a random seed is established to aid in some of the probabalistic algorithms within. This can cause the ordering of the genus representatives to be permuted between separate sessions using the same input level. Due to this, a seed
parameter can be saved, allowing for the same genus ordering in a later session.
sage: my_seed = g.seed()
In a later session, this can then be provided upon constructing your BirchGenus
object.
sage: h = BirchGenus(11*13*17*19*23, seed=my_seed)
If you want to help develop this project, please create your own fork on Github and submit a pull request. I will do my best to integrate any additional useful features as necessary. Alternatively, submit a patch to me via email at assaferan@gmail.com.