Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/etaproducts.py
4056 views
1
r"""
2
Eta-products on modular curves :math:`X_0(N)`
3
4
This package provides a class for representing eta-products, which
5
are meromorphic functions on modular curves of the form
6
7
.. math::
8
9
\prod_{d | N} \eta(q^d)^{r_d}
10
11
where
12
:math:`\eta(q)` is Dirichlet's eta function
13
:math:`q^{1/24} \prod_{n = 1}^\infty(1-q^n)`. These are useful
14
for obtaining explicit models of modular curves.
15
16
See trac ticket #3934 for background.
17
18
AUTHOR:
19
20
- David Loeffler (2008-08-22): initial version
21
"""
22
23
#*****************************************************************************
24
# Copyright (C) 2008 William Stein <[email protected]>
25
# 2008 David Loeffler <[email protected]>
26
#
27
# Distributed under the terms of the GNU General Public License (GPL)
28
# http://www.gnu.org/licenses/
29
#*****************************************************************************
30
31
from sage.structure.sage_object import SageObject
32
from sage.rings.power_series_ring import PowerSeriesRing
33
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
34
from sage.rings.arith import divisors, prime_divisors, is_square, euler_phi, gcd
35
from sage.rings.all import IntegerRing, RationalField
36
from sage.groups.group import AbelianGroup
37
from sage.structure.element import MultiplicativeGroupElement
38
from sage.structure.formal_sum import FormalSum
39
from sage.rings.finite_rings.integer_mod import Mod
40
from sage.rings.finite_rings.integer_mod_ring import IntegerModRing
41
from sage.matrix.constructor import matrix
42
from sage.modules.free_module import FreeModule
43
from sage.misc.misc import union
44
45
from string import join
46
import weakref
47
48
ZZ = IntegerRing()
49
QQ = RationalField()
50
51
_cache = {}
52
def EtaGroup(level):
53
r"""
54
Create the group of eta products of the given level.
55
56
EXAMPLES::
57
58
sage: EtaGroup(12)
59
Group of eta products on X_0(12)
60
sage: EtaGroup(1/2)
61
Traceback (most recent call last):
62
...
63
TypeError: Level (=1/2) must be a positive integer
64
sage: EtaGroup(0)
65
Traceback (most recent call last):
66
...
67
ValueError: Level (=0) must be a positive integer
68
"""
69
if _cache.has_key(level):
70
G = _cache[level]()
71
if not G is None:
72
return G
73
G = EtaGroup_class(level)
74
_cache[level] = weakref.ref(G)
75
return G
76
77
class EtaGroup_class(AbelianGroup):
78
r"""
79
The group of eta products of a given level under multiplication.
80
"""
81
82
def __init__(self, level):
83
r"""
84
Create the group of eta products of a given level, which must be a
85
positive integer.
86
87
EXAMPLES:
88
sage: G = EtaGroup(12); G # indirect doctest
89
Group of eta products on X_0(12)
90
sage: G is loads(dumps(G))
91
True
92
"""
93
try:
94
level = ZZ(level)
95
except TypeError:
96
raise TypeError, "Level (=%s) must be a positive integer" % level
97
if (level < 1):
98
raise ValueError, "Level (=%s) must be a positive integer" % level
99
self._N = level
100
101
def __reduce__(self):
102
r"""
103
Return the data used to construct self. Used for pickling.
104
105
EXAMPLE::
106
107
sage: EtaGroup(13).__reduce__()
108
(<function EtaGroup at ...>, (13,))
109
"""
110
return (EtaGroup, (self.level(),))
111
112
def __cmp__(self, other):
113
r"""
114
Compare self to other. If other is not an EtaGroup, compare by
115
type; otherwise compare by level. EtaGroups of the same level
116
compare as identical.
117
118
EXAMPLE::
119
120
sage: EtaGroup(12) == 12
121
False
122
sage: EtaGroup(12) < EtaGroup(13)
123
True
124
sage: EtaGroup(12) == EtaGroup(12)
125
True
126
"""
127
if not isinstance(other, EtaGroup_class):
128
return cmp(type(self), type(other))
129
else:
130
return cmp(self.level(), other.level())
131
132
def _repr_(self):
133
r"""
134
String representation of self.
135
136
EXAMPLE::
137
138
sage: EtaGroup(12)._repr_()
139
'Group of eta products on X_0(12)'
140
"""
141
return "Group of eta products on X_0(%s)" % self.level()
142
143
def one(self):
144
r"""
145
Return the identity element of ``self``.
146
147
EXAMPLE::
148
149
sage: EtaGroup(12).one()
150
Eta product of level 12 : 1
151
"""
152
return self({})
153
154
def __call__(self, dict):
155
r"""
156
Create an element of this group (an eta product object) with
157
exponents from the given dictionary. See the docstring for the
158
EtaProduct() factory function for how dict is used.
159
160
EXAMPLE::
161
162
sage: EtaGroup(2).__call__({1:24, 2:-24})
163
Eta product of level 2 : (eta_1)^24 (eta_2)^-24
164
"""
165
return EtaGroupElement(self, dict)
166
167
def level(self):
168
r""" Return the level of self.
169
EXAMPLES::
170
171
sage: EtaGroup(10).level()
172
10
173
"""
174
return self._N
175
176
def basis(self, reduce=True):
177
r"""
178
Produce a basis for the free abelian group of eta-products of level
179
N (under multiplication), attempting to find basis vectors of the
180
smallest possible degree.
181
182
INPUT:
183
184
185
- ``reduce`` - a boolean (default True) indicating
186
whether or not to apply LLL-reduction to the calculated basis
187
188
189
EXAMPLE::
190
191
sage: EtaGroup(5).basis()
192
[Eta product of level 5 : (eta_1)^6 (eta_5)^-6]
193
sage: EtaGroup(12).basis()
194
[Eta product of level 12 : (eta_1)^2 (eta_2)^1 (eta_3)^2 (eta_4)^-1 (eta_6)^-7 (eta_12)^3,
195
Eta product of level 12 : (eta_1)^-2 (eta_2)^3 (eta_3)^6 (eta_4)^-1 (eta_6)^-9 (eta_12)^3,
196
Eta product of level 12 : (eta_1)^-3 (eta_2)^2 (eta_3)^1 (eta_4)^-1 (eta_6)^-2 (eta_12)^3,
197
Eta product of level 12 : (eta_1)^1 (eta_2)^-1 (eta_3)^-3 (eta_4)^-2 (eta_6)^7 (eta_12)^-2,
198
Eta product of level 12 : (eta_1)^-6 (eta_2)^9 (eta_3)^2 (eta_4)^-3 (eta_6)^-3 (eta_12)^1]
199
sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients
200
[Eta product of level 12 : (eta_2)^24 (eta_12)^-24,
201
Eta product of level 12 : (eta_1)^-336 (eta_2)^576 (eta_3)^696 (eta_4)^-216 (eta_6)^-576 (eta_12)^-144,
202
Eta product of level 12 : (eta_1)^-8 (eta_2)^-2 (eta_6)^2 (eta_12)^8,
203
Eta product of level 12 : (eta_1)^1 (eta_2)^9 (eta_3)^13 (eta_4)^-4 (eta_6)^-15 (eta_12)^-4,
204
Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5]
205
206
ALGORITHM: An eta product of level `N` is uniquely
207
determined by the integers `r_d` for `d | N` with
208
`d < N`, since `\sum_{d | N} r_d = 0`. The valid
209
`r_d` are those that satisfy two congruences modulo 24,
210
and one congruence modulo 2 for every prime divisor of N. We beef
211
up the congruences modulo 2 to congruences modulo 24 by multiplying
212
by 12. To calculate the kernel of the ensuing map
213
`\ZZ^m \to (\ZZ/24\ZZ)^n`
214
we lift it arbitrarily to an integer matrix and calculate its Smith
215
normal form. This gives a basis for the lattice.
216
217
This lattice typically contains "large" elements, so by default we
218
pass it to the reduce_basis() function which performs
219
LLL-reduction to give a more manageable basis.
220
"""
221
222
N = self.level()
223
divs = divisors(N)[:-1]
224
s = len(divs)
225
primedivs = prime_divisors(N)
226
227
rows = []
228
for i in xrange(s):
229
# generate a row of relation matrix
230
row = [ Mod(divs[i], 24) - Mod(N, 24), Mod(N/divs[i], 24) - Mod(1, 24)]
231
for p in primedivs:
232
row.append( Mod(12*(N/divs[i]).valuation(p), 24))
233
rows.append(row)
234
M = matrix(IntegerModRing(24), rows)
235
Mlift = M.change_ring(ZZ)
236
# now we compute elementary factors of Mlift
237
S,U,V = Mlift.smith_form()
238
good_vects = []
239
for i in xrange(U.nrows()):
240
vect = U.row(i)
241
nf = (i < S.ncols() and S[i,i]) or 0
242
good_vects.append((vect * 24/gcd(nf, 24)).list())
243
for v in good_vects:
244
v.append(-sum([r for r in v]))
245
dicts = []
246
for v in good_vects:
247
dicts.append({})
248
for i in xrange(s):
249
dicts[-1][divs[i]] = v[i]
250
dicts[-1][N] = v[-1]
251
if reduce:
252
return self.reduce_basis([ self(d) for d in dicts])
253
else:
254
return [self(d) for d in dicts]
255
256
def reduce_basis(self, long_etas):
257
r"""
258
Produce a more manageable basis via LLL-reduction.
259
260
INPUT:
261
262
263
- ``long_etas`` - a list of EtaGroupElement objects (which
264
should all be of the same level)
265
266
267
OUTPUT:
268
269
270
- a new list of EtaGroupElement objects having
271
hopefully smaller norm
272
273
274
ALGORITHM: We define the norm of an eta-product to be the
275
`L^2` norm of its divisor (as an element of the free
276
`\ZZ`-module with the cusps as basis and the
277
standard inner product). Applying LLL-reduction to this gives a
278
basis of hopefully more tractable elements. Of course we'd like to
279
use the `L^1` norm as this is just twice the degree, which
280
is a much more natural invariant, but `L^2` norm is easier
281
to work with!
282
283
EXAMPLES::
284
285
sage: EtaGroup(4).reduce_basis([ EtaProduct(4, {1:8,2:24,4:-32}), EtaProduct(4, {1:8, 4:-8})])
286
[Eta product of level 4 : (eta_1)^8 (eta_4)^-8,
287
Eta product of level 4 : (eta_1)^-8 (eta_2)^24 (eta_4)^-16]
288
"""
289
N = self.level()
290
cusps = AllCusps(N)
291
r = matrix(ZZ, [[et.order_at_cusp(c) for c in cusps] for et in long_etas])
292
V = FreeModule(ZZ, r.ncols())
293
A = V.submodule_with_basis([V(rw) for rw in r.rows()])
294
rred = r.LLL()
295
short_etas = []
296
for shortvect in rred.rows():
297
bv = A.coordinates(shortvect)
298
dict = {}
299
for d in divisors(N):
300
dict[d] = sum( [bv[i]*long_etas[i].r(d) for i in xrange(r.nrows())])
301
short_etas.append(self(dict))
302
return short_etas
303
304
305
def EtaProduct(level, dict):
306
r"""
307
Create an EtaGroupElement object representing the function
308
`\prod_{d | N} \eta(q^d)^{r_d}`. Checks the criteria
309
of Ligozat to ensure that this product really is the q-expansion of
310
a meromorphic function on X_0(N).
311
312
INPUT:
313
314
315
- ``level`` - (integer): the N such that this eta
316
product is a function on X_0(N).
317
318
- ``dict`` - (dictionary): a dictionary indexed by
319
divisors of N such that the coefficient of `\eta(q^d)` is
320
r[d]. Only nonzero coefficients need be specified. If Ligozat's
321
criteria are not satisfied, a ValueError will be raised.
322
323
324
OUTPUT:
325
326
327
- an EtaGroupElement object, whose parent is
328
the EtaGroup of level N and whose coefficients are the given
329
dictionary.
330
331
332
.. note::
333
334
The dictionary dict does not uniquely specify N. It is
335
possible for two EtaGroupElements with different `N`'s to
336
be created with the same dictionary, and these represent different
337
objects (although they will have the same `q`-expansion at
338
the cusp `\infty`).
339
340
EXAMPLES::
341
342
sage: EtaProduct(3, {3:12, 1:-12})
343
Eta product of level 3 : (eta_1)^-12 (eta_3)^12
344
sage: EtaProduct(3, {3:6, 1:-6})
345
Traceback (most recent call last):
346
...
347
ValueError: sum d r_d (=12) is not 0 mod 24
348
sage: EtaProduct(3, {4:6, 1:-6})
349
Traceback (most recent call last):
350
...
351
ValueError: 4 does not divide 3
352
"""
353
return EtaGroup(level)(dict)
354
355
class EtaGroupElement(MultiplicativeGroupElement):
356
357
def __init__(self, parent, rdict):
358
r"""
359
Create an eta product object. Usually called implicitly via
360
EtaGroup_class.__call__ or the EtaProduct factory function.
361
362
EXAMPLE::
363
364
sage: EtaGroupElement(EtaGroup(8), {1:24, 2:-24})
365
Eta product of level 8 : (eta_1)^24 (eta_2)^-24
366
sage: g = _; g == loads(dumps(g))
367
True
368
"""
369
MultiplicativeGroupElement.__init__(self, parent)
370
371
self._N = self.parent().level()
372
N = self._N
373
374
if isinstance(rdict, EtaGroupElement):
375
rdict = rdict._rdict
376
# Note: This is needed because the "x in G" test tries to call G(x)
377
# and see if it returns an error. So sometimes this will be getting
378
# called with rdict being an eta product, not a dictionary.
379
380
if rdict == 1:
381
rdict = {}
382
# Check Ligozat criteria
383
sumR = sumDR = sumNoverDr = 0
384
prod = 1
385
386
for d in rdict.keys():
387
if N % d:
388
raise ValueError, "%s does not divide %s" % (d, N)
389
390
for d in rdict.keys():
391
if rdict[d] == 0:
392
rdict.pop(d)
393
continue
394
sumR += rdict[d]
395
sumDR += rdict[d]*d
396
sumNoverDr += rdict[d]*N/d
397
prod *= (N/d)**rdict[d]
398
399
if sumR != 0:
400
raise ValueError, "sum r_d (=%s) is not 0" % sumR
401
if (sumDR % 24) != 0:
402
raise ValueError, "sum d r_d (=%s) is not 0 mod 24" % sumDR
403
if (sumNoverDr % 24) != 0:
404
raise ValueError, "sum (N/d) r_d (=%s) is not 0 mod 24" % sumNoverDr
405
if not is_square(prod):
406
raise ValueError, "product (N/d)^(r_d) (=%s) is not a square" % prod
407
408
self._sumDR = sumDR # this is useful to have around
409
self._rdict = rdict
410
self._keys = rdict.keys() # avoid factoring N every time
411
412
def _mul_(self, other):
413
r"""
414
Return the product of self and other.
415
416
EXAMPLES::
417
418
sage: eta1, eta2 = EtaGroup(4).basis() # indirect doctest
419
sage: eta1 * eta2
420
Eta product of level 4 : (eta_2)^24 (eta_4)^-24
421
"""
422
newdict = {}
423
for d in union(self._keys, other._keys):
424
newdict[d] = self.r(d) + other.r(d)
425
return EtaProduct(self.level(), newdict)
426
427
def _div_(self, other):
428
r"""
429
Return `self * other^{-1}`.
430
431
EXAMPLES::
432
433
sage: eta1, eta2 = EtaGroup(4).basis()
434
sage: eta1 / eta2 # indirect doctest
435
Eta product of level 4 : (eta_1)^-16 (eta_2)^24 (eta_4)^-8
436
sage: (eta1 / eta2) * eta2 == eta1
437
True
438
"""
439
newdict = {}
440
for d in union(self._keys, other._keys):
441
newdict[d] = self.r(d) - other.r(d)
442
return EtaProduct(self.level(), newdict)
443
444
def __cmp__(self, other):
445
r"""
446
Compare self to other. Eta products compare first according to
447
their levels, then according to their rdicts.
448
449
EXAMPLES::
450
451
sage: EtaProduct(2, {2:24,1:-24}) == 1
452
False
453
sage: EtaProduct(2, {2:24, 1:-24}) < EtaProduct(4, {2:24, 1:-24})
454
True
455
sage: EtaProduct(2, {2:24, 1:-24}) == EtaProduct(4, {2:24, 1:-24})
456
False
457
sage: EtaProduct(2, {2:24, 1:-24}) < EtaProduct(4, {2:48, 1:-48})
458
True
459
"""
460
if not isinstance(other, EtaGroupElement):
461
return cmp(type(self), type(other))
462
return (cmp(self.level(), other.level()) or cmp(self._rdict, other._rdict))
463
464
def _short_repr(self):
465
r"""
466
A short string representation of self, which doesn't specify the
467
level.
468
469
EXAMPLES::
470
471
sage: EtaProduct(3, {3:12, 1:-12})._short_repr()
472
'(eta_1)^-12 (eta_3)^12'
473
"""
474
if self.degree() == 0:
475
return "1"
476
else:
477
return join(["(eta_%s)^%s" % (d,self.r(d)) for d in self._keys])
478
479
def _repr_(self):
480
r"""
481
Return the string representation of self.
482
483
EXAMPLES::
484
485
sage: EtaProduct(3, {3:12, 1:-12})._repr_()
486
'Eta product of level 3 : (eta_1)^-12 (eta_3)^12'
487
"""
488
return "Eta product of level %s : " % self.level() + self._short_repr()
489
490
def level(self):
491
r"""
492
Return the level of this eta product.
493
494
EXAMPLES::
495
496
sage: e = EtaProduct(3, {3:12, 1:-12})
497
sage: e.level()
498
3
499
sage: EtaProduct(12, {6:6, 2:-6}).level() # not the lcm of the d's
500
12
501
sage: EtaProduct(36, {6:6, 2:-6}).level() # not minimal
502
36
503
"""
504
return self._N
505
506
def q_expansion(self, n):
507
r"""
508
The q-expansion of self at the cusp at infinity.
509
510
INPUT:
511
512
513
- ``n`` (integer): number of terms to calculate
514
515
516
OUTPUT:
517
518
519
- a power series over `\ZZ` in
520
the variable `q`, with a *relative* precision of
521
`1 + O(q^n)`.
522
523
524
ALGORITHM: Calculates eta to (n/m) terms, where m is the smallest
525
integer dividing self.level() such that self.r(m) != 0. Then
526
multiplies.
527
528
EXAMPLES::
529
530
sage: EtaProduct(36, {6:6, 2:-6}).q_expansion(10)
531
q + 6*q^3 + 27*q^5 + 92*q^7 + 279*q^9 + O(q^11)
532
sage: R.<q> = ZZ[[]]
533
sage: EtaProduct(2,{2:24,1:-24}).q_expansion(100) == delta_qexp(101)(q^2)/delta_qexp(101)(q)
534
True
535
"""
536
R,q = PowerSeriesRing(ZZ, 'q').objgen()
537
pr = R(1)
538
if self == self.parent()(1):
539
return pr
540
eta_n = max([ (n/d).floor() for d in self._keys if self.r(d) != 0])
541
eta = qexp_eta(R, eta_n)
542
for d in self._keys:
543
if self.r(d) != 0:
544
pr *= eta(q**d)**self.r(d)
545
return pr*q**(self._sumDR / ZZ(24))*( R(1).add_bigoh(n))
546
547
def qexp(self, n):
548
"""
549
Alias for ``self.q_expansion()``.
550
551
EXAMPLES::
552
553
sage: e = EtaProduct(36, {6:8, 3:-8})
554
sage: e.qexp(10)
555
q + 8*q^4 + 36*q^7 + O(q^10)
556
sage: e.qexp(30) == e.q_expansion(30)
557
True
558
"""
559
return self.q_expansion(n)
560
561
def order_at_cusp(self, cusp):
562
r"""
563
Return the order of vanishing of self at the given cusp.
564
565
INPUT:
566
567
568
- ``cusp`` - a CuspFamily object
569
570
571
OUTPUT:
572
573
574
- an integer
575
576
577
EXAMPLES::
578
579
sage: e = EtaProduct(2, {2:24, 1:-24})
580
sage: e.order_at_cusp(CuspFamily(2, 1)) # cusp at infinity
581
1
582
sage: e.order_at_cusp(CuspFamily(2, 2)) # cusp 0
583
-1
584
"""
585
if not isinstance(cusp, CuspFamily):
586
raise TypeError, "Argument (=%s) should be a CuspFamily" % cusp
587
if cusp.level() != self.level():
588
raise ValueError, "Cusp not on right curve!"
589
return 1/ZZ(24)/gcd(cusp.width(), self.level()//cusp.width()) * sum( [ell*self.r(ell)/cusp.width() * (gcd(cusp.width(), self.level()//ell))**2 for ell in self._keys] )
590
591
def divisor(self):
592
r"""
593
Return the divisor of self, as a formal sum of CuspFamily objects.
594
595
EXAMPLES::
596
597
sage: e = EtaProduct(12, {1:-336, 2:576, 3:696, 4:-216, 6:-576, 12:-144})
598
sage: e.divisor() # FormalSum seems to print things in a random order?
599
-131*(Inf) - 50*(c_{2}) + 11*(0) + 50*(c_{6}) + 169*(c_{4}) - 49*(c_{3})
600
sage: e = EtaProduct(2^8, {8:1,32:-1})
601
sage: e.divisor() # random
602
-(c_{2}) - (Inf) - (c_{8,2}) - (c_{8,3}) - (c_{8,4}) - (c_{4,2}) - (c_{8,1}) - (c_{4,1}) + (c_{32,4}) + (c_{32,3}) + (c_{64,1}) + (0) + (c_{32,2}) + (c_{64,2}) + (c_{128}) + (c_{32,1})
603
"""
604
return FormalSum([ (self.order_at_cusp(c), c) for c in AllCusps(self.level())])
605
606
def degree(self):
607
r"""
608
Return the degree of self as a map
609
`X_0(N) \to \mathbb{P}^1`, which is equal to the sum of
610
all the positive coefficients in the divisor of self.
611
612
EXAMPLES::
613
614
sage: e = EtaProduct(12, {1:-336, 2:576, 3:696, 4:-216, 6:-576, 12:-144})
615
sage: e.degree()
616
230
617
"""
618
return sum( [self.order_at_cusp(c) for c in AllCusps(self.level()) if self.order_at_cusp(c) > 0])
619
620
# def plot(self):
621
# r""" Returns an error as it's not clear what plotting an eta product means. """
622
# raise NotImplementedError
623
624
def r(self, d):
625
r"""
626
Return the exponent `r_d` of `\eta(q^d)` in self.
627
628
EXAMPLES::
629
630
sage: e = EtaProduct(12, {2:24, 3:-24})
631
sage: e.r(3)
632
-24
633
sage: e.r(4)
634
0
635
"""
636
return self._rdict.get(d, 0)
637
638
# def __call__(self, cusp):
639
# r""" Calculate the value of self at the given cusp. """
640
# assert self.level() == cusp.level()
641
# if self.order_at_cusp(cusp) < 0:
642
# return Infinity
643
# if self.order_at_cusp(cusp) > 0:
644
# return 0
645
# else:
646
# s = ZZ(1)
647
# for ell in divisors(self.level()):
648
# s *= 1/ZZ(cusp.width())*gcd(cusp.width(), self.level() // ell)**(self.r(ell) / ZZ(2))
649
# return s
650
651
def num_cusps_of_width(N, d):
652
r"""
653
Return the number of cusps on `X_0(N)` of width d.
654
655
INPUT:
656
657
658
- ``N`` - (integer): the level
659
660
- ``d`` - (integer): an integer dividing N, the cusp
661
width
662
663
664
EXAMPLES::
665
666
sage: [num_cusps_of_width(18,d) for d in divisors(18)]
667
[1, 1, 2, 2, 1, 1]
668
"""
669
try:
670
N = ZZ(N)
671
d = ZZ(d)
672
assert N>0
673
assert d>0
674
assert ((N % d) == 0)
675
except TypeError:
676
raise TypeError, "N and d must be integers"
677
except AssertionError:
678
raise AssertionError, "N and d must be positive integers with d|N"
679
680
return euler_phi(gcd(d, N//d))
681
682
def AllCusps(N):
683
r"""
684
Return a list of CuspFamily objects corresponding to the cusps of
685
`X_0(N)`.
686
687
INPUT:
688
689
- ``N`` - (integer): the level
690
691
692
EXAMPLES::
693
694
sage: AllCusps(18)
695
[(Inf), (c_{2}), (c_{3,1}), (c_{3,2}), (c_{6,1}), (c_{6,2}), (c_{9}), (0)]
696
"""
697
try:
698
N = ZZ(N)
699
assert N>0
700
except TypeError:
701
raise TypeError, "N must be an integer"
702
except AssertionError:
703
raise AssertionError, "N must be positive"
704
c = []
705
for d in divisors(N):
706
n = num_cusps_of_width(N, d)
707
if n == 1:
708
c.append(CuspFamily(N, d))
709
elif n > 1:
710
for i in xrange(n):
711
c.append(CuspFamily(N, d, label=str(i+1)))
712
return c
713
714
class CuspFamily(SageObject):
715
r"""
716
A family of elliptic curves parametrising a region of
717
`X_0(N)`.
718
"""
719
def __init__(self, N, width, label = None):
720
r"""
721
Create the cusp of width d on X_0(N) corresponding to the family
722
of Tate curves
723
`(\CC_p/q^d, \langle \zeta q\rangle)`. Here
724
`\zeta` is a primitive root of unity of order `r`
725
with `\mathrm{lcm}(r,d) = N`. The cusp doesn't store zeta,
726
so we store an arbitrary label instead.
727
728
EXAMPLE::
729
730
sage: CuspFamily(8, 4)
731
(c_{4})
732
sage: CuspFamily(16, 4, '1')
733
(c_{4,1})
734
"""
735
try:
736
N = ZZ(N)
737
assert N>0
738
except TypeError:
739
raise TypeError, "N must be an integer"
740
except AssertionError:
741
raise AssertionError, "N must be positive"
742
self._N = N
743
self._width = width
744
if (N % width):
745
raise ValueError, "Bad width"
746
if num_cusps_of_width(N, width) > 1 and label == None:
747
raise ValueError, "There are %s > 1 cusps of width %s on X_0(%s): specify a label" % (num_cusps_of_width(N,width), width, N)
748
if num_cusps_of_width(N, width) == 1 and label != None:
749
raise ValueError, "There is only one cusp of width %s on X_0(%s): no need to specify a label" % (width, N)
750
self.label = label
751
752
def width(self):
753
r"""
754
The width of this cusp.
755
756
EXAMPLES::
757
758
sage: e = CuspFamily(10, 1)
759
sage: e.width()
760
1
761
"""
762
return self._width
763
764
def level(self):
765
r"""
766
The level of this cusp.
767
768
EXAMPLES::
769
770
sage: e = CuspFamily(10, 1)
771
sage: e.level()
772
10
773
"""
774
return self._N
775
776
def sage_cusp(self):
777
"""
778
Return the corresponding element of
779
`\mathbb{P}^1(\QQ)`.
780
781
EXAMPLE::
782
783
sage: CuspFamily(10, 1).sage_cusp() # not implemented
784
Infinity
785
"""
786
raise NotImplementedError
787
788
def _repr_(self):
789
r"""
790
Return a string representation of self.
791
792
EXAMPLE::
793
794
sage: CuspFamily(16, 4, "1")._repr_()
795
'(c_{4,1})'
796
"""
797
if self.width() == 1:
798
return "(Inf)"
799
elif self.width() == self.level():
800
return "(0)"
801
else:
802
return "(c_{%s%s})" % (self.width(), ((self.label and (","+self.label)) or ""))
803
804
def qexp_eta(ps_ring, n):
805
r"""
806
Return the q-expansion of `\eta(q) / q^{1/24}`, where
807
`\eta(q)` is Dedekind's function
808
809
.. math::
810
811
\eta(q) = q^{1/24}\prod_{i=1}^\infty (1-q^i)
812
813
814
as an element of ps_ring, to precision n. Completely naive
815
algorithm.
816
817
INPUT:
818
819
820
- ``ps_ring`` - (PowerSeriesRing): a power series
821
ring - we pass this as an argument as we frequently need to create
822
multiple series in the same ring.
823
824
- ``n`` - (integer): the number of terms to compute.
825
826
827
OUTPUT: An element of ps_ring which is the q-expansion of
828
`\eta(q)/q^{1/24}` truncated to n terms.
829
830
ALGORITHM: Multiply out the product
831
`\prod_{i=1}^n (1 - q^i)`. Could perhaps be sped-up by
832
using the identity
833
834
.. math::
835
836
\eta(q) = q^{1/24}( 1 + \sum_{i \ge 1} (-1)^n (q^{n(3n+1)/2} + q^{n(3n-1)/2}),
837
838
839
but I'm lazy.
840
841
EXAMPLES::
842
843
sage: qexp_eta(ZZ[['q']], 100)
844
1 - q - q^2 + q^5 + q^7 - q^12 - q^15 + q^22 + q^26 - q^35 - q^40 + q^51 + q^57 - q^70 - q^77 + q^92 + O(q^100)
845
"""
846
q = ps_ring.gen()
847
t = ps_ring(1).add_bigoh(n)
848
for i in xrange(1,n):
849
t = t*ps_ring( 1 - q**i)
850
return t
851
852
def eta_poly_relations(eta_elements, degree, labels=['x1','x2'], verbose=False):
853
r"""
854
Find polynomial relations between eta products.
855
856
INPUTS:
857
858
- ``eta_elements`` - (list): a list of EtaGroupElement objects.
859
Not implemented unless this list has precisely two elements. degree
860
861
- ``degree`` - (integer): the maximal degree of polynomial to look for.
862
863
- ``labels`` - (list of strings): labels to use for the polynomial returned.
864
865
- ``verbose``` - (boolean, default False): if True, prints information as
866
it goes.
867
868
OUTPUTS: a list of polynomials which is a Groebner basis for the
869
part of the ideal of relations between eta_elements which is
870
generated by elements up to the given degree; or None, if no
871
relations were found.
872
873
ALGORITHM: An expression of the form
874
`\sum_{0 \le i,j \le d} a_{ij} x^i y^j` is zero if and
875
only if it vanishes at the cusp infinity to degree at least
876
`v = d(deg(x) + deg(y))`. For all terms up to `q^v`
877
in the `q`-expansion of this expression to be zero is a
878
system of `v + k` linear equations in `d^2`
879
coefficients, where `k` is the number of nonzero negative
880
coefficients that can appear.
881
882
Solving these equations and calculating a basis for the solution
883
space gives us a set of polynomial relations, but this is generally
884
far from a minimal generating set for the ideal, so we calculate a
885
Groebner basis.
886
887
As a test, we calculate five extra terms of `q`-expansion
888
and check that this doesn't change the answer.
889
890
EXAMPLES::
891
892
sage: t = EtaProduct(26, {2:2,13:2,26:-2,1:-2})
893
sage: u = EtaProduct(26, {2:4,13:2,26:-4,1:-2})
894
sage: eta_poly_relations([t, u], 3)
895
sage: eta_poly_relations([t, u], 4)
896
[x1^3*x2 - 13*x1^3 - 4*x1^2*x2 - 4*x1*x2 - x2^2 + x2]
897
898
Use verbose=True to see the details of the computation::
899
900
sage: eta_poly_relations([t, u], 3, verbose=True)
901
Trying to find a relation of degree 3
902
Lowest order of a term at infinity = -12
903
Highest possible degree of a term = 15
904
Trying all coefficients from q^-12 to q^15 inclusive
905
No polynomial relation of order 3 valid for 28 terms
906
Check: Trying all coefficients from q^-12 to q^20 inclusive
907
No polynomial relation of order 3 valid for 33 terms
908
909
::
910
911
sage: eta_poly_relations([t, u], 4, verbose=True)
912
Trying to find a relation of degree 4
913
Lowest order of a term at infinity = -16
914
Highest possible degree of a term = 20
915
Trying all coefficients from q^-16 to q^20 inclusive
916
Check: Trying all coefficients from q^-16 to q^25 inclusive
917
[x1^3*x2 - 13*x1^3 - 4*x1^2*x2 - 4*x1*x2 - x2^2 + x2]
918
"""
919
if len(eta_elements) > 2:
920
raise NotImplementedError, "Don't know how to find relations between more than two elements"
921
922
eta1, eta2 = eta_elements
923
924
if verbose: print "Trying to find a relation of degree %s" % degree
925
inf = CuspFamily(eta1.level(), 1)
926
loterm = -(min([0, eta1.order_at_cusp(inf)]) + min([0,eta2.order_at_cusp(inf)]))*degree
927
if verbose: print "Lowest order of a term at infinity = %s" % -loterm
928
929
maxdeg = sum([eta1.degree(), eta2.degree()])*degree
930
if verbose: print "Highest possible degree of a term = %s" % maxdeg
931
m = loterm + maxdeg + 1
932
oldgrob = _eta_relations_helper(eta1, eta2, degree, m, labels, verbose)
933
if verbose: print "Check:",
934
newgrob = _eta_relations_helper(eta1, eta2, degree, m+5, labels, verbose)
935
if oldgrob != newgrob:
936
if verbose:
937
raise ArithmeticError, "Answers different!"
938
else:
939
raise ArithmeticError, "Check: answers different!"
940
return newgrob
941
942
def _eta_relations_helper(eta1, eta2, degree, qexp_terms, labels, verbose):
943
r"""
944
Helper function used by eta_poly_relations. Finds a basis for the
945
space of linear relations between the first qexp_terms of the
946
`q`-expansions of the monomials
947
`\eta_1^i * \eta_2^j` for `0 \le i,j < degree`,
948
and calculates a Groebner basis for the ideal generated by these
949
relations.
950
951
Liable to return meaningless results if qexp_terms isn't at least
952
`1 + d*(m_1,m_2)` where
953
954
.. math::
955
956
m_i = min(0, {\text degree of the pole of $\eta_i$ at $\infty$})
957
958
as then 1 will be in the ideal.
959
960
EXAMPLE::
961
962
sage: from sage.modular.etaproducts import _eta_relations_helper
963
sage: r,s = EtaGroup(4).basis()
964
sage: _eta_relations_helper(r,s,4,100,['a','b'],False)
965
[a - b - 16]
966
sage: _eta_relations_helper(EtaProduct(26, {2:2,13:2,26:-2,1:-2}),EtaProduct(26, {2:4,13:2,26:-4,1:-2}),3,12,['a','b'],False) # not enough terms, will return rubbish
967
[1]
968
"""
969
970
indices = [(i,j) for j in range(degree) for i in range(degree)]
971
inf = CuspFamily(eta1.level(), 1)
972
973
pole_at_infinity = -(min([0, eta1.order_at_cusp(inf)]) + min([0,eta2.order_at_cusp(inf)]))*degree
974
if verbose: print "Trying all coefficients from q^%s to q^%s inclusive" % (-pole_at_infinity, -pole_at_infinity + qexp_terms - 1)
975
976
rows = []
977
for j in xrange(qexp_terms):
978
rows.append([])
979
for i in indices:
980
func = (eta1**i[0]*eta2**i[1]).qexp(qexp_terms)
981
for j in xrange(qexp_terms):
982
rows[j].append(func[j - pole_at_infinity])
983
M = matrix(rows)
984
V = M.right_kernel()
985
if V.dimension() == 0:
986
if verbose: print "No polynomial relation of order %s valid for %s terms" % (degree, qexp_terms)
987
return None
988
if V.dimension() >= 1:
989
#print "Found relation: "
990
R = PolynomialRing(QQ, 2, labels)
991
x,y = R.gens()
992
relations = []
993
for c in V.basis():
994
relations.append(sum( [ c[v] * x**indices[v][0] * y**indices[v][1] for v in xrange(len(indices))]))
995
#print relations[-1], " = 0"
996
id = R.ideal(relations)
997
return id.groebner_basis()
998
999