Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
241818 views
1
r"""
2
Classes describing the Fourier expansion of Siegel modular forms of genus n.
3
4
AUTHORS:
5
6
- Martin Raum (2009 - 05 - 10) 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 \
29
import TrivialCharacterMonoid, TrivialRepresentation
30
from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_ring \
31
import EquivariantMonoidPowerSeriesRing
32
from operator import xor
33
from sage.matrix.constructor import diagonal_matrix, matrix, zero_matrix, identity_matrix
34
from sage.matrix.matrix import is_Matrix
35
from sage.misc.flatten import flatten
36
from sage.misc.functional import isqrt
37
from sage.misc.latex import latex
38
from sage.rings.arith import gcd
39
from sage.rings.infinity import infinity
40
from sage.rings.integer import Integer
41
from sage.rings.integer_ring import ZZ
42
from sage.rings.rational_field import QQ
43
from sage.structure.sage_object import SageObject
44
import itertools
45
46
#===============================================================================
47
# SiegelModularFormGnIndices_diagonal_lll
48
#===============================================================================
49
50
class SiegelModularFormGnIndices_diagonal_lll ( SageObject ) :
51
r"""
52
All positive definite quadratic forms filtered by their discriminant.
53
"""
54
def __init__(self, n, reduced = True) :
55
self.__n = n
56
self.__reduced = reduced
57
58
def ngens(self) :
59
##FIXME: This is most probably not correct
60
return (self.__n * (self.__n + 1)) // 2 \
61
if self.__reduced else \
62
self.__n**2
63
64
def gen(self, i = 0) :
65
if i < self.__n :
66
t = diagonal_matrix(ZZ, i * [0] + [2] + (self.__n - i - 1) * [0])
67
t.set_immutable()
68
69
return t
70
elif i >= self.__n and i < (self.__n * (self.__n + 1)) // 2 :
71
i = i - self.__n
72
73
for r in xrange(self.__n) :
74
if i >= self.__n - r - 1 :
75
i = i - (self.__n - r - 1)
76
continue
77
78
c = i + r + 1
79
break
80
81
t = zero_matrix(ZZ, self.__n)
82
t[r,c] = 1
83
t[c,r] = 1
84
85
t.set_immutable()
86
return t
87
elif not self.__reduced and i >= (self.__n * (self.__n + 1)) // 2 \
88
and i < self.__n**2 :
89
i = i - (self.__n * (self.__n + 1)) // 2
90
91
for r in xrange(self.__n) :
92
if i >= self.__n - r - 1 :
93
i = i - (self.__n - r - 1)
94
continue
95
96
c = i + r + 1
97
break
98
99
t = zero_matrix(ZZ, self.__n)
100
t[r,c] = -1
101
t[c,r] = -1
102
103
t.set_immutable()
104
return t
105
106
raise ValueError, "Generator not defined"
107
108
def gens(self) :
109
return [self.gen(i) for i in xrange(self.ngens())]
110
111
def genus(self) :
112
return self.__n
113
114
def is_commutative(self) :
115
return True
116
117
def is_reduced(self) :
118
return self.__reduced
119
120
def monoid(self) :
121
return SiegelModularFormGnIndices_diagonal_lll(self.__n, False)
122
123
def group(self) :
124
return "GL(%s,ZZ)" % (self.__n,)
125
126
def is_monoid_action(self) :
127
"""
128
``True`` if the representation respects the monoid structure.
129
"""
130
return True
131
132
def filter(self, bound) :
133
return SiegelModularFormGnFilter_diagonal_lll(self.__n, bound, self.__reduced)
134
135
def filter_all(self) :
136
return SiegelModularFormGnFilter_diagonal_lll(self.__n, infinity, self.__reduced)
137
138
def minimal_composition_filter(self, ls, rs) :
139
if len(ls) == 0 or len(rs) == 0 :
140
return SiegelModularFormGnFilter_diagonal_lll(self.__n, 0, self.__reduced)
141
142
maxd = flatten( map(lambda (ml, mr): [ml[i,i] + mr[i,i] for i in xrange(self.__n)],
143
itertools.product(ls, rs) ) )
144
145
return SiegelModularFormGnFilter_diagonal_lll(self.__n, maxd + 1, self.__reduced)
146
147
def _gln_lift(self, v, position = 0) :
148
"""
149
Create a unimodular matrix which contains v as a row or column.
150
151
INPUT:
152
153
- `v` -- A primitive vector over `\ZZ`.
154
- ``position`` -- (Integer, default: 0) Determines the position of `v` in the result.
155
- `0` -- left column
156
- `1` -- right column
157
- `2` -- upper row
158
- `3` -- bottom row
159
"""
160
161
n = len(v)
162
u = identity_matrix(n)
163
if n == 1 :
164
return u
165
166
for i in xrange(n) :
167
if v[i] < 0 :
168
v[i] = -v[i]
169
u[i,i] = -1
170
171
while True :
172
(i, e) = min(enumerate(v), key = lambda e: e[1])
173
174
if e == 0 :
175
cur_last_entry = n - 1
176
for j in xrange(n - 1, -1, -1) :
177
if v[j] == 0 :
178
if cur_last_entry != j :
179
us = identity_matrix(n)
180
us[cur_last_entry,cur_last_entry] = 0
181
us[j,j] = 0
182
us[cur_last_entry,j] = 1
183
us[j,cur_last_entry] = 1
184
u = us * u
185
186
v[j] = v[cur_last_entry]
187
v[cur_last_entry] = 0
188
189
cur_last_entry = cur_last_entry - 1
190
191
192
us = identity_matrix(n,n)
193
us.set_block(0, 0, self._gln_lift(v[:cur_last_entry + 1]))
194
195
196
u = u.inverse() * us
197
u = matrix(ZZ, u)
198
199
if position == 1 or position == 3 :
200
us = identity_matrix(n)
201
us[0,0] = 0
202
us[n-1,n-1] = 0
203
us[0,n-1] = 1
204
us[n-1,0] = 1
205
206
u = u * us
207
208
if position == 0 or position == 1 :
209
return u
210
elif position == 2 or position == 3 :
211
return u.transpose()
212
else :
213
raise ValueError("Unknown position")
214
215
if i != 0 :
216
us = identity_matrix(n)
217
us[0,0] = 0
218
us[i,i] = 0
219
us[0,i] = 1
220
us[i,0] = 1
221
u = us * u
222
223
v[i] = v[0]
224
v[0] = e
225
226
for j in xrange(1,n) :
227
h = - (v[j] // e)
228
us = identity_matrix(n)
229
us[j,0] = h
230
u = us * u
231
232
v[j] = v[j] + h*v[0]
233
234
def _check_definiteness(self, t) :
235
"""
236
OUTPUT:
237
238
An integer. `1` if `t` is positive definite, `0` if it is semi definite and `-1` in all other cases.
239
240
NOTE:
241
242
We have to use this implementations since the quadratic forms' implementation has a bug.
243
"""
244
## We compute the rational diagonal form and whenever there is an indefinite upper
245
## left submatrix we abort.
246
t = matrix(QQ, t)
247
n = t.nrows()
248
249
indefinite = False
250
251
for i in xrange(n) :
252
if t[i,i] < 0 :
253
return -1
254
elif t[i,i] == 0 :
255
indefinite = True
256
for j in xrange(i + 1, n) :
257
if t[i,j] != 0 :
258
return -1
259
else :
260
for j in xrange(i + 1, n) :
261
t.add_multiple_of_row(j, i, -t[j,i]/t[i,i])
262
t.add_multiple_of_column(j, i, -t[i,j]/t[i,i])
263
264
return 0 if indefinite else 1
265
266
def _reduction_function(self) :
267
return self.reduce
268
269
def reduce(self, t) :
270
## We compute the rational diagonal form of t. Whenever a zero entry occures we
271
## find a primitive isotropic vector and apply a base change, such that t finally
272
## has the form diag(0,0,...,P) where P is positive definite. P will then be a
273
## LLL reduced.
274
275
ot = t
276
t = matrix(QQ, t)
277
n = t.nrows()
278
u = identity_matrix(QQ, n)
279
280
for i in xrange(n) :
281
if t[i,i] < 0 :
282
return None
283
elif t[i,i] == 0 :
284
## get the isotropic vector
285
v = u.column(i)
286
v = v.denominator() * v
287
v = v / gcd(list(v))
288
u = self._gln_lift(v, 0)
289
290
t = u.transpose() * ot * u
291
ts = self.reduce(t.submatrix(1,1,n-1,n-1))
292
if ts is None :
293
return None
294
t.set_block(1, 1, ts[0])
295
296
t.set_immutable()
297
298
return (t,1)
299
else :
300
for j in xrange(i + 1, n) :
301
us = identity_matrix(QQ, n, n)
302
us[i,j] = -t[i,j]/t[i,i]
303
u = u * us
304
305
t.add_multiple_of_row(j, i, -t[j,i]/t[i,i])
306
t.add_multiple_of_column(j, i, -t[i,j]/t[i,i])
307
308
u = ot.LLL_gram()
309
t = u.transpose() * ot * u
310
t.set_immutable()
311
312
return (t, 1)
313
314
def decompositions(self, t) :
315
for diag in itertools.product(*[xrange(t[i,i] + 1) for i in xrange(self.__n)]) :
316
for subents in itertools.product( *[ xrange(-2 * isqrt(diag[i] * diag[j]), 2 * isqrt(diag[i] * diag[j]) + 1)
317
for i in xrange(self.__n - 1)
318
for j in xrange(i + 1, self.__n) ] ) :
319
t1 = matrix(ZZ, [[ 2 * diag[i]
320
if i == j else
321
(subents[self.__n * i - (i * (i + 1)) // 2 + j - i - 1]
322
if i < j else
323
subents[self.__n * j - (j * (j + 1)) // 2 + i - j - 1])
324
for i in xrange(self.__n) ]
325
for j in xrange(self.__n) ] )
326
327
if self._check_definiteness(t1) == -1 :
328
continue
329
330
t2 = t - t1
331
if self._check_definiteness(t2) == -1 :
332
continue
333
334
t1.set_immutable()
335
t2.set_immutable()
336
337
yield (t1, t2)
338
339
raise StopIteration
340
341
def zero_element(self) :
342
t = zero_matrix(ZZ, self.__n)
343
t.set_immutable()
344
return t
345
346
def __contains__(self, x) :
347
return is_Matrix(x) and x.base_ring() is ZZ and x.nrows() == self.__n and x.ncols() == self.__n
348
349
def __cmp__(self, other) :
350
c = cmp(type(self), type(other))
351
if c == 0 :
352
c = cmp(self.__reduced, other.__reduced)
353
if c == 0 :
354
c = cmp(self.__n, other.__n)
355
356
return c
357
358
def __hash__(self) :
359
return xor(hash(self.__reduced), hash(self.__n))
360
361
def _repr_(self) :
362
if self.__reduced :
363
return "Reduced quadratic forms of rank %s over ZZ" % (self.__n,)
364
else :
365
return "Quadratic forms over of rank %s ZZ" % (self.__n,)
366
367
def _latex_(self) :
368
if self.__reduced :
369
return "Reduced quadratic forms of rank %s over ZZ" % (latex(self.__n),)
370
else :
371
return "Quadratic forms over of rank %s ZZ" % (latex(self.__n),)
372
373
#===============================================================================
374
# SiegelModularFormGnFilter_diagonal_lll
375
#===============================================================================
376
377
class SiegelModularFormGnFilter_diagonal_lll ( SageObject ) :
378
def __init__(self, n, bound, reduced = True) :
379
if isinstance(bound, SiegelModularFormGnFilter_diagonal_lll) :
380
bound = bound.index()
381
382
self.__n = n
383
self.__bound = bound
384
self.__reduced = reduced
385
386
self.__ambient = SiegelModularFormGnIndices_diagonal_lll(n, reduced)
387
388
def filter_all(self) :
389
return SiegelModularFormGnFilter_diagonal_lll(self.__n, infinity, self.__reduced)
390
391
def zero_filter(self) :
392
return SiegelModularFormGnFilter_diagonal_lll(self.__n, 0, self.__reduced)
393
394
def is_infinite(self) :
395
return self.__bound is infinity
396
397
def is_all(self) :
398
return self.is_infinite()
399
400
def genus(self) :
401
return self.__n
402
403
def index(self) :
404
return self.__bound
405
406
def is_reduced(self) :
407
return self.__reduced
408
409
def __contains__(self, t) :
410
if self.__bound is infinity :
411
return True
412
413
for i in xrange(self.__n) :
414
if t[i,i] >= 2 * self.__bound :
415
return False
416
417
return True
418
419
def __iter__(self) :
420
if self.__bound is infinity :
421
raise ValueError, "infinity is not a true filter index"
422
423
if self.__reduced :
424
##TODO: This is really primitive. We barely reduce arbitrary forms.
425
for diag in itertools.product(*[xrange(self.__bound) for _ in xrange(self.__n)]) :
426
for subents in itertools.product( *[ xrange(-2 * isqrt(diag[i] * diag[j]), 2 * isqrt(diag[i] * diag[j]) + 1)
427
for i in xrange(self.__n - 1)
428
for j in xrange(i + 1, self.__n) ] ) :
429
t = matrix(ZZ, [[ 2 * diag[i]
430
if i == j else
431
(subents[self.__n * i - (i * (i + 1)) // 2 + j - i - 1]
432
if i < j else
433
subents[self.__n * j - (j * (j + 1)) // 2 + i - j - 1])
434
for i in xrange(self.__n) ]
435
for j in xrange(self.__n) ] )
436
437
t = self.__ambient.reduce(t)
438
if t is None : continue
439
t = t[0]
440
441
t.set_immutable()
442
443
yield t
444
else :
445
for diag in itertools.product(*[xrange(self.__bound) for _ in xrange(self.__n)]) :
446
for subents in itertools.product( *[ xrange(-2 * isqrt(diag[i] * diag[j]), 2 * isqrt(diag[i] * diag[j]) + 1)
447
for i in xrange(self.__n - 1)
448
for j in xrange(i + 1, self.__n) ] ) :
449
t = matrix(ZZ, [[ 2 * diag[i]
450
if i == j else
451
(subents[self.__n * i - (i * (i + 1)) // 2 + j - i - 1]
452
if i < j else
453
subents[self.__n * j - (j * (j + 1)) // 2 + i - j - 1])
454
for i in xrange(self.__n) ]
455
for j in xrange(self.__n) ] )
456
if self._check_definiteness(t) == -1 :
457
continue
458
459
t.set_immutable()
460
461
yield t
462
463
raise StopIteration
464
465
def iter_positive_forms(self) :
466
if self.__reduced :
467
##TODO: This is really primitive. We barely reduce arbitrary forms.
468
for diag in itertools.product(*[xrange(self.__bound) for _ in xrange(self.__n)]) :
469
for subents in itertools.product( *[ xrange(-2 * isqrt(diag[i] * diag[j]), 2 * isqrt(diag[i] * diag[j]) + 1)
470
for i in xrange(self.__n - 1)
471
for j in xrange(i + 1, self.__n) ] ) :
472
t = matrix(ZZ, [[ 2 * diag[i]
473
if i == j else
474
(subents[self.__n * i - (i * (i + 1)) // 2 + j - i - 1]
475
if i < j else
476
subents[self.__n * j - (j * (j + 1)) // 2 + i - j - 1])
477
for i in xrange(self.__n) ]
478
for j in xrange(self.__n) ] )
479
480
t = self.__ambient.reduce(t)
481
if t is None : continue
482
t = t[0]
483
if t[0,0] == 0 : continue
484
485
t.set_immutable()
486
487
yield t
488
else :
489
for diag in itertools.product(*[xrange(self.__bound) for _ in xrange(self.__n)]) :
490
for subents in itertools.product( *[ xrange(-2 * isqrt(diag[i] * diag[j]), 2 * isqrt(diag[i] * diag[j]) + 1)
491
for i in xrange(self.__n - 1)
492
for j in xrange(i + 1, self.__n) ] ) :
493
t = matrix(ZZ, [[ 2 * diag[i]
494
if i == j else
495
(subents[self.__n * i - (i * (i + 1)) // 2 + j - i - 1]
496
if i < j else
497
subents[self.__n * j - (j * (j + 1)) // 2 + i - j - 1])
498
for i in xrange(self.__n) ]
499
for j in xrange(self.__n) ] )
500
501
if self.__ambient._check_definiteness(t) != 1 :
502
continue
503
504
t.set_immutable()
505
506
yield t
507
508
def __cmp__(self, other) :
509
c = cmp(type(self), type(other))
510
if c == 0 :
511
c = cmp(self.__reduced, other.__reduced)
512
if c == 0 :
513
c = cmp(self.__n, other.__n)
514
if c == 0 :
515
c = cmp(self.__bound, other.__bound)
516
517
return c
518
519
def __hash__(self) :
520
return reduce(xor, map(hash, [type(self), self.__reduced, self.__bound]))
521
522
def _repr_(self) :
523
return "Diagonal filter (%s)" % self.__bound
524
525
def _latex_(self) :
526
return "Diagonal filter (%s)" % latex(self.__bound)
527
528
def SiegelModularFormGnFourierExpansionRing(K, genus) :
529
530
R = EquivariantMonoidPowerSeriesRing(
531
SiegelModularFormGnIndices_diagonal_lll(genus),
532
TrivialCharacterMonoid("GL(%s,ZZ)" % (genus,), ZZ),
533
TrivialRepresentation("GL(%s,ZZ)" % (genus,), K) )
534
535
return R
536
537