Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
r"""
2
Construction of Fourier expansions of Siegel modular forms of arbitrary genus.
3
4
AUTHORS:
5
6
- Martin Raum (2009 - 05 - 11) Initial version
7
"""
8
9
#===============================================================================
10
#
11
# Copyright (C) 2010 Martin Raum
12
#
13
# This program is free software; you can redistribute it and/or
14
# modify it under the terms of the GNU General Public License
15
# as published by the Free Software Foundation; either version 3
16
# of the License, or (at your option) any later version.
17
#
18
# This program is distributed in the hope that it will be useful,
19
# but WITHOUT ANY WARRANTY; without even the implied warranty of
20
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21
# General Public License for more details.
22
#
23
# You should have received a copy of the GNU General Public License
24
# along with this program; if not, see <http://www.gnu.org/licenses/>.
25
#
26
#===============================================================================
27
28
from sage.matrix.constructor import diagonal_matrix
29
from sage.matrix.constructor import matrix
30
from sage.misc.cachefunc import cached_method
31
from sage.misc.functional import isqrt
32
from sage.misc.misc import prod
33
from sage.modules.all import vector
34
from sage.modules.free_module import FreeModule
35
from sage.quadratic_forms.quadratic_form import QuadraticForm
36
from sage.rings.arith import gcd, fundamental_discriminant
37
from sage.rings.all import ZZ, Integer, GF
38
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
39
from sage.structure.sage_object import SageObject
40
from psage.modform.paramodularforms.siegelmodularformgn_fourierexpansion import SiegelModularFormGnFourierExpansionRing,\
41
SiegelModularFormGnFilter_diagonal_lll
42
import itertools
43
from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_element import EquivariantMonoidPowerSeries_abstract
44
45
#===============================================================================
46
# DukeImamogulLift
47
#===============================================================================
48
49
def DukeImamogluLift(f, f_weight, precision) :
50
r"""
51
INPUT:
52
53
- `f` -- Fourier expansion of a Jacobi form of index `1` and weight ``k + 1 - precision.genus / 2``.
54
55
NOTE:
56
57
We follow Kohnen's paper "Lifting modular forms of half-integral weight to Siegel
58
modular forms of even genus", Section 5, Theorem 1.
59
"""
60
if not isinstance(precision, SiegelModularFormGnFilter_diagonal_lll) :
61
raise TypeError, "precision must be an instance of SiegelModularFormGnFilter_diagonal_lll"
62
63
## TODO: Neatly check the input type.
64
half_integral_weight = not isinstance(f, EquivariantMonoidPowerSeries_abstract)
65
if half_integral_weight :
66
f_k = Integer(f_weight - Integer(1)/2)
67
else :
68
f_k = f_weight
69
70
fe_ring = SiegelModularFormGnFourierExpansionRing(
71
f.parent().base_ring() if half_integral_weight else f.parent().coefficient_domain(),
72
precision.genus() )
73
74
form = fe_ring(DukeImamogluLiftFactory().duke_imamoglu_lift( f if half_integral_weight else f.coefficients(),
75
f_k, precision, half_integral_weight))
76
form._set_precision(precision)
77
78
return form
79
80
81
#===============================================================================
82
# DukeImamogluLiftFactory
83
#===============================================================================
84
85
_duke_imamoglu_lift_factory_cache = None
86
87
def DukeImamogluLiftFactory() :
88
global _duke_imamoglu_lift_factory_cache
89
90
if _duke_imamoglu_lift_factory_cache is None :
91
_duke_imamoglu_lift_factory_cache = DukeImamogluLiftFactory_class()
92
93
return _duke_imamoglu_lift_factory_cache
94
95
#===============================================================================
96
# DukeImamogluLiftFactory_class
97
#===============================================================================
98
99
class DukeImamogluLiftFactory_class (SageObject) :
100
101
@cached_method
102
def _kohnen_phi(self, a, t) :
103
## We use a modified power of a, namely for each prime factor p of a
104
## we use p**floor(v_p(a)/2). This is compensated for in the routine
105
## of \rho
106
a_modif = 1
107
for (p,e) in a.factor() :
108
a_modif = a_modif * p**(e // 2)
109
110
res = 0
111
for dsq in filter(lambda d: d.is_square(), a.divisors()) :
112
d = isqrt(dsq)
113
114
for g_diag in itertools.ifilter( lambda diag: prod(diag) == d,
115
itertools.product(*[d.divisors() for _ in xrange(t.nrows())]) ) :
116
for subents in itertools.product(*[xrange(r) for (j,r) in enumerate(g_diag) for _ in xrange(j)]) :
117
columns = [subents[(j * (j - 1)) // 2:(j * (j + 1)) // 2] for j in xrange(t.nrows())]
118
g = diagonal_matrix(list(g_diag))
119
for j in xrange(t.nrows()) :
120
for i in xrange(j) :
121
g[i,j] = columns[j][i]
122
123
ginv = g.inverse()
124
tg = ginv.transpose() * t * ginv
125
try :
126
tg= matrix(ZZ, tg)
127
except :
128
continue
129
if any(tg[i,i] % 2 == 1 for i in xrange(tg.nrows())) :
130
continue
131
132
tg.set_immutable()
133
134
res = res + self._kohnen_rho(tg, a // dsq)
135
136
return a_modif * res
137
138
@cached_method
139
def _kohnen_rho(self, t, a) :
140
dt = (-1)**(t.nrows() // 2) * t.det()
141
d = fundamental_discriminant(dt)
142
eps = isqrt(dt // d)
143
144
res = 1
145
for (p,e) in gcd(a, eps).factor() :
146
pol = self._kohnen_rho_polynomial(t, p).coefficients()
147
if e < len(pol) :
148
res = res * pol[e]
149
150
return res
151
152
@cached_method
153
def _kohnen_rho_polynomial(self, t, p) :
154
P = PolynomialRing(ZZ, 'x')
155
x = P.gen(0)
156
157
#tc = self._minimal_isotropic_subspace_complement_mod_p(t, p)
158
159
if t[0,0] != 0 :
160
return (1 - x**2)
161
162
for i in xrange(t.nrows()) :
163
if t[i,i] != 0 :
164
break
165
166
if i % 2 == 0 :
167
## Since we have semi definite indices Kohnen's lambda simplifies
168
lambda_p = 1 if t == 0 else -1
169
## For we multiply x in the second factor by p**(1/2) to make all coefficients rational.
170
return (1 - x**2) * (1 + lambda_p * p**((i + 1) // 2) * x) * \
171
prod(1 - p**(2*j - 1) * x**2 for j in xrange(1, i // 2))
172
else :
173
return (1 - x**2) * \
174
prod(1 - p**(2*j - 1) * x**2 for j in xrange(1, i // 2))
175
176
#===========================================================================
177
# ## TODO: Speadup by implementation in Cython and using enumeration mod p**n and fast
178
# ## evaluation via shifts
179
# def _minimal_isotropic_subspace_complement_mod_p(self, t, p, cur_form_subspace = None, cur_isotropic_space = None) :
180
# if not t.base_ring() is GF(p) :
181
# t = matrix(GF(p), t)
182
# #q = QuadraticForm(t)
183
#
184
# if cur_form_subspace is None :
185
# cur_form_subspace = FreeModule(GF(p), t.nrows())
186
# if cur_isotropic_space is None :
187
# cur_isotropic_space = cur_form_subspace.ambient_module().submodule([])
188
#
189
# ## We find a zero length vector. In the orthogonal complement we can then proceed.
190
#
191
# for v in cur_form_subspace :
192
# if v not in cur_isotropic_space \
193
# and (v * t * v).is_zero() :
194
# cur_form_subspace = cur_form_subspace.intersection(matrix(GF(p), v * t).right_kernel())
195
# cur_isotropic_space = cur_isotropic_space + cur_isotropic_space.ambient_module().submodule([v])
196
#
197
# return self._minimal_isotropic_subspace_complement_mod_p(t, p, cur_form_subspace, cur_isotropic_space)
198
# # return complement
199
# complement_coords = set(xrange(cur_form_subspace.ambient_module().rank())) \
200
# - set(cur_isotropic_space.echelonized_basis_matrix().pivots())
201
# complement_basis = [ vector(GF(p), [0]*i + [1] + [0]*(cur_form_subspace.ambient_module().rank() - i - 1))
202
# for i in complement_coords ]
203
#
204
# return matrix(GF(p), [[u * t * v for u in complement_basis] for v in complement_basis])
205
#===========================================================================
206
207
def duke_imamoglu_lift(self, f, f_k, precision, half_integral_weight = False) :
208
"""
209
INPUT:
210
211
- ``half_integral_weight`` -- If ``False`` we assume that `f` is the Fourier expansion of a
212
Jacobi form. Otherwise we assume it is the Fourier expansion
213
of a half integral weight elliptic modular form.
214
"""
215
216
if half_integral_weight :
217
coeff_index = lambda d : d
218
else :
219
coeff_index = lambda d : ((d + (-d % 4)) // 4, (-d) % 4)
220
221
coeffs = dict()
222
223
for t in precision.iter_positive_forms() :
224
dt = (-1)**(precision.genus() // 2) * t.det()
225
d = fundamental_discriminant(dt)
226
eps = Integer(isqrt(dt / d))
227
228
coeffs[t] = 0
229
for a in eps.divisors() :
230
d_a = abs(d * (eps // a)**2)
231
232
coeffs[t] = coeffs[t] + a**(f_k - 1) * self._kohnen_phi(a, t) \
233
* f[coeff_index(d_a)]
234
235
return coeffs
236
237