Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/modform/numerical.py
4045 views
1
"""
2
Numerical computation of newforms
3
"""
4
5
#########################################################################
6
# Copyright (C) 2004--2006 William Stein <[email protected]>
7
#
8
# Distributed under the terms of the GNU General Public License (GPL)
9
#
10
# http://www.gnu.org/licenses/
11
#########################################################################
12
13
from sage.structure.sage_object import SageObject
14
from sage.structure.sequence import Sequence
15
from sage.modular.modsym.all import ModularSymbols
16
from sage.modular.arithgroup.all import Gamma0
17
from sage.modules.all import vector
18
from sage.misc.misc import verbose
19
from sage.rings.all import CDF, Integer, QQ, next_prime, prime_range
20
from sage.misc.prandom import randint
21
from sage.matrix.constructor import matrix
22
23
# This variable controls importing the SciPy library sparingly
24
scipy=None
25
26
class NumericalEigenforms(SageObject):
27
"""
28
numerical_eigenforms(group, weight=2, eps=1e-20, delta=1e-2, tp=[2,3,5])
29
30
INPUT:
31
32
- ``group`` - a congruence subgroup of a Dirichlet character of
33
order 1 or 2
34
35
- ``weight`` - an integer >= 2
36
37
- ``eps`` - a small float; abs( ) < eps is what "equal to zero" is
38
interpreted as for floating point numbers.
39
40
- ``delta`` - a small-ish float; eigenvalues are considered distinct
41
if their difference has absolute value at least delta
42
43
- ``tp`` - use the Hecke operators T_p for p in tp when searching
44
for a random Hecke operator with distinct Hecke eigenvalues.
45
46
OUTPUT:
47
48
A numerical eigenforms object, with the following useful methods:
49
50
- :meth:`ap` - return all eigenvalues of $T_p$
51
52
- :meth:`eigenvalues` - list of eigenvalues corresponding
53
to the given list of primes, e.g.,::
54
55
[[eigenvalues of T_2],
56
[eigenvalues of T_3],
57
[eigenvalues of T_5], ...]
58
59
- :meth:`systems_of_eigenvalues` - a list of the systems of
60
eigenvalues of eigenforms such that the chosen random linear
61
combination of Hecke operators has multiplicity 1 eigenvalues.
62
63
EXAMPLES::
64
65
sage: n = numerical_eigenforms(23)
66
sage: n == loads(dumps(n))
67
True
68
sage: n.ap(2)
69
[3.0, 0.61803398875, -1.61803398875]
70
sage: n.systems_of_eigenvalues(7)
71
[
72
[-1.61803398875, 2.2360679775, -3.2360679775],
73
[0.61803398875, -2.2360679775, 1.2360679775],
74
[3.0, 4.0, 6.0]
75
]
76
sage: n.systems_of_abs(7)
77
[
78
[0.6180339887..., 2.236067977..., 1.236067977...],
79
[1.6180339887..., 2.236067977..., 3.236067977...],
80
[3.0, 4.0, 6.0]
81
]
82
sage: n.eigenvalues([2,3,5])
83
[[3.0, 0.61803398875, -1.61803398875],
84
[4.0, -2.2360679775, 2.2360679775],
85
[6.0, 1.2360679775, -3.2360679775]]
86
"""
87
def __init__(self, group, weight=2, eps=1e-20,
88
delta=1e-2, tp=[2,3,5]):
89
"""
90
Create a new space of numerical eigenforms.
91
92
EXAMPLES::
93
94
sage: numerical_eigenforms(61) # indirect doctest
95
Numerical Hecke eigenvalues for Congruence Subgroup Gamma0(61) of weight 2
96
"""
97
if isinstance(group, (int, long, Integer)):
98
group = Gamma0(Integer(group))
99
self._group = group
100
self._weight = Integer(weight)
101
self._tp = tp
102
if self._weight < 2:
103
raise ValueError, "weight must be at least 2"
104
self._eps = eps
105
self._delta = delta
106
107
def __cmp__(self, other):
108
"""
109
Compare two spaces of numerical eigenforms. Currently
110
returns 0 if they come from the same space of modular
111
symbols, and -1 otherwise.
112
113
EXAMPLES::
114
115
sage: n = numerical_eigenforms(23)
116
sage: n.__cmp__(loads(dumps(n)))
117
0
118
"""
119
if not isinstance( other, NumericalEigenforms ):
120
raise ValueError, "%s is not a space of numerical eigenforms"%other
121
if self.modular_symbols() == other.modular_symbols():
122
return 0
123
else:
124
return -1
125
126
def level(self):
127
"""
128
Return the level of this set of modular eigenforms.
129
130
EXAMPLES::
131
132
sage: n = numerical_eigenforms(61) ; n.level()
133
61
134
"""
135
return self._group.level()
136
137
def weight(self):
138
"""
139
Return the weight of this set of modular eigenforms.
140
141
EXAMPLES::
142
143
sage: n = numerical_eigenforms(61) ; n.weight()
144
2
145
"""
146
return self._weight
147
148
def _repr_(self):
149
"""
150
Print string representation of self.
151
152
EXAMPLES::
153
154
sage: n = numerical_eigenforms(61) ; n
155
Numerical Hecke eigenvalues for Congruence Subgroup Gamma0(61) of weight 2
156
157
sage: n._repr_()
158
'Numerical Hecke eigenvalues for Congruence Subgroup Gamma0(61) of weight 2'
159
"""
160
return "Numerical Hecke eigenvalues for %s of weight %s"%(
161
self._group, self._weight)
162
163
def modular_symbols(self):
164
"""
165
Return the space of modular symbols used for computing this
166
set of modular eigenforms.
167
168
EXAMPLES::
169
170
sage: n = numerical_eigenforms(61) ; n.modular_symbols()
171
Modular Symbols space of dimension 5 for Gamma_0(61) of weight 2 with sign 1 over Rational Field
172
"""
173
try:
174
return self.__modular_symbols
175
except AttributeError:
176
M = ModularSymbols(self._group,
177
self._weight, sign=1)
178
if M.base_ring() != QQ:
179
raise ValueError, "modular forms space must be defined over QQ"
180
self.__modular_symbols = M
181
return M
182
183
def _eigenvectors(self):
184
r"""
185
Find numerical approximations to simultaneous eigenvectors in
186
self.modular_symbols() for all T_p in self._tp.
187
188
EXAMPLES::
189
190
sage: n = numerical_eigenforms(61)
191
sage: n._eigenvectors() # random order
192
[ 1.0 0.289473640239 0.176788851952 0.336707726757 2.4182243084e-16]
193
[ 0 -0.0702748344418 0.491416161212 0.155925712173 0.707106781187]
194
[ 0 0.413171180356 0.141163094698 0.0923242547901 0.707106781187]
195
[ 0 0.826342360711 0.282326189397 0.18464850958 6.79812569682e-16]
196
[ 0 0.2402380858 0.792225196393 0.905370774276 4.70805946682e-16]
197
198
TESTS:
199
200
This tests if this routine selects only eigenvectors with
201
multiplicity one. Two of the eigenvalues are
202
(roughly) -92.21 and -90.30 so if we set ``eps = 2.0``
203
then they should compare as equal, causing both eigenvectors
204
to be absent from the matrix returned. The remaining eigenvalues
205
(ostensibly unique) are visible in the test, which should be
206
indepedent of which eigenvectors are returned, but it does presume
207
an ordering of these eigenvectors for the test to succeed.
208
This exercises a correction in Trac 8018. ::
209
210
sage: n = numerical_eigenforms(61, eps=2.0)
211
sage: evectors = n._eigenvectors()
212
sage: evalues = diagonal_matrix(CDF, [-283.0, 108.522012456, 142.0])
213
sage: diff = n._hecke_matrix*evectors - evectors*evalues
214
sage: sum([abs(diff[i,j]) for i in range(5) for j in range(3)]) < 1.0e-9
215
True
216
"""
217
try:
218
return self.__eigenvectors
219
except AttributeError:
220
pass
221
verbose('Finding eigenvector basis')
222
M = self.modular_symbols()
223
N = self.level()
224
225
tp = self._tp
226
p = tp[0]
227
t = M.T(p).matrix()
228
for p in tp[1:]:
229
t += randint(-50,50)*M.T(p).matrix()
230
231
self._hecke_matrix = t
232
233
global scipy
234
if scipy is None:
235
import scipy
236
import scipy.linalg
237
evals,eig = scipy.linalg.eig(self._hecke_matrix.numpy(), right=True, left=False)
238
B = matrix(eig)
239
v = [CDF(evals[i]) for i in range(len(evals))]
240
241
# Determine the eigenvectors with eigenvalues of multiplicity
242
# one, with equality controlled by the value of eps
243
# Keep just these eigenvectors
244
eps = self._eps
245
w = []
246
for i in range(len(v)):
247
e = v[i]
248
uniq = True
249
for j in range(len(v)):
250
if uniq and i != j and abs(e-v[j]) < eps:
251
uniq = False
252
if uniq:
253
w.append(i)
254
self.__eigenvectors = B.matrix_from_columns(w)
255
return self.__eigenvectors
256
257
def _easy_vector(self):
258
"""
259
Return a very sparse vector v such that v times the eigenvector matrix
260
has all entries nonzero.
261
262
ALGORITHM:
263
264
1. Choose row with the most nonzero entries. (put 1 there)
265
266
2. Consider submatrix of columns corresponding to zero entries
267
in row chosen in 1.
268
269
3. Find row of submatrix with most nonzero entries, and add
270
appropriate multiple. Repeat.
271
272
EXAMPLES::
273
274
sage: n = numerical_eigenforms(37)
275
sage: n._easy_vector() # slightly random output
276
(1.0, 1.0, 0)
277
sage: n = numerical_eigenforms(43)
278
sage: n._easy_vector() # slightly random output
279
(1.0, 0, 1.0, 0)
280
sage: n = numerical_eigenforms(125)
281
sage: n._easy_vector() # slightly random output
282
(0, 0, 0, 1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
283
"""
284
try:
285
return self.__easy_vector
286
except AttributeError:
287
pass
288
E = self._eigenvectors()
289
delta = self._delta
290
x = (CDF**E.nrows()).zero_vector()
291
if E.nrows() == 0:
292
return x
293
294
295
296
def best_row(M):
297
"""
298
Find the best row among rows of M, i.e. the row
299
with the most entries supported outside [-delta, delta].
300
301
EXAMPLES::
302
303
sage: numerical_eigenforms(61)._easy_vector() # indirect doctest
304
(1.0, 1.0, 0.0, 0.0, 0.0)
305
"""
306
R = M.rows()
307
v = [len(support(r, delta)) for r in R]
308
m = max(v)
309
i = v.index(m)
310
return i, R[i]
311
312
i, e = best_row(E)
313
314
x[i] = 1
315
316
while True:
317
s = set(support(e, delta))
318
zp = [i for i in range(e.degree()) if not i in s]
319
if len(zp) == 0:
320
break
321
C = E.matrix_from_columns(zp)
322
# best row
323
i, f = best_row(C)
324
x[i] += 1 # simplistic
325
e = x*E
326
327
self.__easy_vector = x
328
return x
329
330
def _eigendata(self):
331
"""
332
Return all eigendata for self._easy_vector().
333
334
EXAMPLES::
335
336
sage: numerical_eigenforms(61)._eigendata() # random order
337
((1.0, 0.668205013164, 0.219198805797, 0.49263343893, 0.707106781187), (1.0, 1.49654668896, 4.5620686498, 2.02990686579, 1.41421356237), [0, 1], (1.0, 1.0))
338
"""
339
try:
340
return self.__eigendata
341
except AttributeError:
342
pass
343
x = self._easy_vector()
344
345
B = self._eigenvectors()
346
def phi(y):
347
"""
348
Take coefficients and a basis, and return that
349
linear combination of basis vectors.
350
351
EXAMPLES::
352
353
sage: n = numerical_eigenforms(61) # indirect doctest
354
sage: n._eigendata() # random order
355
((1.0, 0.668205013164, 0.219198805797, 0.49263343893, 0.707106781187), (1.0, 1.49654668896, 4.5620686498, 2.02990686579, 1.41421356237), [0, 1], (1.0, 1.0))
356
"""
357
return y.element() * B
358
359
phi_x = phi(x)
360
V = phi_x.parent()
361
phi_x_inv = V([a**(-1) for a in phi_x])
362
eps = self._eps
363
nzp = support(x, eps)
364
x_nzp = vector(CDF, x.list_from_positions(nzp))
365
self.__eigendata = (phi_x, phi_x_inv, nzp, x_nzp)
366
return self.__eigendata
367
368
def ap(self, p):
369
"""
370
Return a list of the eigenvalues of the Hecke operator `T_p`
371
on all the computed eigenforms. The eigenvalues match up
372
between one prime and the next.
373
374
INPUT:
375
376
- ``p`` - integer, a prime number
377
378
OUTPUT:
379
380
- ``list`` - a list of double precision complex numbers
381
382
EXAMPLES::
383
384
sage: n = numerical_eigenforms(11,4)
385
sage: n.ap(2) # random order
386
[9.0, 9.0, 2.73205080757, -0.732050807569]
387
sage: n.ap(3) # random order
388
[28.0, 28.0, -7.92820323028, 5.92820323028]
389
sage: m = n.modular_symbols()
390
sage: x = polygen(QQ, 'x')
391
sage: m.T(2).charpoly(x).factor()
392
(x - 9)^2 * (x^2 - 2*x - 2)
393
sage: m.T(3).charpoly(x).factor()
394
(x - 28)^2 * (x^2 + 2*x - 47)
395
"""
396
p = Integer(p)
397
if not p.is_prime():
398
raise ValueError, "p must be a prime"
399
try:
400
return self._ap[p]
401
except AttributeError:
402
self._ap = {}
403
except KeyError:
404
pass
405
a = Sequence(self.eigenvalues([p])[0], immutable=True)
406
self._ap[p] = a
407
return a
408
409
def eigenvalues(self, primes):
410
"""
411
Return the eigenvalues of the Hecke operators corresponding
412
to the primes in the input list of primes. The eigenvalues
413
match up between one prime and the next.
414
415
INPUT:
416
417
- ``primes`` - a list of primes
418
419
OUTPUT:
420
421
list of lists of eigenvalues.
422
423
EXAMPLES::
424
425
sage: n = numerical_eigenforms(1,12)
426
sage: n.eigenvalues([3,5,13])
427
[[177148.0, 252.0], [48828126.0, 4830.0], [1.79216039404e+12, -577737.999...]]
428
"""
429
primes = [Integer(p) for p in primes]
430
for p in primes:
431
if not p.is_prime():
432
raise ValueError, 'each element of primes must be prime.'
433
phi_x, phi_x_inv, nzp, x_nzp = self._eigendata()
434
B = self._eigenvectors()
435
def phi(y):
436
"""
437
Take coefficients and a basis, and return that
438
linear combination of basis vectors.
439
440
EXAMPLES::
441
442
sage: n = numerical_eigenforms(1,12) # indirect doctest
443
sage: n.eigenvalues([3,5,13])
444
[[177148.0, 252.0], [48828126.0, 4830.0], [1.79216039404e+12, -577737.999...]]
445
"""
446
return y.element() * B
447
448
ans = []
449
m = self.modular_symbols().ambient_module()
450
for p in primes:
451
t = m._compute_hecke_matrix_prime(p, nzp)
452
w = phi(x_nzp*t)
453
ans.append([w[i]*phi_x_inv[i] for i in range(w.degree())])
454
return ans
455
456
def systems_of_eigenvalues(self, bound):
457
"""
458
Return all systems of eigenvalues for self for primes
459
up to bound.
460
461
EXAMPLES::
462
463
sage: numerical_eigenforms(61).systems_of_eigenvalues(10)
464
[
465
[-1.48119430409..., 0.806063433525..., 3.15632517466..., 0.675130870567...],
466
[-1.0..., -2.0..., -3.0..., 1.0...],
467
[0.311107817466..., 2.90321192591..., -2.52542756084..., -3.21431974338...],
468
[2.17008648663..., -1.70927535944..., -1.63089761382..., -0.460811127189...],
469
[3.0, 4.0, 6.0, 8.0]
470
]
471
"""
472
P = prime_range(bound)
473
e = self.eigenvalues(P)
474
v = Sequence([], cr=True)
475
if len(e) == 0:
476
return v
477
for i in range(len(e[0])):
478
v.append([e[j][i] for j in range(len(e))])
479
v.sort()
480
v.set_immutable()
481
return v
482
483
def systems_of_abs(self, bound):
484
"""
485
Return the absolute values of all systems of eigenvalues for
486
self for primes up to bound.
487
488
EXAMPLES::
489
490
sage: numerical_eigenforms(61).systems_of_abs(10)
491
[
492
[0.311107817466, 2.90321192591, 2.52542756084, 3.21431974338],
493
[1.0, 2.0, 3.0, 1.0],
494
[1.48119430409, 0.806063433525, 3.15632517466, 0.675130870567],
495
[2.17008648663, 1.70927535944, 1.63089761382, 0.460811127189],
496
[3.0, 4.0, 6.0, 8.0]
497
]
498
"""
499
P = prime_range(bound)
500
e = self.eigenvalues(P)
501
v = Sequence([], cr=True)
502
if len(e) == 0:
503
return v
504
for i in range(len(e[0])):
505
v.append([abs(e[j][i]) for j in range(len(e))])
506
v.sort()
507
v.set_immutable()
508
return v
509
510
def support(v, eps):
511
"""
512
Given a vector `v` and a threshold eps, return all
513
indices where `|v|` is larger than eps.
514
515
EXAMPLES::
516
517
sage: sage.modular.modform.numerical.support( numerical_eigenforms(61)._easy_vector(), 1.0 )
518
[]
519
520
sage: sage.modular.modform.numerical.support( numerical_eigenforms(61)._easy_vector(), 0.5 )
521
[0, 1]
522
523
"""
524
return [i for i in range(v.degree()) if abs(v[i]) > eps]
525
526
527
528
529