Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
sagemath
GitHub Repository: sagemath/sagelib
Path: blob/master/sage/modular/modsym/boundary.py
4057 views
1
# -*- coding: utf-8 -*-
2
r"""
3
Space of boundary modular symbols
4
5
Used mainly for computing the cuspidal subspace of modular symbols. The space
6
of boundary symbols of sign 0 is isomorphic as a Hecke module to the dual of
7
the space of Eisenstein series, but this does not give a useful method of
8
computing Eisenstein series, since there is no easy way to extract the constant
9
terms.
10
11
We represent boundary modular symbols as a sum of Manin symbols of the form
12
`[P, u/v]`, where `u/v` is a cusp for our group `G`. The group of boundary
13
modular symbols naturally embeds into a vector space `B_k(G)` (see Stein,
14
section 8.4, or Merel, section 1.4, where this space is called `\CC[\Gamma
15
\backslash \QQ]_k`, for a definition), which is a finite dimensional `\QQ`
16
vector space of dimension equal to the number of cusps for `G`. The embedding
17
takes `[P, u/v]` to `P(u,v)\cdot [(u,v)]`. We represent the basis vectors by
18
pairs `[(u,v)]` with u, v coprime. On `B_k(G)`, we have the relations
19
20
.. math::
21
22
[\gamma \cdot (u,v)] = [(u,v)]
23
24
for all `\gamma \in G` and
25
26
.. math::
27
28
[(\lambda u, \lambda v)] = \operatorname{sign}(\lambda)^k [(u,v)]
29
30
31
for all `\lambda \in \QQ^\times`.
32
33
It's possible for these relations to kill a class, i.e., for a pair `[(u,v)]`
34
to be 0. For example, when `N=4` and `k=3` then `(-1,-2)` is equivalent mod
35
`\Gamma_1(4)` to `(1,2)` since `2=-2 \bmod 4` and `1=-1 \bmod 2`. But since `k`
36
is odd, `[(-1,-2)]` is also equivalent to `-[(1,2)]`. Thus this symbol is
37
equivalent to its negative, hence 0 (notice that this wouldn't be the case in
38
characteristic 2). This happens for any irregular cusp when the weight is odd;
39
there are no irregular cusps on `\Gamma_1(N)` except when `N = 4`, but there
40
can be more on `\Gamma_H` groups. See also prop 2.30 of Stein's Ph.D. thesis.
41
42
In addition, in the case that our space is of sign `\sigma = 1` or `-1`, we
43
also have the relation `[(-u,v)] = \sigma \cdot [(u,v)]`. This relation can
44
also combine with the above to kill a cusp class - for instance, take (u,v) =
45
(1,3) for `\Gamma_1(5)`. Then since the cusp `\tfrac{1}{3}` is
46
`\Gamma_1(5)`-equivalent to the cusp `-\tfrac{1}{3}`, we have that `[(1,3)] =
47
[(-1,3)]`. Now, on the minus subspace, we also have that `[(-1,3)] = -[(1,3)]`,
48
which means this class must vanish. Notice that this cannot be used to show
49
that `[(1,0)]` or `[(0,1)]` is 0.
50
51
.. note::
52
53
Special care must be taken when working with the images of the cusps 0 and
54
`\infty` in `B_k(G)`. For all cusps *except* 0 and `\infty`, multiplying the
55
cusp by -1 corresponds to taking `[(u,v)]` to `[(-u,v)]` in `B_k(G)`. This
56
means that `[(u,v)]` is equivalent to `[(-u,v)]` whenever `\tfrac{u}{v}` is
57
equivalent to `-\tfrac{u}{v}`, except in the case of 0 and `\infty`. We
58
have the following conditions for `[(1,0)]` and `[(0,1)]`:
59
60
- `[(0,1)] = \sigma \cdot [(0,1)]`, so `[(0,1)]` is 0 exactly when `\sigma =
61
-1`.
62
63
- `[(1,0)] = \sigma \cdot [(-1,0)]` and `[(1,0)] = (-1)^k [(-1,0)]`, so
64
`[(1,0)] = 0` whenever `\sigma \ne (-1)^k`.
65
66
.. note::
67
68
For all the spaces of boundary symbols below, no work is done to determine
69
the cusps for G at creation time. Instead, cusps are added as they are
70
discovered in the course of computation. As a result, the rank of a space
71
can change as a computation proceeds.
72
73
REFERENCES:
74
75
- Merel, "Universal Fourier expansions of modular
76
forms." Springer LNM 1585 (1994), pg. 59-95.
77
78
- Stein, "Modular Forms, a computational approach." AMS (2007).
79
"""
80
81
#*****************************************************************************
82
# Sage: System for Algebra and Geometry Experimentation
83
#
84
# Copyright (C) 2005 William Stein <[email protected]>
85
#
86
# Distributed under the terms of the GNU General Public License (GPL)
87
#
88
# This code is distributed in the hope that it will be useful,
89
# but WITHOUT ANY WARRANTY; without even the implied warranty of
90
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
91
# General Public License for more details.
92
#
93
# The full text of the GPL is available at:
94
#
95
# http://www.gnu.org/licenses/
96
#*****************************************************************************
97
98
__doc_exclude = ['repr_lincomb', 'QQ']
99
100
# Python imports
101
102
# Sage imports
103
from sage.misc.misc import repr_lincomb
104
105
import sage.modules.free_module as free_module
106
from sage.modules.all import is_FreeModuleElement
107
108
import sage.modular.arithgroup.all as arithgroup
109
import sage.modular.cusps as cusps
110
import sage.modular.dirichlet as dirichlet
111
import sage.modular.hecke.all as hecke
112
113
import sage.rings.all as rings
114
import sage.rings.arith as arith
115
116
import ambient
117
import element
118
import manin_symbols
119
120
121
class BoundarySpaceElement(hecke.HeckeModuleElement):
122
def __init__(self, parent, x):
123
"""
124
Create a boundary symbol.
125
126
INPUT:
127
128
129
- ``parent`` - BoundarySpace; a space of boundary
130
modular symbols
131
132
- ``x`` - a dict with integer keys and values in the
133
base field of parent.
134
135
136
EXAMPLES::
137
138
sage: B = ModularSymbols(Gamma0(32), sign=-1).boundary_space()
139
sage: B(Cusp(1,8))
140
[1/8]
141
sage: B.0
142
[1/8]
143
sage: type(B.0)
144
<class 'sage.modular.modsym.boundary.BoundarySpaceElement'>
145
"""
146
self.__x = x
147
self.__vec = parent.free_module()(x)
148
hecke.HeckeModuleElement.__init__(self, parent, self.__vec)
149
150
def coordinate_vector(self):
151
r"""
152
Return self as a vector on the QQ-vector space with basis
153
self.parent()._known_cusps().
154
155
EXAMPLES::
156
157
sage: B = ModularSymbols(18,4,sign=1).boundary_space()
158
sage: x = B(Cusp(1/2)) ; x
159
[1/2]
160
sage: x.coordinate_vector()
161
(1)
162
sage: ((18/5)*x).coordinate_vector()
163
(18/5)
164
sage: B(Cusp(0))
165
[0]
166
sage: x.coordinate_vector()
167
(1)
168
sage: x = B(Cusp(1/2)) ; x
169
[1/2]
170
sage: x.coordinate_vector()
171
(1, 0)
172
"""
173
return self.__vec
174
175
def _repr_(self):
176
"""
177
Return the string representation of self.
178
179
EXAMPLES::
180
181
sage: ModularSymbols(Gamma0(11), 2).boundary_space()(Cusp(0))._repr_()
182
'[0]'
183
sage: (-6*ModularSymbols(Gamma0(11), 2).boundary_space()(Cusp(0)))._repr_()
184
'-6*[0]'
185
"""
186
g = self.parent()._known_gens_repr
187
return repr_lincomb([ (g[i], c) for i,c in self.__x.items() ])
188
189
# can't inherit arithmetic operations from HeckeModule, because basis
190
# dimension might change!
191
192
def _add_(self, other):
193
"""
194
Return self + other. Assumes that other is a BoundarySpaceElement.
195
196
EXAMPLES::
197
198
sage: B = ModularSymbols(Gamma1(16), 4).boundary_space()
199
sage: x = B(Cusp(2/7)) ; y = B(Cusp(13/16))
200
sage: x + y # indirect doctest
201
[2/7] + [13/16]
202
sage: x + x # indirect doctest
203
2*[2/7]
204
"""
205
z = dict(other.__x)
206
for i, c in self.__x.items():
207
if z.has_key(i):
208
z[i] += c
209
else:
210
z[i] = c
211
return BoundarySpaceElement(self.parent(), z)
212
213
def _sub_(self, other):
214
"""
215
Return self - other. Assumes that other is a BoundarySpaceElement.
216
217
EXAMPLES::
218
219
sage: B = ModularSymbols(Gamma1(16), 4).boundary_space()
220
sage: x = B(Cusp(2/7)) ; y = B(Cusp(13/16))
221
sage: x - y # indirect doctest
222
[2/7] - [13/16]
223
sage: x - x # indirect doctest
224
0
225
"""
226
z = dict(self.__x)
227
for i, c in other.__x.items():
228
if z.has_key(i):
229
z[i] -= c
230
else:
231
z[i] = -c
232
return BoundarySpaceElement(self.parent(), z)
233
234
def _rmul_(self, other):
235
"""
236
Return self \* other. Assumes that other can be coerced into
237
self.parent().base_ring().
238
239
EXAMPLES::
240
241
sage: B = ModularSymbols(Gamma1(16), 4).boundary_space()
242
sage: x = B(Cusp(2/7))
243
sage: x*5 # indirect doctest
244
5*[2/7]
245
sage: x*-3/5 # indirect doctest
246
-3/5*[2/7]
247
"""
248
x = {}
249
for i, c in self.__x.items():
250
x[i] = c*other
251
return BoundarySpaceElement(self.parent(), x)
252
253
def _lmul_(self, other):
254
"""
255
Return other \* self. Assumes that other can be coerced into
256
self.parent().base_ring().
257
258
EXAMPLES::
259
260
sage: B = ModularSymbols(Gamma1(16), 4).boundary_space()
261
sage: x = B(Cusp(13/16))
262
sage: 11*x # indirect doctest
263
11*[13/16]
264
sage: 1/3*x # indirect doctest
265
1/3*[13/16]
266
"""
267
x = {}
268
for i, c in self.__x.items():
269
x[i] = other*c
270
return BoundarySpaceElement(self.parent(), x)
271
272
def __neg__(self):
273
"""
274
Return -self.
275
276
EXAMPLES::
277
278
sage: B = ModularSymbols(Gamma1(16), 4).boundary_space()
279
sage: x = B(Cusp(2/7))
280
sage: -x # indirect doctest
281
-[2/7]
282
sage: -x + x # indirect doctest
283
0
284
"""
285
return self*(-1)
286
287
288
class BoundarySpace(hecke.HeckeModule_generic):
289
def __init__(self,
290
group = arithgroup.Gamma0(1),
291
weight = 2,
292
sign = 0,
293
base_ring = rings.QQ,
294
character = None):
295
"""
296
Space of boundary symbols for a congruence subgroup of SL_2(Z).
297
298
This class is an abstract base class, so only derived classes
299
should be instantiated.
300
301
INPUT:
302
303
304
- ``weight`` - int, the weight
305
306
- ``group`` - arithgroup.congroup_generic.CongruenceSubgroup, a congruence
307
subgroup.
308
309
- ``sign`` - int, either -1, 0, or 1
310
311
- ``base_ring`` - rings.Ring (defaults to the
312
rational numbers)
313
314
315
EXAMPLES::
316
317
sage: B = ModularSymbols(Gamma0(11),2).boundary_space()
318
sage: isinstance(B, sage.modular.modsym.boundary.BoundarySpace)
319
True
320
sage: B == loads(dumps(B))
321
True
322
"""
323
weight = int(weight)
324
if weight <= 1:
325
raise ArithmeticError, "weight must be at least 2"
326
if not arithgroup.is_CongruenceSubgroup(group):
327
raise TypeError, "group must be a congruence subgroup"
328
sign = int(sign)
329
if not isinstance(base_ring, rings.Ring) and rings.is_CommutativeRing(base_ring):
330
raise TypeError, "base_ring must be a commutative ring"
331
if character == None and arithgroup.is_Gamma0(group):
332
character = dirichlet.TrivialCharacter(group.level(), base_ring)
333
(self.__group, self.__weight, self.__character,
334
self.__sign, self.__base_ring) = (group, weight,
335
character, sign, base_ring)
336
self._known_gens = []
337
self._known_gens_repr = []
338
self._is_zero = []
339
hecke.HeckeModule_generic.__init__(self, base_ring, group.level())
340
341
def __cmp__(self, other):
342
"""
343
EXAMPLE::
344
345
sage: B2 = ModularSymbols(11, 2).boundary_space()
346
sage: B4 = ModularSymbols(11, 4).boundary_space()
347
sage: B2 == B4
348
False
349
sage: B2 == ModularSymbols(17, 2).boundary_space()
350
False
351
"""
352
if type(self) != type(other):
353
return cmp(type(self), type(other))
354
else:
355
return cmp( (self.group(), self.weight(), self.character()), (other.group(), other.weight(), other.character()) )
356
357
def _known_cusps(self):
358
"""
359
Return the list of cusps found so far.
360
361
EXAMPLES::
362
363
sage: B = ModularSymbols(Gamma1(12), 4).boundary_space()
364
sage: B._known_cusps()
365
[]
366
sage: ls = [ B(Cusp(i,10)) for i in range(10) ]
367
sage: B._known_cusps()
368
[0, 1/10, 1/5]
369
"""
370
return list(self._known_gens)
371
372
def is_ambient(self):
373
"""
374
Return True if self is a space of boundary symbols associated to an
375
ambient space of modular symbols.
376
377
EXAMPLES::
378
379
sage: M = ModularSymbols(Gamma1(6), 4)
380
sage: M.is_ambient()
381
True
382
sage: M.boundary_space().is_ambient()
383
True
384
"""
385
return True
386
387
def group(self):
388
"""
389
Return the congruence subgroup associated to this space of boundary
390
modular symbols.
391
392
EXAMPLES::
393
394
sage: ModularSymbols(GammaH(14,[9]), 2).boundary_space().group()
395
Congruence Subgroup Gamma_H(14) with H generated by [9]
396
"""
397
return self.__group
398
399
def weight(self):
400
"""
401
Return the weight of this space of boundary modular symbols.
402
403
EXAMPLES::
404
405
sage: ModularSymbols(Gamma1(9), 5).boundary_space().weight()
406
5
407
"""
408
return self.__weight
409
410
def character(self):
411
"""
412
Return the Dirichlet character associated to this space of boundary
413
modular symbols.
414
415
EXAMPLES::
416
417
sage: ModularSymbols(DirichletGroup(7).0, 6).boundary_space().character()
418
Dirichlet character modulo 7 of conductor 7 mapping 3 |--> zeta6
419
"""
420
return self.__character
421
422
def sign(self):
423
"""
424
Return the sign of the complex conjugation involution on this space
425
of boundary modular symbols.
426
427
EXAMPLES::
428
429
sage: ModularSymbols(13,2,sign=-1).boundary_space().sign()
430
-1
431
"""
432
return self.__sign
433
434
def gen(self, i=0):
435
"""
436
Return the i-th generator of this space.
437
438
EXAMPLES::
439
440
sage: B = ModularSymbols(Gamma0(24), 4).boundary_space()
441
sage: B.gen(0)
442
Traceback (most recent call last):
443
...
444
ValueError: only 0 generators known for Space of Boundary Modular Symbols for Congruence Subgroup Gamma0(24) of weight 4 and over Rational Field
445
sage: B(Cusp(1/3))
446
[1/3]
447
sage: B.gen(0)
448
[1/3]
449
"""
450
if i >= len(self._known_gens) or i < 0:
451
raise ValueError, "only %s generators known for %s"%(len(self._known_gens), self)
452
return BoundarySpaceElement(self, {i:1})
453
454
def __len__(self):
455
"""
456
Return the length of self, i.e. the dimension of the underlying
457
vector space.
458
459
EXAMPLES::
460
461
sage: B = ModularSymbols(Gamma0(36),4,sign=1).boundary_space()
462
sage: B.__len__()
463
0
464
sage: len(B)
465
0
466
sage: x = B(Cusp(0)) ; y = B(Cusp(oo)) ; len(B)
467
2
468
"""
469
return len(self._known_gens)
470
471
def free_module(self):
472
"""
473
Return the underlying free module for self.
474
475
EXAMPLES::
476
477
sage: B = ModularSymbols(Gamma1(7), 5, sign=-1).boundary_space()
478
sage: B.free_module()
479
Sparse vector space of dimension 0 over Rational Field
480
sage: x = B(Cusp(0)) ; y = B(Cusp(1/7)) ; B.free_module()
481
Sparse vector space of dimension 2 over Rational Field
482
"""
483
return free_module.FreeModule(self.__base_ring, len(self._known_gens), sparse=True)
484
485
def rank(self):
486
"""
487
The rank of the space generated by boundary symbols that have been
488
found so far in the course of computing the boundary map.
489
490
.. warning::
491
492
This number may change as more elements are coerced into
493
this space!! (This is an implementation detail that will
494
likely change.)
495
496
EXAMPLES::
497
498
sage: M = ModularSymbols(Gamma0(72), 2) ; B = M.boundary_space()
499
sage: B.rank()
500
0
501
sage: _ = [ B(x) for x in M.basis() ]
502
sage: B.rank()
503
16
504
"""
505
return len(self._known_gens)
506
507
#####################################################################
508
# Coercion
509
#####################################################################
510
511
def _coerce_in_manin_symbol(self, x):
512
"""
513
Coerce the Manin symbol x into self. (That is, return the image of
514
x under the boundary map.)
515
516
Assumes that x is associated to the same space of modular symbols
517
as self.
518
519
EXAMPLES::
520
521
sage: M = ModularSymbols(Gamma1(5), 4) ; B = M.boundary_space()
522
sage: [ B(x) for x in M.basis() ]
523
[-[2/5], -[-1/5], -[1/2], -[1/2], -[1/4], -[1/4]]
524
sage: [ B._coerce_in_manin_symbol(x) for x in M.manin_symbols_basis() ]
525
[-[2/5], -[-1/5], -[1/2], -[1/2], -[1/4], -[1/4]]
526
"""
527
i = x.i
528
alpha, beta = x.endpoints(self.level())
529
if self.weight() == 2:
530
return self(alpha) - self(beta)
531
if i == 0:
532
return self(alpha)
533
elif i == self.weight() - 2:
534
return -self(beta)
535
else:
536
return self(0)
537
538
def __call__(self, x):
539
"""
540
Coerce x into a boundary symbol space.
541
542
If x is a modular symbol (with the same group, weight, character,
543
sign, and base field), this returns the image of that modular
544
symbol under the boundary map.
545
546
EXAMPLES::
547
548
sage: M = ModularSymbols(Gamma0(15), 2) ; B = M.boundary_space()
549
sage: B(M.0)
550
[Infinity] - [0]
551
sage: B(Cusp(1))
552
[0]
553
sage: B(Cusp(oo))
554
[Infinity]
555
sage: B(7)
556
Traceback (most recent call last):
557
...
558
TypeError: Coercion of 7 (of type <type 'sage.rings.integer.Integer'>) into Space of Boundary Modular Symbols for Congruence Subgroup Gamma0(15) of weight 2 and over Rational Field not (yet) defined.
559
"""
560
if isinstance(x, int) and x == 0:
561
return BoundarySpaceElement(self, {})
562
563
elif isinstance(x, cusps.Cusp):
564
return self._coerce_cusp(x)
565
566
elif manin_symbols.is_ManinSymbol(x):
567
return self._coerce_in_manin_symbol(x)
568
569
elif element.is_ModularSymbolsElement(x):
570
M = x.parent()
571
if not isinstance(M, ambient.ModularSymbolsAmbient):
572
raise TypeError, "x (=%s) must be an element of a space of modular symbols of type ModularSymbolsAmbient"%x
573
if M.level() != self.level():
574
raise TypeError, "x (=%s) must have level %s but has level %s"%(
575
x, self.level(), M.level())
576
S = x.manin_symbol_rep()
577
if len(S) == 0:
578
return self(0)
579
return sum([c*self._coerce_in_manin_symbol(v) for c, v in S])
580
581
elif is_FreeModuleElement(x):
582
y = dict([(i,x[i]) for i in xrange(len(x))])
583
return BoundarySpaceElement(self, y)
584
585
raise TypeError, "Coercion of %s (of type %s) into %s not (yet) defined."%(x, type(x), self)
586
587
def _repr_(self):
588
"""
589
Return the string representation of self.
590
591
EXAMPLES::
592
593
sage: sage.modular.modsym.boundary.BoundarySpace(Gamma0(3), 2)._repr_()
594
'Space of Boundary Modular Symbols of weight 2 for Congruence Subgroup Gamma0(3) with sign 0 and character [1] over Rational Field'
595
"""
596
return ("Space of Boundary Modular Symbols of weight %s for" + \
597
" %s with sign %s and character %s over %s")%(
598
self.weight(), self.group(), self.sign(),
599
self.character()._repr_short_(), self.base_ring())
600
601
def _cusp_index(self, cusp):
602
"""
603
Return the index of the first cusp in self._known_cusps()
604
equivalent to cusp, or -1 if cusp is not equivalent to any cusp
605
found so far.
606
607
EXAMPLES::
608
609
sage: B = ModularSymbols(Gamma0(21), 4).boundary_space()
610
sage: B._cusp_index(Cusp(0))
611
-1
612
sage: _ = B(Cusp(oo))
613
sage: _ = B(Cusp(0))
614
sage: B._cusp_index(Cusp(0))
615
1
616
"""
617
g = self._known_gens
618
N = self.level()
619
for i in xrange(len(g)):
620
if self._is_equiv(cusp, g[i]):
621
return i
622
return -1
623
624
class BoundarySpace_wtk_g0(BoundarySpace):
625
def __init__(self, level, weight, sign, F):
626
"""
627
Initialize a space of boundary symbols of weight k for Gamma_0(N)
628
over base field F.
629
630
INPUT:
631
632
633
- ``level`` - int, the level
634
635
- ``weight`` - integer weight = 2.
636
637
- ``sign`` - int, either -1, 0, or 1
638
639
- ``F`` - field
640
641
642
EXAMPLES::
643
644
sage: B = ModularSymbols(Gamma0(2), 5).boundary_space()
645
sage: type(B)
646
<class 'sage.modular.modsym.boundary.BoundarySpace_wtk_g0_with_category'>
647
sage: B == loads(dumps(B))
648
True
649
"""
650
level = int(level)
651
sign = int(sign)
652
weight = int(weight)
653
if not sign in [-1,0,1]:
654
raise ArithmeticError, "sign must be an int in [-1,0,1]"
655
if level <= 0:
656
raise ArithmeticError, "level must be positive"
657
BoundarySpace.__init__(self,
658
weight = weight,
659
group = arithgroup.Gamma0(level),
660
sign = sign,
661
base_ring = F)
662
663
def _repr_(self):
664
"""
665
Return the string representation of self.
666
667
EXAMPLES::
668
669
sage: B = ModularSymbols(Gamma0(97), 3).boundary_space()
670
sage: B._repr_()
671
'Space of Boundary Modular Symbols for Congruence Subgroup Gamma0(97) of weight 3 and over Rational Field'
672
"""
673
return ("Space of Boundary Modular Symbols for %s of weight %s " + \
674
"and over %s")%(self.group(), self.weight(), self.base_ring())
675
676
def _coerce_cusp(self, c):
677
"""
678
Coerce the cusp c into this boundary symbol space.
679
680
EXAMPLES::
681
682
sage: B = ModularSymbols(Gamma0(17), 6).boundary_space()
683
sage: B._coerce_cusp(Cusp(0))
684
[0]
685
sage: B = ModularSymbols(Gamma0(17), 6, sign=-1).boundary_space()
686
sage: B._coerce_cusp(Cusp(0))
687
0
688
sage: B = ModularSymbols(Gamma0(16), 4).boundary_space()
689
sage: [ B(Cusp(i,4)) for i in range(4) ]
690
[[0], [1/4], [1/2], [3/4]]
691
sage: B = ModularSymbols(Gamma0(16), 4, sign=1).boundary_space()
692
sage: [ B(Cusp(i,4)) for i in range(4) ]
693
[[0], [1/4], [1/2], [1/4]]
694
sage: B = ModularSymbols(Gamma0(16), 4, sign=-1).boundary_space()
695
sage: [ B(Cusp(i,4)) for i in range(4) ]
696
[0, [1/4], 0, -[1/4]]
697
"""
698
if self.weight()%2 != 0:
699
return self(0)
700
N = self.level()
701
702
# see if we've already found this cusp
703
i = self._cusp_index(c)
704
if i != -1:
705
if i in self._is_zero:
706
return self(0)
707
return BoundarySpaceElement(self, {i:1})
708
709
# see if we've already found -c
710
sign = self.sign()
711
if sign != 0:
712
i2 = self._cusp_index(-c)
713
if i2 != -1:
714
if i2 in self._is_zero:
715
return self(0)
716
return BoundarySpaceElement(self, {i2:sign})
717
718
# found a new cusp class
719
g = self._known_gens
720
g.append(c)
721
self._known_gens_repr.append("[%s]"%c)
722
723
# See if the new cusp is killed by sign relations. The
724
# relevant relations (for cusps other than 0 and Infinity)
725
# are:
726
#
727
# [(u,v)] = (-1)^k [(-u,-v)]
728
# [(u,v)] = [gamma * (u,v)]
729
# [(-u,v)] = sign * [(u,v)]
730
#
731
# So since k is always even on Gamma0, we have that [(u,v)] =
732
# 0 from the above relations exactly when (u,v) = gamma*(-u,v)
733
# and the sign is -1.
734
if sign == -1:
735
# NOTE: this code looks wrong. One should do the
736
# following:
737
#
738
# - if c is 0, if the sign is -1, append & return 0
739
# - if c is Infinity, then if the sign
740
# is not equal to (-1)**self.weight(), then
741
# append & return 0
742
# - otherwise, if the sign is -1, and c is
743
# equivalent to -c, append & return 0.
744
#
745
# Interestingly, the code below does precisely that.
746
# (It's important to recall that for Gamma0, odd weight
747
# spaces are 0.)
748
if self._is_equiv(c, -c):
749
self._is_zero.append(len(g)-1)
750
return self(0)
751
752
return BoundarySpaceElement(self, {(len(g)-1):1})
753
754
def _is_equiv(self, c1, c2):
755
"""
756
Determine whether or not c1 and c2 are equivalent for self.
757
758
EXAMPLES::
759
760
sage: B = ModularSymbols(Gamma0(24), 6).boundary_space()
761
sage: B._is_equiv(Cusp(0), Cusp(oo))
762
False
763
sage: B._is_equiv(Cusp(0), Cusp(1))
764
True
765
"""
766
return c1.is_gamma0_equiv(c2, self.level())
767
768
769
class BoundarySpace_wtk_g1(BoundarySpace):
770
def __init__(self, level, weight, sign, F):
771
"""
772
Initialize a space of boundary modular symbols for Gamma1(N).
773
774
INPUT:
775
776
777
- ``level`` - int, the level
778
779
- ``weight`` - int, the weight = 2
780
781
- ``sign`` - int, either -1, 0, or 1
782
783
- ``F`` - base ring
784
785
786
EXAMPLES::
787
788
sage: from sage.modular.modsym.boundary import BoundarySpace_wtk_g1
789
sage: B = BoundarySpace_wtk_g1(17, 2, 0, QQ) ; B
790
Boundary Modular Symbols space for Gamma_1(17) of weight 2 over Rational Field
791
sage: B == loads(dumps(B))
792
True
793
"""
794
level = int(level)
795
sign = int(sign)
796
if not sign in [-1,0,1]:
797
raise ArithmeticError, "sign must be an int in [-1,0,1]"
798
if level <= 0:
799
raise ArithmeticError, "level must be positive"
800
801
BoundarySpace.__init__(self,
802
weight = weight,
803
group = arithgroup.Gamma1(level),
804
sign = sign,
805
base_ring = F)
806
807
def _repr_(self):
808
"""
809
Return the string representation of self.
810
811
EXAMPLES::
812
813
sage: ModularSymbols(Gamma1(5), 3, sign=1).boundary_space()._repr_()
814
'Boundary Modular Symbols space for Gamma_1(5) of weight 3 over Rational Field'
815
"""
816
return ("Boundary Modular Symbols space for Gamma_1(%s) of weight %s " + \
817
"over %s")%(self.level(),self.weight(), self.base_ring())
818
819
820
def _is_equiv(self, c1, c2):
821
"""
822
Return True if c1 and c2 are equivalent cusps for self, and False
823
otherwise.
824
825
EXAMPLES::
826
827
sage: B = ModularSymbols(Gamma1(10), 4).boundary_space()
828
sage: B._is_equiv(Cusp(0), Cusp(1/5))
829
(False, 0)
830
sage: B._is_equiv(Cusp(4/5), Cusp(1/5))
831
(True, -1)
832
sage: B._is_equiv(Cusp(-4/5), Cusp(1/5))
833
(True, 1)
834
"""
835
return c1.is_gamma1_equiv(c2, self.level())
836
837
def _cusp_index(self, cusp):
838
"""
839
Returns a pair (i, t), where i is the index of the first cusp in
840
self._known_cusps() which is equivalent to cusp, and t is 1 or -1
841
as cusp is Gamma1-equivalent to plus or minus
842
self._known_cusps()[i]. If cusp is not equivalent to any known
843
cusp, return (-1, 0).
844
845
EXAMPLES::
846
847
sage: B = ModularSymbols(Gamma1(11),2).boundary_space()
848
sage: B._cusp_index(Cusp(1/11))
849
(-1, 0)
850
sage: B._cusp_index(Cusp(10/11))
851
(-1, 0)
852
sage: B._coerce_cusp(Cusp(1/11))
853
[1/11]
854
sage: B._cusp_index(Cusp(1/11))
855
(0, 1)
856
sage: B._cusp_index(Cusp(10/11))
857
(0, -1)
858
"""
859
g = self._known_gens
860
N = self.level()
861
for i in xrange(len(g)):
862
t, eps = self._is_equiv(cusp, g[i])
863
if t:
864
return i, eps
865
return -1, 0
866
867
def _coerce_cusp(self, c):
868
"""
869
Coerce a cusp into this boundary symbol space.
870
871
EXAMPLES::
872
873
sage: B = ModularSymbols(Gamma1(4), 4).boundary_space()
874
sage: B._coerce_cusp(Cusp(1/2))
875
[1/2]
876
sage: B._coerce_cusp(Cusp(1/4))
877
[1/4]
878
sage: B._coerce_cusp(Cusp(3/4))
879
[1/4]
880
sage: B = ModularSymbols(Gamma1(5), 3, sign=-1).boundary_space()
881
sage: B._coerce_cusp(Cusp(0))
882
0
883
sage: B._coerce_cusp(Cusp(oo))
884
[Infinity]
885
sage: B = ModularSymbols(Gamma1(2), 3, sign=-1).boundary_space()
886
sage: B._coerce_cusp(Cusp(0))
887
0
888
sage: B._coerce_cusp(Cusp(oo))
889
0
890
sage: B = ModularSymbols(Gamma1(7), 3).boundary_space()
891
sage: [ B(Cusp(i,7)) for i in range(7) ]
892
[[0], [1/7], [2/7], [3/7], -[3/7], -[2/7], -[1/7]]
893
sage: B._is_equiv(Cusp(1,6), Cusp(5,6))
894
(True, 1)
895
sage: B._is_equiv(Cusp(1,6), Cusp(0))
896
(True, -1)
897
sage: B(Cusp(0))
898
[0]
899
sage: B = ModularSymbols(Gamma1(7), 3, sign=1).boundary_space()
900
sage: [ B(Cusp(i,7)) for i in range(7) ]
901
[[0], 0, 0, 0, 0, 0, 0]
902
sage: B = ModularSymbols(Gamma1(7), 3, sign=-1).boundary_space()
903
sage: [ B(Cusp(i,7)) for i in range(7) ]
904
[0, [1/7], [2/7], [3/7], -[3/7], -[2/7], -[1/7]]
905
"""
906
N = self.level()
907
k = self.weight()
908
sign = self.sign()
909
i, eps = self._cusp_index(c)
910
if i != -1:
911
if i in self._is_zero:
912
return self(0)
913
return BoundarySpaceElement(self, {i : eps**k})
914
915
if sign != 0:
916
i2, eps = self._cusp_index(-c)
917
if i2 != -1:
918
if i2 in self._is_zero:
919
return self(0)
920
else:
921
return BoundarySpaceElement(self, {i2:sign*(eps**k)})
922
923
# found a new cusp class
924
g = self._known_gens
925
g.append(c)
926
self._known_gens_repr.append("[%s]"%c)
927
928
# Does cusp class vanish because of - relations? (See note at top
929
# of file.)
930
if k % 2 != 0:
931
(u, v) = (c.numerator(), c.denominator())
932
if (2*v) % N == 0:
933
if (2*u) % v.gcd(N) == 0:
934
self._is_zero.append(len(g)-1)
935
return self(0)
936
937
# Does class vanish because of sign relations? The relevant
938
# relations are
939
#
940
# [(u,v)] = (-1)^k [(-u,-v)]
941
# [(u,v)] = sign * [(-u,v)]
942
# [(u,v)] = eps * (-1)^k [(-u,v)]
943
#
944
# where, in the last line, (u,v) is Gamma1-equivalent to
945
# (-u,v) or (u,-v) as eps is 1 or -1.
946
#
947
# Thus (other than for 0 and Infinity), we have that [(u,v)]
948
# can only be killed by sign relations when:
949
#
950
# - (u,v) is Gamma1-equivalent to (-u,v) or (u,-v), and
951
# - eps is 1 and sign is -1, or eps is -1 and sign is not
952
# (-1)^k.
953
#
954
if sign:
955
if c.is_infinity():
956
if sign != (-1)**self.weight():
957
self._is_zero.append(len(g)-1)
958
return self(0)
959
elif c.is_zero():
960
if (sign == -1):
961
self._is_zero.append(len(g)-1)
962
return self(0)
963
else:
964
t, eps = self._is_equiv(c, -c)
965
if t and ((eps == 1 and sign == -1) or \
966
(eps == -1 and sign != (-1)**self.weight())):
967
self._is_zero.append(len(g)-1)
968
return self(0)
969
970
return BoundarySpaceElement(self, {(len(g)-1):1})
971
972
class BoundarySpace_wtk_gamma_h(BoundarySpace):
973
def __init__(self, group, weight, sign, F):
974
"""
975
Initialize a space of boundary modular symbols for GammaH(N).
976
977
INPUT:
978
979
980
- ``group`` - congruence subgroup Gamma_H(N).
981
982
- ``weight`` - int, the weight = 2
983
984
- ``sign`` - int, either -1, 0, or 1
985
986
- ``F`` - base ring
987
988
989
EXAMPLES::
990
991
sage: from sage.modular.modsym.boundary import BoundarySpace_wtk_gamma_h
992
sage: B = BoundarySpace_wtk_gamma_h(GammaH(13,[3]), 2, 0, QQ) ; B
993
Boundary Modular Symbols space for Congruence Subgroup Gamma_H(13) with H generated by [3] of weight 2 over Rational Field
994
sage: B == loads(dumps(B))
995
True
996
"""
997
sign = int(sign)
998
if not sign in [-1,0,1]:
999
raise ArithmeticError, "sign must be an int in [-1,0,1]"
1000
1001
BoundarySpace.__init__(self,
1002
weight = weight,
1003
group = group,
1004
sign = sign,
1005
base_ring = F)
1006
1007
def _repr_(self):
1008
"""
1009
Return the string representation of self.
1010
1011
EXAMPLES::
1012
1013
sage: ModularSymbols(GammaH(7,[2]), 4).boundary_space()._repr_()
1014
'Boundary Modular Symbols space for Congruence Subgroup Gamma_H(7) with H generated by [2] of weight 4 over Rational Field'
1015
"""
1016
return ("Boundary Modular Symbols space for %s of weight %s " + \
1017
"over %s")%(self.group(),self.weight(), self.base_ring())
1018
1019
1020
def _is_equiv(self, c1, c2):
1021
"""
1022
Return a pair of the form (b, t), where b is True if c1 and c2 are
1023
equivalent cusps for self, and False otherwise, and t gives extra
1024
information about the equivalence between c1 and c2.
1025
1026
EXAMPLES::
1027
1028
sage: B = ModularSymbols(GammaH(7,[2]), 4).boundary_space()
1029
sage: B._is_equiv(Cusp(0), Cusp(1/7))
1030
(False, 0)
1031
sage: B._is_equiv(Cusp(2/7), Cusp(1/7))
1032
(True, 1)
1033
sage: B._is_equiv(Cusp(3/7), Cusp(1/7))
1034
(True, -1)
1035
"""
1036
return c1.is_gamma_h_equiv(c2, self.group())
1037
1038
def _cusp_index(self, cusp):
1039
"""
1040
Returns a pair (i, t), where i is the index of the first cusp in
1041
self._known_cusps() which is equivalent to cusp, and t is 1 or -1
1042
as cusp is GammaH-equivalent to plus or minus
1043
self._known_cusps()[i]. If cusp is not equivalent to any known
1044
cusp, return (-1, 0).
1045
1046
EXAMPLES::
1047
1048
sage: M = ModularSymbols(GammaH(9,[4]), 3)
1049
sage: B = M.boundary_space()
1050
sage: B._cusp_index(Cusp(0))
1051
(-1, 0)
1052
sage: _ = [ B(x) for x in M.basis() ]
1053
sage: B._cusp_index(Cusp(0))
1054
(1, -1)
1055
sage: B._cusp_index(Cusp(5/6))
1056
(3, 1)
1057
"""
1058
g = self._known_gens
1059
N = self.level()
1060
for i in xrange(len(g)):
1061
t, eps = self._is_equiv(cusp, g[i])
1062
if t:
1063
return i, eps
1064
return -1, 0
1065
1066
def _coerce_cusp(self, c):
1067
"""
1068
Coerce the cusp c into self.
1069
1070
EXAMPLES::
1071
1072
sage: B = ModularSymbols(GammaH(10,[9]), 2).boundary_space()
1073
sage: B(Cusp(0))
1074
[0]
1075
sage: B(Cusp(1/3))
1076
[1/3]
1077
sage: B(Cusp(1/13))
1078
[1/3]
1079
sage: B = ModularSymbols(GammaH(25, [6]), 2).boundary_space()
1080
sage: B._coerce_cusp(Cusp(0))
1081
[0]
1082
1083
::
1084
1085
sage: B = ModularSymbols(GammaH(11,[3]), 3).boundary_space()
1086
sage: [ B(Cusp(i,11)) for i in range(11) ]
1087
[[0],
1088
[1/11],
1089
-[1/11],
1090
[1/11],
1091
[1/11],
1092
[1/11],
1093
-[1/11],
1094
-[1/11],
1095
-[1/11],
1096
[1/11],
1097
-[1/11]]
1098
sage: B._is_equiv(Cusp(0), Cusp(1,11))
1099
(False, 0)
1100
sage: B._is_equiv(Cusp(oo), Cusp(1,11))
1101
(True, 1)
1102
sage: B = ModularSymbols(GammaH(11,[3]), 3, sign=1).boundary_space()
1103
sage: [ B(Cusp(i,11)) for i in range(11) ]
1104
[[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1105
sage: B = ModularSymbols(GammaH(11,[3]), 3, sign=-1).boundary_space()
1106
sage: [ B(Cusp(i,11)) for i in range(11) ]
1107
[0,
1108
[1/11],
1109
-[1/11],
1110
[1/11],
1111
[1/11],
1112
[1/11],
1113
-[1/11],
1114
-[1/11],
1115
-[1/11],
1116
[1/11],
1117
-[1/11]]
1118
"""
1119
N = self.level()
1120
k = self.weight()
1121
sign = self.sign()
1122
i, eps = self._cusp_index(c)
1123
if i != -1:
1124
if i in self._is_zero:
1125
return self(0)
1126
return BoundarySpaceElement(self, {i : eps**k})
1127
1128
if sign != 0:
1129
i2, eps = self._cusp_index(-c)
1130
if i2 != -1:
1131
if i2 in self._is_zero:
1132
return self(0)
1133
return BoundarySpaceElement(self, {i2:sign*(eps**k)})
1134
1135
# found a new cusp class
1136
g = self._known_gens
1137
g.append(c)
1138
self._known_gens_repr.append("[%s]"%c)
1139
1140
# Does cusp class vanish because of - relations? (See note at top
1141
# of file.)
1142
if k % 2 != 0:
1143
(u, v) = (c.numerator(), c.denominator())
1144
if (2*v) % N == 0:
1145
if (2*u) % v.gcd(N) == 0:
1146
self._is_zero.append(len(g)-1)
1147
return self(0)
1148
1149
# Does class vanish because of sign relations? The relevant
1150
# relations are
1151
#
1152
# [(u,v)] = (-1)^k [(-u,-v)]
1153
# [(u,v)] = sign * [(-u,v)]
1154
# [(u,v)] = eps * (-1)^k [(-u,v)]
1155
#
1156
# where, in the last line, (u,v) is GammaH-equivalent to
1157
# (-u,v) or (u,-v) as eps is 1 or -1.
1158
#
1159
# Thus (other than for 0 and Infinity), we have that [(u,v)]
1160
# can only be killed by sign relations when:
1161
#
1162
# - (u,v) is GammaH-equivalent to (-u,v) or (u,-v), and
1163
# - eps is 1 and sign is -1, or eps is -1 and sign is not
1164
# (-1)^k.
1165
#
1166
# (Notice that while this description looks identical to that
1167
# of Gamma1, it differs in that the condition of being GammaH
1168
# equivalent is weaker than that of being Gamma1 equivalent
1169
# when H is larger than {1}.)
1170
#
1171
if sign:
1172
if c.is_infinity():
1173
if sign != (-1)**self.weight():
1174
self._is_zero.append(len(g)-1)
1175
return self(0)
1176
elif c.is_zero():
1177
if (sign == -1):
1178
self._is_zero.append(len(g)-1)
1179
return self(0)
1180
else:
1181
t, eps = self._is_equiv(c, -c)
1182
if t and ((eps == 1 and sign == -1) or \
1183
(eps == -1 and sign != (-1)**self.weight())):
1184
self._is_zero.append(len(g)-1)
1185
return self(0)
1186
1187
return BoundarySpaceElement(self, {(len(g)-1):1})
1188
1189
1190
class BoundarySpace_wtk_eps(BoundarySpace):
1191
def __init__(self, eps, weight, sign=0):
1192
"""
1193
Space of boundary modular symbols with given weight, character, and
1194
sign.
1195
1196
INPUT:
1197
1198
1199
- ``eps`` - dirichlet.DirichletCharacter, the
1200
"Nebentypus" character.
1201
1202
- ``weight`` - int, the weight = 2
1203
1204
- ``sign`` - int, either -1, 0, or 1
1205
1206
1207
EXAMPLES::
1208
1209
sage: B = ModularSymbols(DirichletGroup(6).0, 4).boundary_space() ; B
1210
Boundary Modular Symbols space of level 6, weight 4, character [-1] and dimension 0 over Rational Field
1211
sage: type(B)
1212
<class 'sage.modular.modsym.boundary.BoundarySpace_wtk_eps_with_category'>
1213
sage: B == loads(dumps(B))
1214
True
1215
"""
1216
level = eps.modulus()
1217
sign = int(sign)
1218
self.__eps = eps
1219
if not sign in [-1,0,1]:
1220
raise ArithmeticError, "sign must be an int in [-1,0,1]"
1221
if level <= 0:
1222
raise ArithmeticError, "level must be positive"
1223
BoundarySpace.__init__(self,
1224
weight = weight,
1225
group = arithgroup.Gamma1(level),
1226
sign = sign,
1227
base_ring = eps.base_ring(),
1228
character = eps)
1229
1230
def _repr_(self):
1231
"""
1232
Return the string representation of self.
1233
1234
EXAMPLES::
1235
1236
sage: ModularSymbols(DirichletGroup(6).0, 4).boundary_space()._repr_()
1237
'Boundary Modular Symbols space of level 6, weight 4, character [-1] and dimension 0 over Rational Field'
1238
"""
1239
return ("Boundary Modular Symbols space of level %s, weight %s, character %s " + \
1240
"and dimension %s over %s")%(self.level(), self.weight(),
1241
self.character()._repr_short_(), self.rank(), self.base_ring())
1242
1243
1244
def _is_equiv(self, c1, c2):
1245
"""
1246
Return a pair (b, t), where b is True if c1 and c2 are equivalent
1247
cusps for self, and False otherwise, and t gives extra information
1248
about the equivalence of c1 and c2.
1249
1250
EXAMPLES::
1251
1252
sage: B = ModularSymbols(DirichletGroup(12).1, 3).boundary_space()
1253
sage: B._is_equiv(Cusp(0), Cusp(1/3))
1254
(False, None)
1255
sage: B._is_equiv(Cusp(2/3), Cusp(1/3))
1256
(True, 5)
1257
sage: B._is_equiv(Cusp(3/4), Cusp(1/4))
1258
(True, 7)
1259
"""
1260
return c1.is_gamma0_equiv(c2, self.level(), transformation=True)
1261
1262
def _cusp_index(self, cusp):
1263
"""
1264
Returns a pair (i, s), where i is the index of the first cusp in
1265
self._known_cusps() which is equivalent to cusp, and such that
1266
cusp is Gamma0-equivalent to self.character()(s) times
1267
self._known_cusps()[i]. If cusp is not equivalent to any known
1268
cusp, return (-1, 0).
1269
1270
EXAMPLES::
1271
1272
sage: B = ModularSymbols(DirichletGroup(11).0**3, 5).boundary_space()
1273
sage: B._cusp_index(Cusp(0))
1274
(-1, 0)
1275
sage: B._coerce_cusp(Cusp(0))
1276
[0]
1277
sage: B._cusp_index(Cusp(0))
1278
(0, 1)
1279
sage: B._coerce_cusp(Cusp(1,11))
1280
[1/11]
1281
sage: B._cusp_index(Cusp(2,11))
1282
(1, -zeta10^2)
1283
"""
1284
g = self._known_gens
1285
N = self.level()
1286
for i in xrange(len(g)):
1287
t, s = self._is_equiv(cusp, g[i])
1288
if t:
1289
return i, self.__eps(s)
1290
return -1, 0
1291
1292
def _coerce_cusp(self, c):
1293
"""
1294
Coerce the cusp c into self.
1295
1296
EXAMPLES::
1297
1298
sage: B = ModularSymbols(DirichletGroup(13).0**3, 5, sign=0).boundary_space()
1299
sage: [ B(Cusp(i,13)) for i in range(13) ]
1300
[[0],
1301
[1/13],
1302
(-zeta4)*[1/13],
1303
[1/13],
1304
(-1)*[1/13],
1305
(-zeta4)*[1/13],
1306
(-zeta4)*[1/13],
1307
zeta4*[1/13],
1308
zeta4*[1/13],
1309
[1/13],
1310
(-1)*[1/13],
1311
zeta4*[1/13],
1312
(-1)*[1/13]]
1313
sage: B._is_equiv(Cusp(oo), Cusp(1,13))
1314
(True, 1)
1315
sage: B._is_equiv(Cusp(0), Cusp(1,13))
1316
(False, None)
1317
sage: B = ModularSymbols(DirichletGroup(13).0**3, 5, sign=1).boundary_space()
1318
sage: [ B(Cusp(i,13)) for i in range(13) ]
1319
[[0], 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
1320
sage: B._coerce_cusp(Cusp(oo))
1321
0
1322
sage: B = ModularSymbols(DirichletGroup(13).0**3, 5, sign=-1).boundary_space()
1323
sage: [ B(Cusp(i,13)) for i in range(13) ]
1324
[0,
1325
[1/13],
1326
(-zeta4)*[1/13],
1327
[1/13],
1328
(-1)*[1/13],
1329
(-zeta4)*[1/13],
1330
(-zeta4)*[1/13],
1331
zeta4*[1/13],
1332
zeta4*[1/13],
1333
[1/13],
1334
(-1)*[1/13],
1335
zeta4*[1/13],
1336
(-1)*[1/13]]
1337
sage: B = ModularSymbols(DirichletGroup(13).0**4, 5, sign=1).boundary_space()
1338
sage: B._coerce_cusp(Cusp(0))
1339
[0]
1340
sage: B = ModularSymbols(DirichletGroup(13).0**4, 5, sign=-1).boundary_space()
1341
sage: B._coerce_cusp(Cusp(0))
1342
0
1343
"""
1344
N = self.level()
1345
k = self.weight()
1346
sign = self.sign()
1347
i, eps = self._cusp_index(c)
1348
if i != -1:
1349
if i in self._is_zero:
1350
return self(0)
1351
return BoundarySpaceElement(self, {i : eps})
1352
1353
if sign != 0:
1354
i2, eps = self._cusp_index(-c)
1355
if i2 != -1:
1356
if i2 in self._is_zero:
1357
return self(0)
1358
return BoundarySpaceElement(self, {i2:sign*eps})
1359
1360
# found a new cusp class
1361
g = self._known_gens
1362
g.append(c)
1363
self._known_gens_repr.append("[%s]"%c)
1364
1365
###############################################################
1366
# TODO?: This is a very dumb way to check for solutions to an
1367
# equation (see Prop 2.30 of Stein's Ph.D. thesis for which
1368
# equation); however, computing the cusp equivalence for the
1369
# boundary map takes much less time than computing the kernel
1370
# of the boundary map, so it's not worth optimizing this now.
1371
###############################################################
1372
1373
(u, v) = (c.numerator(), c.denominator())
1374
gcd = arith.gcd
1375
d = gcd(v,N)
1376
x = N//d
1377
1378
for j in range(d):
1379
alpha = 1 - j*x
1380
if gcd(alpha, N) == 1:
1381
if (v*(1-alpha))%N == 0 and (u*(1-alpha))%d == 0:
1382
if self.__eps(alpha) != 1:
1383
self._is_zero.append(len(g)-1)
1384
return self(0)
1385
1386
# Does class vanish because of sign relations? The relevant
1387
# relations are
1388
#
1389
# [(u,v)] = (-1)^k [(-u,-v)]
1390
# [(u,v)] = sign * [(-u,v)]
1391
# [(u,v)] = eps(d) * [(-u,v)]
1392
#
1393
# where, in the last line, eps is the character defining
1394
# our space, and [a,b;c,d] takes (u,v) to (-u,v).
1395
#
1396
# Thus (other than for 0 and Infinity), we have that [(u,v)]
1397
# can only be killed by sign relations when the sign is not
1398
# equal to eps(d).
1399
#
1400
if sign:
1401
if c.is_zero():
1402
if sign == -1:
1403
self._is_zero.append(len(g)-1)
1404
return self(0)
1405
elif c.is_infinity():
1406
if sign != (-1)**self.weight():
1407
self._is_zero.append(len(g)-1)
1408
return self(0)
1409
else:
1410
t, s = self._is_equiv(c, -c)
1411
if t:
1412
if sign != self.__eps(s):
1413
self._is_zero.append(len(g)-1)
1414
return self(0)
1415
1416
return BoundarySpaceElement(self, {(len(g)-1):1})
1417
1418
1419
1420