Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/quatalg/brandt.py
4097 views
1
r"""
2
Brandt Modules
3
4
AUTHORS:
5
6
- Jon Bober
7
8
- Alia Hamieh
9
10
- Victoria de Quehen
11
12
- William Stein
13
14
- Gonzalo Tornaria
15
16
Introduction
17
============
18
19
This tutorial outlines the construction of Brandt modules in Sage. The
20
importance of this construction is that it provides us with a method
21
to compute modular forms on `\Gamma_0(N)` as outlined in Pizer's paper
22
[Pi]. In fact there exists a non-canonical Hecke algebra isomorphism
23
between the Brandt modules and a certain subspace of
24
`S_{2}(\Gamma_0(pM))` which contains all the newforms.
25
26
The Brandt module is the free abelian group on right ideal classes of
27
a quaternion order together with a natural Hecke action determined by
28
Brandt matrices.
29
30
Quaternion Algebras
31
-------------------
32
33
A quaternion algebra over `\QQ` is a central simple algebra of
34
dimension 4 over `\QQ`. Such an algebra `A` is said to be
35
ramified at a place `v` of `\QQ` if and only if `A_v=A\otimes
36
\QQ_v` is a division algebra. Otherwise `A` is said to be split
37
at `v`.
38
39
``A = QuaternionAlgebra(p)`` returns the quaternion algebra `A` over
40
`\QQ` ramified precisely at the places `p` and `\infty`.
41
42
``A = QuaternionAlgebra(k,a,b)`` returns a quaternion algebra with basis
43
`\{1,i,j,j\}` over `\mathbb{K}` such that `i^2=a`, `j^2=b` and `ij=k.`
44
45
An order `R` in a quaternion algebra is a 4-dimensional lattice on `A`
46
which is also a subring containing the identity.
47
48
``R = A.maximal_order()`` returns a maximal order `R` in the quaternion
49
algebra `A.`
50
51
An Eichler order `\mathcal{O}` in a quaternion algebra is the
52
intersection of two maximal orders. The level of `\mathcal{O}` is its
53
index in any maximal order containing it.
54
55
``O = A.order_of_level_N`` returns an Eichler order `\mathcal{O}` in `A`
56
of level `N` where `p` does not divide `N`.
57
58
59
A right `\mathcal{O}`-ideal `I` is a lattice on `A` such that
60
`I_p=a_p\mathcal{O}` (for some `a_p\in A_p^*`) for all `p<\infty`. Two
61
right `\mathcal{O}`-ideals `I` and `J` are said to belong to the same
62
class if `I=aJ` for some `a \in A^*`. (Left `\mathcal{O}`-ideals are
63
defined in a similar fashion.)
64
65
The right order of `I` is defined to be the set of elements in `A`
66
which fix `I` under right multiplication.
67
68
right_order (R, basis) returns the right ideal of `I` in `R` given a
69
basis for the right ideal `I` contained in the maximal order `R.`
70
71
ideal_classes(self) returns a tuple of all right ideal classes in self
72
which, for the purpose of constructing the Brandt module B(p,M), is
73
taken to be an Eichler order of level M.
74
75
The implementation of this method is especially interesting. It
76
depends on the construction of a Hecke module defined as a free
77
abelian group on right ideal classes of a quaternion algebra with the
78
following action
79
80
.. math:
81
82
T_n[I]=\sum_{\phi} [J]
83
84
where `(n,pM)=1` and the sum is over cyclic `\mathcal{O}`-module
85
homomorphisms `\phi :I\rightarrow J ` of degree `n` up to isomorphism
86
of `J`. Equivalently one can sum over the inclusions of the submodules
87
`J \rightarrow n^{-1}I`. The rough idea is to start with the trivial
88
ideal class containing the order `\mathcal{O}` itself. Using the
89
method cyclic_submodules(self, I, p) one computes `T_p([\mathcal{O}])`
90
for some prime integer $p$ not dividing the level of the order
91
`\mathcal{O}`. Apply this method repeatedly and test for equivalence
92
among resulting ideals. A theorem of Serre asserts that one gets a
93
complete set of ideal class representatives after a finite number of
94
repetitions.
95
96
One can prove that two ideals `I` and `J` are equivalent if and only
97
if there exists an element `\alpha \in I \overline{J}` such
98
`N(\alpha)=N(I)N(J)`.
99
100
is_equivalent(I,J) returns true if `I` and `J` are equivalent. This
101
method first compares the theta series of `I` and `J`. If they are the
102
same, it computes the theta series of the lattice `I\overline(J)`. It
103
returns true if the `n^{th}` coefficient of this series is nonzero
104
where `n=N(J)N(I)`.
105
106
The theta series of a lattice `L` over the quaternion algebra `A` is
107
defined as
108
109
.. math:
110
111
\theta_L(q)=\sum_{x \in L} q^{\frac{N(x)}{N(L)}}
112
113
L.theta_series(T,q) returns a power series representing `\theta_L(q)`
114
up to a precision of `\mathcal{O}(q^{T+1})`.
115
116
117
Hecke Structure
118
---------------
119
120
The Hecke structure defined on the Brandt module is given by the
121
Brandt matrices which can be computed using the definition of the
122
Hecke operators given earlier.
123
124
hecke_matrix_from_defn (self,n) returns the matrix of the nth Hecke
125
operator `B_{0}(n)` acting on self, computed directly from the
126
definition.
127
128
However, one can efficiently compute Brandt matrices using theta
129
series. In fact, let {`I_{1},.....,I_{h}`} be a set of right
130
`\mathcal{O}`-ideal class representatives. The (i,j) entry in the
131
Brandt matrix `B_{0}(n)` is the product of the `n^{th}` coefficient in
132
the theta series of the lattice `I_{i}\overline{I_{j}}` and the first
133
coefficient in the theta series of the lattice
134
`I_{i}\overline{I_{i}}`.
135
136
compute_hecke_matrix_brandt(self,n) returns the nth Hecke matrix,
137
computed using theta series.
138
139
Example
140
-------
141
142
::
143
144
sage: B = BrandtModule(23)
145
146
sage: B.maximal_order()
147
Order of Quaternion Algebra (-1, -23) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
148
149
sage: B.right_ideals()
150
(Fractional ideal (2 + 2*j, 2*i + 2*k, 4*j, 4*k), Fractional ideal (2 + 2*j, 2*i + 6*k, 8*j, 8*k), Fractional ideal (2 + 10*j + 8*k, 2*i + 8*j + 6*k, 16*j, 16*k))
151
152
sage: B.hecke_matrix(2)
153
[1 2 0]
154
[1 1 1]
155
[0 3 0]
156
157
sage: B.brandt_series(3)
158
[1/4 + q + q^2 + O(q^3) 1/4 + q^2 + O(q^3) 1/4 + O(q^3)]
159
[ 1/2 + 2*q^2 + O(q^3) 1/2 + q + q^2 + O(q^3) 1/2 + 3*q^2 + O(q^3)]
160
[ 1/6 + O(q^3) 1/6 + q^2 + O(q^3) 1/6 + q + O(q^3)]
161
162
163
164
References
165
----------
166
167
- [Pi] Arnold Pizer, *An Algorithm for Computing Modular Forms on* `\Gamma_{0}(N)`
168
169
- [Ko] David Kohel, *Hecke Module Structure of Quaternions*
170
171
172
Further Examples
173
----------------
174
We decompose a Brandt module over both `\ZZ` and `\QQ`.::
175
176
sage: B = BrandtModule(43, base_ring=ZZ); B
177
Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring
178
sage: D = B.decomposition()
179
sage: D
180
[
181
Subspace of dimension 1 of Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring,
182
Subspace of dimension 1 of Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring,
183
Subspace of dimension 2 of Brandt module of dimension 4 of level 43 of weight 2 over Integer Ring
184
]
185
sage: D[0].basis()
186
((0, 0, 1, -1),)
187
sage: D[1].basis()
188
((1, 2, 2, 2),)
189
sage: D[2].basis()
190
((1, 1, -1, -1), (0, 2, -1, -1))
191
sage: B = BrandtModule(43, base_ring=QQ); B
192
Brandt module of dimension 4 of level 43 of weight 2 over Rational Field
193
sage: B.decomposition()[2].basis()
194
((1, 0, -1/2, -1/2), (0, 1, -1/2, -1/2))
195
196
"""
197
198
################################################################################
199
# Sage: Open Source Mathematical Software
200
#
201
# Copyright (C) 2009 William Stein <[email protected]>
202
#
203
# Distributed under the terms of the GNU General Public License (GPL)
204
#
205
# This code is distributed in the hope that it will be useful,
206
# but WITHOUT ANY WARRANTY; without even the implied warranty of
207
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
208
# General Public License for more details.
209
#
210
# The full text of the GPL is available at:
211
#
212
# http://www.gnu.org/licenses/
213
################################################################################
214
215
216
# imports
217
from sage.misc.all import prod, verbose
218
from sage.rings.all import (Integer, ZZ, QQ, is_CommutativeRing, prime_divisors,
219
kronecker, PolynomialRing, GF, next_prime, lcm, gcd)
220
221
from sage.algebras.quatalg.quaternion_algebra import QuaternionAlgebra, basis_for_quaternion_lattice
222
from sage.algebras.quatalg.quaternion_algebra_cython import rational_matrix_from_rational_quaternions
223
224
from sage.rings.arith import gcd, factor, kronecker_symbol
225
from sage.modular.hecke.all import (AmbientHeckeModule, HeckeSubmodule, HeckeModuleElement)
226
from sage.matrix.all import MatrixSpace, matrix
227
from sage.rings.rational_field import is_RationalField
228
from sage.misc.mrange import cartesian_product_iterator
229
230
from sage.misc.cachefunc import cached_method
231
232
from copy import copy
233
234
cache = {}
235
236
def BrandtModule(N, M=1, weight=2, base_ring=QQ, use_cache=True):
237
"""
238
Return the Brandt module of given weight associated to the prime
239
power p^r and integer M, where p and M are coprime.
240
241
INPUT:
242
- N -- a product of primes with odd exponents
243
- M -- an integer coprime to q (default: 1)
244
- weight -- an integer that is at least 2 (default: 2)
245
- base_ring -- the base ring (default: QQ)
246
- use_cache -- whether to use the cache (default: True)
247
248
OUTPUT:
249
- a Brandt module
250
251
EXAMPLES::
252
253
sage: BrandtModule(17)
254
Brandt module of dimension 2 of level 17 of weight 2 over Rational Field
255
sage: BrandtModule(17,15)
256
Brandt module of dimension 32 of level 17*15 of weight 2 over Rational Field
257
sage: BrandtModule(3,7)
258
Brandt module of dimension 2 of level 3*7 of weight 2 over Rational Field
259
sage: BrandtModule(3,weight=2)
260
Brandt module of dimension 1 of level 3 of weight 2 over Rational Field
261
sage: BrandtModule(11, base_ring=ZZ)
262
Brandt module of dimension 2 of level 11 of weight 2 over Integer Ring
263
sage: BrandtModule(11, base_ring=QQbar)
264
Brandt module of dimension 2 of level 11 of weight 2 over Algebraic Field
265
266
The use_cache option determines whether the Brandt module returned
267
by this function is cached.::
268
269
sage: BrandtModule(37) is BrandtModule(37)
270
True
271
sage: BrandtModule(37,use_cache=False) is BrandtModule(37,use_cache=False)
272
False
273
274
TESTS:
275
276
Note that N and M must be coprime::
277
278
sage: BrandtModule(3,15)
279
Traceback (most recent call last):
280
...
281
ValueError: M must be coprime to N
282
283
Only weight 2 is currently implemented::
284
285
sage: BrandtModule(3,weight=4)
286
Traceback (most recent call last):
287
...
288
NotImplementedError: weight != 2 not yet implemented
289
290
Brandt modules are cached::
291
292
sage: B = BrandtModule(3,5,2,ZZ)
293
sage: B is BrandtModule(3,5,2,ZZ)
294
True
295
"""
296
N, M, weight = Integer(N), Integer(M), Integer(weight)
297
if not N.is_prime():
298
raise NotImplementedError, "Brandt modules currently only implemented when N is a prime"
299
if M < 1:
300
raise ValueError, "M must be positive"
301
if gcd(M,N) != 1:
302
raise ValueError, "M must be coprime to N"
303
if weight < 2:
304
raise ValueError, "weight must be at least 2"
305
if not is_CommutativeRing(base_ring):
306
raise TypeError, "base_ring must be a commutative ring"
307
key = (N, M, weight, base_ring)
308
if use_cache:
309
if cache.has_key(key): # TODO: re-enable caching!
310
return cache[key]
311
if weight != 2:
312
raise NotImplementedError, "weight != 2 not yet implemented"
313
B = BrandtModule_class(*key)
314
if use_cache:
315
cache[key] = B
316
return B
317
318
def class_number(p, r, M):
319
"""
320
Return the class number of an order of level N = p^r*M in the
321
quaternion algebra over QQ ramified precisely at p and infinity.
322
323
This is an implementation of Theorem 1.12 of [Pizer, 1980].
324
325
INPUT:
326
- p -- a prime
327
- r -- an odd positive integer (default: 1)
328
- M -- an integer coprime to q (default: 1)
329
330
OUTPUT:
331
Integer
332
333
EXAMPLES::
334
335
sage: sage.modular.quatalg.brandt.class_number(389,1,1)
336
33
337
sage: sage.modular.quatalg.brandt.class_number(389,1,2) # TODO -- right?
338
97
339
sage: sage.modular.quatalg.brandt.class_number(389,3,1) # TODO -- right?
340
4892713
341
"""
342
N = M * p**r
343
D = prime_divisors(M)
344
s = 0; t = 0
345
if N % 4 != 0:
346
s = (1 - kronecker(-4,p))/4 * prod(1 + kronecker(-4,q) for q in D)
347
if N % 9 != 0:
348
t = (1 - kronecker(-3,p))/3 * prod(1 + kronecker(-3,q) for q in D)
349
h = (N/Integer(12))*(1 - 1/p)*prod(1+1/q for q in D) + s + t
350
return Integer(h)
351
352
def maximal_order(A):
353
"""
354
Return a maximal order in the quaternion algebra ramified
355
at p and infinity.
356
357
This is an implementation of Proposition 5.2 of [Pizer, 1980].
358
359
INPUT:
360
- A -- quaternion algebra ramified precisely at p and infinity
361
362
OUTPUT:
363
- a maximal order in A
364
365
EXAMPLES::
366
367
sage: A = BrandtModule(17).quaternion_algebra()
368
sage: sage.modular.quatalg.brandt.maximal_order(A)
369
Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k)
370
371
sage: A = QuaternionAlgebra(17,names='i,j,k')
372
sage: A.maximal_order()
373
Order of Quaternion Algebra (-3, -17) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k)
374
"""
375
L = A.ramified_primes()
376
if len(L) > 1:
377
raise NotImplementedError
378
p = L[0]
379
380
i,j,k = A.gens()
381
if p == 2:
382
basis = [(1+i+j+k)/2, i, j, k]
383
elif p % 4 == 3:
384
basis = [(1+j)/2, (i+k)/2, j, k]
385
elif p % 8 == 5:
386
basis = [(1+j+k)/2, (i+2*j+k)/4, j, k]
387
elif p % 8 == 1:
388
QA, QB = A.invariants()
389
if(QA%p==0):
390
q=-QB
391
else:
392
q=-QA
393
assert(q%4==3)
394
assert(kronecker_symbol(p,q)==-1)
395
a = 0
396
while (a*a*p + 1)%q != 0:
397
a += 1
398
basis = [(1+j)/2, (i+k)/2, -(j+a*k)/q, k]
399
return A.quaternion_order(basis)
400
401
def basis_for_left_ideal(R, gens):
402
"""
403
Return a basis for the left ideal of R with given generators.
404
405
INPUT:
406
- R -- quaternion order
407
- gens -- list of elements of R
408
409
OUTPUT:
410
- list of four elements of R
411
412
EXAMPLES::
413
414
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
415
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i+j,i-j,2*k,A(3)])
416
[1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k]
417
sage: sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [3*(i+j),3*(i-j),6*k,A(3)])
418
[3/2 + 1/2*j + 2*k, 3/2*i + 3/2*k, j + k, 3*k]
419
"""
420
return basis_for_quaternion_lattice([b*g for b in R.basis() for g in gens])
421
422
423
def right_order(R, basis):
424
"""
425
Given a basis for a left ideal I, return the right order in the
426
quaternion order R of elements such that I*x is contained in I.
427
428
INPUT:
429
- R -- order in quaternion algebra
430
- basis -- basis for an ideal I
431
432
OUTPUT:
433
- order in quaternion algebra
434
435
EXAMPLES:
436
437
We do a consistency check with the ideal equal to a maximal order.::
438
439
sage: B = BrandtModule(17); basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), B.maximal_order().basis())
440
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
441
Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k)
442
sage: basis
443
[1/2 + 1/6*j + 2/3*k, 1/2*i + 1/2*k, 1/3*j + 1/3*k, k]
444
445
sage: B = BrandtModule(17); A = B.quaternion_algebra(); i,j,k = A.gens()
446
sage: basis = sage.modular.quatalg.brandt.basis_for_left_ideal(B.maximal_order(), [i*j-j])
447
sage: sage.modular.quatalg.brandt.right_order(B.maximal_order(), basis)
448
Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*i + 1/2*j + 17/2*k, i, j + 8*k, 9*k)
449
"""
450
# Compute matrix of multiplication by each element of the basis.
451
B = R.basis()
452
Z = R.quaternion_algebra()
453
M = MatrixSpace(QQ, 4)
454
455
# I = matrix with rows the given basis for I
456
I = M([list(f) for f in basis])
457
458
# psi = matrix of right multiplication on each basis element
459
psi = [M([list(f*x) for x in Z.basis()]) for f in basis]
460
461
# invert them
462
psi_inv = [x**(-1) for x in psi]
463
464
# apply the four inverses to I
465
W = [I*x for x in psi_inv]
466
467
# The right order is the intersection of the row span of the W with the row span of B.
468
X = M([list(b) for b in B]).row_module(ZZ)
469
for A in W:
470
X = X.intersection(A.row_module(ZZ))
471
C = [Z(list(b)) for b in X.basis()]
472
return Z.quaternion_order(C)
473
474
475
class BrandtModule_class(AmbientHeckeModule):
476
"""
477
A Brandt module.
478
479
EXAMPLES::
480
481
sage: BrandtModule(3, 10)
482
Brandt module of dimension 4 of level 3*10 of weight 2 over Rational Field
483
"""
484
def __init__(self, N, M, weight, base_ring):
485
"""
486
INPUT:
487
- N -- ramification number (coprime to M)
488
- M -- auxiliary level
489
- weight -- integer 2
490
- base_ring -- the base ring
491
492
EXAMPLES::
493
494
sage: BrandtModule(3, 5, weight=2, base_ring=ZZ)
495
Brandt module of dimension 2 of level 3*5 of weight 2 over Integer Ring
496
"""
497
assert weight == 2
498
self.__N = N
499
self.__M = M
500
if not N.is_prime():
501
raise NotImplementedError, "right now N must be prime"
502
rank = class_number(N, 1, M)
503
self.__key = (N, M, weight, base_ring)
504
AmbientHeckeModule.__init__(self, base_ring, rank, N * M, weight=2)
505
self._populate_coercion_lists_(coerce_list=[self.free_module()], element_constructor=BrandtModuleElement)
506
507
def _submodule_class(self):
508
"""
509
Return the Python class of submodules of this ambient Brandt module.
510
511
EXAMPLES::
512
513
sage: BrandtModule(37)._submodule_class()
514
<class 'sage.modular.quatalg.brandt.BrandtSubmodule'>
515
"""
516
return BrandtSubmodule
517
518
def free_module(self):
519
"""
520
Return the underlying free module of the Brandt module.
521
522
EXAMPLES::
523
524
sage: B = BrandtModule(10007,389)
525
sage: B.free_module()
526
Vector space of dimension 325196 over Rational Field
527
"""
528
try: return self.__free_module
529
except AttributeError: pass
530
V = self.base_ring()**self.dimension()
531
self.__free_module = V
532
return V
533
534
def N(self):
535
"""
536
Return ramification level N.
537
538
EXAMPLES::
539
540
sage: BrandtModule(7,5,2,ZZ).N()
541
7
542
"""
543
return self.__N
544
545
546
def M(self):
547
"""
548
Return the auxiliary level (prime to p part) of the quaternion
549
order used to compute this Brandt module.
550
551
EXAMPLES::
552
553
sage: BrandtModule(7,5,2,ZZ).M()
554
5
555
"""
556
return self.__M
557
558
def _repr_(self):
559
"""
560
Return string representation of this Brandt module.
561
562
EXAMPLES::
563
564
sage: BrandtModule(7,5,2,ZZ)._repr_()
565
'Brandt module of dimension 4 of level 7*5 of weight 2 over Integer Ring'
566
"""
567
aux = '' if self.__M == 1 else '*%s'%self.__M
568
return "Brandt module of dimension %s of level %s%s of weight %s over %s"%(
569
self.rank(), self.__N, aux, self.weight(), self.base_ring())
570
571
572
def __cmp__(self, other):
573
r"""
574
Compare self to other.
575
576
EXAMPLES::
577
578
sage: BrandtModule(37, 5, 2, ZZ) == BrandtModule(37, 5, 2, QQ)
579
False
580
sage: BrandtModule(37, 5, 2, ZZ) == BrandtModule(37, 5, 2, ZZ)
581
True
582
sage: BrandtModule(37, 5, 2, ZZ) == loads(dumps(BrandtModule(37, 5, 2, ZZ)))
583
True
584
"""
585
if not isinstance(other, BrandtModule_class):
586
return cmp(type(self), type(other))
587
else:
588
return cmp( (self.__M, self.__N, self.weight(), self.base_ring()), (other.__M, other.__N, other.weight(), other.base_ring()))
589
590
def quaternion_algebra(self):
591
"""
592
Return the quaternion algebra A over QQ ramified precisely at
593
p and infinity used to compute this Brandt module.
594
595
EXAMPLES::
596
597
sage: BrandtModule(997).quaternion_algebra()
598
Quaternion Algebra (-2, -997) with base ring Rational Field
599
sage: BrandtModule(2).quaternion_algebra()
600
Quaternion Algebra (-1, -1) with base ring Rational Field
601
sage: BrandtModule(3).quaternion_algebra()
602
Quaternion Algebra (-1, -3) with base ring Rational Field
603
sage: BrandtModule(5).quaternion_algebra()
604
Quaternion Algebra (-2, -5) with base ring Rational Field
605
sage: BrandtModule(17).quaternion_algebra()
606
Quaternion Algebra (-17, -3) with base ring Rational Field
607
"""
608
try:
609
return self.__quaternion_algebra
610
except AttributeError:
611
pass
612
p = self.N()
613
assert p.is_prime(), "we have only implemented the prime case"
614
if p == 2:
615
QA = -1; QB = -1
616
elif p%4 == 3:
617
QA = -1; QB = -p
618
elif p%8 == 5:
619
QA = -2; QB = -p
620
elif p%8 == 1:
621
q = 3
622
while q%4 != 3 or kronecker(p, q) != -1:
623
q = next_prime(q)
624
QA = -p; QB = -q
625
A = QuaternionAlgebra(QQ, QA, QB)
626
self.__quaternion_algebra = A
627
return A
628
629
def maximal_order(self):
630
"""
631
Return a maximal order in the quaternion algebra associated to this Brandt module.
632
633
EXAMPLES::
634
635
sage: BrandtModule(17).maximal_order()
636
Order of Quaternion Algebra (-17, -3) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, -1/3*j - 1/3*k, k)
637
sage: BrandtModule(17).maximal_order() is BrandtModule(17).maximal_order()
638
True
639
"""
640
try: return self.__maximal_order
641
except AttributeError: pass
642
R = maximal_order(self.quaternion_algebra())
643
self.__maximal_order = R
644
return R
645
646
def order_of_level_N(self):
647
"""
648
Return Eichler order of level N = p^(2*r+1)*M in the quaternion algebra.
649
650
EXAMPLES::
651
652
sage: BrandtModule(7).order_of_level_N()
653
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j, 1/2*i + 1/2*k, j, k)
654
sage: BrandtModule(7,13).order_of_level_N()
655
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j + 12*k, 1/2*i + 9/2*k, j + 11*k, 13*k)
656
sage: BrandtModule(7,3*17).order_of_level_N()
657
Order of Quaternion Algebra (-1, -7) with base ring Rational Field with basis (1/2 + 1/2*j + 35*k, 1/2*i + 65/2*k, j + 19*k, 51*k)
658
"""
659
try: return self.__order_of_level_N
660
except AttributeError: pass
661
R = quaternion_order_with_given_level(self.quaternion_algebra(), self.level())
662
self.__order_of_level_N = R
663
return R
664
665
#@cached_method
666
def cyclic_submodules(self, I, p):
667
"""
668
Return a list of rescaled versions of the fractional right
669
ideals `J` such that `J` contains `I` and the quotient has
670
group structure the product of two cyclic groups of order `p`.
671
672
We emphasize again that `J` is rescaled to be integral.
673
674
INPUT:
675
I -- ideal I in R = self.order_of_level_N()
676
p -- prime p coprime to self.level()
677
678
OUTPUT:
679
list of the p+1 fractional right R-ideals that contain I
680
such that J/I is GF(p) x GF(p).
681
682
EXAMPLES::
683
684
sage: B = BrandtModule(11)
685
sage: I = B.order_of_level_N().unit_ideal()
686
sage: B.cyclic_submodules(I, 2)
687
[Fractional ideal (1/2 + 3/2*j + k, 1/2*i + j + 1/2*k, 2*j, 2*k),
688
Fractional ideal (1/2 + 1/2*i + 1/2*j + 1/2*k, i + k, j + k, 2*k),
689
Fractional ideal (1/2 + 1/2*j + k, 1/2*i + j + 3/2*k, 2*j, 2*k)]
690
sage: B.cyclic_submodules(I, 3)
691
[Fractional ideal (1/2 + 1/2*j, 1/2*i + 5/2*k, 3*j, 3*k),
692
Fractional ideal (1/2 + 3/2*j + 2*k, 1/2*i + 2*j + 3/2*k, 3*j, 3*k),
693
Fractional ideal (1/2 + 3/2*j + k, 1/2*i + j + 3/2*k, 3*j, 3*k),
694
Fractional ideal (1/2 + 5/2*j, 1/2*i + 1/2*k, 3*j, 3*k)]
695
sage: B.cyclic_submodules(I, 11)
696
Traceback (most recent call last):
697
...
698
ValueError: p must be coprime to the level
699
"""
700
if not Integer(p).is_prime():
701
raise ValueError, "p must be a prime"
702
if self.level() % p == 0:
703
raise ValueError, "p must be coprime to the level"
704
705
R = self.order_of_level_N()
706
A = R.quaternion_algebra()
707
B = R.basis()
708
V = GF(p)**4
709
710
# step 1: Compute alpha, beta, and the matrix of their action on I/pI.
711
# NOTE: Move this code to orders once we have it all working...
712
try:
713
alpha, beta = self.__cyclic_submodules[p]
714
compute = False
715
except AttributeError:
716
self.__cyclic_submodules = {}
717
compute = True
718
except KeyError:
719
compute = True
720
721
if compute:
722
d = R.free_module().basis_matrix().determinant()
723
S = None
724
for v in V:
725
if not v: continue
726
alpha = sum(Integer(v[i])*B[i] for i in range(4))
727
# If the quadratic polynomial over GF(p) given by
728
# X^2 - alpha.reduced_trace() * X + alpha.reduced_norm()
729
# is not irreducible, we try again with a new element.
730
if p == 2:
731
# special case p == 2, since there is a unique quadratic irreducible poly.
732
if alpha.reduced_trace()%2 == 0 or alpha.reduced_norm()%2 == 0:
733
continue
734
else:
735
# check if the discriminant is a square -- if so, poly is reducible
736
b = alpha.reduced_trace(); c = alpha.reduced_norm()
737
if kronecker(b*b - 4*c, p) != -1:
738
continue
739
for w in V:
740
if not w: continue
741
beta = sum(Integer(w[i])*B[i] for i in range(4))
742
v = [A(1), alpha, beta, alpha*beta]
743
M = rational_matrix_from_rational_quaternions(v)
744
e = M.determinant()
745
if e and (d/e).valuation(p) == 0:
746
S = A.quaternion_order(v)
747
break
748
if S is not None: break
749
self.__cyclic_submodules[p] = (alpha, beta)
750
751
# right multiplication by X changes something to be written
752
# in terms of the basis for I.
753
Y = I.basis_matrix()
754
X = Y**(-1)
755
756
# Compute the matrix of right multiplication by alpha acting on
757
# our fixed choice of basis for this ideal.
758
759
M_alpha = (matrix([(i*alpha).coefficient_tuple() for i in I.basis()]) * X).change_ring(GF(p)).change_ring(GF(p))
760
M_beta = (matrix([(i*beta).coefficient_tuple() for i in I.basis()]) * X).change_ring(GF(p)).change_ring(GF(p))
761
762
# step 2: Find j such that if f=I[j], then mod 2 we have span(I[0],alpha*I[i])
763
# has trivial intersection with span(I[j],alpha*I[j]).
764
#
765
# In terms of our matrices alpha, beta, we can now think of I/p*I
766
# as being the GF(p)^4 that M_alpha and M_beta naturally act on,
767
# and I[0], I[1], I[2], I[3] correspond to the standard basis.
768
#
769
# We try each of the standard basis vectors.
770
W0 = V.span([V.gen(0), V.gen(0)*M_alpha])
771
assert W0.dimension() == 2
772
j = None
773
for i in range(1,4):
774
Wi = V.span([V.gen(i), V.gen(i) * M_alpha])
775
if Wi.dimension() == 2 and W0.intersection(Wi).dimension() == 0:
776
j = i
777
break
778
assert j is not None, "bug -- couldn't find basis"
779
780
# step 3: Enumerate the elements of P^1(GF(p^2)), recording each
781
# cyclic submodule of degree p.
782
answer = []
783
f = V.gen(0)
784
g = V.gen(j)
785
M2_4 = MatrixSpace(GF(p),4)
786
M2_2 = MatrixSpace(QQ,2,4)
787
Yp = p*Y
788
from sage.algebras.quatalg.quaternion_algebra_cython import\
789
rational_quaternions_from_integral_matrix_and_denom
790
for v in [f + g*(a+b*M_alpha) for a in GF(p) for b in GF(p)] + [g]:
791
v0 = v
792
v1 = v*M_alpha
793
v2 = v*M_beta
794
v3 = v1*M_beta
795
W = M2_4([v0, v1, v2, v3], coerce=False)
796
if W.rank() == 2:
797
gen_mat = Yp.stack(M2_2([v0.lift()*Y, v1.lift()*Y], coerce=False))
798
gen_mat, d = gen_mat._clear_denom()
799
H = gen_mat._hnf_pari(0, include_zero_rows=False)
800
gens = tuple(rational_quaternions_from_integral_matrix_and_denom(A, H, d))
801
answer.append( R.right_ideal(gens, check=False) )
802
if len(answer) == p+1: break
803
return answer
804
805
def hecke_matrix(self, n, algorithm='default', sparse=False, B=None):
806
"""
807
Return the matrix of the n-th Hecke operator.
808
809
INPUT:
810
811
- `n` -- integer
812
813
- ``algorithm`` -- string (default: 'default')
814
815
- 'default' -- let Sage guess which algorithm is best
816
817
- 'direct' -- use cyclic subideals (generally much
818
better when you want few Hecke operators and the
819
dimension is very large); uses 'theta' if n divides
820
the level.
821
822
- 'brandt' -- use Brandt matrices (generally much
823
better when you want many Hecke operators and the
824
dimension is very small; bad when the dimension
825
is large)
826
827
- ``sparse`` -- bool (default: False)
828
829
- `B` -- integer or None (default: None); in direct algorithm,
830
use theta series to this precision as an initial check for
831
equality of ideal classes.
832
833
EXAMPLES::
834
835
sage: B = BrandtModule(3,7); B.hecke_matrix(2)
836
[0 3]
837
[1 2]
838
sage: B.hecke_matrix(5, algorithm='brandt')
839
[0 6]
840
[2 4]
841
sage: t = B.hecke_matrix(11, algorithm='brandt', sparse=True); t
842
[ 6 6]
843
[ 2 10]
844
sage: type(t)
845
<type 'sage.matrix.matrix_rational_sparse.Matrix_rational_sparse'>
846
sage: B.hecke_matrix(19, algorithm='direct', B=2)
847
[ 8 12]
848
[ 4 16]
849
"""
850
n = ZZ(n)
851
if n <= 0:
852
raise IndexError, "n must be positive."
853
if not self._hecke_matrices.has_key(n):
854
if algorithm == 'default':
855
try: pr = len(self.__brandt_series_vectors[0][0])
856
except (AttributeError, IndexError): pr = 0
857
if n <= pr:
858
# already trivially know the hecke operator in this case
859
algorithm = 'brandt'
860
if algorithm == 'default': # still don't know
861
algorithm = 'direct'
862
863
if self.level().gcd(n) != 1:
864
algorithm = 'brandt'
865
866
if algorithm == 'direct':
867
T = self._compute_hecke_matrix(n, sparse=sparse, B=B)
868
elif algorithm == 'brandt':
869
T = self._compute_hecke_matrix_brandt(n, sparse=sparse)
870
else:
871
raise ValueError, "unknown algorithm '%s'"%algorithm
872
T.set_immutable()
873
self._hecke_matrices[n] = T
874
return self._hecke_matrices[n]
875
876
877
def _compute_hecke_matrix_prime(self, p, sparse=False, B=None):
878
"""
879
Return matrix of the `p`-th Hecke operator on self. The matrix
880
is always computed using the direct algorithm.
881
882
INPUT:
883
884
- `p` -- prime number
885
886
- `B` -- integer or None (default: None); in direct algorithm,
887
use theta series to this precision as an initial check for
888
equality of ideal classes.
889
890
- ``sparse`` -- bool (default: False); whether matrix should be sparse
891
892
EXAMPLES::
893
894
sage: B = BrandtModule(37)
895
sage: t = B._compute_hecke_matrix_prime(2); t
896
[1 1 1]
897
[1 0 2]
898
[1 2 0]
899
sage: type(t)
900
<type 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
901
sage: type(B._compute_hecke_matrix_prime(2,sparse=True))
902
<type 'sage.matrix.matrix_rational_sparse.Matrix_rational_sparse'>
903
"""
904
return self._compute_hecke_matrix_directly(n=p,B=B,sparse=sparse)
905
906
907
def _compute_hecke_matrix_directly(self, n, B=None, sparse=False):
908
"""
909
Given an integer `n` coprime to the level, return the matrix of
910
the n-th Hecke operator on self, computed on our fixed basis
911
by directly using the definition of the Hecke action in terms
912
of fractional ideals.
913
914
INPUT:
915
916
- `n` -- integer, coprime to level
917
918
- ``sparse`` -- bool (default: False); whether matrix should be sparse
919
920
EXAMPLES::
921
sage: B = BrandtModule(37)
922
sage: t = B._compute_hecke_matrix_directly(2); t
923
[1 1 1]
924
[1 0 2]
925
[1 2 0]
926
sage: type(t)
927
<type 'sage.matrix.matrix_rational_dense.Matrix_rational_dense'>
928
sage: type(B._compute_hecke_matrix_directly(2,sparse=True))
929
<type 'sage.matrix.matrix_rational_sparse.Matrix_rational_sparse'>
930
931
You can't compute the Hecke operator for n not coprime to the level using this function::
932
933
sage: B._compute_hecke_matrix_directly(37)
934
Traceback (most recent call last):
935
...
936
ValueError: n must be coprime to the level
937
938
The generic function (which uses theta series) does work, though::
939
940
sage: B.hecke_matrix(37)
941
[1 0 0]
942
[0 0 1]
943
[0 1 0]
944
945
An example where the Hecke operator isn't symmetric.::
946
947
sage: B = BrandtModule(43)
948
sage: B._compute_hecke_matrix_directly(2)
949
[1 2 0 0]
950
[1 0 1 1]
951
[0 1 0 2]
952
[0 1 2 0]
953
sage: B._compute_hecke_matrix_brandt(2)
954
[1 2 0 0]
955
[1 0 1 1]
956
[0 1 0 2]
957
[0 1 2 0]
958
"""
959
level = self.level()
960
if gcd(n, level) != 1:
961
raise ValueError, "n must be coprime to the level"
962
963
# For rigor it does not matter at all what bound we chose.
964
# This B is used only for the first phase of checking equality
965
# of ideals modulo equivalence -- we always provably check
966
# equivalence if the theta series are the same up to this
967
# bound.
968
if B is None:
969
B = self.dimension() // 2 + 5
970
971
T = copy(matrix(self.base_ring(), self.dimension(), sparse=sparse))
972
C = self.right_ideals()
973
theta_dict = self._theta_dict(B)
974
# I think the runtime of this algorithm is now dominated by
975
# computing theta series of ideals. The computation of
976
# cyclic submodules is a lower order term.
977
q = self._smallest_good_prime()
978
d = lcm([a.denominator() for a in self.order_of_level_N().basis()])
979
980
# TODO: temporary!! -- it's not sufficiently *optimized* to be
981
# sure this is best in these cases.
982
#if gcd(2*d*q,n) == 1:
983
# use_fast_alg = True
984
#else:
985
# use_fast_alg = False
986
987
use_fast_alg = False
988
989
last_percent = 0
990
for r in range(len(C)):
991
percent_done = 100*r//len(C)
992
if percent_done != last_percent:
993
if percent_done%5 == 0:
994
verbose("percent done: %s"%percent_done)
995
last_percent = percent_done
996
if use_fast_alg:
997
v = C[r].cyclic_right_subideals(n)
998
else:
999
v = self.cyclic_submodules(C[r], n)
1000
for J in v:
1001
J_theta = tuple(J.theta_series_vector(B))
1002
v = theta_dict[J_theta]
1003
if len(v) == 1:
1004
T[r,v[0]] += 1
1005
else:
1006
for i in v:
1007
if C[i].is_equivalent(J, 0):
1008
T[r,i] += 1
1009
break
1010
return T
1011
1012
@cached_method
1013
def _theta_dict(self, B):
1014
"""
1015
Return a dictionary from theta series vectors of degree `B` to
1016
list of integers `i`, where the key is the vector of
1017
coefficients of the normalized theta series of the `i`th right
1018
ideal, as indexed by ``self.right_ideals()``.
1019
1020
INPUT:
1021
1022
- `B` -- positive integer, precision of theta series vectors
1023
1024
OUTPUT:
1025
1026
- dictionary
1027
1028
EXAMPLES::
1029
1030
In this example the theta series determine the ideal classes::
1031
1032
sage: B = BrandtModule(5,11); B
1033
Brandt module of dimension 4 of level 5*11 of weight 2 over Rational Field
1034
sage: sorted(list(B._theta_dict(5).iteritems()))
1035
[((1, 0, 0, 4, 0), [3]),
1036
((1, 0, 0, 4, 2), [2]),
1037
((1, 0, 2, 0, 6), [1]),
1038
((1, 2, 4, 0, 6), [0])]
1039
1040
In this example, the theta series does not determine the ideal class::
1041
1042
sage: sorted(list(BrandtModule(37)._theta_dict(6).iteritems()))
1043
[((1, 0, 2, 2, 6, 4), [1, 2]), ((1, 2, 2, 4, 2, 4), [0])]
1044
"""
1045
C = self.right_ideals()
1046
theta_dict = {}
1047
for i in range(len(C)):
1048
I_theta = tuple(C[i].theta_series_vector(B))
1049
if I_theta in theta_dict:
1050
theta_dict[I_theta].append(i)
1051
else:
1052
theta_dict[I_theta] = [i]
1053
return theta_dict
1054
1055
def _compute_hecke_matrix_brandt(self, n, sparse=False):
1056
"""
1057
Return the n-th hecke matrix, computed using Brandt matrices
1058
(theta series).
1059
1060
When the n-th Hecke operator is requested, we computed theta
1061
series to precision `2n+20`, since it only takes slightly
1062
longer, and this means that any Hecke operator $T_m$ can
1063
quickly be computed, for `m<2n+20`.
1064
1065
INPUT:
1066
- n -- integer, coprime to level
1067
- sparse -- bool (default: False); whether matrix should be sparse
1068
1069
EXAMPLES::
1070
1071
sage: B = BrandtModule(3,17)
1072
sage: B._compute_hecke_matrix_brandt(3)
1073
[0 1 0 0]
1074
[1 0 0 0]
1075
[0 0 0 1]
1076
[0 0 1 0]
1077
sage: B._compute_hecke_matrix_brandt(5)
1078
[4 1 1 0]
1079
[1 4 0 1]
1080
[2 0 2 2]
1081
[0 2 2 2]
1082
sage: B._compute_hecke_matrix_brandt(5).fcp()
1083
(x - 6) * (x - 3) * (x^2 - 3*x - 2)
1084
1085
"""
1086
# we go out to 2*n+20 for efficiency, since it takes only a
1087
# little longer, but saves a lot of time if one computes
1088
# successive Hecke operators, which is a very common thing to
1089
# do.
1090
B = self._brandt_series_vectors()
1091
if len(B[0][0]) <= n:
1092
B = self._brandt_series_vectors(2*n+10)
1093
m = len(B)
1094
K = self.base_ring()
1095
Bmat = copy(matrix(K, m, m, sparse=sparse))
1096
for i in range(m):
1097
for j in range(m):
1098
Bmat[i,j] = K(B[j][i][n])
1099
return Bmat
1100
1101
@cached_method
1102
def _smallest_good_prime(self):
1103
"""
1104
Return the smallest prime number that does not divide the level.
1105
1106
EXAMPLES::
1107
1108
sage: BrandtModule(17,6)._smallest_good_prime()
1109
5
1110
"""
1111
level = self.level()
1112
p = ZZ(2)
1113
while level % p == 0:
1114
p = next_prime(p)
1115
return p
1116
1117
def right_ideals(self, B=None):
1118
"""
1119
Return sorted tuple of representatives for the equivalence
1120
classes of right ideals in self.
1121
1122
OUTPUT:
1123
- sorted tuple of fractional ideals
1124
1125
EXAMPLES::
1126
1127
sage: B = BrandtModule(23)
1128
sage: B.right_ideals()
1129
(Fractional ideal (2 + 2*j, 2*i + 2*k, 4*j, 4*k),
1130
Fractional ideal (2 + 2*j, 2*i + 6*k, 8*j, 8*k),
1131
Fractional ideal (2 + 10*j + 8*k, 2*i + 8*j + 6*k, 16*j, 16*k))
1132
1133
TEST::
1134
1135
sage: B = BrandtModule(1009)
1136
sage: Is = B.right_ideals()
1137
sage: n = len(Is)
1138
sage: prod(not Is[i].is_equivalent(Is[j]) for i in range(n) for j in range(i))
1139
1
1140
"""
1141
try: return self.__right_ideals
1142
except AttributeError: pass
1143
1144
p = self._smallest_good_prime()
1145
R = self.order_of_level_N()
1146
I = R.unit_ideal()
1147
I = R.right_ideal([4*x for x in I.basis()])
1148
1149
if B is None:
1150
B = self.dimension() // 2 + 5
1151
1152
ideals = [I]
1153
ideals_theta = { tuple(I.theta_series_vector(B)) : [I] }
1154
new_ideals = [I]
1155
1156
newly_computed_ideals = []
1157
got_something_new = True
1158
1159
while got_something_new:
1160
got_something_new = False
1161
newly_computed_ideals = []
1162
for I in new_ideals:
1163
L = self.cyclic_submodules(I, p)
1164
for J in L:
1165
is_new = True
1166
J_theta = tuple(J.theta_series_vector(B))
1167
if J_theta in ideals_theta:
1168
for K in ideals_theta[J_theta]:
1169
if J.is_equivalent(K, 0):
1170
is_new = False
1171
break
1172
if is_new:
1173
newly_computed_ideals.append(J)
1174
ideals.append(J)
1175
if J_theta in ideals_theta:
1176
ideals_theta[J_theta].append(J)
1177
else:
1178
ideals_theta[J_theta] = [J]
1179
verbose("found %s of %s ideals"%(len(ideals), self.dimension()), level=2)
1180
if len(ideals) >= self.dimension():
1181
ideals = tuple(sorted(ideals))
1182
self.__right_ideals = ideals
1183
return ideals
1184
got_something_new = True
1185
new_ideals = list(newly_computed_ideals)
1186
ideals = tuple(sorted(ideals))
1187
self.__right_ideals = ideals
1188
return ideals
1189
1190
1191
def _ideal_products(self):
1192
"""
1193
Return all products of right ideals, which are used in computing the Brandt matrices.
1194
1195
This function is used internally by the Brandt matrices
1196
algorithms.
1197
1198
OUTPUT:
1199
list of ideals
1200
1201
EXAMPLES::
1202
sage: B = BrandtModule(37)
1203
sage: B._ideal_products()
1204
[[Fractional ideal (8 + 8*j + 8*k, 4*i + 8*j + 4*k, 16*j, 16*k)],
1205
[Fractional ideal (8 + 24*j + 8*k, 4*i + 8*j + 4*k, 32*j, 32*k),
1206
Fractional ideal (16 + 16*j + 48*k, 4*i + 8*j + 36*k, 32*j + 32*k, 64*k)],
1207
[Fractional ideal (8 + 24*j + 24*k, 4*i + 24*j + 4*k, 32*j, 32*k),
1208
Fractional ideal (8 + 4*i + 16*j + 28*k, 8*i + 16*j + 8*k, 32*j, 64*k),
1209
Fractional ideal (16 + 16*j + 16*k, 4*i + 24*j + 4*k, 32*j + 32*k, 64*k)]]
1210
"""
1211
try: return self.__ideal_products
1212
except AttributeError: pass
1213
1214
L = self.right_ideals()
1215
n = len(L)
1216
if n == 0:
1217
return matrix(self.base_ring()[[`q`]],0)
1218
1219
# 1. Compute the theta series
1220
P = []
1221
for i in range(n):
1222
P.append([L[i].multiply_by_conjugate(L[j]) for j in range(i+1)])
1223
self.__ideal_products = P
1224
return P
1225
1226
def _brandt_series_vectors(self, prec=None):
1227
"""
1228
Return Brandt series coefficient vectors out to precision *at least* prec.
1229
1230
EXAMPLES::
1231
sage: B = BrandtModule(37, use_cache=False)
1232
sage: B._brandt_series_vectors(5)
1233
[[(1/2, 1, 1, 2, 1), (1/2, 0, 1, 1, 3), (1/2, 0, 1, 1, 3)],
1234
[(1/2, 0, 1, 1, 3), (1/2, 1, 0, 0, 3), (1/2, 0, 2, 3, 1)],
1235
[(1/2, 0, 1, 1, 3), (1/2, 0, 2, 3, 1), (1/2, 1, 0, 0, 3)]]
1236
1237
If you have computed to higher precision and ask for a lower
1238
precision, the higher precision is still returned.::
1239
1240
sage: B._brandt_series_vectors(2)
1241
[[(1/2, 1, 1, 2, 1), (1/2, 0, 1, 1, 3), (1/2, 0, 1, 1, 3)],
1242
[(1/2, 0, 1, 1, 3), (1/2, 1, 0, 0, 3), (1/2, 0, 2, 3, 1)],
1243
[(1/2, 0, 1, 1, 3), (1/2, 0, 2, 3, 1), (1/2, 1, 0, 0, 3)]]
1244
"""
1245
if prec is None:
1246
try:
1247
return self.__brandt_series_vectors
1248
except AttributeError:
1249
prec = 2
1250
elif prec < 2:
1251
raise ValueError, "prec must be at least 2"
1252
L = self.right_ideals()
1253
n = len(L)
1254
K = QQ
1255
if n == 0:
1256
return [[]]
1257
try:
1258
if len(self.__brandt_series_vectors[0][0]) >= prec:
1259
return self.__brandt_series_vectors
1260
except AttributeError: pass
1261
1262
# 1. Compute the theta series
1263
theta = [[I.theta_series_vector(prec) for I in x] for x in self._ideal_products()]
1264
1265
# 2. Compute the number e_j
1266
e = [theta[j][j][1] for j in range(n)]
1267
1268
B = [[0 for _ in range(n)] for _ in range(n)]
1269
1270
# 3. Make the brandt matrix series
1271
for i in range(n):
1272
B[i][i] = theta[i][i]/e[i]
1273
for j in range(i):
1274
B[j][i] = theta[i][j]/e[j]
1275
B[i][j] = theta[i][j]/e[i]
1276
1277
self.__brandt_series_vectors = B
1278
return B
1279
1280
def brandt_series(self, prec, var='q'):
1281
r"""
1282
Return matrix of power series `\sum T_n q^n` to the given
1283
precision. Note that the Hecke operators in this series are
1284
always over `\QQ`, even if the base ring of this Brandt module
1285
is not `\QQ`.
1286
1287
INPUT:
1288
- prec -- positive intege
1289
- var -- string (default: `q`)
1290
1291
OUTPUT:
1292
matrix of power series with coefficients in `\QQ`
1293
1294
EXAMPLES::
1295
1296
sage: B = BrandtModule(11)
1297
sage: B.brandt_series(2)
1298
[1/4 + q + O(q^2) 1/4 + O(q^2)]
1299
[ 1/6 + O(q^2) 1/6 + q + O(q^2)]
1300
sage: B.brandt_series(5)
1301
[1/4 + q + q^2 + 2*q^3 + 5*q^4 + O(q^5) 1/4 + 3*q^2 + 3*q^3 + 3*q^4 + O(q^5)]
1302
[ 1/6 + 2*q^2 + 2*q^3 + 2*q^4 + O(q^5) 1/6 + q + q^3 + 4*q^4 + O(q^5)]
1303
1304
1305
Asking for a smaller precision works.::
1306
1307
sage: B.brandt_series(3)
1308
[1/4 + q + q^2 + O(q^3) 1/4 + 3*q^2 + O(q^3)]
1309
[ 1/6 + 2*q^2 + O(q^3) 1/6 + q + O(q^3)]
1310
sage: B.brandt_series(3,var='t')
1311
[1/4 + t + t^2 + O(t^3) 1/4 + 3*t^2 + O(t^3)]
1312
[ 1/6 + 2*t^2 + O(t^3) 1/6 + t + O(t^3)]
1313
"""
1314
A = self._brandt_series_vectors(prec)
1315
R = QQ[[var]]
1316
n = len(A[0])
1317
return matrix(R, n, n, [[R(x.list()[:prec],prec) for x in Y] for Y in A])
1318
1319
def eisenstein_subspace(self):
1320
"""
1321
Return the 1-dimensional subspace of self on which the Hecke
1322
operators `T_p` act as `p+1` for `p` coprime to the level.
1323
1324
NOTE: This function assumes that the base field has
1325
characteristic 0.
1326
1327
EXAMPLES::
1328
1329
sage: B = BrandtModule(11); B.eisenstein_subspace()
1330
Subspace of dimension 1 of Brandt module of dimension 2 of level 11 of weight 2 over Rational Field
1331
sage: B.eisenstein_subspace() is B.eisenstein_subspace()
1332
True
1333
sage: BrandtModule(3,11).eisenstein_subspace().basis()
1334
((1, 1),)
1335
sage: BrandtModule(7,10).eisenstein_subspace().basis()
1336
((1, 1, 1, 1/2, 1, 1, 1/2, 1, 1, 1),)
1337
sage: BrandtModule(7,10,base_ring=ZZ).eisenstein_subspace().basis()
1338
((2, 2, 2, 1, 2, 2, 1, 2, 2, 2),)
1339
"""
1340
try: return self.__eisenstein_subspace
1341
except AttributeError: pass
1342
if self.base_ring().characteristic() != 0:
1343
raise ValueError, "characteristic must be 0"
1344
# cut down until we get a 1-d space using Hecke operators T_p
1345
# with p coprime to the level.
1346
V = self
1347
p = Integer(2)
1348
N = self.level()
1349
while V.dimension() >= 2:
1350
while N%p == 0:
1351
p = p.next_prime()
1352
A = V.T(p) - (p+1)
1353
V = A.kernel()
1354
self.__eisenstein_subspace = V
1355
return V
1356
1357
def is_cuspidal(self):
1358
r"""
1359
Returns whether self is cuspidal, i.e. has no Eisenstein part.
1360
1361
EXAMPLES:
1362
sage: B = BrandtModule(3, 4)
1363
sage: B.is_cuspidal()
1364
False
1365
sage: B.eisenstein_subspace()
1366
Brandt module of dimension 1 of level 3*4 of weight 2 over Rational Field
1367
"""
1368
return self.eisenstein_subspace().dimension() == 0
1369
1370
def monodromy_weights(self):
1371
r"""
1372
Return the weights for the monodromy pairing on this Brandt
1373
module. The weights are associated to each ideal class in our
1374
fixed choice of basis. The weight of an ideal class `[I]` is
1375
half the number of units of the right order `I`.
1376
1377
NOTE: The base ring must be `\QQ` or `\ZZ`.
1378
1379
EXAMPLES::
1380
1381
sage: BrandtModule(11).monodromy_weights()
1382
(2, 3)
1383
sage: BrandtModule(37).monodromy_weights()
1384
(1, 1, 1)
1385
sage: BrandtModule(43).monodromy_weights()
1386
(2, 1, 1, 1)
1387
sage: BrandtModule(7,10).monodromy_weights()
1388
(1, 1, 1, 2, 1, 1, 2, 1, 1, 1)
1389
sage: BrandtModule(5,13).monodromy_weights()
1390
(1, 3, 1, 1, 1, 3)
1391
"""
1392
try: return self.__monodromy_weights
1393
except AttributeError: pass
1394
e = self.eisenstein_subspace().basis()[0].element()
1395
if e.base_ring() != QQ:
1396
e = e.change_ring(QQ)
1397
# Normalize e by making all entries integral so that the common gcd is 1.
1398
e = e * e.denominator()
1399
# then divide by the LCM.
1400
e = e / lcm(list(e))
1401
# Then the denominators are the monodromy weights.
1402
w = tuple([z.denominator() for z in e])
1403
self.__monodromy_weights = w
1404
return w
1405
1406
def quaternion_order_with_given_level(A, level):
1407
"""
1408
Return an order in the quaternion algebra A with given level.
1409
(Implemented only when the base field is the rational numbers.)
1410
1411
INPUT:
1412
level -- The level of the order to be returned. Currently this is only implemented
1413
when the level is divisible by at most one power of a prime that
1414
ramifies in this quaternion algebra.
1415
1416
EXAMPLES::
1417
1418
sage: from sage.modular.quatalg.brandt import quaternion_order_with_given_level, maximal_order
1419
sage: A.<i,j,k> = QuaternionAlgebra(5)
1420
sage: level = 2 * 5 * 17
1421
sage: O = quaternion_order_with_given_level(A, level)
1422
sage: M = maximal_order(A)
1423
sage: L = O.free_module()
1424
sage: N = M.free_module()
1425
sage: print L.index_in(N) == level/5 #check that the order has the right index in the maximal order
1426
True
1427
"""
1428
1429
if not is_RationalField(A.base_ring()):
1430
raise NotImplementedError, "base field must be rational numbers"
1431
1432
from sage.modular.quatalg.brandt import maximal_order
1433
1434
if len(A.ramified_primes()) > 1:
1435
raise NotImplementedError, "Currently this algorithm only works when the quaternion algebra is only ramified at one prime."
1436
1437
# (The algorithm we use is similar to that in Magma (by David Kohel).)
1438
# in the following magma code, M denotes is the level
1439
level = abs(level)
1440
N = A.discriminant()
1441
N1 = gcd(level, N)
1442
M1 = level/N1
1443
1444
O = maximal_order(A)
1445
if 0 and N1 != 1: # we don't know why magma does the following, so we don't do it.
1446
for p in A.ramified_primes():
1447
if level % p**2 == 0:
1448
raise NotImplementedError, "Currently sage can only compute orders whose level is divisible by at most one power of any prime that ramifies in the quaternion algebra"
1449
1450
P = basis_for_left_ideal(O, [N1] + [x*y - y*x for x, y in cartesian_product_iterator([A.basis(), A.basis()]) ])
1451
O = A.quaternion_order(P)
1452
1453
fact = factor(M1)
1454
B = O.basis()
1455
1456
for (p, r) in fact:
1457
a = int((-p/2))
1458
for v in GF(p)**4:
1459
x = sum([int(v[i]+a)*B[i] for i in range(4)])
1460
D = x.reduced_trace()**2 - 4 * x.reduced_norm()
1461
#x = O.random_element((-p/2).floor(), (p/2).ceil())
1462
if kronecker_symbol(D, p) == 1: break
1463
X = PolynomialRing(GF(p), 'x').gen()
1464
a = ZZ((X**2 - ZZ(x.reduced_trace()) * X + ZZ(x.reduced_norm())).roots()[0][0])
1465
I = basis_for_left_ideal(O, [p**r, (x-a)**r] )
1466
O = right_order(O, I) # right_order returns the RightOrder of I inside O, so we don't need to do another intersection
1467
1468
return O
1469
1470
class BrandtSubmodule(HeckeSubmodule):
1471
def _repr_(self):
1472
"""
1473
Return string representation of this Brandt submodule.
1474
1475
EXAMPLES::
1476
1477
sage: BrandtModule(11)[0]._repr_()
1478
'Subspace of dimension 1 of Brandt module of dimension 2 of level 11 of weight 2 over Rational Field'
1479
"""
1480
return "Subspace of dimension %s of %s"%(self.dimension(), self.ambient_module())
1481
1482
1483
class BrandtModuleElement(HeckeModuleElement):
1484
def __init__(self, parent, x):
1485
"""
1486
EXAMPLES::
1487
1488
sage: B = BrandtModule(37)
1489
sage: x = B([1,2,3]); x
1490
(1, 2, 3)
1491
sage: parent(x)
1492
Brandt module of dimension 3 of level 37 of weight 2 over Rational Field
1493
"""
1494
if isinstance(x, BrandtModuleElement):
1495
x = x.element()
1496
HeckeModuleElement.__init__(self, parent, parent.free_module()(x))
1497
1498
def __cmp__(self, other):
1499
"""
1500
EXAMPLES::
1501
1502
sage: B = BrandtModule(13,5)
1503
sage: B.0
1504
(1, 0, 0, 0, 0, 0)
1505
sage: B.0 == B.1
1506
False
1507
sage: B.0 == 0
1508
False
1509
sage: B(0) == 0
1510
True
1511
sage: B.0 + 2*B.1 == 2*B.1 + B.0
1512
True
1513
sage: loads(dumps(B.0)) == B.0
1514
True
1515
"""
1516
if not isinstance(other, BrandtModuleElement):
1517
other = self.parent()(other)
1518
else:
1519
c = cmp(self.parent(), other.parent())
1520
if c: return c
1521
return cmp(self.element(), other.element())
1522
1523
1524
def monodromy_pairing(self, x):
1525
"""
1526
Return the monodromy pairing of self and x.
1527
1528
EXAMPLES::
1529
1530
sage: B = BrandtModule(5,13)
1531
sage: B.monodromy_weights()
1532
(1, 3, 1, 1, 1, 3)
1533
sage: (B.0 + B.1).monodromy_pairing(B.0 + B.1)
1534
4
1535
"""
1536
B = self.parent()
1537
w = B.monodromy_weights()
1538
x = B(x).element()
1539
v = self.element()
1540
return sum(x[i]*v[i]*w[i] for i in range(len(v)))
1541
1542
def __mul__(self, right):
1543
"""
1544
Return the monodromy pairing of self and right.
1545
1546
EXAMPLES::
1547
1548
sage: B = BrandtModule(7,10)
1549
sage: B.monodromy_weights()
1550
(1, 1, 1, 2, 1, 1, 2, 1, 1, 1)
1551
sage: B.0 * B.0
1552
1
1553
sage: B.3 * B.3
1554
2
1555
sage: (B.0+B.3) * (B.0 + B.1 + 2*B.3)
1556
5
1557
"""
1558
return self.monodromy_pairing(right)
1559
1560
def _add_(self, right):
1561
"""
1562
Return sum of self and right.
1563
1564
EXAMPLES::
1565
1566
sage: B = BrandtModule(11)
1567
sage: B.0 + B.1 # indirect doctest
1568
(1, 1)
1569
"""
1570
return BrandtModuleElement(self.parent(), self.element() + right.element())
1571
1572
def _sub_(self, right):
1573
"""
1574
EXAMPLES::
1575
1576
sage: B = BrandtModule(11)
1577
sage: B.0 - B.1 # indirect doctest
1578
(1, -1)
1579
"""
1580
return BrandtModuleElement(self.parent(), self.element() - right.element())
1581
1582
def _neg_(self):
1583
"""
1584
EXAMPLES::
1585
1586
sage: B = BrandtModule(11)
1587
sage: -B.0 # indirect doctest
1588
(-1, 0)
1589
"""
1590
return BrandtModuleElement(self.parent(), -self.element())
1591
1592
1593
#############################################################################
1594
# Benchmarking
1595
#############################################################################
1596
def benchmark_magma(levels, silent=False):
1597
"""
1598
INPUT:
1599
- levels -- list of pairs (p,M) where p is a prime not dividing M
1600
- silent -- bool, default False; if True suppress printing during computation
1601
1602
OUTPUT:
1603
- list of 4-tuples ('magma', p, M, tm), where tm is the
1604
CPU time in seconds to compute T2 using Magma
1605
1606
EXAMPLES::
1607
1608
sage: a = sage.modular.quatalg.brandt.benchmark_magma([(11,1), (37,1), (43,1), (97,1)]) # optional - magma
1609
('magma', 11, 1, ...)
1610
('magma', 37, 1, ...)
1611
('magma', 43, 1, ...)
1612
('magma', 97, 1, ...)
1613
sage: a = sage.modular.quatalg.brandt.benchmark_magma([(11,2), (37,2), (43,2), (97,2)]) # optional - magma
1614
('magma', 11, 2, ...)
1615
('magma', 37, 2, ...)
1616
('magma', 43, 2, ...)
1617
('magma', 97, 2, ...)
1618
"""
1619
ans = []
1620
from sage.interfaces.all import magma
1621
for p, M in levels:
1622
t = magma.cputime()
1623
magma.eval('HeckeOperator(BrandtModule(%s, %s),2)'%(p,M))
1624
tm = magma.cputime(t)
1625
v = ('magma', p, M, tm)
1626
if not silent: print v
1627
ans.append(v)
1628
return ans
1629
1630
def benchmark_sage(levels, silent=False):
1631
"""
1632
INPUT:
1633
- levels -- list of pairs (p,M) where p is a prime not dividing M
1634
- silent -- bool, default False; if True suppress printing during computation
1635
1636
OUTPUT:
1637
- list of 4-tuples ('sage', p, M, tm), where tm is the
1638
CPU time in seconds to compute T2 using Sage
1639
1640
EXAMPLES::
1641
1642
sage: a = sage.modular.quatalg.brandt.benchmark_sage([(11,1), (37,1), (43,1), (97,1)])
1643
('sage', 11, 1, ...)
1644
('sage', 37, 1, ...)
1645
('sage', 43, 1, ...)
1646
('sage', 97, 1, ...)
1647
sage: a = sage.modular.quatalg.brandt.benchmark_sage([(11,2), (37,2), (43,2), (97,2)])
1648
('sage', 11, 2, ...)
1649
('sage', 37, 2, ...)
1650
('sage', 43, 2, ...)
1651
('sage', 97, 2, ...)
1652
"""
1653
from sage.misc.all import cputime
1654
ans = []
1655
for p, M in levels:
1656
t = cputime()
1657
B = BrandtModule(p,M,use_cache=False).hecke_matrix(2)
1658
tm = cputime(t)
1659
v = ('sage', p, M, tm)
1660
if not silent: print v
1661
ans.append(v)
1662
return ans
1663
1664
1665