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.

@@ -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))