Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
r"""
2
Generator functions for the fourier expansion of Siegel modular forms.
3
4
AUTHORS:
5
6
- Nils-Peter Skorupa
7
Factory function SiegelModularForm().
8
Code parts concerning the Maass lift are partly based on code
9
written by David Gruenewald in August 2006 which in turn is
10
based on the PARI/GP code by Nils-Peter Skoruppa from 2003.
11
- Martin Raum (2009 - 07 - 28) Port to new framework.
12
- Martin Raum (2010 - 03 - 16) Implement the vector valued case.
13
14
REFERENCES:
15
16
- [Sko] Nils-Peter Skoruppa, ...
17
- [I-H] Tomoyoshi Ibukiyama and Shuichi Hayashida, ...
18
19
"""
20
21
#===============================================================================
22
#
23
# Copyright (C) 2009 Martin Raum
24
#
25
# This program is free software; you can redistribute it and/or
26
# modify it under the terms of the GNU General Public License
27
# as published by the Free Software Foundation; either version 3
28
# of the License, or (at your option) any later version.
29
#
30
# This program is distributed in the hope that it will be useful,
31
# but WITHOUT ANY WARRANTY; without even the implied warranty of
32
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
33
# General Public License for more details.
34
#
35
# You should have received a copy of the GNU General Public License
36
# along with this program; if not, see <http://www.gnu.org/licenses/>.
37
#
38
#===============================================================================
39
40
from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_lazyelement import EquivariantMonoidPowerSeries_lazy
41
from psage.modform.paramodularforms.siegelmodularformg2_fourierexpansion import SiegelModularFormG2FourierExpansionRing
42
from sage.combinat.partition import number_of_partitions
43
from sage.misc.functional import isqrt
44
from sage.misc.latex import latex
45
from sage.modular.modform.constructor import ModularForms
46
from sage.modular.modform.element import ModularFormElement
47
from sage.rings.arith import bernoulli, sigma
48
from sage.rings.integer import Integer
49
from sage.rings.integer_ring import ZZ
50
from sage.rings.power_series_ring import PowerSeriesRing
51
from sage.rings.rational_field import QQ
52
from sage.structure.sage_object import SageObject
53
from psage.modform.paramodularforms.siegelmodularformg2_fourierexpansion import SiegelModularFormG2Filter_discriminant
54
from psage.modform.paramodularforms import siegelmodularformg2_misc_cython
55
56
#===============================================================================
57
# SiegelModularFormG2MaassLift
58
#===============================================================================
59
60
def SiegelModularFormG2MaassLift(f, g, precision, is_integral = True, weight = None) :
61
factory = SiegelModularFormG2Factory(precision)
62
if is_integral :
63
expansion_ring = SiegelModularFormG2FourierExpansionRing(ZZ)
64
else :
65
expansion_ring = SiegelModularFormG2FourierExpansionRing(QQ)
66
67
if f == 0 :
68
fexp = lambda p : 0
69
if hasattr(f, "qexp") :
70
fexp = lambda p : f.qexp(p)
71
else :
72
fexp = f
73
74
if g == 0 :
75
gexp = lambda p : 0
76
if hasattr(g, "qexp") :
77
gexp = lambda p : g.qexp(p)
78
else :
79
gexp = g
80
81
if weight is None :
82
try :
83
weight = f.weight()
84
except AttributeError :
85
weight = g.weight() -2
86
87
coefficients_factory = DelayedFactory_SMFG2_masslift( factory, fexp, gexp, weight )
88
89
return EquivariantMonoidPowerSeries_lazy(expansion_ring, precision, coefficients_factory.getcoeff)
90
91
#===============================================================================
92
# DelayedFactory_SMFG2_masslift
93
#===============================================================================
94
95
class DelayedFactory_SMFG2_masslift :
96
def __init__(self, factory, f, g, weight) :
97
self.__factory = factory
98
self.__f = f
99
self.__g = g
100
self.__weight = weight
101
102
self.__series = None
103
104
def getcoeff(self, key, **kwds) :
105
(_, k) = key
106
# for speed we ignore the character
107
if self.__series is None :
108
self.__series = \
109
self.__factory.maass_form( self.__f, self.__g, self.__weight,
110
is_integral = True)
111
112
return self.__series[k]
113
114
#===============================================================================
115
# SiegelModularFormG2Factory
116
#===============================================================================
117
118
_siegel_modular_form_g2_factory_cache = dict()
119
120
def SiegelModularFormG2Factory(precision) :
121
if not isinstance(precision, SiegelModularFormG2Filter_discriminant) :
122
precision = SiegelModularFormG2Filter_discriminant(precision)
123
124
global _siegel_modular_form_g2_factory_cache
125
try :
126
return _siegel_modular_form_g2_factory_cache[precision]
127
except KeyError :
128
factory = SiegelModularFormG2Factory_class(precision)
129
_siegel_modular_form_g2_factory_cache[precision] = factory
130
131
return factory
132
133
#===============================================================================
134
# SiegelModularFormG2Factory_class
135
#===============================================================================
136
137
class SiegelModularFormG2Factory_class( SageObject ) :
138
r"""
139
A class producing dictionarys saving fourier expansions of Siegel
140
modular forms of genus `2`.
141
"""
142
143
def __init__(self, precision) :
144
r"""
145
INPUT:
146
147
- ``prec`` -- a SiegelModularFormPrec, which will be the precision of
148
every object returned by an instance of this class.
149
"""
150
self.__precision = precision
151
152
## Conversion of power series is not expensive but powers of interger
153
## series are much cheaper then powers of rational series
154
self._power_series_ring_ZZ = PowerSeriesRing(ZZ, 'q')
155
self._power_series_ring = PowerSeriesRing(QQ, 'q')
156
157
def power_series_ring(self) :
158
return self._power_series_ring
159
160
def integral_power_series_ring(self) :
161
return self._power_series_ring_ZZ
162
163
def _get_maass_form_qexp_prec(self) :
164
r"""
165
Return the precision of eta, needed to calculate the Maass lift in
166
self.as_maass_spezial_form
167
"""
168
return ( self.__precision.discriminant() + 1)//4 + 1
169
170
def _divisor_dict(self) :
171
r"""
172
Return a dictionary of assigning to each `k < n` a list of its divisors.
173
174
INPUT:
175
176
- `n` -- a positive integer
177
"""
178
try :
179
return self.__divisor_dict
180
except AttributeError :
181
self.__divisor_dict = siegelmodularformg2_misc_cython.divisor_dict(self.__precision.discriminant())
182
183
return self.__divisor_dict
184
185
def _eta_power(self) :
186
try :
187
return self.__eta_power
188
except AttributeError :
189
qexp_prec = self._get_maass_form_qexp_prec()
190
191
self.__eta_power = self.integral_power_series_ring() \
192
([number_of_partitions(n) for n in xrange(qexp_prec)], qexp_prec)**6
193
194
return self.__eta_power
195
196
def precision(self) :
197
return self.__precision
198
199
def _negative_fundamental_discriminants(self) :
200
r"""
201
Return a list of all negative fundamental discriminants.
202
"""
203
try :
204
return self.__negative_fundamental_discriminants
205
except AttributeError :
206
self.__negative_fundamental_discriminants = \
207
siegelmodularformg2_misc_cython.negative_fundamental_discriminants(
208
self.__precision.discriminant() )
209
210
return self.__negative_fundamental_discriminants
211
212
def maass_form( self, f, g, k = None, is_integral = False) :
213
r"""
214
Return the Siegel modular form `I(f,g)` (Notation as in [Sko]).
215
216
INPUT:
217
- `f` -- modular form of level `1`
218
- `g` -- cusp form of level `1` and weight = ``weight of f + 2``
219
- ``is_integral`` -- ``True`` if the result is garanteed to have integer
220
coefficients
221
"""
222
223
## we introduce an abbreviations
224
if is_integral :
225
PS = self.integral_power_series_ring()
226
else :
227
PS = self.power_series_ring()
228
229
fismodular = isinstance(f, ModularFormElement)
230
gismodular = isinstance(g, ModularFormElement)
231
232
## We only check the arguments if f and g are ModularFormElements.
233
## Otherwise we trust in the user
234
if fismodular and gismodular :
235
assert( f.weight() + 2 == g.weight() | (f==0) | (g==0)), \
236
"incorrect weights!"
237
assert( g.q_expansion(1) == 0), "second argument is not a cusp form"
238
239
qexp_prec = self._get_maass_form_qexp_prec()
240
if qexp_prec is None : # there are no forms below prec
241
return dict()
242
243
if fismodular :
244
k = f.weight()
245
if f == f.parent()(0) :
246
f = PS(0, qexp_prec)
247
else :
248
f = PS(f.qexp(qexp_prec), qexp_prec)
249
elif f == 0 :
250
f = PS(0, qexp_prec)
251
else :
252
f = PS(f(qexp_prec), qexp_prec)
253
254
if gismodular :
255
k = g.weight() - 2
256
if g == g.parent()(0) :
257
g = PS(0, qexp_prec)
258
else :
259
g = PS(g.qexp(qexp_prec), qexp_prec)
260
elif g == 0 :
261
g = PS(0, qexp_prec)
262
else :
263
g = PS(g(qexp_prec), qexp_prec)
264
265
if k is None :
266
raise ValueError, "if neither f nor g are not ModularFormElements " + \
267
"you must pass k"
268
269
fderiv = f.derivative().shift(1)
270
f *= Integer(k/2)
271
gfderiv = g - fderiv
272
273
## Form A and B - the Jacobi forms used in [Sko]'s I map.
274
## This is not necessary if we multiply Ifg0 and Ifg1 by etapow
275
# (A0,A1,B0,B1) = (a0*etapow, a1*etapow, b0*etapow, b1*etapow)
276
277
## Calculate the image of the pair of modular forms (f,g) under
278
## [Sko]'s isomorphism I : M_{k} \oplus S_{k+2} -> J_{k,1}.
279
280
# Multiplication of big polynomials may take > 60 GB, so wie have
281
# to do it in small parts; This is only implemented for integral
282
# coefficients.
283
284
"""
285
Create the Jacobi form I(f,g) as in [Sko].
286
287
It suffices to construct for all Jacobi forms phi only the part
288
sum_{r=0,1;n} c_phi(r^2-4n) q^n zeta^r.
289
When, in this code part, we speak of Jacobi form we only mean this part.
290
291
We need to compute Ifg = \sum_{r=0,1; n} c(r^2-4n) q^n zeta^r up to
292
4n-r^2 <= Dtop, i.e. n < precision
293
"""
294
295
## Create the Jacobi forms A=a*etapow and B=b*etapow in stages.
296
## Recall a = sum_{s != r mod 2} s^2*(-1)^r*q^((s^2+r^2-1)/4)*zeta^r
297
## b = sum_{s != r mod 2} (-1)^r*q^((s^2+r^2-1)/4)*zeta^r
298
## r, s run over ZZ but with opposite parities.
299
## For r=0, we need s odd, (s^2-1)/4 < precision, with s=2t+1 hence t^2+t < precision.
300
## For r=1, we need s even, s^2/4 < precision, with s=2t hence t^2 < precision.
301
302
## we use a slightly overestimated ab_prec
303
304
ab_prec = isqrt(qexp_prec + 1)
305
a1dict = dict(); a0dict = dict()
306
b1dict = dict(); b0dict = dict()
307
308
for t in xrange(1, ab_prec + 1) :
309
tmp = t**2
310
a1dict[tmp] = -8*tmp
311
b1dict[tmp] = -2
312
313
tmp += t
314
a0dict[tmp] = 8*tmp + 2
315
b0dict[tmp] = 2
316
b1dict[0] = -1
317
a0dict[0] = 2; b0dict[0] = 2
318
319
a1 = PS(a1dict); b1 = PS(b1dict)
320
a0 = PS(a0dict); b0 = PS(b0dict)
321
322
## Finally: I(f,g) is given by the formula below:
323
## We multiply by etapow explecitely and save two multiplications
324
# Ifg0 = k/2*f*A0 - fderiv*B0 + g*B0 + O(q^precision)
325
# Ifg1 = k/2*f*A1 - fderiv*B1 + g*B1 + O(q^precision)
326
Ifg0 = (self._eta_power() * (f*a0 + gfderiv*b0)).list()
327
Ifg1 = (self._eta_power() * (f*a1 + gfderiv*b1)).list()
328
329
if len(Ifg0) < qexp_prec :
330
Ifg0 += [0]*(qexp_prec - len(Ifg0))
331
if len(Ifg1) < qexp_prec :
332
Ifg1 += [0]*(qexp_prec - len(Ifg1))
333
334
## For applying the Maass' lifting to genus 2 modular forms.
335
## we put the coefficients of Ifg into a dictionary Chi
336
## so that we can access the coefficient corresponding to
337
## discriminant D by going Chi[D].
338
339
Cphi = dict([(0,0)])
340
for i in xrange(qexp_prec) :
341
Cphi[-4*i] = Ifg0[i]
342
Cphi[1-4*i] = Ifg1[i]
343
344
del Ifg0[:], Ifg1[:]
345
346
"""
347
Create the Maas lift F := VI(f,g) as in [Sko].
348
"""
349
350
## The constant term is given by -Cphi[0]*B_{2k}/(4*k)
351
## (note in [Sko] this coeff has typos).
352
## For nonconstant terms,
353
## The Siegel coefficient of q^n * zeta^r * qdash^m is given
354
## by the formula \sum_{ a | gcd(n,r,m) } Cphi[D/a^2] where
355
## D = r^2-4*n*m is the discriminant.
356
## Hence in either case the coefficient
357
## is fully deterimined by the pair (D,gcd(n,r,m)).
358
## Put (D,t) -> \sum_{ a | t } Cphi[D/a^2]
359
## in a dictionary (hash table) maassc.
360
361
maass_coeffs = dict()
362
divisor_dict = self._divisor_dict()
363
364
## First calculate maass coefficients corresponding to strictly positive definite matrices:
365
for disc in self._negative_fundamental_discriminants() :
366
for s in xrange(1, isqrt((-self.__precision.discriminant()) // disc) + 1) :
367
## add (disc*s^2,t) as a hash key, for each t that divides s
368
for t in divisor_dict[s] :
369
maass_coeffs[(disc * s**2,t)] = \
370
sum( a**(k-1) * Cphi[disc * s**2 / a**2]
371
for a in divisor_dict[t] )
372
373
## Compute the coefficients of the Siegel form $F$:
374
siegel_coeffs = dict()
375
for (n,r,m), g in self.__precision.iter_positive_forms_with_content() :
376
siegel_coeffs[(n,r,m)] = maass_coeffs[(r**2 - 4*m*n, g)]
377
378
## Secondly, deal with the singular part.
379
## Include the coeff corresponding to (0,0,0):
380
## maass_coeffs = {(0,0): -bernoulli(k)/(2*k)*Cphi[0]}
381
siegel_coeffs[(0,0,0)] = -bernoulli(k)/(2*k)*Cphi[0]
382
if is_integral :
383
siegel_coeffs[(0,0,0)] = Integer(siegel_coeffs[(0,0,0)])
384
385
## Calculate the other discriminant-zero maass coefficients.
386
## Since sigma is quite cheap it is faster to estimate the bound and
387
## save the time for repeated calculation
388
for i in xrange(1, self.__precision._indefinite_content_bound()) :
389
## maass_coeffs[(0,i)] = sigma(i, k-1) * Cphi[0]
390
siegel_coeffs[(0,0,i)] = sigma(i, k-1) * Cphi[0]
391
392
return siegel_coeffs
393
394
def maass_eisensteinseries(self, k) :
395
if not isinstance(k, (int, Integer)) :
396
raise TypeError, "k must be an integer"
397
if k % 2 != 0 or k < 4 :
398
raise ValueError, "k must be even and greater than 4"
399
400
return self.maass_form(ModularForms(1,k).eisenstein_subspace().gen(0), 0)
401
402
## TODO: Rework these functions
403
def from_gram_matrix( self, gram, name = None) :
404
raise NotImplementedError, "matrix argument not yet implemented"
405
406
def _SiegelModularForm_borcherds_lift( self, f, prec = 100, name = None):
407
408
raise NotImplementedError, "matrix argument not yet implemented"
409
410
def _SiegelModularForm_yoshida_lift( self, f, g, prec = 100, name = None):
411
412
raise NotImplementedError, "matrix argument not yet implemented"
413
414
def _SiegelModularForm_from_weil_representation( self, gram, prec = 100, name = None):
415
416
raise NotImplementedError, "matrix argument not yet implemented"
417
418
def _SiegelModularForm_singular_weight( self, gram, prec = 100, name = None):
419
420
raise NotImplementedError, "matrix argument not yet implemented"
421
422
def _repr_(self) :
423
return "Factory class for Siegel modular forms of genus 2 with precision %s" % \
424
repr(self.__precision.discriminant())
425
426
def _latex_(self) :
427
return "Factory class for Siegel modular forms of genus $2$ with precision %s" % \
428
latex(self.__precision.discriminant())
429
430