Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
r"""
2
Classes describing the Fourier expansion of Paramodular modular forms.
3
4
AUTHORS:
5
6
- Martin Raum (2010 - 04 - 09) 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 psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_basicmonoids import TrivialCharacterMonoid, \
29
TrivialRepresentation
30
from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring import EquivariantMonoidPowerSeriesRing
31
from psage.modform.jacobiforms.jacobiformd1nn_types import JacobiFormD1NN_Gamma, JacobiFormsD1NN
32
from operator import xor
33
from psage.modform.paramodularforms.paramodularformd2_fourierexpansion_cython import apply_GL_to_form, reduce_GL#, \
34
from sage.functions.other import ceil, floor
35
from sage.functions.other import sqrt
36
from sage.misc.functional import isqrt
37
from sage.misc.latex import latex
38
from sage.modular.modsym.p1list import P1List
39
from sage.rings.all import Mod
40
from sage.rings.arith import gcd, kronecker_symbol
41
from sage.rings.arith import legendre_symbol
42
from sage.rings.infinity import infinity
43
from sage.rings.integer import Integer
44
from sage.rings.integer_ring import ZZ
45
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
46
from sage.structure.sage_object import SageObject
47
import itertools
48
49
#===============================================================================
50
# ParamodularFormD2Indices_discriminant
51
#===============================================================================
52
53
class ParamodularFormD2Indices_discriminant( SageObject ) :
54
"""
55
Associated with a form of level `N`.
56
Indices are pairs of positive binary quadratic forms `(a, b, Nc)` and an
57
element of the projective line over `\ZZ/ N \ZZ` . The last is modeled by an
58
index of the element with in ``P1List(N)``.
59
60
A pair `(t, v)` of a quadratic form and a left coset representative represents
61
the equivalence class `v^{-1} t v^{-tr} . Every `v` represented by its first column
62
of its inverse which is well defined up to units of `\ZZ / N \ZZ`.
63
"""
64
def __init__(self, level, reduced = True) :
65
if level == 1 :
66
## P1List(1) does not accept 1 as an index which is what we consider the
67
## unit element in GL(2, ZZ)
68
raise NotImplementedError( "Level must not be 1")
69
self.__level = level
70
self.__reduced = reduced
71
72
self.__p1list = P1List(level)
73
74
def ngens(self) :
75
## FIXME: This is not correct if N != 1
76
return 4 if not self.__reduced else 3
77
78
def gen(self, i = 0) :
79
## FIXME: This is not correct if N != 1
80
if i == 0 :
81
return ((1, 0, 0), 0)
82
elif i == 1 :
83
return ((0, 0, 1), 0)
84
elif i == 2 :
85
return ((1, 1, 1), 0)
86
elif not self.__reduced and i == 3 :
87
return ((1, -1, 1), 0)
88
89
raise ValueError, "Generator not defined"
90
91
def gens(self) :
92
return [self.gen(i) for i in xrange(self.ngens())]
93
94
def is_commutative(self) :
95
return True
96
97
def monoid(self) :
98
return ParamodularFormD2Indices_discriminant(self.__level, False)
99
100
def group(self) :
101
return "GL(2,ZZ)_0 (%s)" % (self.__level,)
102
103
def level(self) :
104
return self.__level
105
106
def _p1list(self) :
107
return self.__p1list
108
109
def is_monoid_action(self) :
110
"""
111
True if the representation respects the monoid structure.
112
"""
113
return True
114
115
def filter(self, disc) :
116
return ParamodularFormD2Filter_discriminant(disc, self.__level, self.__reduced)
117
118
def filter_all(self) :
119
return ParamodularFormD2Filter_discriminant(infinity, self.__level, self.__reduced)
120
121
def zero_filter(self) :
122
return ParamodularFormD2Filter_discriminant(0, self.__level, self.__reduced)
123
124
def minimal_composition_filter(self, ls, rs) :
125
if len(ls) == 0 or len(rs) == 0 :
126
return ParamodularFormD2Filter_discriminant(0, self.__reduced)
127
128
if len(ls) == 1 and ls[0][0] == (0,0,0) :
129
return ParamodularFormD2Filter_discriminant(
130
min(4*self.__level*a*c - b**2 for (a,b,c) in rs),
131
self.__reduced)
132
if len(rs) == 1 and rs[0][0] == (0,0,0) :
133
return ParamodularFormD2Filter_discriminant(
134
min(4*self.__level*a*c - b**2 for (a,b,c) in ls),
135
self.__reduced)
136
137
raise ArithmeticError, "Discriminant filter does not " + \
138
"admit minimal composition filters"
139
140
def _reduction_function(self) :
141
return lambda s: reduce_GL(s, self.__p1list)
142
143
def reduce(self, s) :
144
return reduce_GL(s, self.__p1list)
145
146
def decompositions(self, s) :
147
((a, b, c), l) = s
148
149
# r t r^tr = t_1 + t_2
150
# \Rightleftarrow t = r t_1 r^tr + r t_2 r^tr
151
for a1 in xrange(a + 1) :
152
a2 = a - a1
153
for c1 in xrange(c + 1) :
154
c2 = c - c1
155
156
B1 = isqrt(4*a1*c1)
157
B2 = isqrt(4*a2*c2)
158
for b1 in xrange(max(-B1, b - B2), min(B1 + 1, b + B2 + 1)) :
159
h1 = apply_GL_to_form(self.__p1list[l], (a1, b1, c1))
160
h2 = apply_GL_to_form(self.__p1list[l], (a2, b - b1, c2))
161
if h1[2] % self.__level == 0 and h2[2] % self.__level == 0:
162
yield ((h1, 1), (h2,1))
163
164
raise StopIteration
165
166
def zero_element(self) :
167
return ((0,0,0), 1)
168
169
def __contains__(self, x) :
170
return isinstance(x, tuple) and len(x) == 2 and \
171
isinstance(x[0], tuple) and len(x[0]) == 3 and \
172
all(isinstance(e, (int,Integer)) for e in x[0]) and \
173
isinstance(x[1], (int,Integer))
174
175
def __cmp__(self, other) :
176
c = cmp(type(self), type(other))
177
178
if c == 0 :
179
c = cmp(self.__level, other.__level)
180
if c == 0 :
181
c = cmp(self.__reduced, other.__reduced)
182
183
return c
184
185
def __hash__(self) :
186
return xor(hash(self.__level), hash(self.__reduced))
187
188
def _repr_(self) :
189
if self.__reduced :
190
return "Paramodular indices for level %s" % (self.__level,)
191
else :
192
return "Quadratic forms over ZZ with P^1(ZZ/%s ZZ) structure" % (self.__level,)
193
194
def _latex_(self) :
195
if self.__reduced :
196
return "Paramodular indices for level %s" % (latex(self.__level),)
197
else :
198
return "Quadratic forms over $\ZZ$ with $\mathbb{P}^1(\ZZ/%s \ZZ)$ structure" \
199
% (latex(self.__level),)
200
201
#===============================================================================
202
# ParamodularFormD2Filter_discriminant
203
#===============================================================================
204
205
class ParamodularFormD2Filter_discriminant ( SageObject ) :
206
def __init__(self, disc, level, reduced = True) :
207
self.__level = level
208
209
if isinstance(disc, ParamodularFormD2Filter_discriminant) :
210
disc = disc.index()
211
212
if disc is infinity :
213
self.__disc = disc
214
else :
215
oDmod = (-disc + 1) % (4 * level)
216
Dmod = oDmod
217
while Dmod > 0 :
218
if not Mod(Dmod, 4 * level) :
219
Dmod = Dmod - 1
220
else :
221
break
222
223
self.__disc = disc - (oDmod - Dmod)
224
225
self.__reduced = reduced
226
227
self.__p1list = P1List(level)
228
229
def filter_all(self) :
230
return ParamodularFormD2Filter_discriminant(infinity, self.__level, self.__reduced)
231
232
def zero_filter(self) :
233
return ParamodularFormD2Filter_discriminant(0, self.__level, self.__reduced)
234
235
def is_infinite(self) :
236
return self.__disc is infinity
237
238
def is_all(self) :
239
return self.is_infinite()
240
241
def level(self) :
242
return self.__level
243
244
def index(self) :
245
return self.__disc
246
247
def _p1list(self) :
248
return self.__p1list
249
250
def _contained_trace_bound(self) :
251
if self.index() == 3 :
252
return 2
253
else :
254
return 2 * self.index() // 3
255
256
def _contained_discriminant_bound(self) :
257
return self.index()
258
259
def _enveloping_discriminant_bound(self) :
260
return self.index()
261
262
def is_reduced(self) :
263
return self.__reduced
264
265
def __contains__(self, f) :
266
"""
267
Check whether an index has discriminant less than ``self.__index`` and
268
whether its bottom right entry is divisible by ``self.__level``.
269
"""
270
if self.__disc is infinity :
271
return True
272
273
(s, l) = f
274
275
(a, b, c) = apply_GL_to_form(self.__p1list[l], s)
276
if not c % self.__level == 0 :
277
return False
278
279
disc = 4*a*c - b**2
280
if disc == 0 :
281
return gcd([a,b,c]) < self._indefinite_content_bound()
282
else :
283
return disc < self.__disc
284
285
def _indefinite_content_bound(self) :
286
r"""
287
Return the maximal trace for semi definite forms, which are considered
288
to be below this precision.
289
290
NOTE:
291
292
The optimal value would be `\sqrt{D / 3}`, but this leads to very small precisions
293
on converting to trace bounds.
294
"""
295
return 2 * self.index() // 3
296
297
def __iter__(self) :
298
return itertools.chain( self.iter_positive_forms(),
299
self.iter_indefinite_forms() )
300
301
def iter_positive_forms(self) :
302
if self.__disc is infinity :
303
raise ValueError, "infinity is not a true filter index"
304
305
if self.__reduced :
306
for (l, (u,x)) in enumerate(self.__p1list) :
307
if u == 0 :
308
for a in xrange(self.__level, isqrt(self.__disc // 4) + 1, self.__level) :
309
for b in xrange(a+1) :
310
for c in xrange(a, (b**2 + (self.__disc - 1))//(4*a) + 1) :
311
yield ((a,b,c), l)
312
else :
313
for a in xrange(1, isqrt(self.__disc // 3) + 1) :
314
for b in xrange(a+1) :
315
## We need x**2 * a + x * b + c % N == 0
316
h = (-((x**2 + 1) * a + x * b)) % self.__level
317
for c in xrange( a + h,
318
(b**2 + (self.__disc - 1))//(4*a) + 1, self.__level ) :
319
yield ((a,b,c), l)
320
#! if self.__reduced
321
else :
322
maxtrace = floor(self.__disc / Integer(3) + sqrt(self.__disc / Integer(3)))
323
for (l, (u,x)) in enumerate(self.__p1list) :
324
if u == 0 :
325
for a in xrange(self.__level, maxtrace + 1, self.__level) :
326
for c in xrange(1, maxtrace - a + 1) :
327
Bu = isqrt(4*a*c - 1)
328
329
di = 4*a*c - self.__disc
330
if di >= 0 :
331
Bl = isqrt(di) + 1
332
else :
333
Bl = 0
334
335
for b in xrange(-Bu, -Bl + 1) :
336
yield ((a,b,c), l)
337
for b in xrange(Bl, Bu + 1) :
338
yield ((a,b,c), l)
339
else :
340
for a in xrange(1, maxtrace + 1) :
341
for c in xrange(1, maxtrace - a + 1) :
342
Bu = isqrt(4*a*c - 1)
343
344
di = 4*a*c - self.__disc
345
if di >= 0 :
346
Bl = isqrt(di) + 1
347
else :
348
Bl = 0
349
350
h = (-x * a - int(Mod(x, self.__level)**-1) * c - Bu) % self.__level \
351
if x != 0 else \
352
(-Bu) % self.__level
353
for b in xrange(-Bu + self.__level - h, -Bl + 1, self.__level) :
354
yield ((a,b,c), l)
355
h = (-x * a - int(Mod(x, self.__level)**-1) * c + Bl) % self.__level \
356
if x !=0 else \
357
Bl % self.__level
358
for b in xrange(Bl + self.__level - h, Bu + 1, self.__level) :
359
yield ((a,b,c), l)
360
#! else self.__reduced
361
362
raise StopIteration
363
364
def iter_indefinite_forms(self) :
365
if self.__disc is infinity :
366
raise ValueError, "infinity is not a true filter index"
367
368
369
if self.__reduced :
370
for (l, (u,_)) in enumerate(self.__p1list) :
371
if u == 0 :
372
for c in xrange(self._indefinite_content_bound()) :
373
yield ((0,0,c), l)
374
else :
375
for c in xrange(0, self._indefinite_content_bound(), self.__level) :
376
yield ((0,0,c), l)
377
else :
378
raise NotImplementedError
379
380
raise StopIteration
381
382
def _iter_positive_forms_with_content_and_discriminant(self) :
383
if self.__disc is infinity :
384
raise ValueError, "infinity is not a true filter index"
385
386
if self.__reduced :
387
for (l, (u,x)) in enumerate(self.__p1list) :
388
if u == 0 :
389
for a in xrange(self.__level, isqrt(self.__disc // 4) + 1, self.__level) :
390
frpa = 4 * a
391
for b in xrange(a+1) :
392
g = gcd(a // self.__level,b)
393
bsq = b**2
394
395
for c in xrange(a, (b**2 + (self.__disc - 1))//(4*a) + 1) :
396
yield (((a,b,c), l), gcd(g,c), frpa*c - bsq)
397
else :
398
for a in xrange(1, isqrt(self.__disc // 3) + 1) :
399
frpa = 4 * a
400
for b in xrange(a+1) :
401
g = gcd(a, b)
402
bsq = b**2
403
404
## We need x**2 * a + x * b + c % N == 0
405
h = (-((x**2 + 1) * a + x * b)) % self.__level
406
for c in xrange( a + h,
407
(b**2 + (self.__disc - 1))//(4*a) + 1, self.__level ) :
408
yield (((a,b,c), l), gcd(g,(x**2 * a + x * b + c) // self.__level), frpa*c - bsq)
409
#! if self.__reduced
410
else :
411
raise NotImplementedError
412
413
raise StopIteration
414
415
def _hecke_operator(self, n) :
416
if gcd(n, self.__level) != 1 :
417
raise NotImplementedError
418
else :
419
return ParamodularFormD2Filter_discriminant(self.__disc // n**2, self.__level, self.__reduced)
420
421
def __cmp__(self, other) :
422
c = cmp(type(self), type(other))
423
424
if c == 0 :
425
c = cmp(self.__level, other.__level)
426
if c == 0 :
427
c = cmp(self.__reduced, other.__reduced)
428
if c == 0 :
429
c = cmp(self.__disc, other.__disc)
430
431
return c
432
433
def __hash__(self) :
434
return reduce(xor, map(hash, [type(self), self.__level, self.__disc]))
435
436
def _repr_(self) :
437
return "Discriminant filter (%s) for paramodular forms of level %s " \
438
% (self.__disc, self.__level)
439
440
def _latex_(self) :
441
return "Discriminant filter (%s) for paramodular forms of level %s " \
442
% (latex(self.__disc), latex(self.__level))
443
444
445
class ParamodularFormD2Filter_trace (SageObject) :
446
def __init__(self, precision, level, reduced = True) :
447
self.__level = level
448
449
if isinstance(precision, ParamodularFormD2Filter_trace) :
450
precision = precision.index()
451
elif isinstance(precision, ParamodularFormD2Filter_discriminant) :
452
if precision.index() is infinity :
453
precision = infinity
454
else :
455
precision = isqrt(precision.index() - 1) + 1
456
457
self.__trace = precision
458
self.__reduced = reduced
459
self.__p1list = P1List(level)
460
461
def filter_all(self) :
462
return ParamodularFormD2Filter_trace(infinity, self.__level, self.__reduced)
463
464
def zero_filter(self) :
465
return ParamodularFormD2Filter_trace(0, self.__level, self.__reduced)
466
467
def is_infinite(self) :
468
return self.__trace is infinity
469
470
def is_all(self) :
471
return self.is_infinite()
472
473
def is_all(self) :
474
return self.is_infinite()
475
476
def level(self) :
477
return self.__level
478
479
def index(self) :
480
return self.__trace
481
482
def _contained_trace_bound(self) :
483
return self.index()
484
485
def _contained_discriminant_bound(self) :
486
return floor( (self.index() / ( Integer(1) + self.__level + self.__level**2) )**2 )
487
488
def _enveloping_discriminant_bound(self) :
489
return ceil(3 * self.index() / Integer(2))
490
491
def is_reduced(self) :
492
return self.__reduced
493
494
def __contains__(self, f) :
495
r"""
496
Check whether an index has discriminant less than ``self.__index`` and
497
whether its bottom right entry is divisible by ``self.__level``.
498
"""
499
if self.__disc is infinity :
500
return True
501
502
(s, l) = f
503
504
(a, _, c) = apply_GL_to_form(self.__p1list[l], s)
505
if not c % self.__level == 0 :
506
return False
507
508
return a + c < self.index()
509
510
def __iter__(self) :
511
return itertools.chain( self.iter_positive_forms(),
512
self.iter_indefinite_forms() )
513
514
def iter_positive_forms(self) :
515
if self.__disc is infinity :
516
raise ValueError, "infinity is not a true filter index"
517
518
if self.__reduced :
519
for (l, (u,x)) in enumerate(self.__p1list) :
520
if u == 0 :
521
for a in xrange(self.__level, self.__trace, self.__level) :
522
for c in xrange(a, self.__trace - a) :
523
for b in xrange( 2 * isqrt(a * c - 1) + 2 if a*c != 1 else 1,
524
2 * isqrt(a * c) ) :
525
yield ((a,b,c), l)
526
else :
527
for a in xrange(1, self.__trace) :
528
for b in xrange(a+1) :
529
## We need x**2 * a + x * b + c % N == 0
530
h = ((x**2 + 1) * a + x * b) % self.__level
531
if x == 0 and h == 0 : h = 1
532
for c in xrange( h, self.__trace - x * b - (x**2 + 1) * a, self.__level ) :
533
yield ((a,b,c), l)
534
#! if self.__reduced
535
else :
536
for (l, (u,x)) in enumerate(self.__p1list) :
537
if u == 0 :
538
for a in xrange(self.__level, self.__trace, self.__level) :
539
for c in xrange(1, self.__trace - a) :
540
for b in xrange( 2 * isqrt(a * c - 1) + 2 if a*c != 1 else 1,
541
2 * isqrt(a * c) ) :
542
yield ((a,b,c), l)
543
else :
544
for a in xrange(1, self.__trace) :
545
for c in xrange(self.__level, self.__trace - a, self.__level) :
546
for b in xrange( 2 * isqrt(a * c - 1) + 2 if a*c != 1 else 1,
547
2 * isqrt(a * c) ) :
548
yield ((a, b - 2 * x * a, c - x * b - x**2 * a), l)
549
#! else self.__reduced
550
551
raise StopIteration
552
553
def iter_indefinite_forms(self) :
554
if self.__disc is infinity :
555
raise ValueError, "infinity is not a true filter index"
556
557
if self.__reduced :
558
for (l, (u,_)) in enumerate(self.__p1list) :
559
if u == 0 :
560
for c in xrange(self.__trace) :
561
yield ((0,0,c), l)
562
else :
563
for c in xrange(0, self.__trace, self.__level) :
564
yield ((0,0,c), l)
565
else :
566
raise NotImplementedError
567
568
raise StopIteration
569
570
def _hecke_operator(self, n) :
571
raise ValueError( "Non-GL(2, ZZ) invariant filter does not admit Hecke action.\n" +
572
"Use conversion to discriminant filters first.")
573
574
def __cmp__(self, other) :
575
c = cmp(type(self), type(other))
576
577
if c == 0 :
578
c = cmp(self.__level, other.__level)
579
if c == 0 :
580
c = cmp(self.__reduced, other.__reduced)
581
if c == 0 :
582
c = cmp(self.__trace, other.__trace)
583
584
return c
585
586
def __hash__(self) :
587
return reduce(xor, map(hash, [type(self), self.__level, self.__trace]))
588
589
def _repr_(self) :
590
return "Trace filter (%s) for paramodular forms of level %s " \
591
% (self.__disc, self.__level)
592
593
def _latex_(self) :
594
return "Trace filter (%s) for paramodular forms of level %s " \
595
% (latex(self.__disc), latex(self.__level))
596
597
#===============================================================================
598
# SiegelModularFormG2FourierExpansionRing
599
#===============================================================================
600
601
def ParamodularFormD2FourierExpansionRing(K, level) :
602
603
R = EquivariantMonoidPowerSeriesRing(
604
ParamodularFormD2Indices_discriminant(level),
605
TrivialCharacterMonoid("GL(2,ZZ)_0 (%s)" % (level,), ZZ),
606
TrivialRepresentation("GL(2,ZZ)_0 (%s)" % (level,), K) )
607
608
# R._set_reduction_function(reduce_GL)
609
610
# if K is ZZ :
611
# R._set_multiply_function(mult_coeff_int)
612
# else :
613
# R._set_multiply_function(mult_coeff_generic)
614
615
return R
616
617