Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/modform/vm_basis.py
4054 views
1
"""
2
The Victor Miller Basis
3
4
This module contains functions for quick calculation of a basis of
5
`q`-expansions for the space of modular forms of level 1 and any weight. The
6
basis returned is the Victor Miller basis, which is the unique basis of
7
elliptic modular forms `f_1, \dots, f_d` for which `a_i(f_j) = \delta_{ij}`
8
for `1 \le i, j \le d` (where `d` is the dimension of the space).
9
10
This basis is calculated using a standard set of generators for the ring of
11
modular forms, using the fast multiplication algorithms for polynomials and
12
power series provided by the FLINT library. (This is far quicker than using
13
modular symbols).
14
15
TESTS::
16
17
sage: ModularSymbols(1, 36, 1).cuspidal_submodule().q_expansion_basis(30) == victor_miller_basis(36, 30, cusp_only=True)
18
True
19
"""
20
21
#*****************************************************************************
22
# Copyright (C) 2006 William Stein <[email protected]>
23
#
24
# Distributed under the terms of the GNU General Public License (GPL)
25
# as published by the Free Software Foundation; either version 2 of
26
# the License, or (at your option) any later version.
27
# http://www.gnu.org/licenses/
28
#*****************************************************************************
29
30
import math
31
32
from sage.rings.all import QQ, ZZ, Integer, \
33
PolynomialRing, PowerSeriesRing, O as bigO
34
from sage.structure.all import Sequence
35
from sage.libs.flint.fmpz_poly import Fmpz_poly
36
from sage.misc.all import verbose
37
38
from eis_series_cython import eisenstein_series_poly
39
40
def victor_miller_basis(k, prec=10, cusp_only=False, var='q'):
41
r"""
42
Compute and return the Victor Miller basis for modular forms of
43
weight `k` and level 1 to precision `O(q^{prec})`. If
44
``cusp_only`` is True, return only a basis for the cuspidal
45
subspace.
46
47
INPUT:
48
49
- ``k`` -- an integer
50
51
- ``prec`` -- (default: 10) a positive integer
52
53
- ``cusp_only`` -- bool (default: False)
54
55
- ``var`` -- string (default: 'q')
56
57
OUTPUT:
58
59
A sequence whose entries are power series in ``ZZ[[var]]``.
60
61
EXAMPLES::
62
63
sage: victor_miller_basis(1, 6)
64
[]
65
sage: victor_miller_basis(0, 6)
66
[
67
1 + O(q^6)
68
]
69
sage: victor_miller_basis(2, 6)
70
[]
71
sage: victor_miller_basis(4, 6)
72
[
73
1 + 240*q + 2160*q^2 + 6720*q^3 + 17520*q^4 + 30240*q^5 + O(q^6)
74
]
75
76
sage: victor_miller_basis(6, 6, var='w')
77
[
78
1 - 504*w - 16632*w^2 - 122976*w^3 - 532728*w^4 - 1575504*w^5 + O(w^6)
79
]
80
81
sage: victor_miller_basis(6, 6)
82
[
83
1 - 504*q - 16632*q^2 - 122976*q^3 - 532728*q^4 - 1575504*q^5 + O(q^6)
84
]
85
sage: victor_miller_basis(12, 6)
86
[
87
1 + 196560*q^2 + 16773120*q^3 + 398034000*q^4 + 4629381120*q^5 + O(q^6),
88
q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6)
89
]
90
91
sage: victor_miller_basis(12, 6, cusp_only=True)
92
[
93
q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 + O(q^6)
94
]
95
sage: victor_miller_basis(24, 6, cusp_only=True)
96
[
97
q + 195660*q^3 + 12080128*q^4 + 44656110*q^5 + O(q^6),
98
q^2 - 48*q^3 + 1080*q^4 - 15040*q^5 + O(q^6)
99
]
100
sage: victor_miller_basis(24, 6)
101
[
102
1 + 52416000*q^3 + 39007332000*q^4 + 6609020221440*q^5 + O(q^6),
103
q + 195660*q^3 + 12080128*q^4 + 44656110*q^5 + O(q^6),
104
q^2 - 48*q^3 + 1080*q^4 - 15040*q^5 + O(q^6)
105
]
106
sage: victor_miller_basis(32, 6)
107
[
108
1 + 2611200*q^3 + 19524758400*q^4 + 19715347537920*q^5 + O(q^6),
109
q + 50220*q^3 + 87866368*q^4 + 18647219790*q^5 + O(q^6),
110
q^2 + 432*q^3 + 39960*q^4 - 1418560*q^5 + O(q^6)
111
]
112
113
sage: victor_miller_basis(40,200)[1:] == victor_miller_basis(40,200,cusp_only=True)
114
True
115
sage: victor_miller_basis(200,40)[1:] == victor_miller_basis(200,40,cusp_only=True)
116
True
117
118
AUTHORS:
119
120
- William Stein, Craig Citro: original code
121
122
- Martin Raum (2009-08-02): use FLINT for polynomial arithmetic (instead of NTL)
123
"""
124
k = Integer(k)
125
if k%2 == 1 or k==2:
126
return Sequence([])
127
elif k < 0:
128
raise ValueError, "k must be non-negative"
129
elif k == 0:
130
return Sequence([PowerSeriesRing(ZZ,var)(1).add_bigoh(prec)], cr=True)
131
e = k.mod(12)
132
if e == 2: e += 12
133
n = (k-e) // 12
134
135
if n == 0 and cusp_only:
136
return Sequence([])
137
138
# If prec is less than or equal to the dimension of the space of
139
# cusp forms, which is just n, then we know the answer, and we
140
# simply return it.
141
if prec <= n:
142
q = PowerSeriesRing(ZZ,var).gen(0)
143
err = bigO(q**prec)
144
ls = [0] * (n+1)
145
if not cusp_only:
146
ls[0] = 1 + err
147
for i in range(1,prec):
148
ls[i] = q**i + err
149
for i in range(prec,n+1):
150
ls[i] = err
151
return Sequence(ls, cr=True)
152
153
F6 = eisenstein_series_poly(6,prec)
154
155
if e == 0:
156
A = Fmpz_poly(1)
157
elif e == 4:
158
A = eisenstein_series_poly(4,prec)
159
elif e == 6:
160
A = F6
161
elif e == 8:
162
A = eisenstein_series_poly(8,prec)
163
elif e == 10:
164
A = eisenstein_series_poly(10,prec)
165
else: # e == 14
166
A = eisenstein_series_poly(14,prec)
167
168
if A[0] == -1 :
169
A = -A
170
171
if n == 0:
172
return Sequence([PowerSeriesRing(ZZ,var)(A.list()).add_bigoh(prec)],cr=True)
173
174
F6_squared = F6**2
175
F6_squared._unsafe_mutate_truncate(prec)
176
D = _delta_poly(prec)
177
Fprod = F6_squared
178
Dprod = D
179
180
if cusp_only:
181
ls = [Fmpz_poly(0)] + [A] * n
182
else:
183
ls = [A] * (n+1)
184
185
for i in xrange(1,n+1):
186
ls[n-i] *= Fprod
187
ls[i] *= Dprod
188
ls[n-i]._unsafe_mutate_truncate(prec)
189
ls[i]._unsafe_mutate_truncate(prec)
190
191
Fprod *= F6_squared
192
Dprod *= D
193
Fprod._unsafe_mutate_truncate(prec)
194
Dprod._unsafe_mutate_truncate(prec)
195
196
197
P = PowerSeriesRing(ZZ,var)
198
if cusp_only :
199
for i in xrange(1,n+1) :
200
for j in xrange(1, i) :
201
ls[j] = ls[j] - ls[j][i]*ls[i]
202
203
return Sequence(map(lambda l: P(l.list()).add_bigoh(prec), ls[1:]),cr=True)
204
else :
205
for i in xrange(1,n+1) :
206
for j in xrange(i) :
207
ls[j] = ls[j] - ls[j][i]*ls[i]
208
209
return Sequence(map(lambda l: P(l.list()).add_bigoh(prec), ls), cr=True)
210
211
def _delta_poly(prec=10):
212
"""
213
Return the q-expansion of Delta as a FLINT polynomial. Used internally by
214
the :func:`~delta_qexp` function. See the docstring of :func:`~delta_qexp`
215
for more information.
216
217
INPUT:
218
219
- ``prec`` -- integer; the absolute precision of the output
220
221
OUTPUT:
222
223
the q-expansion of Delta to precision ``prec``, as a FLINT
224
:class:`~sage.libs.flint.fmpz_poly.Fmpz_poly` object.
225
226
EXAMPLES::
227
228
sage: from sage.modular.modform.vm_basis import _delta_poly
229
sage: _delta_poly(7)
230
7 0 1 -24 252 -1472 4830 -6048
231
"""
232
if prec <= 0:
233
raise ValueError, "prec must be positive"
234
v = [0] * prec
235
236
# Let F = \sum_{n >= 0} (-1)^n (2n+1) q^(floor(n(n+1)/2)).
237
# Then delta is F^8.
238
239
# First compute F^2 directly by naive polynomial multiplication,
240
# since F is very sparse.
241
242
stop = int((-1+math.sqrt(1+8*prec))/2.0)
243
# make list of index/value pairs for the sparse poly
244
values = [(n*(n+1)//2, ((-2*n-1) if (n & 1) else (2*n+1))) \
245
for n in xrange(stop+1)]
246
247
for (i1, v1) in values:
248
for (i2, v2) in values:
249
try:
250
v[i1 + i2] += v1 * v2
251
except IndexError:
252
break
253
254
f = Fmpz_poly(v)
255
t = verbose('made series')
256
f = f*f
257
f._unsafe_mutate_truncate(prec)
258
t = verbose('squared (2 of 3)', t)
259
f = f*f
260
f._unsafe_mutate_truncate(prec - 1)
261
t = verbose('squared (3 of 3)', t)
262
f = f.left_shift(1)
263
t = verbose('shifted', t)
264
265
return f
266
267
def _delta_poly_modulo(N, prec=10):
268
"""
269
Return the q-expansion of `\Delta` modulo `N`. Used internally by
270
the :func:`~delta_qexp` function. See the docstring of :func:`~delta_qexp`
271
for more information.
272
273
INPUT:
274
275
- `N` -- positive integer modulo which we want to compute `\Delta`
276
277
- ``prec`` -- integer; the absolute precision of the output
278
279
OUTPUT:
280
281
the polynomial of degree ``prec``-1 which is the truncation
282
of `\Delta` modulo `N`, as an element of the polynomial
283
ring in `q` over the integers modulo `N`.
284
285
EXAMPLES::
286
287
sage: from sage.modular.modform.vm_basis import _delta_poly_modulo
288
sage: _delta_poly_modulo(5, 7)
289
2*q^6 + 3*q^4 + 2*q^3 + q^2 + q
290
sage: _delta_poly_modulo(10, 12)
291
2*q^11 + 7*q^9 + 6*q^7 + 2*q^6 + 8*q^4 + 2*q^3 + 6*q^2 + q
292
"""
293
if prec <= 0:
294
raise ValueError( "prec must be positive" )
295
v = [0] * prec
296
297
# Let F = \sum_{n >= 0} (-1)^n (2n+1) q^(floor(n(n+1)/2)).
298
# Then delta is F^8.
299
300
stop = int((-1+math.sqrt(8*prec))/2.0)
301
302
for n in xrange(stop+1):
303
v[n*(n+1)//2] = ((N-1)*(2*n+1) if (n & 1) else (2*n+1))
304
305
from sage.rings.all import Integers
306
307
P = PolynomialRing(Integers(N), 'q')
308
f = P(v)
309
t = verbose('made series')
310
# fast way of computing f*f truncated at prec
311
f = f._mul_trunc(f, prec)
312
t = verbose('squared (1 of 3)', t)
313
f = f._mul_trunc(f, prec)
314
t = verbose('squared (2 of 3)', t)
315
f = f._mul_trunc(f, prec - 1)
316
t = verbose('squared (3 of 3)', t)
317
f = f.shift(1)
318
t = verbose('shifted', t)
319
320
return f
321
322
323
def delta_qexp(prec=10, var='q', K=ZZ) :
324
"""
325
Return the `q`-expansion of the weight 12 cusp form `\Delta` as a power
326
series with coefficients in the ring K (`= \ZZ` by default).
327
328
INPUT:
329
330
- ``prec`` -- integer (default 10), the absolute precision of the output
331
(must be positive)
332
333
- ``var`` -- string (default: 'q'), variable name
334
335
- ``K`` -- ring (default: `\ZZ`), base ring of answer
336
337
OUTPUT:
338
339
a power series over K in the variable ``var``
340
341
ALGORITHM:
342
343
Compute the theta series
344
345
.. math::
346
347
\sum_{n \ge 0} (-1)^n (2n+1) q^{n(n+1)/2},
348
349
a very simple explicit modular form whose 8th power is `\Delta`. Then
350
compute the 8th power. All computations are done over `\ZZ` or `\ZZ`
351
modulo `N` depending on the characteristic of the given coefficient
352
ring `K`, and coerced into `K` afterwards.
353
354
EXAMPLES::
355
356
sage: delta_qexp(7)
357
q - 24*q^2 + 252*q^3 - 1472*q^4 + 4830*q^5 - 6048*q^6 + O(q^7)
358
sage: delta_qexp(7,'z')
359
z - 24*z^2 + 252*z^3 - 1472*z^4 + 4830*z^5 - 6048*z^6 + O(z^7)
360
sage: delta_qexp(-3)
361
Traceback (most recent call last):
362
...
363
ValueError: prec must be positive
364
sage: delta_qexp(20, K = GF(3))
365
q + q^4 + 2*q^7 + 2*q^13 + q^16 + 2*q^19 + O(q^20)
366
sage: delta_qexp(20, K = GF(3^5, 'a'))
367
q + q^4 + 2*q^7 + 2*q^13 + q^16 + 2*q^19 + O(q^20)
368
sage: delta_qexp(10, K = IntegerModRing(60))
369
q + 36*q^2 + 12*q^3 + 28*q^4 + 30*q^5 + 12*q^6 + 56*q^7 + 57*q^9 + O(q^10)
370
371
TESTS:
372
373
Test algorithm with modular arithmetic (see also #11804)::
374
375
sage: delta_qexp(10^4).change_ring(GF(13)) == delta_qexp(10^4, K=GF(13))
376
True
377
sage: delta_qexp(1000).change_ring(IntegerModRing(5^100)) == delta_qexp(1000, K=IntegerModRing(5^100))
378
True
379
380
AUTHORS:
381
382
- William Stein: original code
383
384
- David Harvey (2007-05): sped up first squaring step
385
386
- Martin Raum (2009-08-02): use FLINT for polynomial arithmetic (instead of NTL)
387
"""
388
R = PowerSeriesRing(K, var)
389
if K in (ZZ, QQ):
390
return R(_delta_poly(prec).list(), prec, check=False)
391
ch = K.characteristic()
392
if ch > 0 and prec > 150:
393
return R(_delta_poly_modulo(ch, prec), prec, check=False)
394
else:
395
# compute over ZZ and coerce
396
return R(_delta_poly(prec).list(), prec, check=True)
397
398