..nodoctest

Period matrices

x.__init__(...) initializes x; see help(type(x)) for signature

recip.period_matrices.PeriodMatrix(arg1, arg2=None, arg3=None, check=True)

Create a period matrix.

Currently, period matrices with CM by non-maximal orders may not completely work.

INPUT:

  • \(arg1\) – a CM-type \(\Phi\) of a CM-field \(K\)
  • \(arg2\) – an ideal \(A\) of a maximal order of \(K\)
  • \(arg3\) – an element \(\xi\) of \(K\) with totally positive imaginary part and real part 0, such that \(\xi A\) is the trace dual of \(\bar{A}\). (If \(A\) is an ideal of the maximal order \(O_K\) and \(D\) is the different, then the final condition is equivalent to \(\xi A\bar{A} D = O_K\).)
  • \(check\) (default:True) – whether to check the condition on \(arg3\).

Instead of an ideal of a maximal order, it is possible to give a basis of any lattice in \(K\). This basis does not have to be symplectic. For this, call directly the class (it is not implemented in this constructor yet).

OUTPUT:

A period matrix corresponding to some symplectic basis for the input data.

class recip.period_matrices.PeriodMatrix_CM(CM_type=None, ideal=None, xi=None, basis=None, matrix=None, check=True)

Bases: sage.matrix.matrix_generic_dense.Matrix_generic_dense

Object representing a CM period matrix. See PeriodMatrix().

CM_field()
CM_type()
Shimura_reciprocity(A, n, m=None, reduce=100, period_matrix=False, transformation=True)

Returns matrices M in GSp(QQ)^+ and u in GSp(ZZ/n*ZZ) that give the Galois action of the ray class A mod m on a modular function f of level n.

INPUT:

  • A – an ideal of K = self.CM_type().reflex_field()
  • n – a positive integer
  • m (default:None) – a positive integer or None. If None, then use m=n. For every prime P of K dividing m, we must have ord_P(A)=0.
  • reduce (default:100) – a positive integer or False. If a positive integer, then M*self is reduced (with precision reduce).
  • period_matrix (default:False) – whether to also return Z = (M^-1)*self
  • transformation (default:True) – whether to return M. period_matrix and transformation can not both be false.

OUTPUT:

A pair (M, u) or (Z, u) or a triple (Z, M, u) depending on period_matrix and transformation. Here we have M in GSp(QQ)^+, u in GSp(ZZ/n*ZZ), and Z = M^-1*self a period matrix such that the following holds.

Let f be a modular function of level n and assume that f(self) is in the ray class field of K of conductor m. (This assumption automatically holds in the case m=n.)

Then f(self)^Artin(A) = f^u(Z).

NOTE:

If m=n, then M and Z are as in self.galois_action(x, transformation=True).

EXAMPLES:

sage: from recip import *
sage: k = CM_Field([5,13,41])
sage: Z = k.one_period_matrix()
sage: a = Z.CM_type().reflex_field().gen()
sage: i = igusa_invariants_absolute()[0]

The following is supposed to be real, so the precision of 100 is not really 100. This should be corrected (TODO):

sage: i(Z, prec=100)
6464.1219280862927825807751509 + 1.4177571165844968616946581863e-26*I
sage: (U, M, u) = Z.Shimura_reciprocity(a.parent().ideal(a), m=1, n=8, period_matrix=True)
sage: M
[ 1 -2 -1 -1]
[-2 -1 -1 -2]
[ 0  0  1 -2]
[ 0  0 -2 -1]
sage: u
[1 6 7 7]
[6 7 7 6]
[0 0 1 6]
[0 0 6 7]
sage: (i^u)(U, prec=100)
6464.12192808629278258077515...

The following sign error points out a bug, or not? a is not 1 mod 8, so it is allowed to have a non-trivial effect:

sage: P = theta_ring(2, 2)[0]
sage: t = ThetaModForm(P.gens()[4]/P.gens()[6])
sage: t(Z, prec=100)
0.99854006288205177918601876434 - 0.054016134807185886527908776...*I
sage: t
t4/t6

Simplification of theta quotient expressions does not wcompletely work yet in all cases (TODO):

sage: t^u
((-zeta8)*t6)/((zeta8)*t4)
sage: (t^u)(U, prec=100)
-0.99854006288205177918601876434 + 0.054016134807185886527908776...*I
sage: P = theta_ring(2,2)[0]
Sp_action(M)

