hilbert-modular-group 0.1.3__cp312-cp312-macosx_15_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hilbert-modular-group might be problematic. Click here for more details.
- hilbert_modgroup/__init__.py +1 -0
- hilbert_modgroup/all.py +9 -0
- hilbert_modgroup/hilbert_modular_group_class.py +987 -0
- hilbert_modgroup/hilbert_modular_group_element.cpython-312-darwin.so +0 -0
- hilbert_modgroup/hilbert_modular_group_element.pyx +623 -0
- hilbert_modgroup/pullback.py +2256 -0
- hilbert_modgroup/pullback_cython.cpython-312-darwin.so +0 -0
- hilbert_modgroup/pullback_cython.pyx +411 -0
- hilbert_modgroup/upper_half_plane.cpython-312-darwin.so +0 -0
- hilbert_modgroup/upper_half_plane.pxd +59 -0
- hilbert_modgroup/upper_half_plane.pyx +1828 -0
- hilbert_modgroup/utils.py +66 -0
- hilbert_modgroup/version.py +21 -0
- hilbert_modular_group-0.1.3.dist-info/METADATA +891 -0
- hilbert_modular_group-0.1.3.dist-info/RECORD +18 -0
- hilbert_modular_group-0.1.3.dist-info/WHEEL +5 -0
- hilbert_modular_group-0.1.3.dist-info/licenses/LICENSE +674 -0
- hilbert_modular_group-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,987 @@
|
|
|
1
|
+
r"""
|
|
2
|
+
Classes for (projective) Hilbert modular groups PSL_2(OK)
|
|
3
|
+
|
|
4
|
+
EXAMPLES::
|
|
5
|
+
|
|
6
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
7
|
+
sage: HilbertModularGroup(5)
|
|
8
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
9
|
+
sage: HilbertModularGroup(QuadraticField(5))
|
|
10
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
AUTHORS:
|
|
14
|
+
- Fredrik Stromberg (2021)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
"""
|
|
18
|
+
import sage
|
|
19
|
+
from sage.categories.groups import Groups
|
|
20
|
+
from sage.groups.matrix_gps.linear import LinearMatrixGroup_generic
|
|
21
|
+
from sage.modular.cusps_nf import NFCusp
|
|
22
|
+
from sage.rings.infinity import infinity
|
|
23
|
+
from sage.rings.number_field.number_field import QuadraticField, CyclotomicField
|
|
24
|
+
from sage.misc.latex import latex
|
|
25
|
+
from sage.rings.integer import Integer
|
|
26
|
+
from sage.matrix.constructor import Matrix
|
|
27
|
+
from sage.matrix.constructor import Matrix as matrix
|
|
28
|
+
from sage.rings.integer_ring import Z as ZZ
|
|
29
|
+
from sage.misc.cachefunc import cached_method
|
|
30
|
+
from sage.rings.number_field.order import Order
|
|
31
|
+
|
|
32
|
+
# from .upper_half_plane import ComplexPlaneOtimesK
|
|
33
|
+
from hilbert_modgroup.upper_half_plane import ComplexPlaneProductElement__class
|
|
34
|
+
from .hilbert_modular_group_element import HilbertModularGroupElement
|
|
35
|
+
|
|
36
|
+
import logging
|
|
37
|
+
|
|
38
|
+
logger = logging.getLogger(__name__)
|
|
39
|
+
logger.setLevel(10)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def is_HilbertModularGroup(x) -> bool:
|
|
43
|
+
"""
|
|
44
|
+
Return `True` if ``x`` is an instance of a HilbertModularGroup
|
|
45
|
+
|
|
46
|
+
INPUT:
|
|
47
|
+
|
|
48
|
+
- ``x`` -- something to test if it is a Hilbert modular group or not
|
|
49
|
+
|
|
50
|
+
OUTPUT:
|
|
51
|
+
|
|
52
|
+
- boolean
|
|
53
|
+
|
|
54
|
+
EXAMPLES::
|
|
55
|
+
|
|
56
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup,is_HilbertModularGroup
|
|
57
|
+
sage: is_HilbertModularGroup(1)
|
|
58
|
+
False
|
|
59
|
+
sage: H = HilbertModularGroup(5)
|
|
60
|
+
sage: is_HilbertModularGroup(H)
|
|
61
|
+
True
|
|
62
|
+
"""
|
|
63
|
+
return isinstance(x, HilbertModularGroup_class)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def HilbertModularGroup(number_field, projective=True):
|
|
67
|
+
r"""
|
|
68
|
+
Create the Hilbert modular group over the ring of integers in the given number field
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
INPUT:
|
|
72
|
+
|
|
73
|
+
- ``number_field`` (NumberField) -- a totally real number field or positive integer.
|
|
74
|
+
If a positive integer D is specified
|
|
75
|
+
then the number field $Q(\sqrt(D))$ is used.
|
|
76
|
+
- ``projective`` (bool) - ``True`` if you want PSL(2,K) and ``False`` for SL(2,K)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
EXAMPLES::
|
|
80
|
+
|
|
81
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
82
|
+
sage: HilbertModularGroup(5)
|
|
83
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
84
|
+
sage: HilbertModularGroup(QuadraticField(5))
|
|
85
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
86
|
+
|
|
87
|
+
TESTS::
|
|
88
|
+
|
|
89
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
90
|
+
sage: HilbertModularGroup(5)
|
|
91
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
92
|
+
sage: HilbertModularGroup(QuadraticField(5))
|
|
93
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
94
|
+
|
|
95
|
+
"""
|
|
96
|
+
if isinstance(number_field, (int, Integer)) and number_field > 0:
|
|
97
|
+
ring = QuadraticField(number_field).ring_of_integers()
|
|
98
|
+
elif isinstance(number_field, sage.rings.number_field.number_field_base.NumberField) \
|
|
99
|
+
and number_field.is_totally_real():
|
|
100
|
+
ring = number_field.ring_of_integers()
|
|
101
|
+
else:
|
|
102
|
+
raise ValueError("The input must be a totally real Number Field or a positive integer")
|
|
103
|
+
if not projective:
|
|
104
|
+
raise NotImplementedError("Only PSL2 is implemented at the moment.")
|
|
105
|
+
degree = Integer(2)
|
|
106
|
+
name = f'Hilbert Modular Group PSL({degree}) over {ring}'
|
|
107
|
+
ltx = 'PSL({0}, {1})'.format(degree, latex(ring))
|
|
108
|
+
return HilbertModularGroup_class(base_ring=ring, sage_name=name, latex_string=ltx)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class HilbertModularGroup_class(LinearMatrixGroup_generic):
|
|
112
|
+
r"""
|
|
113
|
+
Class for Hilbert modular groups, here defined as either PSL(2) (default) or SL(2)
|
|
114
|
+
over rings of integers in totally real number fields.
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
Element = HilbertModularGroupElement
|
|
120
|
+
|
|
121
|
+
def __init__(self, base_ring, sage_name, latex_string):
|
|
122
|
+
r"""
|
|
123
|
+
Init a Hilbert modular group over the ring of integers in the given number field
|
|
124
|
+
|
|
125
|
+
INPUT:
|
|
126
|
+
|
|
127
|
+
- ``base_ring`` - ring
|
|
128
|
+
- ``sage_name`` - string
|
|
129
|
+
- ``latex_string`` - string
|
|
130
|
+
|
|
131
|
+
EXAMPLES::
|
|
132
|
+
|
|
133
|
+
sage: from hilbert_modgroup.hilbert_modular_group_class import *
|
|
134
|
+
sage: OK=QuadraticField(5).OK()
|
|
135
|
+
sage: name = f'Hilbert Modular Group PSL(2) over {OK}'
|
|
136
|
+
sage: ltx = f'PSL(2, {latex(OK)})'
|
|
137
|
+
sage: HilbertModularGroup_class(base_ring=OK,sage_name=name,latex_string=ltx)
|
|
138
|
+
Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
|
|
139
|
+
sage: H1=HilbertModularGroup(5)
|
|
140
|
+
sage: TestSuite(H1).run()
|
|
141
|
+
sage: H1(1)
|
|
142
|
+
[1 0]
|
|
143
|
+
[0 1]
|
|
144
|
+
sage: H1(2)
|
|
145
|
+
Traceback (most recent call last):
|
|
146
|
+
...
|
|
147
|
+
TypeError: matrix must have determinant 1
|
|
148
|
+
sage: H1([1,1,0,1])
|
|
149
|
+
[1 1]
|
|
150
|
+
[0 1]
|
|
151
|
+
sage: H1([1,H1.base_ring().gens()[0],0,1])
|
|
152
|
+
[ 1 1/2*a + 1/2]
|
|
153
|
+
[ 0 1]
|
|
154
|
+
"""
|
|
155
|
+
if base_ring != ZZ and (not isinstance(base_ring, Order)
|
|
156
|
+
or not base_ring.number_field().is_totally_real()):
|
|
157
|
+
raise ValueError("Input (={0}) can not be used to create a Hilbert modular group. " +
|
|
158
|
+
"Need an order of a totally real number field")
|
|
159
|
+
# Instance data related to elliptic elements
|
|
160
|
+
self._elliptic_elements_traces = []
|
|
161
|
+
self._elliptic_elements_orders = []
|
|
162
|
+
self._elliptic_elements_traces_of_orders = {}
|
|
163
|
+
self._elliptic_elements_fields_of_orders = {}
|
|
164
|
+
self._elliptic_elements_orders_of_traces = {}
|
|
165
|
+
# Instance data related to cusps
|
|
166
|
+
self._ncusps = None
|
|
167
|
+
self._cusps = []
|
|
168
|
+
self._ideal_cusp_representatives = []
|
|
169
|
+
self._cusp_normalizing_maps = {}
|
|
170
|
+
self._cusp_normalizing_maps_inverse = {}
|
|
171
|
+
# At the moment we only deal with full level (1)
|
|
172
|
+
self._level = base_ring.fractional_ideal(1)
|
|
173
|
+
super().__init__(degree=Integer(2), base_ring=base_ring,
|
|
174
|
+
special=True,
|
|
175
|
+
sage_name=sage_name,
|
|
176
|
+
latex_string=latex_string,
|
|
177
|
+
category=Groups().Infinite(),
|
|
178
|
+
invariant_form=None)
|
|
179
|
+
|
|
180
|
+
@cached_method
|
|
181
|
+
def generators(self, algorithm='standard'):
|
|
182
|
+
r"""
|
|
183
|
+
Return a list of generators of ``self``.
|
|
184
|
+
|
|
185
|
+
INPUT:
|
|
186
|
+
|
|
187
|
+
- ``algorithm`` (string) either 'standard' or 'elementary'.
|
|
188
|
+
|
|
189
|
+
If 'elementary' is given return a set of generators
|
|
190
|
+
consisting of elementary (i.e. upper- and lower-triangular) matrices.
|
|
191
|
+
Otherwise return a set of reflections and translations.
|
|
192
|
+
|
|
193
|
+
EXAMPLES::
|
|
194
|
+
|
|
195
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
196
|
+
sage: H = HilbertModularGroup(5)
|
|
197
|
+
sage: H.generators()
|
|
198
|
+
[
|
|
199
|
+
[ 0 -1] [ 1 1/2*a + 1/2] [1 a]
|
|
200
|
+
[ 1 0], [ 0 1], [0 1]
|
|
201
|
+
]
|
|
202
|
+
|
|
203
|
+
sage: H.generators(algorithm='elementary')
|
|
204
|
+
[
|
|
205
|
+
[ 1 1/2*a + 1/2] [ 1 0] [1 a] [1 0]
|
|
206
|
+
[ 0 1], [1/2*a + 1/2 1], [0 1], [a 1]
|
|
207
|
+
]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
"""
|
|
211
|
+
if algorithm == 'standard':
|
|
212
|
+
gens = [self.S()]
|
|
213
|
+
gens.extend(self.T(x) for x in self.base_ring().basis())
|
|
214
|
+
elif algorithm == 'elementary':
|
|
215
|
+
gens = []
|
|
216
|
+
for x in self.base_ring().basis():
|
|
217
|
+
gens.append(self.T(x))
|
|
218
|
+
gens.append(self.L(x))
|
|
219
|
+
else:
|
|
220
|
+
raise ValueError(f"Unknown algorithm '{algorithm}'. "
|
|
221
|
+
f"Expected one of 'standard' or 'elementary'")
|
|
222
|
+
return gens
|
|
223
|
+
|
|
224
|
+
@cached_method
|
|
225
|
+
def S(self):
|
|
226
|
+
"""
|
|
227
|
+
Return the element S = ( 0 & -1 // 1 & 0 ) of self.
|
|
228
|
+
|
|
229
|
+
EXAMPLES::
|
|
230
|
+
|
|
231
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
232
|
+
sage: HilbertModularGroup(5).S()
|
|
233
|
+
[ 0 -1]
|
|
234
|
+
[ 1 0]
|
|
235
|
+
sage: HilbertModularGroup(10).S()
|
|
236
|
+
[ 0 -1]
|
|
237
|
+
[ 1 0]
|
|
238
|
+
"""
|
|
239
|
+
return self([0, -1, 1, 0])
|
|
240
|
+
|
|
241
|
+
@cached_method
|
|
242
|
+
def T(self, a=1):
|
|
243
|
+
"""
|
|
244
|
+
Return the element T^a = ( 1 & a // 0 & 1 ) of self.
|
|
245
|
+
|
|
246
|
+
INPUT:
|
|
247
|
+
|
|
248
|
+
- ``a`` -- integer in number field (default=1)
|
|
249
|
+
|
|
250
|
+
EXAMPLES::
|
|
251
|
+
|
|
252
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
253
|
+
sage: H = HilbertModularGroup(5)
|
|
254
|
+
sage: H.T()
|
|
255
|
+
[1 1]
|
|
256
|
+
[0 1]
|
|
257
|
+
sage: u0,u1=H.base_ring().number_field().unit_group().gens()
|
|
258
|
+
sage: H.T(u0)
|
|
259
|
+
[ 1 -1]
|
|
260
|
+
[ 0 1]
|
|
261
|
+
sage: H.T(u0*u1)
|
|
262
|
+
[ 1 1/2*a - 1/2]
|
|
263
|
+
[ 0 1]
|
|
264
|
+
"""
|
|
265
|
+
return self([1, a, 0, 1])
|
|
266
|
+
|
|
267
|
+
@cached_method
|
|
268
|
+
def L(self, a):
|
|
269
|
+
"""
|
|
270
|
+
Return the element L=( 1 & 0 // a & 1 ) of self.
|
|
271
|
+
|
|
272
|
+
INPUT:
|
|
273
|
+
|
|
274
|
+
- ``a`` -- integer in number field
|
|
275
|
+
|
|
276
|
+
EXAMPLES::
|
|
277
|
+
|
|
278
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
279
|
+
sage: H = HilbertModularGroup(5)
|
|
280
|
+
sage: H.L(1)
|
|
281
|
+
[1 0]
|
|
282
|
+
[1 1]
|
|
283
|
+
sage: u0,u1=H.base_ring().number_field().unit_group().gens()
|
|
284
|
+
sage: H.L(u0)
|
|
285
|
+
[ 1 0]
|
|
286
|
+
[-1 1]
|
|
287
|
+
sage: H.L(u0*u1)
|
|
288
|
+
[ 1 0]
|
|
289
|
+
[1/2*a - 1/2 1]
|
|
290
|
+
|
|
291
|
+
"""
|
|
292
|
+
return self([1, 0, a, 1])
|
|
293
|
+
|
|
294
|
+
@cached_method
|
|
295
|
+
def E(self, u):
|
|
296
|
+
"""
|
|
297
|
+
Return the element U=( u & 0 // 0 & u**-1 ) of self.
|
|
298
|
+
|
|
299
|
+
INPUT:
|
|
300
|
+
- `u` unit in self.base_ring()
|
|
301
|
+
|
|
302
|
+
EXAMPLES::
|
|
303
|
+
|
|
304
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
305
|
+
sage: H = HilbertModularGroup(5)
|
|
306
|
+
sage: H.E(1)
|
|
307
|
+
[1 0]
|
|
308
|
+
[0 1]
|
|
309
|
+
sage: u0,u1=H.base_ring().number_field().unit_group().gens()
|
|
310
|
+
sage: H.E(u0)
|
|
311
|
+
[-1 0]
|
|
312
|
+
[ 0 -1]
|
|
313
|
+
sage: H.E(u0*u1)
|
|
314
|
+
[1/2*a - 1/2 0]
|
|
315
|
+
[ 0 1/2*a + 1/2]
|
|
316
|
+
|
|
317
|
+
"""
|
|
318
|
+
return self([u, 0, 0, u ** -1])
|
|
319
|
+
|
|
320
|
+
def gens(self, algorithm='standard'):
|
|
321
|
+
r"""
|
|
322
|
+
Return a tuple of generators for this Hilbert modular group.
|
|
323
|
+
|
|
324
|
+
The generators need not be minimal. For arguments, see :meth:`~generators`.
|
|
325
|
+
|
|
326
|
+
INPUT:
|
|
327
|
+
|
|
328
|
+
- ``algorithm`` -- string (default='standard') give the algorithm to compute the generators
|
|
329
|
+
|
|
330
|
+
NOTE: Different 'algorithms' give different choices of generators.
|
|
331
|
+
|
|
332
|
+
EXAMPLES::
|
|
333
|
+
|
|
334
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
335
|
+
sage: HilbertModularGroup(5).gens()
|
|
336
|
+
(
|
|
337
|
+
[ 0 -1] [ 1 1/2*a + 1/2] [1 a]
|
|
338
|
+
[ 1 0], [ 0 1], [0 1]
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
"""
|
|
342
|
+
return tuple(self.generators(algorithm))
|
|
343
|
+
|
|
344
|
+
def ngens(self, algorithm='standard'):
|
|
345
|
+
r"""
|
|
346
|
+
Return the number of generators of self as given by the function 'gens'.
|
|
347
|
+
|
|
348
|
+
INPUT:
|
|
349
|
+
|
|
350
|
+
- ``algorithm`` -- string (default='standard') give the algorithm to compute the generators
|
|
351
|
+
|
|
352
|
+
EXAMPLES::
|
|
353
|
+
|
|
354
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
355
|
+
sage: HilbertModularGroup(5).ngens()
|
|
356
|
+
3
|
|
357
|
+
|
|
358
|
+
"""
|
|
359
|
+
return len(self.generators(algorithm))
|
|
360
|
+
|
|
361
|
+
def gen(self, i):
|
|
362
|
+
r"""
|
|
363
|
+
Return the i-th generator of self, i.e. the i-th element of the
|
|
364
|
+
tuple self.gens().
|
|
365
|
+
|
|
366
|
+
EXAMPLES::
|
|
367
|
+
|
|
368
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
369
|
+
sage: HilbertModularGroup(5).gen(1)
|
|
370
|
+
[ 1 1/2*a + 1/2]
|
|
371
|
+
[ 0 1]
|
|
372
|
+
"""
|
|
373
|
+
return self.generators()[i]
|
|
374
|
+
|
|
375
|
+
def random_element(self, *args, **kwds):
|
|
376
|
+
r"""
|
|
377
|
+
Return a 'random' element of this Hilbert Modular Group.
|
|
378
|
+
|
|
379
|
+
INPUT:
|
|
380
|
+
|
|
381
|
+
- `args`, `kwds` -- arguments passed to the base ring's random element function
|
|
382
|
+
and are in turn passed to the random integer function.
|
|
383
|
+
See the documentation for "ZZ.random_element()" for details.
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
EXAMPLES::
|
|
387
|
+
|
|
388
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
389
|
+
sage: H = HilbertModularGroup(5)
|
|
390
|
+
sage: A = H.random_element()
|
|
391
|
+
sage: A in H
|
|
392
|
+
True
|
|
393
|
+
|
|
394
|
+
"""
|
|
395
|
+
a = self.base_ring().random_element(**kwds)
|
|
396
|
+
b = self.base_ring().random_element(**kwds)
|
|
397
|
+
return self.T(a)*self.L(b)
|
|
398
|
+
|
|
399
|
+
def level(self):
|
|
400
|
+
"""
|
|
401
|
+
Return the level of this Hilbert modular group (currently only (1))
|
|
402
|
+
|
|
403
|
+
EXAMPLES::
|
|
404
|
+
|
|
405
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
406
|
+
sage: HilbertModularGroup(5).level()
|
|
407
|
+
Fractional ideal (1)
|
|
408
|
+
|
|
409
|
+
"""
|
|
410
|
+
return self._level
|
|
411
|
+
|
|
412
|
+
def ncusps(self):
|
|
413
|
+
"""
|
|
414
|
+
Return number of cusps of self.
|
|
415
|
+
|
|
416
|
+
EXAMPLES::
|
|
417
|
+
|
|
418
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
419
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
420
|
+
sage: H1=HilbertModularGroup(5)
|
|
421
|
+
sage: H1.ncusps()
|
|
422
|
+
1
|
|
423
|
+
|
|
424
|
+
sage: H2=HilbertModularGroup(10)
|
|
425
|
+
sage: H2.ncusps()
|
|
426
|
+
2
|
|
427
|
+
|
|
428
|
+
sage: x = ZZ['x'].gen()
|
|
429
|
+
sage: K = NumberField(x^3-36*x-1, names='a')
|
|
430
|
+
sage: H3=HilbertModularGroup(K)
|
|
431
|
+
sage: H3.ncusps()
|
|
432
|
+
5
|
|
433
|
+
|
|
434
|
+
sage: K4 = NumberField(x**4 - 17*x**2 + 36,names='a'); a=K4.gen()
|
|
435
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
436
|
+
sage: H4.ncusps()
|
|
437
|
+
2
|
|
438
|
+
"""
|
|
439
|
+
if not self._ncusps:
|
|
440
|
+
self._ncusps = self.base_ring().class_number()
|
|
441
|
+
return self._ncusps
|
|
442
|
+
|
|
443
|
+
@cached_method
|
|
444
|
+
def cusps(self):
|
|
445
|
+
"""
|
|
446
|
+
A set of cusp representatives of self.
|
|
447
|
+
|
|
448
|
+
EXAMPLES::
|
|
449
|
+
|
|
450
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
451
|
+
sage: H1=HilbertModularGroup(5)
|
|
452
|
+
sage: H1.cusps()
|
|
453
|
+
[Cusp Infinity of Number Field ... polynomial x^2 - 5 with a = 2.236067977499790?]
|
|
454
|
+
|
|
455
|
+
sage: H2=HilbertModularGroup(10)
|
|
456
|
+
sage: H2.cusps()
|
|
457
|
+
[Cusp Infinity of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?,
|
|
458
|
+
Cusp [2: a] of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?]
|
|
459
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
460
|
+
sage: x = ZZ['x'].gen()
|
|
461
|
+
sage: K = NumberField(x^3-36*x-1, names='a')
|
|
462
|
+
sage: H3=HilbertModularGroup(K); H3
|
|
463
|
+
Hilbert Modular Group ... x^3 - 36*x - 1
|
|
464
|
+
sage: H3.cusps()
|
|
465
|
+
[Cusp Infinity of Number Field in a with defining polynomial x^3 - 36*x - 1,
|
|
466
|
+
Cusp [2: a + 1] of Number Field in a with defining polynomial x^3 - 36*x - 1,
|
|
467
|
+
Cusp [3: 1/3*a^2 + 1/3*a - 26/3] of Number Field ... polynomial x^3 - 36*x - 1,
|
|
468
|
+
Cusp [2: 1/3*a^2 + 1/3*a - 23/3] of Number Field ... polynomial x^3 - 36*x - 1,
|
|
469
|
+
Cusp [6: 1/3*a^2 + 1/3*a - 26/3] of Number Field ... polynomial x^3 - 36*x - 1]
|
|
470
|
+
|
|
471
|
+
sage: K4 = NumberField(x**4 - 17*x**2 + 36,names='a'); a=K4.gen()
|
|
472
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
473
|
+
sage: H4.cusps()
|
|
474
|
+
[Cusp Infinity of Number Field in a with defining polynomial x^4 - 17*x^2 + 36,
|
|
475
|
+
Cusp [2: a + 1] of Number Field in a with defining polynomial x^4 - 17*x^2 + 36]
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
"""
|
|
479
|
+
for a in self.ideal_cusp_representatives():
|
|
480
|
+
logger.debug("Set cusp info for ideal a={0}".format(a))
|
|
481
|
+
if a.is_trivial():
|
|
482
|
+
ca = NFCusp(self.base_ring().number_field(),
|
|
483
|
+
self.base_ring()(1),
|
|
484
|
+
self.base_ring()(0),
|
|
485
|
+
lreps=self.ideal_cusp_representatives())
|
|
486
|
+
else:
|
|
487
|
+
ag = a.gens_reduced()
|
|
488
|
+
ca = NFCusp(self.base_ring().number_field(), ag[0], ag[1],
|
|
489
|
+
lreps=self.ideal_cusp_representatives())
|
|
490
|
+
self._cusps.append(ca)
|
|
491
|
+
if ca.ideal() != a:
|
|
492
|
+
raise ArithmeticError("Failed to associate a cusp to ideal {0}".format(a))
|
|
493
|
+
return self._cusps
|
|
494
|
+
|
|
495
|
+
def ideal_cusp_representatives(self):
|
|
496
|
+
r"""
|
|
497
|
+
Return a list of ideals corresponding to cusp representatives, i.e.
|
|
498
|
+
ideal representatives of ideal classes.
|
|
499
|
+
|
|
500
|
+
Note: We choose an ideal of smallest norm in each class.
|
|
501
|
+
If the ideal given by sage is already minimal we return this.
|
|
502
|
+
|
|
503
|
+
EXAMPLES::
|
|
504
|
+
|
|
505
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
506
|
+
sage: H1=HilbertModularGroup(5)
|
|
507
|
+
sage: H1.ideal_cusp_representatives()
|
|
508
|
+
[Fractional ideal (1)]
|
|
509
|
+
|
|
510
|
+
sage: H2=HilbertModularGroup(10)
|
|
511
|
+
sage: H2.ideal_cusp_representatives()
|
|
512
|
+
[Fractional ideal (1), Fractional ideal (2, a)]
|
|
513
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
514
|
+
sage: x = ZZ['x'].gen()
|
|
515
|
+
sage: K = NumberField(x^3-36*x-1, names='a')
|
|
516
|
+
sage: H3=HilbertModularGroup(K); H3
|
|
517
|
+
Hilbert Modular Group ... x^3 - 36*x - 1
|
|
518
|
+
sage: H3.ideal_cusp_representatives()
|
|
519
|
+
[Fractional ideal (1),
|
|
520
|
+
Fractional ideal (2, a + 1),
|
|
521
|
+
Fractional ideal (3, 1/3*a^2 + 1/3*a - 26/3),
|
|
522
|
+
Fractional ideal (2, 1/3*a^2 + 1/3*a - 23/3),
|
|
523
|
+
Fractional ideal (6, 1/3*a^2 + 1/3*a - 26/3)]
|
|
524
|
+
sage: K4 = NumberField(x**4 - 17*x**2 + 36,names='a'); a=K4.gen()
|
|
525
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
526
|
+
sage: H4.ideal_cusp_representatives()
|
|
527
|
+
[Fractional ideal (1), Fractional ideal (2, a + 1)]
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
"""
|
|
531
|
+
if not self._ideal_cusp_representatives:
|
|
532
|
+
self._ideal_cusp_representatives = []
|
|
533
|
+
|
|
534
|
+
def _find_equivalent_ideal_of_minimal_norm(c):
|
|
535
|
+
for a in self.base_ring().number_field().ideals_of_bdd_norm(c.norm() - 1).items():
|
|
536
|
+
for ideala in a[1]:
|
|
537
|
+
if (ideala * c ** -1).is_principal():
|
|
538
|
+
if c.norm() <= ideala.norm():
|
|
539
|
+
return c
|
|
540
|
+
return ideala
|
|
541
|
+
return c
|
|
542
|
+
|
|
543
|
+
for ideal_class in self.base_ring().class_group():
|
|
544
|
+
c = ideal_class.ideal().reduce_equiv()
|
|
545
|
+
# NOTE: Even though we use 'reduce_equiv' we are not guaranteed a representative
|
|
546
|
+
# with minimal **norm**
|
|
547
|
+
# To make certain we choose a representative of minimal norm explicitly.
|
|
548
|
+
c = _find_equivalent_ideal_of_minimal_norm(c)
|
|
549
|
+
self._ideal_cusp_representatives.append(c)
|
|
550
|
+
# We finally sort all representatives according to norm.
|
|
551
|
+
self._ideal_cusp_representatives.sort(key=lambda x: x.norm())
|
|
552
|
+
|
|
553
|
+
return self._ideal_cusp_representatives
|
|
554
|
+
|
|
555
|
+
def cusp_representative(self, cusp, return_map=False):
|
|
556
|
+
r"""
|
|
557
|
+
Return a representative cusp and optionally a corresponding map.
|
|
558
|
+
|
|
559
|
+
INPUT:
|
|
560
|
+
|
|
561
|
+
- ``cusp`` -- cusp
|
|
562
|
+
- ``return_map`` -- bool (default: False)
|
|
563
|
+
Set to True to also return the map giving the equivalence.
|
|
564
|
+
|
|
565
|
+
EXAMPLES::
|
|
566
|
+
|
|
567
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
568
|
+
sage: H1=HilbertModularGroup(5)
|
|
569
|
+
sage: c = NFCusp(H1.base_ring().number_field(),2,4)
|
|
570
|
+
sage: H1.cusp_representative(c)
|
|
571
|
+
Cusp Infinity of Number Field ... polynomial x^2 - 5 with a = 2.236067977499790?
|
|
572
|
+
|
|
573
|
+
sage: H2=HilbertModularGroup(10)
|
|
574
|
+
sage: c = NFCusp(H2.base_ring().number_field(),2,4)
|
|
575
|
+
sage: H2.cusp_representative(c)
|
|
576
|
+
Cusp Infinity of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?
|
|
577
|
+
|
|
578
|
+
sage: a = H2.base_ring().number_field().gen()
|
|
579
|
+
sage: x,y = 3*a - 10, a - 4
|
|
580
|
+
sage: c = NFCusp(H2.base_ring().number_field(),x,y)
|
|
581
|
+
sage: H2.cusp_representative(c)
|
|
582
|
+
Cusp [2: a] of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?
|
|
583
|
+
|
|
584
|
+
sage: x = ZZ['x'].gen()
|
|
585
|
+
sage: K = NumberField(x^3-36*x-1, names='a')
|
|
586
|
+
sage: H3=HilbertModularGroup(K)
|
|
587
|
+
sage: a = K.gen()
|
|
588
|
+
sage: c = NFCusp(H3.base_ring().number_field(),2,3)
|
|
589
|
+
sage: H3.cusp_representative(c)
|
|
590
|
+
Cusp Infinity of Number Field in a with defining polynomial x^3 - 36*x - 1
|
|
591
|
+
sage: x,y = 3*a - 10, a - 4
|
|
592
|
+
sage: c = NFCusp(H3.base_ring().number_field(),16,a+3)
|
|
593
|
+
sage: H3.cusp_representative(c)
|
|
594
|
+
Cusp [2: 1/3*a^2 + 1/3*a - 23/3] of Number Field ... polynomial x^3 - 36*x - 1
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
"""
|
|
598
|
+
for c in self.cusps():
|
|
599
|
+
if return_map:
|
|
600
|
+
t, B = cusp.is_Gamma0_equivalent(c, self.level(), Transformation=True)
|
|
601
|
+
if t:
|
|
602
|
+
return c, self(B)
|
|
603
|
+
elif cusp.is_Gamma0_equivalent(c, self.level()):
|
|
604
|
+
return c
|
|
605
|
+
raise ArithmeticError(f"Could not find cusp representative for {cusp}")
|
|
606
|
+
|
|
607
|
+
# Functions for elliptic elements
|
|
608
|
+
|
|
609
|
+
def _compute_traces_of_elliptic_elements(self):
|
|
610
|
+
r"""
|
|
611
|
+
Compute all possible traces of elliptic elements for self.
|
|
612
|
+
|
|
613
|
+
EXAMPLES::
|
|
614
|
+
|
|
615
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
616
|
+
sage: H1=HilbertModularGroup(5)
|
|
617
|
+
sage: H1._compute_traces_of_elliptic_elements()
|
|
618
|
+
[-1, 0, -1/2*a - 1/2, 1/2*a - 1/2, 1, -1/2*a + 1/2, 1/2*a + 1/2]
|
|
619
|
+
sage: H2=HilbertModularGroup(10)
|
|
620
|
+
sage: H2._compute_traces_of_elliptic_elements()
|
|
621
|
+
[-1, 0, 1]
|
|
622
|
+
|
|
623
|
+
|
|
624
|
+
ALGORITHM: (from the paper "Dimension formulas...", by Boylan, Skoruppa and Stromberg)
|
|
625
|
+
|
|
626
|
+
If m is an elliptic element then:
|
|
627
|
+
1. The order, l, must satisfy euler_phi(l)=2d where d | n = degree of the number field.
|
|
628
|
+
2. The trace t must satisfy z+z^-1 where z is an l-th root of unity.
|
|
629
|
+
3. The subfield QQ(z+z^-1) of QQ(z) must be a subfield of the base field K
|
|
630
|
+
Conversely, if t is such then there is an elliptic element with t as trace.
|
|
631
|
+
These two conditions therefore characterise the traces of elliptic points completely.
|
|
632
|
+
|
|
633
|
+
"""
|
|
634
|
+
from sage.arith.misc import euler_phi
|
|
635
|
+
if self._elliptic_elements_traces:
|
|
636
|
+
return self._elliptic_elements_traces
|
|
637
|
+
K = self.base_ring().number_field()
|
|
638
|
+
n = K.degree()
|
|
639
|
+
list_of_possible_orders = [o for o in range(2, 8*n**2+1) if euler_phi(o).divides(2*n)]
|
|
640
|
+
for o in list_of_possible_orders:
|
|
641
|
+
C = CyclotomicField(o, 'z')
|
|
642
|
+
z = C.gen()
|
|
643
|
+
F, emb = C.subfield(z+z**-1, 't')
|
|
644
|
+
if not F.embeddings(K):
|
|
645
|
+
continue
|
|
646
|
+
t = F.gen()
|
|
647
|
+
possible_traces_in_K = [s(t) for s in F.embeddings(K)]
|
|
648
|
+
logger.debug("F={0}".format(F))
|
|
649
|
+
logger.debug("z={0}".format(z))
|
|
650
|
+
logger.debug("t={0}".format(t))
|
|
651
|
+
logger.debug("|F.emb(K)|={0}".format(len(F.embeddings(K))))
|
|
652
|
+
logger.debug("t={0}".format(possible_traces_in_K))
|
|
653
|
+
traces_of_order_o = []
|
|
654
|
+
for st in possible_traces_in_K:
|
|
655
|
+
# Make sure that the trace have all embeddings with absolute value <2
|
|
656
|
+
test = [x for x in st.complex_embeddings() if abs(x) >= 2]
|
|
657
|
+
logger.debug("st={0}".format(st))
|
|
658
|
+
logger.debug("test={0}".format(test))
|
|
659
|
+
if test:
|
|
660
|
+
continue
|
|
661
|
+
# We want to choose a representative since the point only depends
|
|
662
|
+
# on t^2 we don't need t and -t
|
|
663
|
+
if st not in traces_of_order_o:
|
|
664
|
+
traces_of_order_o.append(st)
|
|
665
|
+
if traces_of_order_o:
|
|
666
|
+
self._elliptic_elements_traces.extend(traces_of_order_o)
|
|
667
|
+
self._elliptic_elements_fields_of_orders[o] = F
|
|
668
|
+
self._elliptic_elements_traces_of_orders[o] = traces_of_order_o
|
|
669
|
+
self._elliptic_elements_orders.append(o)
|
|
670
|
+
self._elliptic_elements_orders_of_traces = {
|
|
671
|
+
value: key
|
|
672
|
+
for key in self._elliptic_elements_traces_of_orders
|
|
673
|
+
for value in self._elliptic_elements_traces_of_orders[key]}
|
|
674
|
+
return self._elliptic_elements_traces
|
|
675
|
+
|
|
676
|
+
def orders_of_elliptic_elements(self):
|
|
677
|
+
r"""
|
|
678
|
+
Return a list of possible orders of elliptic elements.
|
|
679
|
+
|
|
680
|
+
EXAMPLES::
|
|
681
|
+
|
|
682
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
683
|
+
sage: H1=HilbertModularGroup(5)
|
|
684
|
+
sage: H1.orders_of_elliptic_elements()
|
|
685
|
+
[3, 4, 5, 6, 10]
|
|
686
|
+
|
|
687
|
+
sage: H2=HilbertModularGroup(10)
|
|
688
|
+
sage: H2.orders_of_elliptic_elements()
|
|
689
|
+
[3, 4, 6]
|
|
690
|
+
|
|
691
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
692
|
+
sage: x = ZZ['x'].gen()
|
|
693
|
+
sage: H4=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
|
|
694
|
+
sage: H4.orders_of_elliptic_elements()
|
|
695
|
+
[3, 4, 6]
|
|
696
|
+
|
|
697
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
698
|
+
sage: H4.orders_of_elliptic_elements()
|
|
699
|
+
[3, 4, 5, 6, 10]
|
|
700
|
+
|
|
701
|
+
"""
|
|
702
|
+
if not self._elliptic_elements_orders:
|
|
703
|
+
self._compute_traces_of_elliptic_elements()
|
|
704
|
+
return self._elliptic_elements_orders
|
|
705
|
+
|
|
706
|
+
def traces_of_elliptic_elements(self):
|
|
707
|
+
r"""
|
|
708
|
+
Return a list of possible traces of elliptic elements.
|
|
709
|
+
|
|
710
|
+
EXAMPLES::
|
|
711
|
+
|
|
712
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
713
|
+
sage: H = HilbertModularGroup(5)
|
|
714
|
+
sage: H.traces_of_elliptic_elements()
|
|
715
|
+
[-1, 0, -1/2*a - 1/2, 1/2*a - 1/2, 1, -1/2*a + 1/2, 1/2*a + 1/2]
|
|
716
|
+
|
|
717
|
+
sage: H2=HilbertModularGroup(10)
|
|
718
|
+
sage: H2.traces_of_elliptic_elements()
|
|
719
|
+
[-1, 0, 1]
|
|
720
|
+
|
|
721
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
722
|
+
sage: x = ZZ['x'].gen()
|
|
723
|
+
sage: K = NumberField(x^3-36*x-1, names='a')
|
|
724
|
+
sage: H = HilbertModularGroup(K)
|
|
725
|
+
sage: H2.traces_of_elliptic_elements()
|
|
726
|
+
[-1, 0, 1]
|
|
727
|
+
|
|
728
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
729
|
+
sage: H4.orders_of_elliptic_elements()
|
|
730
|
+
[3, 4, 5, 6, 10]
|
|
731
|
+
sage: H4.traces_of_elliptic_elements()
|
|
732
|
+
[-1,
|
|
733
|
+
0,
|
|
734
|
+
1/12*a^3 - 11/12*a - 1/2,
|
|
735
|
+
-1/12*a^3 + 11/12*a - 1/2,
|
|
736
|
+
1,
|
|
737
|
+
1/12*a^3 - 11/12*a + 1/2,
|
|
738
|
+
-1/12*a^3 + 11/12*a + 1/2]
|
|
739
|
+
|
|
740
|
+
"""
|
|
741
|
+
if not self._elliptic_elements_traces:
|
|
742
|
+
self._compute_traces_of_elliptic_elements()
|
|
743
|
+
return self._elliptic_elements_traces
|
|
744
|
+
|
|
745
|
+
def traces_of_elliptic_elements_of_order(self, o):
|
|
746
|
+
r"""
|
|
747
|
+
Return a list of traces of elliptic elements of given order.
|
|
748
|
+
|
|
749
|
+
EXAMPLES::
|
|
750
|
+
|
|
751
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
752
|
+
sage: H1=HilbertModularGroup(5)
|
|
753
|
+
sage: H1.traces_of_elliptic_elements_of_order(4)
|
|
754
|
+
[0]
|
|
755
|
+
sage: H1.traces_of_elliptic_elements_of_order(7)
|
|
756
|
+
[]
|
|
757
|
+
|
|
758
|
+
sage: H2=HilbertModularGroup(10)
|
|
759
|
+
sage: H2.traces_of_elliptic_elements_of_order(3)
|
|
760
|
+
[-1]
|
|
761
|
+
|
|
762
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
763
|
+
sage: x = ZZ['x'].gen()
|
|
764
|
+
sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
|
|
765
|
+
sage: H3.traces_of_elliptic_elements_of_order(6)
|
|
766
|
+
[1]
|
|
767
|
+
|
|
768
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
769
|
+
sage: H4.traces_of_elliptic_elements_of_order(10)
|
|
770
|
+
[1/12*a^3 - 11/12*a + 1/2, -1/12*a^3 + 11/12*a + 1/2]
|
|
771
|
+
sage: H4.traces_of_elliptic_elements_of_order(5)
|
|
772
|
+
[1/12*a^3 - 11/12*a - 1/2, -1/12*a^3 + 11/12*a - 1/2]
|
|
773
|
+
|
|
774
|
+
"""
|
|
775
|
+
if not self._elliptic_elements_traces_of_orders:
|
|
776
|
+
self._compute_traces_of_elliptic_elements()
|
|
777
|
+
return self._elliptic_elements_traces_of_orders.get(o, [])
|
|
778
|
+
|
|
779
|
+
def order_of_elliptic_element_of_trace(self, t):
|
|
780
|
+
r"""
|
|
781
|
+
Return the order of elliptic elements of a given trace.
|
|
782
|
+
Returns None if no elliptic element with this trace exists.
|
|
783
|
+
|
|
784
|
+
INPUT:
|
|
785
|
+
|
|
786
|
+
- `t` number field element
|
|
787
|
+
|
|
788
|
+
OUTPUT:
|
|
789
|
+
|
|
790
|
+
- integer or ``None``
|
|
791
|
+
|
|
792
|
+
EXAMPLES::
|
|
793
|
+
|
|
794
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
795
|
+
sage: H1=HilbertModularGroup(5)
|
|
796
|
+
sage: H1.order_of_elliptic_element_of_trace(0)
|
|
797
|
+
4
|
|
798
|
+
sage: H1.order_of_elliptic_element_of_trace(2)
|
|
799
|
+
|
|
800
|
+
|
|
801
|
+
sage: H2=HilbertModularGroup(10)
|
|
802
|
+
sage: H2.order_of_elliptic_element_of_trace(-1)
|
|
803
|
+
3
|
|
804
|
+
|
|
805
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
806
|
+
sage: x = ZZ['x'].gen()
|
|
807
|
+
sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
|
|
808
|
+
sage: H2.order_of_elliptic_element_of_trace(1)
|
|
809
|
+
6
|
|
810
|
+
|
|
811
|
+
sage: K4 = NumberField(x**4 - 17*x**2 + 36,names='a'); a=K4.gen()
|
|
812
|
+
sage: H4=HilbertModularGroup(NumberField(x**4 - 17*x**2 + 36,names='a'))
|
|
813
|
+
sage: H4.order_of_elliptic_element_of_trace(1)
|
|
814
|
+
6
|
|
815
|
+
sage: H4.order_of_elliptic_element_of_trace(1/12*a^3 - 11/12*a + 1/2)
|
|
816
|
+
10
|
|
817
|
+
|
|
818
|
+
"""
|
|
819
|
+
if not self._elliptic_elements_orders_of_traces:
|
|
820
|
+
self._compute_traces_of_elliptic_elements()
|
|
821
|
+
return self._elliptic_elements_orders_of_traces.get(t, None)
|
|
822
|
+
|
|
823
|
+
#
|
|
824
|
+
# Functions for working with cusps.
|
|
825
|
+
#
|
|
826
|
+
|
|
827
|
+
def cusp_normalizing_map(self, cusp, inverse=False, check=False):
|
|
828
|
+
r"""
|
|
829
|
+
Given a cusp (a:c) Return a matrix A = [[ a ,b ], [c , d]] in SL(2,K) such that
|
|
830
|
+
A(Infinity)=(a:c) and b, d in self.base_ring().ideal(a,c)**-1
|
|
831
|
+
|
|
832
|
+
INPUT:
|
|
833
|
+
|
|
834
|
+
- ``cusp`` -- Instance of NFCusp
|
|
835
|
+
- ``inverse`` -- bool (default: False) set to True to return the inverse map
|
|
836
|
+
- ``check`` -- bool (default: False) set to True to check the result
|
|
837
|
+
|
|
838
|
+
NOTE: The sage function NFCusp.ABmatrix() returns a matrix with determinant which is not
|
|
839
|
+
necessarily equal to 1 even though 1 is a generator of the ideal (1)=(a,c)*(a,c)**-1
|
|
840
|
+
|
|
841
|
+
If inverse = True then return A^-1
|
|
842
|
+
|
|
843
|
+
EXAMPLES::
|
|
844
|
+
|
|
845
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup
|
|
846
|
+
sage: H1=HilbertModularGroup(5)
|
|
847
|
+
sage: H1.cusp_normalizing_map(H1.cusps()[0])
|
|
848
|
+
[1 0]
|
|
849
|
+
[0 1]
|
|
850
|
+
sage: H1.cusp_normalizing_map(NFCusp(H1.base_ring().number_field(),0,1))
|
|
851
|
+
[ 0 -1]
|
|
852
|
+
[ 1 0]
|
|
853
|
+
sage: H1.cusp_normalizing_map(NFCusp(H1.base_ring().number_field(),1,1))
|
|
854
|
+
[ 1 -1]
|
|
855
|
+
[ 1 0]
|
|
856
|
+
sage: a=H1.base_ring().number_field().gen()
|
|
857
|
+
sage: H1.cusp_normalizing_map(NFCusp(H1.base_ring().number_field(),a,1+a))
|
|
858
|
+
[ a a - 1]
|
|
859
|
+
[a + 1 a]
|
|
860
|
+
|
|
861
|
+
sage: H2=HilbertModularGroup(10)
|
|
862
|
+
sage: H2.cusp_normalizing_map(H2.cusps()[1])
|
|
863
|
+
[ 2 -1/2*a]
|
|
864
|
+
[ a -2]
|
|
865
|
+
sage: from sage.rings.integer_ring import Z as ZZ
|
|
866
|
+
sage: x = ZZ['x'].gen()
|
|
867
|
+
sage: K = NumberField(x^3-36*x-1, names='a')
|
|
868
|
+
sage: H3=HilbertModularGroup(K)
|
|
869
|
+
sage: H3.cusp_normalizing_map(H3.cusps()[1])
|
|
870
|
+
[ 2 1/2*a^2 - 1/2*a - 35/2]
|
|
871
|
+
[ a + 1 -8]
|
|
872
|
+
sage: H3.cusp_normalizing_map(H3.cusps()[2])
|
|
873
|
+
[ 3 1/3*a^2 - a - 7/3]
|
|
874
|
+
[1/3*a^2 + 1/3*a - 26/3 7]
|
|
875
|
+
sage: H3.cusp_normalizing_map(H3.cusps()[3])
|
|
876
|
+
[ 2 1]
|
|
877
|
+
[1/3*a^2 + 1/3*a - 23/3 1/6*a^2 + 1/6*a - 10/3]
|
|
878
|
+
|
|
879
|
+
sage: K4 = NumberField(x**4 - 17*x**2 + 36,names='a'); a=K4.gen()
|
|
880
|
+
sage: H4=HilbertModularGroup(K4)
|
|
881
|
+
sage: H4.cusp_normalizing_map(H4.cusps()[1])
|
|
882
|
+
[ 2 1/4*a^3 - 1/4*a^2 - 4*a + 4]
|
|
883
|
+
[ a + 1 -2]
|
|
884
|
+
|
|
885
|
+
"""
|
|
886
|
+
base_nf = self.base_ring().number_field()
|
|
887
|
+
if not isinstance(cusp, NFCusp) or cusp.number_field() != base_nf:
|
|
888
|
+
raise ValueError(f"Input should be a NF cusp defined over {base_nf}!")
|
|
889
|
+
ca, cb = (cusp.numerator(), cusp.denominator())
|
|
890
|
+
if (ca, cb) not in self._cusp_normalizing_maps:
|
|
891
|
+
# First find the equivalent representative
|
|
892
|
+
# crep, B = self.cusp_representative(cusp,return_map=True)
|
|
893
|
+
# crepa,crepb = crep.numerator(),crep.denominator()
|
|
894
|
+
# crep_normalizing_map = self._cusp_normalizing_maps.get((crepa,crepb))
|
|
895
|
+
# if not crep_normalizing_map:
|
|
896
|
+
# Find a normalizing map of the cusp representative
|
|
897
|
+
a, b, c, d = cusp.ABmatrix()
|
|
898
|
+
det = a * d - b * c
|
|
899
|
+
A = Matrix(self.base_ring().number_field(), 2, 2, [a, b / det, c, d / det])
|
|
900
|
+
# A = B.matrix().inverse()*crep_normalizing_map
|
|
901
|
+
if check:
|
|
902
|
+
infinity = NFCusp(self.base_ring().number_field(), 1, 0)
|
|
903
|
+
if infinity.apply(A.list()) != cusp or A.det() != 1:
|
|
904
|
+
msg = f"Did not get correct normalizing map A={A} to cusp: {cusp}"
|
|
905
|
+
raise ArithmeticError(msg)
|
|
906
|
+
logger.debug(f"A={0}".format(A))
|
|
907
|
+
logger.debug("A.det()={0}".format(A.det().complex_embeddings()))
|
|
908
|
+
self._cusp_normalizing_maps_inverse[(ca, cb)] = A.inverse()
|
|
909
|
+
self._cusp_normalizing_maps[(ca, cb)] = A
|
|
910
|
+
if inverse:
|
|
911
|
+
return self._cusp_normalizing_maps_inverse[(ca, cb)]
|
|
912
|
+
else:
|
|
913
|
+
return self._cusp_normalizing_maps[(ca, cb)]
|
|
914
|
+
|
|
915
|
+
def apply_cusp_normalizing_map(self, cusp, z, inverse=False):
|
|
916
|
+
"""
|
|
917
|
+
Apply the cusp normalising map associated with the cusp to an element z
|
|
918
|
+
|
|
919
|
+
INPUT:
|
|
920
|
+
|
|
921
|
+
- `cusp` - an instance of NFcusp
|
|
922
|
+
- `z` - an element in
|
|
923
|
+
- the base number field
|
|
924
|
+
- the set o cusps
|
|
925
|
+
- in ComplexPlaneProductElement__class
|
|
926
|
+
- `inverse` - set to True if applying the inverse map
|
|
927
|
+
|
|
928
|
+
EXAMPLES::
|
|
929
|
+
|
|
930
|
+
sage: from hilbert_modgroup.all import HilbertModularGroup,ComplexPlaneProductElement
|
|
931
|
+
sage: H2=HilbertModularGroup(10)
|
|
932
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],1.0)
|
|
933
|
+
0.360379610028063
|
|
934
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],1)
|
|
935
|
+
1/6*a - 1/6
|
|
936
|
+
sage: z = NFCusp(H2.base_ring().number_field(),1)
|
|
937
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],z)
|
|
938
|
+
Cusp [a - 1: 6] of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?
|
|
939
|
+
sage: a=H2.base_ring().gens()[1]
|
|
940
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],a)
|
|
941
|
+
3/16*a
|
|
942
|
+
sage: z=ComplexPlaneProductElement([CC(1,0),CC(1,0)]); z
|
|
943
|
+
[1.00000000000000, 1.00000000000000]
|
|
944
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],z)
|
|
945
|
+
[-0.693712943361397, 0.360379610028063]
|
|
946
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],1).complex_embeddings()
|
|
947
|
+
[-0.693712943361397, 0.360379610028063]
|
|
948
|
+
|
|
949
|
+
# If we apply a matrix to a an element in K we get back an element in K
|
|
950
|
+
sage: s,t = H2.cusps()[1].numerator(),H2.cusps()[1].denominator()
|
|
951
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],s/t)
|
|
952
|
+
Traceback (most recent call last):
|
|
953
|
+
...
|
|
954
|
+
ZeroDivisionError: number field element division by zero
|
|
955
|
+
|
|
956
|
+
# If we apply the matrix to a cusp we return a cusp.
|
|
957
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],H2.cusps()[1])
|
|
958
|
+
Cusp Infinity of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?
|
|
959
|
+
|
|
960
|
+
# Applying the inverse of a cusp normalising map to the same cusp returns infinity.
|
|
961
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],H2.cusps()[1],inverse=True)
|
|
962
|
+
Cusp Infinity of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?
|
|
963
|
+
sage: c1 = H2.cusps()[1]
|
|
964
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],Infinity)
|
|
965
|
+
1/5*a
|
|
966
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],Infinity) == c1
|
|
967
|
+
False
|
|
968
|
+
sage: q = c1.numerator()/c1.denominator()
|
|
969
|
+
sage: H2.apply_cusp_normalizing_map(H2.cusps()[1],Infinity) == q
|
|
970
|
+
True
|
|
971
|
+
sage: c0, c1 = H2.cusps()
|
|
972
|
+
sage: H2.apply_cusp_normalizing_map(c1,c0) == c1
|
|
973
|
+
True
|
|
974
|
+
|
|
975
|
+
"""
|
|
976
|
+
a, b, c, d = self.cusp_normalizing_map(cusp, inverse=inverse).list()
|
|
977
|
+
if z == infinity:
|
|
978
|
+
return a / c
|
|
979
|
+
number_field = self.base_ring().number_field()
|
|
980
|
+
if isinstance(z, NFCusp) and z.number_field() == number_field:
|
|
981
|
+
return z.apply([a, b, c, d])
|
|
982
|
+
if z in number_field:
|
|
983
|
+
return (a * z + b) / (c * z + d)
|
|
984
|
+
if isinstance(z, ComplexPlaneProductElement__class) and \
|
|
985
|
+
z.degree() == number_field.absolute_degree():
|
|
986
|
+
return z.apply(matrix(2, 2, [a, b, c, d]))
|
|
987
|
+
raise ValueError("Unsupported type for acting with cusp normalizer! (z={0})".format(z))
|