Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241852 views
1
#################################################################################
2
#
3
# (c) Copyright 2010 William Stein
4
#
5
# This file is part of PSAGE
6
#
7
# PSAGE is free software: you can redistribute it and/or modify
8
# it under the terms of the GNU General Public License as published by
9
# the Free Software Foundation, either version 3 of the License, or
10
# (at your option) any later version.
11
#
12
# PSAGE is distributed in the hope that it will be useful,
13
# but WITHOUT ANY WARRANTY; without even the implied warranty of
14
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
# GNU General Public License for more details.
16
#
17
# You should have received a copy of the GNU General Public License
18
# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
#
20
#################################################################################
21
22
"""
23
Weight 2 Hilbert modular forms over F = Q(sqrt(5)).
24
"""
25
26
from sage.misc.cachefunc import cached_method
27
28
from sqrt5 import F, O_F
29
30
from psage.number_fields.sqrt5 import primes_of_bounded_norm
31
32
33
from sqrt5_fast import IcosiansModP1ModN
34
35
from sage.rings.ideal import is_Ideal
36
37
from sage.rings.all import Integer, prime_divisors, QQ, next_prime, ZZ
38
from tables import ideals_of_norm
39
from sage.matrix.all import matrix
40
from sage.structure.all import Sequence
41
42
def ideal(X):
43
if not is_Ideal(X):
44
return O_F.ideal(X)
45
return X
46
47
def next_prime_of_characteristic_coprime_to(P, I):
48
p = next_prime(P.smallest_integer())
49
N = ZZ(I.norm())
50
while N%p == 0:
51
p = next_prime(p)
52
return F.primes_above(p)[0]
53
54
def next_prime_not_dividing(P, I):
55
while True:
56
p = P.smallest_integer()
57
if p == 1:
58
Q = F.ideal(2)
59
elif p % 5 in [2,3]: # inert
60
Q = F.primes_above(next_prime(p))[0]
61
elif p == 5:
62
Q = F.ideal(7)
63
else: # p split
64
A = F.primes_above(p)
65
if A[0] == P:
66
Q = A[1]
67
else:
68
Q = F.primes_above(next_prime(p))[0]
69
if not Q.divides(I):
70
return Q
71
else:
72
P = Q # try again
73
74
class Space(object):
75
def __cmp__(self, right):
76
if not isinstance(right, Space):
77
raise NotImplementedError
78
return cmp((self.level(), self.dimension(), self.vector_space()),
79
(right.level(), right.dimension(), right.vector_space()))
80
81
def subspace(self, V):
82
raise NotImplementedError
83
84
def vector_space(self):
85
raise NotImplementedError
86
87
def basis(self):
88
return self.vector_space().basis()
89
90
def new_subspace(self, p=None):
91
"""
92
Return (p-)new subspace of this space of Hilbert modular forms.
93
94
WARNING: There are known examples where this is still wrong somehow...
95
96
INPUT:
97
- p -- None or a prime divisor of the level
98
99
OUTPUT:
100
- subspace of this space of Hilbert modular forms
101
102
EXAMPLES::
103
104
We make a space of level a product of 2 split primes and (2)::
105
106
sage: from psage.modform.hilbert.sqrt5.hmf import F, HilbertModularForms
107
sage: P = F.prime_above(31); Q = F.prime_above(11); R = F.prime_above(2)
108
sage: H = HilbertModularForms(P*Q*R); H
109
Hilbert modular forms of dimension 32, level 2*a-38 (of norm 1364=2^2*11*31) over QQ(sqrt(5))
110
111
The full new space::
112
113
sage: N = H.new_subspace(); N
114
Subspace of dimension 22 of Hilbert modular forms of dimension 32, level 2*a-38 (of norm 1364=2^2*11*31) over QQ(sqrt(5))
115
116
The new subspace for each prime divisor of the level::
117
118
sage: N_P = H.new_subspace(P); N_P
119
Subspace of dimension 31 of Hilbert modular forms of dimension 32, level 2*a-38 (of norm 1364=2^2*11*31) over QQ(sqrt(5))
120
sage: N_Q = H.new_subspace(Q); N_Q
121
Subspace of dimension 28 of Hilbert modular forms of dimension 32, level 2*a-38 (of norm 1364=2^2*11*31) over QQ(sqrt(5))
122
sage: N_R = H.new_subspace(R); N_R
123
Subspace of dimension 24 of Hilbert modular forms of dimension 32, level 2*a-38 (of norm 1364=2^2*11*31) over QQ(sqrt(5))
124
sage: N_P.intersection(N_Q).intersection(N_R) == N
125
True
126
127
"""
128
V = self.degeneracy_matrix(p).kernel()
129
return self.subspace(V)
130
131
def decomposition(self, B, verbose=False):
132
"""
133
Return Hecke decomposition of self using Hecke operators T_p
134
coprime to the level with norm(p) <= B.
135
"""
136
137
# TODO: rewrite to use primes_of_bounded_norm so that we
138
# iterate through primes ordered by *norm*, which is
139
# potentially vastly faster. Delete these functions
140
# involving characteristic!
141
142
p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
143
T = self.hecke_matrix(p)
144
D = T.decomposition()
145
while len([X for X in D if not X[1]]) > 0:
146
p = next_prime_of_characteristic_coprime_to(p, self.level())
147
if p.norm() > B:
148
break
149
if verbose: print p.norm()
150
T = self.hecke_matrix(p)
151
D2 = []
152
for X in D:
153
if X[1]:
154
D2.append(X)
155
else:
156
if verbose: print T.restrict(X[0]).fcp()
157
for Z in T.decomposition_of_subspace(X[0]):
158
D2.append(Z)
159
D = D2
160
D = [self.subspace(X[0]) for X in D]
161
D.sort()
162
S = Sequence(D, immutable=True, cr=True, universe=int, check=False)
163
return S
164
165
def new_decomposition(self, verbose=False):
166
"""
167
Return complete irreducible Hecke decomposition of new subspace of self.
168
"""
169
V = self.degeneracy_matrix().kernel()
170
p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
171
T = self.hecke_matrix(p)
172
D = T.decomposition_of_subspace(V)
173
while len([X for X in D if not X[1]]) > 0:
174
p = next_prime_of_characteristic_coprime_to(p, self.level())
175
if verbose: print p.norm()
176
T = self.hecke_matrix(p)
177
D2 = []
178
for X in D:
179
if X[1]:
180
D2.append(X)
181
else:
182
if verbose: print T.restrict(X[0]).fcp()
183
for Z in T.decomposition_of_subspace(X[0]):
184
D2.append(Z)
185
D = D2
186
D = [self.subspace(X[0]) for X in D]
187
D.sort()
188
S = Sequence(D, immutable=True, cr=True, universe=int, check=False)
189
return S
190
191
class HilbertModularForms(Space):
192
def __init__(self, level):
193
"""
194
Space of Hilbert modular forms of weight (2,2) over Q(sqrt(5)).
195
196
INPUT:
197
- level -- an ideal or element of ZZ[(1+sqrt(5))/2].
198
199
TESTS::
200
201
sage: import psage
202
sage: H = psage.hilbert.sqrt5.HilbertModularForms(3); H
203
Hilbert modular forms of dimension 1, level 3 (of norm 9=3^2) over QQ(sqrt(5))
204
sage: loads(dumps(H)) == H
205
True
206
"""
207
self._level = ideal(level)
208
self._gen = self._level.gens_reduced()[0]
209
self._icosians_mod_p1 = IcosiansModP1ModN(self._level)
210
self._dimension = self._icosians_mod_p1.cardinality()
211
self._vector_space = QQ**self._dimension
212
self._hecke_matrices = {}
213
self._degeneracy_matrices = {}
214
215
def __repr__(self):
216
return "Hilbert modular forms of dimension %s, level %s (of norm %s=%s) over QQ(sqrt(5))"%(
217
self._dimension, str(self._gen).replace(' ',''),
218
self._level.norm(), str(self._level.norm().factor()).replace(' ',''))
219
220
def intersection(self, M):
221
if isinstance(M, HilbertModularForms):
222
assert self == M
223
return self
224
if isinstance(M, HilbertModularFormsSubspace):
225
assert self == M.ambient()
226
return M
227
raise TypeError
228
229
def level(self):
230
return self._level
231
232
def vector_space(self):
233
return self._vector_space
234
235
def weight(self):
236
return (Integer(2),Integer(2))
237
238
def dimension(self):
239
return self._dimension
240
241
def hecke_matrix(self, n):
242
# I'm not using @cached_method, since I want to ensure that
243
# the input "n" is properly normalized. I also want it
244
# to be transparent to see which matrices have been computed,
245
# to clear the cache, etc.
246
n = ideal(n)
247
if self._hecke_matrices.has_key(n):
248
return self._hecke_matrices[n]
249
t = self._icosians_mod_p1.hecke_matrix(n)
250
t.set_immutable()
251
self._hecke_matrices[n] = t
252
return t
253
254
T = hecke_matrix
255
256
def degeneracy_matrix(self, p=None):
257
if self.level().is_prime():
258
return matrix(QQ, self.dimension(), 0, sparse=True)
259
if p is None:
260
A = None
261
for p in prime_divisors(self._level):
262
if A is None:
263
A = self.degeneracy_matrix(p)
264
else:
265
A = A.augment(self.degeneracy_matrix(p))
266
return A
267
p = ideal(p)
268
if self._degeneracy_matrices.has_key(p):
269
return self._degeneracy_matrices[p]
270
d = self._icosians_mod_p1.degeneracy_matrix(p)
271
d.set_immutable()
272
self._degeneracy_matrices[p] = d
273
return d
274
275
def __cmp__(self, other):
276
if not isinstance(other, HilbertModularForms):
277
raise NotImplementedError
278
# first sort by norms
279
return cmp((self._level.norm(), self._level), (other._level.norm(), other._level))
280
281
def subspace(self, V):
282
return HilbertModularFormsSubspace(self, V)
283
284
def elliptic_curve_factors(self):
285
D = [X for X in self.new_decomposition() if X.dimension() == 1]
286
# Have to get rid of the Eisenstein factor
287
p = next_prime_of_characteristic_coprime_to(F.ideal(1), self.level())
288
while True:
289
q = p.residue_field().cardinality() + 1
290
E = [A for A in D if A.hecke_matrix(p)[0,0] == q]
291
if len(E) == 0:
292
break
293
elif len(E) == 1:
294
D = [A for A in D if A != E[0]]
295
break
296
else:
297
p = next_prime_of_characteristic_coprime_to(p, self.level())
298
return Sequence([EllipticCurveFactor(X, number) for number, X in enumerate(D)],
299
immutable=True, cr=True, universe=int, check=False)
300
301
class HilbertModularFormsSubspace(Space):
302
def __init__(self, H, V):
303
assert H.dimension() == V.degree()
304
self._H = H
305
self._V = V
306
307
def __repr__(self):
308
return "Subspace of dimension %s of %s"%(self._V.dimension(), self._H)
309
310
def subspace(self, V):
311
raise NotImplementedError
312
#return HilbertModularFormsSubspace(self._H, V)
313
314
def intersection(self, M):
315
if isinstance(M, HilbertModularForms):
316
assert self.ambient() == M
317
return self
318
if isinstance(M, HilbertModularFormsSubspace):
319
assert self.ambient() == M.ambient()
320
H = self.ambient()
321
V = self.vector_space().intersection(M.vector_space())
322
return HilbertModularFormsSubspace(H, V)
323
raise TypeError
324
325
def ambient(self):
326
return self._H
327
328
def vector_space(self):
329
return self._V
330
331
def hecke_matrix(self, n):
332
return self._H.hecke_matrix(n).restrict(self._V)
333
T = hecke_matrix
334
335
def degeneracy_matrix(self, p):
336
return self._H.degeneracy_matrix(p).restrict_domain(self._V)
337
338
def level(self):
339
return self._H.level()
340
341
def dimension(self):
342
return self._V.dimension()
343
344
345
class EllipticCurveFactor(object):
346
"""
347
A subspace of the new subspace of a space of weight 2 Hilbert
348
modular forms that (conjecturally) corresponds to an elliptic
349
curve.
350
"""
351
def __init__(self, S, number):
352
"""
353
INPUT:
354
- S -- subspace of a space of Hilbert modular forms
355
- ``number`` -- nonnegative integer indicating some
356
ordering among the factors of a given level.
357
"""
358
self._S = S
359
self._number = number
360
361
def __repr__(self):
362
"""
363
EXAMPLES::
364
365
sage: from psage.modform.hilbert.sqrt5.hmf import HilbertModularForms, F
366
sage: H = HilbertModularForms(F.prime_above(31)).elliptic_curve_factors()[0]
367
sage: type(H)
368
<class 'psage.modform.hilbert.sqrt5.hmf.EllipticCurveFactor'>
369
sage: H.__repr__()
370
'Isogeny class of elliptic curves over QQ(sqrt(5)) attached to form number 0 in Hilbert modular forms of dimension 2, level 5*a-2 (of norm 31=31) over QQ(sqrt(5))'
371
"""
372
return "Isogeny class of elliptic curves over QQ(sqrt(5)) attached to form number %s in %s"%(self._number, self._S.ambient())
373
374
def base_field(self):
375
"""
376
Return the base field of this elliptic curve factor.
377
378
OUTPUT:
379
- the field Q(sqrt(5))
380
381
EXAMPLES::
382
383
sage: from psage.modform.hilbert.sqrt5.hmf import HilbertModularForms, F
384
sage: H = HilbertModularForms(F.prime_above(31)).elliptic_curve_factors()[0]
385
sage: H.base_field()
386
Number Field in a with defining polynomial x^2 - x - 1
387
"""
388
return F
389
390
def conductor(self):
391
"""
392
Return the conductor of this elliptic curve factor, which is
393
the level of the space of Hilbert modular forms.
394
395
OUTPUT:
396
- ideal of the ring of integers of Q(sqrt(5))
397
398
EXAMPLES::
399
400
"""
401
return self._S.level()
402
403
def ap(self, P):
404
"""
405
Return the trace of Frobenius at the prime P, for a prime P of
406
good reduction.
407
408
INPUT:
409
- `P` -- a prime ideal of the ring of integers of Q(sqrt(5)).
410
411
OUTPUT:
412
- an integer
413
414
EXAMPLES::
415
416
sage: from psage.modform.hilbert.sqrt5.hmf import HilbertModularForms, F
417
sage: H = HilbertModularForms(F.primes_above(31)[0]).elliptic_curve_factors()[0]
418
sage: H.ap(F.primes_above(11)[0])
419
4
420
sage: H.ap(F.prime_above(5))
421
-2
422
sage: H.ap(F.prime_above(7))
423
2
424
425
We check that the ap we compute here match with those of a known elliptic curve
426
of this conductor::
427
428
sage: a = F.0; E = EllipticCurve(F, [1,a+1,a,a,0])
429
sage: E.conductor().norm()
430
31
431
sage: 11+1 - E.change_ring(F.primes_above(11)[0].residue_field()).cardinality()
432
4
433
sage: 5+1 - E.change_ring(F.prime_above(5).residue_field()).cardinality()
434
-2
435
sage: 49+1 - E.change_ring(F.prime_above(7).residue_field()).cardinality()
436
2
437
"""
438
if P.divides(self.conductor()):
439
if (P*P).divides(self.conductor()):
440
# It is 0, because the reduction is additive.
441
return ZZ(0)
442
else:
443
# TODO: It is +1 or -1, but I do not yet know how to
444
# compute which without using the L-function.
445
return '?'
446
else:
447
return self._S.hecke_matrix(P)[0,0]
448
449
@cached_method
450
def dual_eigenspace(self, B=None):
451
"""
452
Return 1-dimensional subspace of the dual of the ambient space
453
with the same system of eigenvalues as self. This is useful when
454
computing a large number of `a_P`.
455
456
If we can't find such a subspace using Hecke operators of norm
457
less than B, then we raise a RuntimeError. This should only happen
458
if you set B way too small, or self is actually not new.
459
460
INPUT:
461
- B -- Integer or None; if None, defaults to a heuristic bound.
462
"""
463
N = self.conductor()
464
H = self._S.ambient()
465
V = H.vector_space()
466
if B is None:
467
# TODO: This is a heuristic guess at a "Sturm bound"; it's the same
468
# formula as the one over QQ for Gamma_0(N). I have no idea if this
469
# is correct or not yet. It is probably much too large. -- William Stein
470
from sage.modular.all import Gamma0
471
B = Gamma0(N.norm()).index()//6 + 1
472
for P in primes_of_bounded_norm(B+1):
473
P = P.sage_ideal()
474
if V.dimension() == 1:
475
return V
476
if not P.divides(N):
477
T = H.hecke_matrix(P).transpose()
478
V = (T - self.ap(P)).kernel_on(V)
479
raise RuntimeError, "unable to isolate 1-dimensional space"
480
481
@cached_method
482
def dual_eigenvector(self, B=None):
483
# 1. compute dual eigenspace
484
E = self.dual_eigenspace(B)
485
assert E.dimension() == 1
486
# 2. compute normalized integer eigenvector in dual
487
return E.basis_matrix()._clear_denom()[0][0]
488
489
def aplist(self, B, dual_bound=None, algorithm='dual'):
490
"""
491
Return list of traces of Frobenius for all primes P of norm
492
less than bound. Use the function
493
psage.number_fields.sqrt5.primes_of_bounded_norm(B)
494
to get the corresponding primes.
495
496
INPUT:
497
- `B` -- a nonnegative integer
498
- ``dual_bound`` -- default None; passed to dual_eigenvector function
499
- ``algorithm`` -- 'dual' (default) or 'direct'
500
501
OUTPUT:
502
- a list of Sage integers
503
504
EXAMPLES::
505
506
We compute the aplists up to B=50::
507
508
sage: from psage.modform.hilbert.sqrt5.hmf import HilbertModularForms, F
509
sage: H = HilbertModularForms(F.primes_above(71)[1]).elliptic_curve_factors()[0]
510
sage: v = H.aplist(50); v
511
[-1, 0, -2, 0, 0, 2, -4, 6, -6, 8, 2, 6, 12, -4]
512
513
This agrees with what we get using an elliptic curve of this
514
conductor::
515
516
sage: a = F.0; E = EllipticCurve(F, [a,a+1,a,a,0])
517
sage: from psage.ellcurve.lseries.aplist_sqrt5 import aplist
518
sage: w = aplist(E, 50)
519
sage: v == w
520
True
521
522
We compare the output from the two algorithms up to norm 75::
523
524
sage: H.aplist(75, algorithm='direct') == H.aplist(75, algorithm='dual')
525
True
526
"""
527
primes = [P.sage_ideal() for P in primes_of_bounded_norm(B)]
528
if algorithm == 'direct':
529
return [self.ap(P) for P in primes]
530
elif algorithm == 'dual':
531
v = self.dual_eigenvector(dual_bound)
532
i = v.nonzero_positions()[0]
533
c = v[i]
534
I = self._S.ambient()._icosians_mod_p1
535
N = self.conductor()
536
aplist = []
537
for P in primes:
538
if P.divides(N):
539
ap = self.ap(P)
540
else:
541
ap = (I.hecke_operator_on_basis_element(P,i).dot_product(v))/c
542
aplist.append(ap)
543
return aplist
544
else:
545
raise ValueError, "unknown algorithm '%s'"%algorithm
546
547
548