Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
r"""
2
Generator functions for the fourier expansion of paramodular modular forms.
3
Therefore apply Gritsenko's lift to a Jacobi form of index N.
4
5
AUTHORS:
6
7
- Martin Raum (2010 - 04 - 09) Initial version.
8
"""
9
10
#===============================================================================
11
#
12
# Copyright (C) 2010 Martin Raum
13
#
14
# This program is free software; you can redistribute it and/or
15
# modify it under the terms of the GNU General Public License
16
# as published by the Free Software Foundation; either version 3
17
# of the License, or (at your option) any later version.
18
#
19
# This program is distributed in the hope that it will be useful,
20
# but WITHOUT ANY WARRANTY; without even the implied warranty of
21
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22
# General Public License for more details.
23
#
24
# You should have received a copy of the GNU General Public License
25
# along with this program; if not, see <http://www.gnu.org/licenses/>.
26
#
27
#===============================================================================
28
29
from psage.modform.jacobiforms.jacobiformd1nn_fegenerators import JacobiFormD1NNFactory
30
from psage.modform.jacobiforms.jacobiformd1nn_types import JacobiFormsD1NN,\
31
JacobiFormD1NN_Gamma
32
from psage.modform.paramodularforms.paramodularformd2_fourierexpansion import ParamodularFormD2Filter_discriminant
33
from psage.modform.paramodularforms.paramodularformd2_fourierexpansion import ParamodularFormD2FourierExpansionRing
34
from psage.modform.paramodularforms.paramodularformd2_fourierexpansion_cython import apply_GL_to_form
35
from psage.modform.paramodularforms.siegelmodularformg2_types import SiegelModularFormsG2,\
36
SiegelModularFormG2_Classical_Gamma
37
from sage.matrix.constructor import matrix
38
from sage.misc.cachefunc import cached_function
39
from sage.misc.cachefunc import cached_method
40
from sage.rings.arith import bernoulli, sigma
41
from sage.rings.arith import random_prime
42
from sage.rings.all import GF
43
from sage.rings.integer import Integer
44
from sage.rings.integer_ring import ZZ
45
from sage.rings.rational_field import QQ
46
from sage.structure.all import Sequence
47
from sage.structure.sage_object import SageObject
48
from psage.modform.paramodularforms.siegelmodularformg2_misc_cython import divisor_dict
49
50
#===============================================================================
51
# _minimal_paramodular_precision
52
#===============================================================================
53
54
def _minimal_paramodular_precision(level, weight) :
55
## This is the bound, that Poor and Yuen provide in Paramodular
56
## cusp forms, Corollary 5.7
57
58
if Integer(level).is_prime() :
59
return ParamodularFormD2Filter_discriminant( 2 * weight**2 / Integer(225)
60
* (1 + level**2)**2 / Integer(1 + level)**2,
61
2 )
62
else :
63
raise NotImplementedError( "Minimal bound can only be provided for prime level" )
64
65
def symmetrise_siegel_modular_form(f, level, weight) :
66
"""
67
ALGORITHM:
68
We use the Hecke operators \delta_p and [1,level,1,1/level], where the last one
69
can be chosen, since we assume that f is invariant with respect to the full
70
Siegel modular group.
71
"""
72
if not Integer(level).is_prime() :
73
raise NotImplementedError( "Symmetrisation is only implemented for prime levels" )
74
fr = ParamodularFormD2FourierExpansionRing(f.parent().coefficient_domain(), level)
75
76
precision = ParamodularFormD2Filter_discriminant(f.precision().discriminant() // level**2, level)
77
78
coeffs = dict()
79
for (k,l) in precision :
80
kp = apply_GL_to_form(precision._p1list()[l], k)
81
coeffs[(k,l)] = f[kp]
82
83
g1 = fr( {fr.characters().one_element() : coeffs} )
84
g1._set_precision(precision)
85
g1._cleanup_coefficients()
86
87
coeffs = dict()
88
for (k,l) in precision :
89
kp = apply_GL_to_form(precision._p1list()[l], k)
90
if kp[1] % level == 0 and kp[2] % level**2 == 0 :
91
coeffs[(k,l)] = f[(kp[0], kp[1] // level, kp[2] // level**2)]
92
93
g2 = fr( {fr.characters().one_element() : coeffs} )
94
g2._set_precision(precision)
95
g2._cleanup_coefficients()
96
97
return g1 + level**(weight-1) * g2
98
99
#===============================================================================
100
# symmetrised_siegel_modular_forms
101
#===============================================================================
102
103
def symmetrised_siegel_modular_forms(level, weight, precision) :
104
min_precision = _minimal_paramodular_precision(level, weight)
105
if precision is None :
106
precision = min_precision
107
else :
108
precision = max(precision, min_precision)
109
110
sr = SiegelModularFormsG2( QQ, SiegelModularFormG2_Classical_Gamma(),
111
precision._enveloping_discriminant_bound() * level**2 )
112
return map( lambda b: symmetrise_siegel_modular_form(b.fourier_expansion(), level, weight),
113
sr.graded_submodule(weight).basis() )
114
115
#===============================================================================
116
# gritsenko_lift_fourier_expansion
117
#===============================================================================
118
119
_gritsenko_lift_factory_cache = dict()
120
121
def gritsenko_lift_fourier_expansion(f, precision, is_integral = False) :
122
"""
123
Given initial data return the Fourier expansion of a Gritsenko lift
124
for given level and weight.
125
126
INPUT:
127
128
- `f` -- A Jacobi form.
129
130
OUTPUT:
131
132
A dictionary of coefficients.
133
134
TODO:
135
136
Make this return lazy power series.
137
"""
138
global _gritsenko_lift_factory_cache
139
N = f.parent().type().index()
140
141
try :
142
fact = _gritsenko_lift_factory_cache[(precision, N)]
143
except KeyError :
144
fact = ParamodularFormD2Factory(precision, N)
145
_gritsenko_lift_factory_cache[(precision, N)] = fact
146
147
148
fe = ParamodularFormD2FourierExpansionRing(ZZ, N)(
149
fact.gritsenko_lift(f.fourier_expansion(), f.parent().type().weight(), is_integral) )
150
fe._set_precision(precision)
151
return fe
152
153
#===============================================================================
154
# gritsenko_lift_subspace
155
#===============================================================================
156
157
@cached_function
158
def gritsenko_lift_subspace(N, weight, precision) :
159
"""
160
Return a list of data, which can be used by the Fourier expansion
161
generator, for the space of Gritsenko lifts.
162
163
INPUT:
164
- `N` -- the level of the paramodular group
165
- ``weight`` -- the weight of the space, that we consider
166
- ``precision`` -- A precision class
167
168
NOTE:
169
The lifts returned have to have integral Fourier coefficients.
170
"""
171
jf = JacobiFormsD1NN( QQ, JacobiFormD1NN_Gamma(N, weight),
172
(4 * N**2 + precision._enveloping_discriminant_bound() - 1)//(4 * N) + 1)
173
174
return Sequence( [ gritsenko_lift_fourier_expansion( g if i != 0 else (bernoulli(weight) / (2 * weight)).denominator() * g,
175
precision, True )
176
for (i,g) in enumerate(jf.gens()) ],
177
universe = ParamodularFormD2FourierExpansionRing(ZZ, N), immutable = True,
178
check = False )
179
180
#===============================================================================
181
# gritsenko_products
182
#===============================================================================
183
184
def gritsenko_products(N, weight, dimension, precision = None) :
185
"""
186
INPUT:
187
- `N` -- the level of the paramodular group.
188
- ``weight`` -- the weight of the space, that we consider.
189
- ``precision`` -- the precision to be used for the underlying
190
Fourier expansions.
191
"""
192
min_precision = _minimal_paramodular_precision(N, weight)
193
if precision is None :
194
precision = min_precision
195
else :
196
precision = max(precision, min_precision)
197
198
# - Lifts betrachten
199
# - Zerlegung des Gewichts benutzen, dort Lifts nach und nach
200
# multiplizieren, um jeweils den Rang der unterliegenden Matrix
201
# zu bestimmen. Dabei mod p mit zufaelligem p arbeiten
202
203
fourier_expansions = list(gritsenko_lift_subspace(N, weight, precision))
204
products = [ [(weight, i)] for i in xrange(len(fourier_expansions)) ]
205
206
fourier_indices = list(precision)
207
fourier_matrix = matrix(ZZ, [ [e[k] for k in fourier_indices]
208
for e in fourier_expansions] )
209
210
for k in xrange(min((weight // 2) - ((weight // 2) % 2), weight - 2), 1, -2) :
211
space_k = list(enumerate(gritsenko_lift_subspace(N, k, precision)))
212
space_wk = list(enumerate(gritsenko_lift_subspace(N, weight - k, precision)))
213
214
if len(space_wk) == 0 :
215
continue
216
217
for (i,f) in space_k :
218
219
for (j,g) in space_wk :
220
if fourier_matrix.nrows() == dimension :
221
return products, fourier_expansions
222
223
cur_fe = f * g
224
# We are using lazy rank checks
225
for p in [random_prime(10**10) for _ in range(2)] :
226
fourier_matrix_cur = matrix(GF(p), fourier_matrix.nrows() + 1,
227
fourier_matrix.ncols())
228
fourier_matrix_cur.set_block(0, 0, fourier_matrix)
229
fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0,
230
matrix(GF(p), 1, [cur_fe[fi] for fi in fourier_indices]) )
231
232
if fourier_matrix_cur.rank() == fourier_matrix_cur.nrows() :
233
break
234
else :
235
continue
236
237
products.append( [(k, i), (weight - k, j)] )
238
fourier_expansions.append(cur_fe)
239
240
fourier_matrix_cur = matrix( ZZ, fourier_matrix.nrows() + 1,
241
fourier_matrix.ncols() )
242
fourier_matrix_cur.set_block(0, 0, fourier_matrix)
243
fourier_matrix_cur.set_block( fourier_matrix.nrows(), 0,
244
matrix(ZZ, 1, [cur_fe[fi] for fi in fourier_indices]) )
245
fourier_matrix = fourier_matrix_cur
246
247
return products, fourier_expansions
248
249
#===============================================================================
250
# ParamodularFormD2Factory
251
#===============================================================================
252
253
_paramodular_form_d2_factory_cache = dict()
254
255
def ParamodularFormD2Factory(precision, level) :
256
global _paramodular_form_d2_factory_cache
257
258
if not isinstance(precision, ParamodularFormD2Filter_discriminant) :
259
precision = ParamodularFormD2Filter_discriminant(precision, level)
260
261
try :
262
return _paramodular_form_d2_factory_cache[precision]
263
except KeyError :
264
tmp = ParamodularFormD2Factory_class(precision)
265
_paramodular_form_d2_factory_cache[precision] = tmp
266
267
return tmp
268
269
class ParamodularFormD2Factory_class ( SageObject ) :
270
271
def __init__(self, precision) :
272
self.__precision = precision
273
self.__jacobi_factory = None
274
275
def precision(self) :
276
return self.__precision
277
278
def level(self) :
279
return self.__precision.level()
280
281
def _discriminant_bound(self) :
282
return self.__precision._contained_discriminant_bound()
283
284
@cached_method
285
def _divisor_dict(self) :
286
return divisor_dict(self._discriminant_bound())
287
288
def gritsenko_lift(self, f, k, is_integral = False) :
289
"""
290
INPUT:
291
- `f` -- the Fourier expansion of a Jacobi form as a dictionary
292
"""
293
N = self.level()
294
frN = 4 * N
295
p1list = self.precision()._p1list()
296
297
divisor_dict = self._divisor_dict()
298
299
##TODO: Precalculate disc_coeffs or at least use a recursive definition
300
disc_coeffs = dict()
301
coeffs = dict()
302
f_index = lambda d,b : ((d + b**2)//frN, b)
303
304
for (((a,b,c),l), eps, disc) in self.__precision._iter_positive_forms_with_content_and_discriminant() :
305
(_,bp,_) = apply_GL_to_form(p1list[l], (a,b,c))
306
try :
307
coeffs[((a,b,c),l)] = disc_coeffs[(disc, bp, eps)]
308
except KeyError :
309
disc_coeffs[(disc, bp, eps)] = \
310
sum( t**(k-1) * f[ f_index(disc//t**2, (bp // t) % (2 * N)) ]
311
for t in divisor_dict[eps] )
312
313
if disc_coeffs[(disc, bp, eps)] != 0 :
314
coeffs[((a,b,c),l)] = disc_coeffs[(disc, bp, eps)]
315
316
for ((a,b,c), l) in self.__precision.iter_indefinite_forms() :
317
if l == 0 :
318
coeffs[((a,b,c),l)] = ( sigma(c, k-1) * f[(0,0)]
319
if c != 0 else
320
( Integer(-bernoulli(k) / Integer(2 * k) * f[(0,0)])
321
if is_integral else
322
-bernoulli(k) / Integer(2 * k) * f[(0,0)] ) )
323
else :
324
coeffs[((a,b,c),l)] = ( sigma(c//self.level(), k-1) * f[(0,0)]
325
if c != 0 else
326
( Integer(-bernoulli(k) / Integer(2 * k) * f[(0,0)])
327
if is_integral else
328
-bernoulli(k) / Integer(2 * k) * f[(0,0)] ) )
329
330
return coeffs
331
332