Returns the period matrix M*self for any M in GSp_{2g}(QQ)^+.

basis()
complex_conjugate(transformation=False)

Returns minus the complex conjugate of self.

If transformation is True, then also returns a matrix in Sp_2g(QQ) (if possible in Sp_2g(ZZ)) that maps self to the output.

EXAMPLES:

sage: from recip import *
sage: U = CM_Field(x^4+5*x^2+5).period_matrices_iter().next(); U
Period Matrix
[ 0.30901699437...? + 0.95105651629...?*I  0.50000000000...? + 0.36327126400268?*I]
[ 0.50000000000...? + 0.36327126400268?*I -0.30901699437495? + 0.95105651629...?*I]
sage: Ubar, M = U.complex_conjugate(transformation=True); Ubar
Period Matrix
[-0.30901699437...? + 0.95105651629...?*I -0.50000000000...? + 0.36327126400268?*I]
[-0.50000000000...? + 0.36327126400268?*I  0.30901699437495? + 0.95105651629...?*I]
sage: M.base_ring()
Integer Ring
sage: M
[-1  0  0  0]
[ 0 -1  0  0]
[ 0  1 -1  0]
[ 1 -1  0 -1]
sage: U.complex_matrix(20)
[ 0.30902 + 0.95106*I  0.50000 + 0.36327*I]
[ 0.50000 + 0.36327*I -0.30902 + 0.95106*I]
sage: Ubar.complex_matrix(20)
[-0.30902 + 0.95106*I -0.50000 + 0.36327*I]
[-0.50000 + 0.36327*I  0.30902 + 0.95106*I]
sage: U.Sp_action(M) == Ubar
True
complex_conjugation_symplectic_matrix(level, mu=None, A=None)

EXAMPLES:

sage: class Foo:
....:     def __init__(self, x):
....:         self._x = x
....:     @cached_method
....:     def f(self,*args):
....:         return self._x^2
sage: a = Foo(2)
sage: a.f.cache
{}
sage: a.f()
4
sage: a.f.cache
{((), ()): 4}
complex_matrix(prec=None)

Returns self as a matrix over a complex field.

INPUT:

  • prec (default=None) – Either None or a positive integer.

OUTPUT:

  • If prec is an integer, returns self as a matrix over ComplexField(prec)
  • If prec=None, returns self as a matrix over self.base_ring().embedding().codomain()
epsilon(x)

The map epsilon of page 57 of Shimura’s “on certain reciprocity laws...” Returns the transpose of the matrix of multiplication by x wrt the basis self.basis()

evaluate_theta(c, prec, use_magma=False, interval=False)
g()
galois_action(A, transformation=False, reduce=100)

Returns a period matrix Z with the same CM-type as self, and with Z.ideal() = N_Phi(A)^-1 * self.ideal() and Z.xi() = N(A)^-1 * self.xi().

For a modular function f of level 1, we have f(self)^sigma(A) = f(Z), where sigma is the Artin map.

INPUT:

  • A – an ideal of self.CM_type().reflex_field()
  • N (default=1) – interpret x as an element of I(N)/R(N),
    where I(N) is the set of ideals coprime to N and R(N) is the set of ideals generated by elements that are 1 mod N.
  • transformation (default=False) – whether to also
    return a matrix M in Mat(ZZ,2g) with M*Z.basis() = self.basis() (and M*Z = self)
  • reduce (default=100) – if False, do not reduce the basis,
    if a positive integer, reduce the period matrix numerically with that precision.

OUTPUT:

With transformation=False, a matrix Z as above. With transformation=True, a pair (Z,M) as above.

has_real_moduli()

Returns true if and only if self is isomorphic to its complex conjugate over CC.

EXAMPLES:

sage: from recip import *
sage: K = CM_Field(x^4+x^3+x^2+x+1, embedding=QQbar) # the embedding is not added afterwards, and the reflex field is taken to be the field itself, so this is necessary for now
sage: Z = K.one_period_matrix()
sage: Z.has_real_moduli()
True
sage: K = CM_Field([521,27,52])
sage: mat = list(K.period_matrices_iter())
sage: ZZ(sum([Z.has_real_moduli() for Z in mat])) / ZZ(len(mat))
1/7
ideal()
negative_inverse()

Returns -1/self.

reduce(prec=None, transformation=False)

Returns a reduced period matrix Z that is Sp_{2g}-equivalent to self.

INPUT:

  • prec (default=None) – the working precision when reducing. If None, use the embedding of self.base_ring() into the complex numbers. If a positive integer, use a complex field with that many bits of precision.
  • transformation (default=False) – whether to also return a matrix M such that Z = M*self.
reflex_field()
xi()
recip.period_matrices.Sp_action(gamma, Z)

Given a matrix gamma in GSp_2g(QQ)^+ and a gxg period matrix Z, returns gamma*Z as an immutable gxg Matrix.

recip.period_matrices.evaluate_theta(c, z, u=None, use_magma=False)

Numerically evaluate ` heta[c](z,u) = sum_{n in ZZ^2}exp(pi i (n+c’)^t z (n+c’) + 2pi i (n+c’)^t (z+c’‘))`, where \(c = (c',c'')\). If u is unspecified, use u = (0,...,0).

TODO:

Uses bounds from my thesis, but according to errata, these should be changed slightly for actual correctness.

NOTE:

algorithm=’magma’ requires Sage 5.6 or higher

EXAMPLES:

sage: from recip import *
sage: I = CC.gen()
sage: evaluate_theta([0,0,0,0], Matrix(CC, [[I+1, 1/2+I/3], [1/2+I/3, 3/2*I+1/5]]), use_magma=True) # optional - magma
0.933295835691982 + 0.0143249911726235*I
recip.period_matrices.evaluate_theta_interval(c, z, R=None, reduce_first=True)

Numerically evaluate ` heta[c](z) = sum_{n in ZZ^2}exp(pi i (n+c’)^t z (n+c’) + 2pi i (n+c’)^t (z+c’‘))`, where \(c = (c',c'')\).

\(R\) specifies the square over which to sum for n. It is automatically determined if not specified.

TODO: Make a more sensible choice for \(R\) in case g=2: one that is different for the two coordinates m and n in the code below, and which depends on Z.

TODO: It turns out that sometimes \(R\) is the limiting factor, and increasing precision by 100 bits has no effect as \(R\) is not raised. Check that \(R\) is large enough, especially in the non-interval setting!

EXAMPLES:

sage: from recip import *
sage: I = ComplexIntervalField(100).gen()
sage: Z = Matrix([[I+1, 1/2+I/3], [1/2+I/3, 3/2*I+1/5]])
sage: c = [0,0,0,0]
sage: evaluate_theta_interval(c, Z, 1)
0.?e1 + 0.?e1*I
sage: evaluate_theta_interval(c, Z, 2)
1.0? + 0.1?*I
sage: evaluate_theta_interval(c, Z, 3)
0.93330? + 0.0144?*I
sage: evaluate_theta_interval(c, Z, 4)
0.9332958357? + 0.014324992?*I
sage: evaluate_theta_interval(c, Z, 5)
0.933295835691983? + 0.0143249911726235?*I
sage: evaluate_theta_interval(c, Z, 6)
0.93329583569198215423213? + 0.01432499117262351644467?*I
sage: evaluate_theta_interval(c, Z, 7)
0.9332958356919821542321268818? + 0.01432499117262351644467161792?*I
sage: v = [evaluate_theta_interval(c, Z, k) for k in range(1, 8)+[20]]
sage: vru = [x.real().upper() for x in v]
sage: all([vru[k] >= vru[k+1] for k in range(6)])
True
sage: vrl = [x.real().lower() for x in v]
sage: all([vrl[k] <= vrl[k+1] for k in range(6)])
True
sage: [RR(x.diameter()) for x in v]
[4.38675115849014, 1.97112678302085, 0.000734124840204993, 1.23050838590045e-8, 8.92050564190827e-15, 2.79460042829428e-22, 1.53160261406578e-28, 9.18961568439468e-28]

TESTS:

sage: cs = [(1/2, 0), (0, 1/2), (0,0), (5/3, 111/7), (-3/4, 8), (1/100, -3/2), (-1,-1)]
sage: I = sqrt(-1)
sage: zs = [I/20, I*0.9, 2*I+8.3, I+0.1, I/3+1/7, I/5-10/3]
sage: pairs = [(c,z) for c in cs for z in zs]
sage: CIF_values = [evaluate_theta_interval(c, Matrix([[CIF(z)]]), reduce_first=True) for c in cs for z in zs]
sage: CIF_values_non_reduced_first = [evaluate_theta_interval(c, Matrix([[CIF(z)]]), reduce_first=False) for c in cs for z in zs]
sage: magma_values = [evaluate_theta(c, Matrix([[CC(z)]]), use_magma=True) for c in cs for z in zs] # optional - magma
sage: quotients = [CIF_values[i]/CIF_values_non_reduced_first[i] for i in range(len(CIF_values))]
sage: magma_quotients = [CIF_values_non_reduced_first[i].center()/magma_values[i] for i in range(len(CIF_values))] # optional - magma
sage: [i for i in range(len(pairs)) if (magma_quotients[i]-1).abs() > 10^-8] # optional - magma
[]
sage: [i for i in range(len(pairs)) if (quotients[i]-1).abs().lower() != 0]
[]
sage: [i for i in range(len(pairs)) if (quotients[i]-1).abs().upper() > 10^-7]
[]
recip.period_matrices.gottschling_matrices()

Returns a set of matrices in Sp(4,ZZ) containing gottschling’s set.

recip.period_matrices.is_period_matrix(m)

Returns \(True\) if and only if m is a period matrix.

EXAMPLES:

sage: from recip import *
sage: i = CC.gen()
sage: is_period_matrix(Matrix([[1+i, 2+i], [2+i, 5*i]]))
True
sage: is_period_matrix(Matrix([[1+i, 2+i], [2+i, i]]))
False
recip.period_matrices.is_positive_definite(m)

Returns \(True\) if and only if symmetric real matrix \(m\) is positive definite.

EXAMPLES:

sage: from recip import *
sage: is_positive_definite(Matrix(AA,[[2,2],[2,4]]))
True
sage: is_positive_definite(Matrix(AA,[[1,2],[2,4]]))
False
sage: is_positive_definite(Matrix(AA,[[-3,2],[2,-4]]))
False
recip.period_matrices.is_symplectic(gamma, g=None)

Tests whether a 4x4 matrix is an element of Sp_g(ZZ).

EXAMPLES:

sage: from recip import *
sage: g = gottschling_matrices()
sage: all([is_symplectic(gamma) for gamma in g]) and len(g) == 38
True
sage: is_symplectic(g[0]+1)
False
recip.period_matrices.lift_ray_class_group_element(A, M, N, generator=False)

Given a ray class mod M, lift it to a ray class mod N.

INPUT:

  • A – a fractional ideal of a field K
  • M – an integral ideal of K, such that the valuation of A at every divisor of M is zero. Actually, the current implementation requires that M can be defined already over the rationals.
  • N – an integral ideal of K divisible by M
  • generator – whether to also output an element x with x = 1 mod* M and output = A / x.

OUTPUT:

A fractional ideal B such that every valuation of A at every divisor of N is zero, and such that A is in the same ray class modulo M as A.

Or a pair (B, x) if generator is True

EXAMPLES:

sage: from recip import *
sage: P.<x> = QQ[]
sage: k = NumberField(x^2+5,'a')
sage: A = k.class_group().gen().ideal()
sage: B = lift_ray_class_group_element(A, M=1, N=2); B
Fractional ideal (3, a + 1)
sage: (B/A).is_principal()
True

sage: B, x = lift_ray_class_group_element(A, M=3, N=6, generator=True)
sage: B
Fractional ideal (23/2, 1/2*a + 15/2)
sage: B*x == A
True
sage: (x - 1)/3
2/23*a - 7/23

A must be coprime to M to start with:

sage: lift_ray_class_group_element(A, M=2, N=2)
Traceback (most recent call last):
...
ValueError: A (=Fractional ideal (2, a + 1)) and M (=Fractional ideal (2)) are not coprime
recip.period_matrices.my_ceil(a)
recip.period_matrices.my_floor(a)
recip.period_matrices.random_period_matrix(prec=53, g=2)

Outputs a pseudorandom Z in H_g. Only implemented for g=2.

The period matrix is in the block with diagonal entries uniformly in [-1/2, 1/2] + [0.1, 2]*i and off-diagonal entries with real part in [-1/2, 1/2] and imaginary part in [0, min(Im(diagonal entries))].

EXAMPLE:

sage: from recip import *
sage: random_period_matrix(200, 2).parent()
Full MatrixSpace of 2 by 2 dense matrices over Complex Field with 200 bits of precision
sage: random_period_matrix().parent()
Full MatrixSpace of 2 by 2 dense matrices over Complex Field with 53 bits of precision
sage: random_period_matrix(g=3)
Traceback (most recent call last):
...
NotImplementedError