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,2256 @@
1
+ r"""
2
+ A utility class for managing the reduction algorithm for Hilbert modular groups
3
+
4
+ AUTHORS:
5
+
6
+ - Fredrik Stromberg (2021)
7
+
8
+ NOTE: I know it is often a bad idea to write utility classes but I decided to \
9
+ do it anyway at least for the moment.
10
+ """
11
+ from hilbert_modgroup.hilbert_modular_group_class import \
12
+ HilbertModularGroup_class
13
+ from sage.rings.integer_ring import Z as ZZ
14
+ from sage.categories.sets_cat import cartesian_product
15
+ from sage.functions.other import floor
16
+ from sage.matrix.constructor import matrix
17
+ from sage.misc.cachefunc import cached_method
18
+ from sage.misc.misc_c import prod
19
+ from sage.modular.cusps_nf import NFCusp
20
+ from sage.modules.free_module_element import vector
21
+ from sage.rings.infinity import Infinity
22
+ from sage.rings.number_field.number_field_ideal import NumberFieldIdeal
23
+ from sage.rings.real_double import RDF
24
+ from sage.rings.real_mpfr import RealField
25
+ from sage.structure.sage_object import SageObject
26
+ from sage.matrix.constructor import Matrix
27
+
28
+ from hilbert_modgroup.upper_half_plane import \
29
+ ComplexPlaneProductElement__class, \
30
+ UpperHalfPlaneProductElement__class, UpperHalfPlaneProductElement
31
+
32
+ from hilbert_modgroup.utils import upper, lower
33
+
34
+ from hilbert_modgroup.pullback_cython import lattice_elements_in_box, \
35
+ coordinates_to_ideal_elements, find_closest_cusp, find_candidate_cusps, \
36
+ distance_to_cusp
37
+ import logging
38
+
39
+ log = logging.getLogger(__name__)
40
+
41
+
42
+ class HilbertPullback(SageObject):
43
+ r"""
44
+ Utility class for pullback/reduction algorithms for Hilbert modular groups.
45
+
46
+ """
47
+ def __init__(self, G):
48
+ r"""
49
+ Init self.
50
+
51
+ INPUT:
52
+
53
+ - ``G`` - A Hilbert modular group
54
+
55
+ EXAMPLES:
56
+
57
+ sage: from hilbert_modgroup.all import *
58
+ sage: H = HilbertModularGroup(5)
59
+ sage: P = HilbertPullback(H)
60
+ sage: TestSuite(P).run()
61
+ sage: P
62
+ Pullback class ... polynomial x^2 - 5 with a = 2.236067977499790?
63
+
64
+ """
65
+ if not isinstance(G, HilbertModularGroup_class):
66
+ raise ValueError("Need a Hilbert modular group")
67
+ self._group = G
68
+
69
+ def __eq__(self, other):
70
+ r"""
71
+ Check if self is equal to other.
72
+
73
+ EXAMPLES::
74
+
75
+ sage: from hilbert_modgroup.all import *
76
+ sage: H1 = HilbertModularGroup(5)
77
+ sage: P1 = HilbertPullback(H1)
78
+ sage: P12 = HilbertPullback(H1)
79
+ sage: P1 == P12
80
+ True
81
+ sage: H2 = HilbertModularGroup(3)
82
+ sage: P2 = HilbertPullback(H2)
83
+ sage: P1 == P2
84
+ False
85
+ """
86
+ if not isinstance(other, type(self)) or self._group != other._group:
87
+ return False
88
+ return True
89
+
90
+ def __str__(self):
91
+ r"""
92
+ Return string representation of self.
93
+
94
+ EXAMPLES::
95
+
96
+ sage: from hilbert_modgroup.all import HilbertModularGroup, \
97
+ HilbertPullback
98
+ sage: H = HilbertModularGroup(5)
99
+ sage: P = HilbertPullback(H)
100
+ sage: str(P)
101
+ 'Pullback class ... polynomial x^2 - 5 with a = 2.236067977499790?'
102
+
103
+ """
104
+ return f"Pullback class for {self._group}"
105
+
106
+ def __repr__(self):
107
+ r"""
108
+ Return string representation of self.
109
+
110
+ EXAMPLES::
111
+
112
+ sage: from hilbert_modgroup.all import *
113
+ sage: H = HilbertModularGroup(5)
114
+ sage: P = HilbertPullback(H)
115
+ sage: P
116
+ Pullback class ... polynomial x^2 - 5 with a = 2.236067977499790?
117
+
118
+ """
119
+ return str(self)
120
+
121
+ def group(self):
122
+ """
123
+ Return the group of self.
124
+
125
+ EXAMPLES::
126
+
127
+ sage: from hilbert_modgroup.all import *
128
+ sage: H = HilbertModularGroup(5)
129
+ sage: P = HilbertPullback(H)
130
+ sage: P.group()
131
+ Hilbert Modular Group ... x^2 - 5 with a = 2.236067977499790?
132
+
133
+ """
134
+ return self._group
135
+
136
+ def _check_upper_half_plane_element(self, z):
137
+ r"""
138
+ Check if z is an element of type UpperHalfPlaneProductElement__class
139
+ of the correct degree.
140
+
141
+ INPUT:
142
+ - `z` - potential element of a product of upper half planes.
143
+
144
+ EXAMPLES::
145
+
146
+ sage: from hilbert_modgroup.all import *
147
+ sage: H = HilbertModularGroup(5)
148
+ sage: P = HilbertPullback(H)
149
+ sage: P._check_upper_half_plane_element([1,1])
150
+ Traceback (most recent call last):
151
+ ...
152
+ ValueError: Need ...UpperHalfPlaneProductElement__class of degree 2
153
+ sage: z = UpperHalfPlaneProductElement([1+I,1+I])
154
+ sage: P._check_upper_half_plane_element(z)
155
+ True
156
+ sage: z = UpperHalfPlaneProductElement([1+I,1+I,1+I])
157
+ sage: P._check_upper_half_plane_element(z)
158
+ Traceback (most recent call last):
159
+ ...
160
+ ValueError: Need ...UpperHalfPlaneProductElement__class of degree 2
161
+ sage: z = ComplexPlaneProductElement([1+I,1+I])
162
+ sage: P._check_upper_half_plane_element(z)
163
+ Traceback (most recent call last):
164
+ ...
165
+ ValueError: Need ...UpperHalfPlaneProductElement__class of degree 2
166
+
167
+
168
+ """
169
+ if not isinstance(z, UpperHalfPlaneProductElement__class) or\
170
+ z.degree() != self.group().base_ring().number_field().degree():
171
+ msg = f"Need an element of type: " \
172
+ f"UpperHalfPlaneProductElement__class of degree " \
173
+ f"{self.group().base_ring().number_field().degree()}"
174
+ raise ValueError(msg)
175
+ return True
176
+
177
+ @cached_method
178
+ def basis_matrix_logarithmic_unit_lattice(self, prec=53):
179
+ """
180
+ Return the Basis matrix for the logarithmic unit lattice / +-1
181
+
182
+ EXAMPLES::
183
+
184
+ sage: from sage.rings.integer_ring import Z as ZZ
185
+ sage: from hilbert_modgroup.all import *
186
+ sage: H1 = HilbertModularGroup(5)
187
+ sage: P1 = HilbertPullback(H1)
188
+ sage: P1.basis_matrix_logarithmic_unit_lattice()
189
+ [ 0.481211825059603]
190
+ [-0.481211825059603]
191
+ sage: H2=HilbertModularGroup(10)
192
+ sage: P2 = HilbertPullback(H2)
193
+ sage: P2.basis_matrix_logarithmic_unit_lattice()
194
+ [ 1.81844645923207]
195
+ [-1.81844645923207]
196
+
197
+ sage: x = ZZ['x'].gen()
198
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
199
+ sage: P3=HilbertPullback(H3)
200
+ sage: P3.basis_matrix_logarithmic_unit_lattice()
201
+ [ 1.78943386474421 -4.27317838834468]
202
+ [-3.58349750383703 1.78711898997458]
203
+ [ 1.79406363909282 2.48605939837008]
204
+
205
+ """
206
+ n = self.group().base_ring().degree()
207
+ entries = [[x.abs().log() for x in u.complex_embeddings(prec)] for u
208
+ in self.fundamental_units()]
209
+ return matrix(RealField(prec), n-1, n, entries).transpose()
210
+
211
+ @cached_method()
212
+ def fundamental_units(self):
213
+ r"""
214
+ Return fundamental the units for the group of self.
215
+
216
+ EXAMPLES::
217
+
218
+ sage: from hilbert_modgroup.all import *
219
+ sage: H1 = HilbertModularGroup(5)
220
+ sage: P1 = HilbertPullback(H1)
221
+ sage: P1.fundamental_units()
222
+ [-1/2*a + 1/2]
223
+ sage: x = ZZ['x'].gen()
224
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
225
+ sage: P3=HilbertPullback(H3)
226
+ sage: P3.fundamental_units()
227
+ [a, -a - 6]
228
+
229
+ """
230
+ nf = self.group().base_ring().number_field()
231
+ return nf.unit_group().fundamental_units()
232
+
233
+ def Y(self, z, return_error_estimate=False):
234
+ r"""
235
+ Compute the coordinate of y=Im(z)/N(y)^(1/n)
236
+ with respect to the logarithmic unit lattice.
237
+
238
+ INPUT:
239
+
240
+ - ``z`` -- element of type UpperHalfPlaneProductElement
241
+ - ``return_error_estimate`` -- boolean (default=False) set to True
242
+ to return a tuple including an estimate
243
+ of the error.
244
+
245
+ EXAMPLES::
246
+
247
+ sage: from hilbert_modgroup.all import *
248
+ sage: from hilbert_modgroup.upper_half_plane import \
249
+ UpperHalfPlaneProductElement
250
+ sage: H1 = HilbertModularGroup(5)
251
+ sage: P1 = HilbertPullback(H1)
252
+ sage: u0,u1=H1.base_ring().number_field().unit_group().gens()
253
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
254
+ sage: P1.Y(z)
255
+ (0.000000000000000)
256
+ sage: P1.Y(u1*z)
257
+ Traceback (most recent call last):
258
+ ...
259
+ ValueError: Need an element of type: UpperHalfPlaneProductElement__class of degree 2
260
+ sage: P1.Y(u1**2*z)
261
+ (2.00000000000000)
262
+ sage: P1.Y(u1**-2*z)
263
+ (-2.00000000000000)
264
+ sage: z=UpperHalfPlaneProductElement([CC(1,3),CC(0,2)])
265
+ sage: P1.Y(z)
266
+ (0.421295869088362)
267
+ sage: P1.Y(u1**2*z)
268
+ (2.42129586908836)
269
+
270
+ # The precision gets worse when the imaginary parts have large
271
+ # differences.
272
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
273
+ sage: P3=HilbertPullback(H3)
274
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,2),CC(0,1)])
275
+ sage: P3.Y(z,return_error_estimate=True) # abs tol 1e-10
276
+ ((-0.128907673154173, 0.0000882959672881647), 2.220446049250313e-16)
277
+ sage: z=UpperHalfPlaneProductElement([CC(0,100),CC(0,200),CC(0,100)])
278
+ sage: P3.Y(z,return_error_estimate=True) # abs tol 1e-10
279
+ ((-0.128907673154173, 0.0000882959672880335), 6.38378239159465e-16)
280
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,2),CC(0,100)])
281
+ sage: P3.Y(z)
282
+ (0.638975592292095, 0.680877344221728)
283
+ sage: P3.Y(z,return_error_estimate=True) # abs tol 1e-10
284
+ ((0.638975592292095, 0.680877344221728), 1.1546319456101628e-14)
285
+ sage: CF=ComplexField(106)
286
+ sage: z=UpperHalfPlaneProductElement([CF(0,1),CF(0,2),CF(0,100)])
287
+ sage: P3.Y(z,return_error_estimate=True)
288
+ ((0.6389755922920966140584010614944, 0.6808773442217317886140786738603),
289
+ 2.0214560696288428e-30)
290
+
291
+
292
+ """
293
+ self._check_upper_half_plane_element(z)
294
+ normalized_imag = z/z.imag_norm()**(1/self.group().base_ring().degree())
295
+ log_vector = matrix(vector(normalized_imag.imag_log())).transpose()
296
+ B = self.basis_matrix_logarithmic_unit_lattice(prec=z.base_ring().prec())
297
+ coordinate_vector = B.solve_right(log_vector, check=False)
298
+ if return_error_estimate:
299
+ return vector(coordinate_vector), (B*coordinate_vector-log_vector).norm(Infinity)
300
+ return vector(coordinate_vector)
301
+
302
+ def reduce_by_units(self, z, return_map=False):
303
+ r"""
304
+ Reduce the point z with respect to action of units to a point where Im(z) belongs
305
+ to a fundamental domain for the logarithmic unit lattice.
306
+
307
+ INPUT:
308
+
309
+ - ``z`` -- element of type UpperHalfPlaneProductElement
310
+ - ``return_map`` -- boolean (default=False)
311
+ Set to True to return the map which does the reduction.
312
+
313
+ EXAMPLES::
314
+
315
+ sage: from hilbert_modgroup.all import *
316
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
317
+ sage: H1 = HilbertModularGroup(5)
318
+ sage: P1 = HilbertPullback(H1)
319
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)]); z
320
+ [1.00000000000000 + 1.00000000000000*I, 1.00000000000000 + 1.00000000000000*I]
321
+ sage: P1.reduce_by_units(z)
322
+ [1.00000000000000 + 1.00000000000000*I, 1.00000000000000 + 1.00000000000000*I]
323
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(0,1000)]);z
324
+ [1.00000000000000 + 1.00000000000000*I, 1000.00000000000*I]
325
+ sage: P1.reduce_by_units(z,return_map=True)
326
+ (
327
+ [46.9787137637478 + 46.9787137637478*I, 21.2862362522082*I],
328
+ [-3/2*a + 7/2 0]
329
+ [ 0 3/2*a + 7/2]
330
+ )
331
+ sage: w,A=_
332
+ sage: z.apply(A) == w
333
+ True
334
+
335
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
336
+ sage: P3=HilbertPullback(H3)
337
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,2),CC(0,1)]); z
338
+ [1.00000000000000*I, 2.00000000000000*I, 1.00000000000000*I]
339
+ sage: P3.reduce_by_units(z)
340
+ [1.00000000000000*I, 2.00000000000000*I, 1.00000000000000*I]
341
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.001),CC(0,2),CC(0,1000)])
342
+ sage: P3.reduce_by_units(z,return_map=True)
343
+ (
344
+ [5.14796503476537*I, 0.0560735644527675*I, 6.92845248925837*I],
345
+ <BLANKLINE>
346
+ [-a^2 + 6*a 0]
347
+ [ 0 -a - 6]
348
+ )
349
+
350
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.0001),CC(0,0.1),CC(0,100)])
351
+ sage: P3.reduce_by_units(z,return_map=True)
352
+ (
353
+ [0.0143665696311556*I, 3.63341121163000*I, 0.0191572146738068*I],
354
+ <BLANKLINE>
355
+ [ -a + 6 0]
356
+ [ 0 -a^2 - 6*a]
357
+ )
358
+
359
+ """
360
+ K = self.group().base_ring().number_field()
361
+ units = K.unit_group().gens()[1:] # Only include the units != -1
362
+ # To avoid overflow it is more efficient to apply the map,
363
+ # e.g. compute (z*u**-k)/u**k instead of z*u**-(2k)
364
+ floors = [-floor(y/2+1/2) for y in self.Y(z)]
365
+ reducing_map = prod([self.group().E(u ** y) for u, y in zip(units, floors)])
366
+ reduced_point = z.apply(reducing_map)
367
+ if return_map:
368
+ return reduced_point, reducing_map
369
+ return reduced_point
370
+
371
+ def is_reduced_by_units(self, z):
372
+ r"""
373
+ Return True if z is reduced with respect to the logarithmic unit lattice,
374
+ otherwise return False.
375
+
376
+ INPUT:
377
+
378
+ - ``z`` -- element of the type UpperHalfPlaneProductElement_class
379
+
380
+ EXAMPLES::
381
+
382
+ sage: from hilbert_modgroup.all import *
383
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
384
+ sage: H1 = HilbertModularGroup(5)
385
+ sage: P1 = HilbertPullback(H1)
386
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)]); z
387
+ [1.00000000000000 + 1.00000000000000*I, 1.00000000000000 + 1.00000000000000*I]
388
+ sage: P1.is_reduced_by_units(z)
389
+ True
390
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(0,1000)]);z
391
+ [1.00000000000000 + 1.00000000000000*I, 1000.00000000000*I]
392
+ sage: P1.is_reduced_by_units(z)
393
+ False
394
+ sage: P1.reduce_by_units(z,return_map=True)
395
+ (
396
+ [46.9787137637478 + 46.9787137637478*I, 21.2862362522082*I],
397
+ <BLANKLINE>
398
+ [-3/2*a + 7/2 0]
399
+ [ 0 3/2*a + 7/2]
400
+ )
401
+ sage: w,A=_
402
+ sage: P1.is_reduced_by_units(w)
403
+ True
404
+
405
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
406
+ sage: P3=HilbertPullback(H3)
407
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,2),CC(0,1)])
408
+ sage: P3.is_reduced_by_units(z)
409
+ True
410
+
411
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.0001),CC(0,0.1),CC(0,100)])
412
+ sage: P3.is_reduced_by_units(z)
413
+ False
414
+ sage: w,A=P3.reduce_by_units(z,return_map=True)
415
+ sage: P3.is_reduced_by_units(w)
416
+ True
417
+
418
+ """
419
+ return all(-1 <= y < 1 for y in self.Y(z))
420
+
421
+ @cached_method
422
+ def basis_matrix_ideal(self, a=None, prec=53):
423
+ r"""
424
+ Return the Basis matrix corresponding to an integer basis of an ideal a.
425
+
426
+ INPUT:
427
+
428
+ - ``a`` -- ideal or number field element.
429
+ - ``prec`` -- integer (default=53)
430
+
431
+ EXAMPLES::
432
+
433
+ sage: from sage.rings.integer_ring import Z as ZZ
434
+ sage: from hilbert_modgroup.all import *
435
+ sage: H1 = HilbertModularGroup(5)
436
+ sage: P1 = HilbertPullback(H1)
437
+ sage: P1.basis_matrix_ideal()
438
+ [ 1.00000000000000 -1.61803398874989]
439
+ [ 1.00000000000000 0.618033988749895]
440
+ sage: P1.basis_matrix_ideal(2)
441
+ [ 2.00000000000000 -3.23606797749979]
442
+ [ 2.00000000000000 1.23606797749979]
443
+
444
+ sage: H2=HilbertModularGroup(10)
445
+ sage: P2 = HilbertPullback(H2)
446
+ sage: P2.basis_matrix_ideal()
447
+ [ 1.00000000000000 -3.16227766016838]
448
+ [ 1.00000000000000 3.16227766016838]
449
+ sage: a=H2.base_ring().gen(1)
450
+ sage: P2.basis_matrix_ideal(a+1)
451
+ [ 9.00000000000000 -2.16227766016838]
452
+ [ 9.00000000000000 4.16227766016838]
453
+
454
+
455
+ sage: x = ZZ['x'].gen()
456
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
457
+ sage: P3=HilbertPullback(H3)
458
+ sage: P3.basis_matrix_ideal()
459
+ [ 1.00000000000000 -5.98606258583498 2.28229423189948]
460
+ [ 1.00000000000000 -0.0277783731902446 -7.67566891172438]
461
+ [ 1.00000000000000 6.01384095902523 6.39337467982490]
462
+ sage: c = P3.group().base_ring().class_group()[1].ideal()
463
+ sage: P3.basis_matrix_ideal(c)
464
+ [ 2.00000000000000 -4.98606258583498 3.28229423189948]
465
+ [ 2.00000000000000 0.972221626809755 -6.67566891172438]
466
+ [ 2.00000000000000 7.01384095902523 7.39337467982490]
467
+
468
+ """
469
+ ideala = self._construct_ideal(a)
470
+ entries = [list(beta.complex_embeddings(prec)) for beta in ideala.integral_basis()]
471
+ n = self.group().base_ring().degree()
472
+ return matrix(RealField(prec), n, n, entries).transpose()
473
+
474
+ @cached_method
475
+ def basis_matrix_ideal_on_power_basis(self, a=None):
476
+ r"""
477
+ Return the Basis matrix corresponding to an integer basis of an ideal a
478
+ in terms of the standard power basis.
479
+
480
+ INPUT:
481
+
482
+ - ``a`` -- ideal or number field element.
483
+
484
+ EXAMPLES::
485
+
486
+ sage: from hilbert_modgroup.all import *
487
+ sage: H1 = HilbertModularGroup(5)
488
+ sage: P1 = HilbertPullback(H1)
489
+ sage: P1.basis_matrix_ideal_on_power_basis()
490
+ [ 1 -1/2]
491
+ [ 0 1/2]
492
+ sage: P1.basis_matrix_ideal_on_power_basis(2)
493
+ [ 2 -1]
494
+ [ 0 1]
495
+ sage: H2=HilbertModularGroup(10)
496
+ sage: P2 = HilbertPullback(H2)
497
+ sage: P2.basis_matrix_ideal_on_power_basis()
498
+ [1 0]
499
+ [0 1]
500
+ sage: a=H2.base_ring().gen(1)
501
+ sage: P2.basis_matrix_ideal_on_power_basis(a+1)
502
+ [9 1]
503
+ [0 1]
504
+
505
+ sage: from sage.rings.integer_ring import Z as ZZ
506
+ sage: x = ZZ['x'].gen()
507
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
508
+ sage: P3=HilbertPullback(H3)
509
+ sage: P3.basis_matrix_ideal_on_power_basis()
510
+ [ 1 0 -23/3]
511
+ [ 0 1 1/3]
512
+ [ 0 0 1/3]
513
+ sage: c = P3.group().base_ring().class_group()[1].ideal()
514
+ sage: P3.basis_matrix_ideal_on_power_basis(c)
515
+ [ 2 1 -20/3]
516
+ [ 0 1 1/3]
517
+ [ 0 0 1/3]
518
+
519
+ """
520
+ ideala = self._construct_ideal(a)
521
+ entries = [list(beta.vector()) for beta in ideala.integral_basis()]
522
+ n = self.group().base_ring().degree()
523
+ return matrix(self.number_field(), n, n, entries).transpose()
524
+
525
+ def coordinates_in_number_field_ideal(self, x, a=None):
526
+ r"""
527
+ Return the coordinates of x with respect to an integral basia of a.
528
+
529
+ INPUT:
530
+
531
+ - ``x`` -- element of ideal a
532
+ - ``a`` -- ideal or number field element.
533
+
534
+ EXAMPLES::
535
+
536
+ sage: from hilbert_modgroup.all import *
537
+ sage: H1 = HilbertModularGroup(5)
538
+ sage: P1 = HilbertPullback(H1)
539
+ sage: b1,b2=P1.number_field().fractional_ideal(1).basis()
540
+ sage: P1.coordinates_in_number_field_ideal(b1)
541
+ (1, 0)
542
+ sage: P1.coordinates_in_number_field_ideal(b2)
543
+ (0, 1)
544
+ sage: H2=HilbertModularGroup(10)
545
+ sage: P2 = HilbertPullback(H2)
546
+ sage: b1,b2=P2.number_field().fractional_ideal(1).basis()
547
+ sage: P2.coordinates_in_number_field_ideal(b1)
548
+ (1, 0)
549
+ sage: P2.coordinates_in_number_field_ideal(b2)
550
+ (0, 1)
551
+ sage: from sage.rings.integer_ring import Z as ZZ
552
+ sage: x = ZZ['x'].gen()
553
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
554
+ sage: P3=HilbertPullback(H3)
555
+ sage: b1,b2,b3=P3.number_field().fractional_ideal(1).basis()
556
+ sage: P3.coordinates_in_number_field_ideal(b1)
557
+ (1, 0, 0)
558
+ sage: P3.coordinates_in_number_field_ideal(b2)
559
+ (0, 1, 0)
560
+ sage: P3.coordinates_in_number_field_ideal(b3)
561
+ (0, 0, 1)
562
+
563
+ """
564
+ B = self.basis_matrix_ideal_on_power_basis(a=a)
565
+ return B.inverse()*x.vector()
566
+
567
+ @cached_method
568
+ def basis_matrix_ideal__norm(self, a=None, prec=53, row=None):
569
+ r"""
570
+ Return the Basis matrix corresponding to an integer basis of an ideal a.
571
+
572
+ INPUT:
573
+
574
+ - ``a`` -- ideal or number field element.
575
+ - ``prec`` -- integer (default=53)
576
+
577
+ EXAMPLES::
578
+
579
+ sage: from hilbert_modgroup.all import *
580
+ sage: H1 = HilbertModularGroup(5)
581
+ sage: P1 = HilbertPullback(H1)
582
+ sage: P1.basis_matrix_ideal__norm() # abs tol 1e-10
583
+ 2.61803398874989
584
+ sage: P1.basis_matrix_ideal__norm(2) # abs tol 1e-10
585
+ 5.23606797749979
586
+ sage: H2=HilbertModularGroup(10)
587
+ sage: P2 = HilbertPullback(H2)
588
+ sage: P2.basis_matrix_ideal__norm() # abs tol 1e-10
589
+ 4.16227766016838
590
+ sage: a=H2.base_ring().gen(1)
591
+ sage: P2.basis_matrix_ideal__norm(a+1) # abs tol 1e-10
592
+ 13.16227766016838
593
+ sage: x = ZZ['x'].gen()
594
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
595
+ sage: P3=HilbertPullback(H3)
596
+ sage: P3.basis_matrix_ideal__norm() # abs tol 1e-10
597
+ 13.40721563885013
598
+ sage: c = P3.group().base_ring().class_group()[1].ideal()
599
+ sage: P3.basis_matrix_ideal__norm(c) # abs tol 1e-10
600
+ 16.407215638850133
601
+
602
+ """
603
+ B = self.basis_matrix_ideal(a, prec=prec)
604
+ if row is None:
605
+ return B.norm(Infinity)
606
+ elif 0 <= row < B.nrows():
607
+ return sum([abs(x) for x in B.row(row)])
608
+ else:
609
+ raise ValueError(f"Can not find row:{row}")
610
+
611
+ def basis_matrix_ideal_plusz(self, z, a=None):
612
+ r"""
613
+ Return the Basis matrix corresponding to the lattice
614
+ L = OKz + OK embedded in R^{2n} and given by the fixed
615
+ integral basis of the ideal a.
616
+
617
+ INPUT:
618
+
619
+ - ``z`` -- element of a product of complex planes
620
+ - ``a`` -- ideal or number field element.
621
+ - ``prec`` -- integer (default=53)
622
+
623
+ EXAMPLES::
624
+
625
+ sage: from hilbert_modgroup.all import *
626
+ sage: H1 = HilbertModularGroup(5)
627
+ sage: P1 = HilbertPullback(H1)
628
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
629
+ sage: P1.basis_matrix_ideal_plusz(z,P1._construct_ideal(1))
630
+ [ 1.00000000000000 1.00000000000000 0.000000000000000 0.000000000000000]
631
+ [ -1.61803398874989 0.618033988749895 0.000000000000000 0.000000000000000]
632
+ [ 0.000000000000000 0.000000000000000 1.00000000000000 1.00000000000000]
633
+ [-0.000000000000000 0.000000000000000 -1.61803398874989 0.618033988749895]
634
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.5),CC(0,1)])
635
+ sage: P1.basis_matrix_ideal_plusz(z,P1._construct_ideal(1))
636
+ [ 1.00000000000000 1.00000000000000 0.000000000000000 0.000000000000000]
637
+ [ -1.61803398874989 0.618033988749895 0.000000000000000 0.000000000000000]
638
+ [ 0.000000000000000 0.000000000000000 0.500000000000000 1.00000000000000]
639
+ [-0.000000000000000 0.000000000000000 -0.809016994374947 0.618033988749895]
640
+ sage: z=UpperHalfPlaneProductElement([CC(2.2,0.5),CC(1,0.5)])
641
+ sage: P1.basis_matrix_ideal_plusz(z,P1._construct_ideal(1))
642
+ [ 1.00000000000000 1.00000000000000 0.000000000000000 0.000000000000000]
643
+ [ -1.61803398874989 0.618033988749895 0.000000000000000 0.000000000000000]
644
+ [ 2.20000000000000 1.00000000000000 0.500000000000000 0.500000000000000]
645
+ [ -3.55967477524977 0.618033988749895 -0.809016994374947 0.309016994374947]
646
+ sage: H2 = HilbertModularGroup(10)
647
+ sage: P2 = HilbertPullback(H2)
648
+ sage: z=UpperHalfPlaneProductElement([CC(2,0.5),CC(1,0.2)])
649
+ sage: P2.basis_matrix_ideal_plusz(z,P2._construct_ideal(1))
650
+ [ 1.00000000000000 1.00000000000000 0.000000000000000 0.000000000000000]
651
+ [-3.16227766016838 3.16227766016838 0.000000000000000 0.000000000000000]
652
+ [ 2.00000000000000 1.00000000000000 0.500000000000000 0.200000000000000]
653
+ [-6.32455532033676 3.16227766016838 -1.58113883008419 0.632455532033676]
654
+ sage: zv = [23.3400000000000 + 0.0100000000000000*I,\
655
+ 0.0200000000000000 + 0.0300000000000000*I]
656
+ sage: z=UpperHalfPlaneProductElement(zv)
657
+ sage: P2.basis_matrix_ideal_plusz(z)
658
+ [ 1.00000000000000 1.00000000000000 0.000000000000000 0.000000000000000]
659
+ [ -3.16227766016838 3.16227766016838 0.000000000000000 0.000000000000000]
660
+ [ 23.3400000000000 0.0200000000000000 0.0100000000000000 0.0300000000000000]
661
+ [ -73.8075605883300 0.0632455532033676 -0.0316227766016838 0.0948683298050514]
662
+ sage: x = ZZ['x'].gen()
663
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
664
+ sage: P3=HilbertPullback(H3)
665
+ sage: K3=P3.number_field()
666
+ sage: z = UpperHalfPlaneProductElement([0+0.02*I,10+0.2*I,1+0.2*I])
667
+ sage: P3.basis_matrix_ideal_plusz(z,K3.fractional_ideal(1))
668
+ [ 1.00000000000000 1.00000000000000 1.00000000000000 0.000000000000000...
669
+ [ -5.98606258583498 -0.0277783731902446 6.01384095902523 0.000000000000000...
670
+ [ 2.28229423189948 -7.67566891172438 6.39337467982490 0.000000000000000...
671
+ [ 0.000000000000000 10.0000000000000 1.00000000000000 0.0200000000000000...
672
+ [ -0.000000000000000 -0.277783731902446 6.01384095902523 -0.119721251716700...
673
+ [ 0.000000000000000 -76.7566891172438 6.39337467982490 0.0456458846379896...
674
+
675
+
676
+ """
677
+ ideala = self._construct_ideal(a)
678
+ zero = self.number_field()(0)
679
+ prec = z.prec()
680
+ # In case it is an Upper-half plane element we need to allow multiplication
681
+ # by elements on the real axis.
682
+ z = z.as_ComplexPlaneProductElement()
683
+ entries = [
684
+ beta.complex_embeddings(prec) + zero.complex_embeddings(prec)
685
+ for beta in ideala.integral_basis()]
686
+ entries += [
687
+ (z*z.parent()(beta)).real() + (z*z.parent()(beta)).imag()
688
+ for beta in ideala.integral_basis()
689
+ ]
690
+ n = self.group().base_ring().degree()
691
+ return matrix(RealField(prec), 2*n, 2*n, entries)
692
+
693
+ def _shortest_vectors_ideal_plusz(self, z, a=None, return_scaled_matrix=False):
694
+ r"""
695
+ Compute a list of potentially shortest vectors in the lattice az+a using LLL.
696
+
697
+ INPUT:
698
+
699
+ - ``z`` -- point in the upper half-plane
700
+ - ``a`` -- ideal (default =None) if None we use the entire ring of integers as lattice.
701
+ - ``return_scaled_matrix`` -- boolean (default False)
702
+ Set to True to return the scaled matrix.
703
+
704
+ EXAMPLES::
705
+
706
+ sage: from hilbert_modgroup.all import *
707
+ sage: H1 = HilbertModularGroup(5)
708
+ sage: P1 = HilbertPullback(H1)
709
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
710
+ sage: P1._shortest_vectors_ideal_plusz(z)
711
+ [1 0 0 0]
712
+ [0 0 1 0]
713
+ [0 1 0 0]
714
+ [0 0 0 1]
715
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.5),CC(0,1)])
716
+ sage: P1._shortest_vectors_ideal_plusz(z)
717
+ [0 0 1 0]
718
+ [0 0 0 1]
719
+ [1 0 0 0]
720
+ [1 1 0 0]
721
+ sage: z=UpperHalfPlaneProductElement([CC(2.2,0.5),CC(1,0.5)])
722
+ sage: P1._shortest_vectors_ideal_plusz(z,P1._construct_ideal(1))
723
+ [-1 1 1 0]
724
+ [ 0 -2 0 1]
725
+ [ 1 -2 0 1]
726
+ [ 2 0 -1 0]
727
+ sage: H2 = HilbertModularGroup(10)
728
+ sage: P2 = HilbertPullback(H2)
729
+ sage: z=UpperHalfPlaneProductElement([CC(2,0.5),CC(1,0.2)])
730
+ sage: P2._shortest_vectors_ideal_plusz(z,P2._construct_ideal(1))
731
+ [-1 0 1 0]
732
+ [ 2 0 -1 0]
733
+ [-3 -1 3 1]
734
+ [-5 2 2 -1]
735
+ sage: z=UpperHalfPlaneProductElement([23.3400000000000 + 0.0100000000000000*I,\
736
+ 0.0200000000000000 + 0.0300000000000000*I])
737
+ sage: P2._shortest_vectors_ideal_plusz(z)
738
+ [ 25 -8 1 1]
739
+ [ 10 -3 -4 -1]
740
+ [ 212 -67 -15 1]
741
+ [ 348 -110 -14 5]
742
+ sage: x = ZZ['x'].gen()
743
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
744
+ sage: P3=HilbertPullback(H3)
745
+ sage: K3=P3.number_field()
746
+ sage: z = UpperHalfPlaneProductElement([0+0.02*I,10+0.2*I,1+0.2*I])
747
+ sage: P3._shortest_vectors_ideal_plusz(z,K3.fractional_ideal(1))
748
+ [ 2 0 -1 -1 1 0]
749
+ [ 15 1 -4 3 -1 1]
750
+ [-28 -2 7 8 -2 0]
751
+ [ 36 3 -8 -2 -1 1]
752
+ [ 18 1 -5 2 0 1]
753
+ [ 21 2 -4 -13 2 -1]
754
+
755
+
756
+ NOTE:
757
+ The idea behind this function was taken from [BoSt2015] (especially the scaling factor)
758
+
759
+ REFERENCES:
760
+ [BoSt2015] F. Boyer and M. Streng,
761
+ "Examples of CM curves of genus two defined over the reflex field",
762
+ LMS Journal of Comp. Math., Vol. 18 (2015), issue 01, pp 507-538\n",
763
+
764
+
765
+
766
+ """
767
+ basis_matrix = self.basis_matrix_ideal_plusz(z, a)
768
+ n = basis_matrix.nrows() // 2
769
+ # This is essentially the inverse of "machine epsilon"
770
+ epsilon_inverse = 2 ** (z[0].parent().prec())
771
+ # Scale all rows by epsilon_inverse*y^{-1/2} (we assume that N(y) is small)
772
+ scaled_rows = []
773
+ for basis_row in basis_matrix:
774
+ row = []
775
+ for i, b in enumerate(basis_row):
776
+ entry = (epsilon_inverse * b / (z[i % n].imag().sqrt())).round()
777
+ # entry = (epsilon_inverse * b).round()
778
+ row.append(entry)
779
+ scaled_rows.append(row)
780
+ integral_scaled_basis_matrix = Matrix(ZZ, scaled_rows)
781
+ if return_scaled_matrix:
782
+ return integral_scaled_basis_matrix
783
+ # Apply LLL to find a reduced basis
784
+ R, U = integral_scaled_basis_matrix.LLL(transformation=True)
785
+ # The shortest basis vector should be the first but in practice
786
+ # one of the other vectors can provide a better estimate.
787
+ return U
788
+
789
+ def get_heuristic_closest_cusp(self, z, a=None, as_cusp=False):
790
+ """
791
+ Try to find a heuristic closest cusp using LLL.
792
+
793
+ INPUT:
794
+
795
+ - ``z`` -- point in the upper half-plane
796
+ - ``a`` -- ideal or number field element (default = None)
797
+ If None then this is set to the entire ring of integers.
798
+ - ``as_cusp`` -- boolean (default: False)
799
+ If True we return an element of type NFCusp
800
+
801
+ EXAMPLES::
802
+
803
+ sage: from hilbert_modgroup.all import *
804
+ sage: H1 = HilbertModularGroup(5)
805
+ sage: P1 = HilbertPullback(H1)
806
+ sage: z = UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
807
+ sage: P1.get_heuristic_closest_cusp(z)
808
+ (-1, 0)
809
+ sage: z = UpperHalfPlaneProductElement([CC(0,0.5),CC(0,1)])
810
+ sage: P1.get_heuristic_closest_cusp(z)
811
+ (0, 1)
812
+ sage: z = UpperHalfPlaneProductElement([CC(2.2,0.5),CC(1,0.5)])
813
+ sage: P1.get_heuristic_closest_cusp(z)
814
+ (-1/2*a + 3/2, 1)
815
+ sage: H2 = HilbertModularGroup(10)
816
+ sage: P2 = HilbertPullback(H2)
817
+ sage: z = UpperHalfPlaneProductElement([CC(2,0.5),CC(1,0.2)])
818
+ sage: P2.get_heuristic_closest_cusp(z)
819
+ (1, 1)
820
+ sage: z = UpperHalfPlaneProductElement([23.3400000000000 + 0.0100000000000000*I,\
821
+ 0.0200000000000000 + 0.0300000000000000*I])
822
+ sage: P2.get_heuristic_closest_cusp(z)
823
+ (3*a - 10, -a - 4)
824
+ sage: w=UpperHalfPlaneProductElement([-0.668903800800698 + 0.0362571615120737*I,\
825
+ 0.708560139622790 + 0.00414937759336099*I])
826
+ sage: P2.get_heuristic_closest_cusp(w)
827
+ (-a + 4, 2*a - 5)
828
+ sage: x = ZZ['x'].gen()
829
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
830
+ sage: P3=HilbertPullback(H3)
831
+ sage: K3=P3.number_field()
832
+ sage: z = UpperHalfPlaneProductElement([0+0.02*I,10+0.2*I,1+0.2*I])
833
+ sage: P3.get_heuristic_closest_cusp(z)
834
+ (1/3*a^2 + 1/3*a - 29/3, a - 1)
835
+
836
+
837
+ """
838
+ a = self._construct_ideal(a)
839
+ try:
840
+ shortest_basis_vectors = self._shortest_vectors_ideal_plusz(z, a)
841
+ except ValueError:
842
+ log.critical("The LLL 'finding shortest vector' has failed. " +
843
+ "It is likely that you need to upgrade your version of Sage to 9.4+.")
844
+ return None
845
+ # Convert this lattice vector to two integers sigma and rho:
846
+ n = len(a.basis())
847
+ dmin = None
848
+ cusp_min = None
849
+ for vec in shortest_basis_vectors:
850
+ rho = sum([vec[i] * a.basis()[i] for i in range(n)])
851
+ sigma = sum([vec[n + i] * a.basis()[i] for i in range(n)])
852
+ # The actual cusp that minimizes |sigma*z+rho| is of course -rho/sigma
853
+ d = distance_to_cusp(self, -rho, sigma, z)
854
+ if not dmin or d < dmin:
855
+ dmin = d
856
+ cusp_min = -rho, sigma
857
+ if as_cusp:
858
+ cusp_min = self._construct_cusp(cusp_min)
859
+ return cusp_min
860
+
861
+ def _construct_ideal(self, a, b=None):
862
+ r"""
863
+ Construct an ideal of the number field associated with self from a.
864
+
865
+ EXAMPLES::
866
+
867
+ sage: from hilbert_modgroup.all import *
868
+ sage: P1 = HilbertPullback(HilbertModularGroup(5))
869
+ sage: P1._construct_ideal(1)
870
+ Fractional ideal (1)
871
+ sage: P1._construct_ideal(P1.number_field().fractional_ideal(1))
872
+ Fractional ideal (1)
873
+ sage: a = P1.number_field().fractional_ideal(1)
874
+ sage: b = P1.number_field().fractional_ideal(2)
875
+ sage: P1._construct_ideal(a, b)
876
+ Fractional ideal (1/2)
877
+ """
878
+ if a is None:
879
+ ideala = self.group().base_ring().fractional_ideal(1)
880
+ elif isinstance(a, NumberFieldIdeal):
881
+ if b:
882
+ ideala = a / b
883
+ else:
884
+ ideala = a
885
+ elif a in self.group().base_ring() and not b:
886
+ ideala = self.group().base_ring().fractional_ideal(a)
887
+ elif a in self.group().base_ring() and b in self.group().base_ring():
888
+ ideala = self.group().base_ring().fractional_ideal(a, b)
889
+ else:
890
+ raise ValueError(f"Could not construct a number field ideal from a={a} and b={b}")
891
+ return ideala
892
+
893
+ def _construct_cusp(self, c, d=None):
894
+ r"""
895
+ Return an instance of NFCusp for the number field of self from input c and optional d
896
+
897
+ INPUT:
898
+
899
+ - ``c`` -- instance of NFCusp, element or tuple of elements of number field
900
+ - ``d`` -- element of number field or None (default: None)
901
+
902
+ EXAMPLES::
903
+
904
+ sage: from hilbert_modgroup.all import *
905
+ sage: P1=HilbertPullback(HilbertModularGroup(5))
906
+ sage: P1._construct_cusp(1,0)
907
+ Cusp Infinity of Number Field in a with ... polynomial
908
+ x^2 - 5 with a = 2.236067977499790?
909
+ sage: P1._construct_cusp((1,0))
910
+ Cusp Infinity of Number Field in a with ... polynomial
911
+ x^2 - 5 with a = 2.236067977499790?
912
+ sage: P1._construct_cusp(0,1)
913
+ Cusp [0: 1] of Number Field in a with ... polynomial
914
+ x^2 - 5 with a = 2.236067977499790?
915
+ """
916
+ if isinstance(c, NFCusp) and c.number_field() == self.number_field():
917
+ return c
918
+ if isinstance(c, NFCusp) and c.number_field() != self.number_field():
919
+ raise ValueError(f"The input cusp {c} has wrong base number field.")
920
+ if isinstance(c, tuple) and len(c) == 2:
921
+ c, d = c
922
+ try:
923
+ cusp = NFCusp(self.number_field(), c, d)
924
+ except Exception:
925
+ raise ValueError(f"Could not construct a number field cusp from c={c} and d={d}")
926
+ return cusp
927
+
928
+ def X(self, z, a=None):
929
+ r"""
930
+ Coordinate of z with respect to the integral basis of an ideal.
931
+
932
+ EXAMPLES::
933
+
934
+ sage: from hilbert_modgroup.all import *
935
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
936
+ sage: H1 = HilbertModularGroup(5)
937
+ sage: P1 = HilbertPullback(H1)
938
+ sage: z = UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
939
+ sage: P1.X(z)
940
+ (1.00000000000000, 0.000000000000000)
941
+ sage: b1,b2 = H1.base_ring().fractional_ideal(1).integral_basis(); b1,b2
942
+ (1, 1/2*a - 1/2)
943
+ sage: P1.X(z+b1)
944
+ (2.00000000000000, 0.000000000000000)
945
+ sage: P1.X(z-b1)
946
+ (0.000000000000000, 0.000000000000000)
947
+ sage: P1.X(z+b2)
948
+ (0.999999999999999, 1.00000000000000)
949
+ sage: P1.X(z-b2)
950
+ (1.00000000000000, -1.00000000000000)
951
+
952
+ """
953
+ self._check_upper_half_plane_element(z)
954
+ B = self.basis_matrix_ideal(a, prec=z.base_ring().prec())
955
+ return vector(B**-1 * vector(z.real()))
956
+
957
+ def reduce_by_translations(self, z, a=None, return_map=False):
958
+ r"""
959
+ Reduce the point z with respect to the cuspidal region in a neighbourhood of the cusp .
960
+
961
+ INPUT:
962
+
963
+ - ``z`` -- point in the upper half-plane.
964
+ - ``a`` -- ideal cusp (default=None) if None use the ring of integers.
965
+ - ``return_map`` -- boolean (default=False),
966
+ If set to True also return the map that makes the reduction.
967
+
968
+ EXAMPLES::
969
+
970
+ sage: from hilbert_modgroup.all import *
971
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
972
+ sage: H1 = HilbertModularGroup(5)
973
+ sage: P1 = HilbertPullback(H1)
974
+ sage: z = UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
975
+ sage: P1.reduce_by_translations(z,return_map=True)
976
+ (
977
+ [ 1 -1]
978
+ [1.00000000000000*I, 1.00000000000000*I], [ 0 1]
979
+ )
980
+
981
+
982
+ sage: z = UpperHalfPlaneProductElement([CC(3,1),CC(-1,1)])
983
+ sage: P1.reduce_by_translations(z,return_map=True) # abs tol 1e-10
984
+ (
985
+ [-0.236067977499790 + 1.00000000000000*I, 0.236067977499790 + 1.00000000000000*I],
986
+ <BLANKLINE>
987
+ [ 1 a - 1]
988
+ [ 0 1]
989
+ )
990
+ sage: b1,b2=H1.base_ring().gens(); b1+b2
991
+ 3/2*a + 1/2
992
+ sage: P1.reduce_by_translations(z,b1+b2,return_map=True) # abs tol 1e-10
993
+ (
994
+ [4.76393202250021 + 1.00000000000000*I, 5.23606797749979 + 1.00000000000000*I],
995
+ <BLANKLINE>
996
+ [ 1 a + 4]
997
+ [ 0 1]
998
+ )
999
+
1000
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1001
+ sage: P3=HilbertPullback(H3)
1002
+ sage: z = UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
1003
+ sage: P3.reduce_by_translations(z,return_map=True)
1004
+ Traceback (most recent call last):
1005
+ ...
1006
+ ValueError: Need an element of type: UpperHalfPlaneProductElement__class of degree 3
1007
+ sage: z = UpperHalfPlaneProductElement([CC(1,1),CC(1,1),CC(1,1)])
1008
+ sage: P3.reduce_by_translations(z,return_map=True)
1009
+ (
1010
+ [ 1 -1]
1011
+ [1.00000000000000*I, 1.00000000000000*I, 1.00000000000000*I], [ 0 1]
1012
+ )
1013
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(2,1),CC(10,1)])
1014
+ sage: P3.reduce_by_translations(z,return_map=True) # abs tol 1e-10
1015
+ (
1016
+ [2.98606258583498 + 1.0*I, -1.97222162680976 + 1.0*I,-0.0138409590252291 + 1.0*I],
1017
+ <BLANKLINE>
1018
+ [ 1 -a - 4]
1019
+ [ 0 1]
1020
+ )
1021
+ sage: w,A=_
1022
+ sage: vector([floor(x+0.5) for x in P3.X(z)]) + A[0,1].vector() == 0
1023
+ True
1024
+ """
1025
+ X = self.X(z, a)
1026
+ ideala = self._construct_ideal(a)
1027
+ basis = ideala.integral_basis()
1028
+ correction = sum([b*floor(X[i]+0.5) for i, b in enumerate(basis)])
1029
+ reduced_point = z - correction
1030
+ if return_map:
1031
+ return reduced_point, Matrix(2, 2, [1, -correction, 0, 1])
1032
+ return reduced_point
1033
+
1034
+ def is_reduced_by_translations(self, z, a=None, prec=53):
1035
+ r"""
1036
+ Check if the given point is reduced in the cuspidal region.
1037
+
1038
+ EXAMPLES::
1039
+
1040
+ sage: from hilbert_modgroup.all import *
1041
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
1042
+ sage: H1 = HilbertModularGroup(5)
1043
+ sage: P1 = HilbertPullback(H1)
1044
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
1045
+ sage: P1.is_reduced_by_translations(z)
1046
+ False
1047
+ sage: P1.reduce_by_translations(z)
1048
+ [1.00000000000000*I, 1.00000000000000*I]
1049
+ sage: P1.is_reduced_by_translations(P1.reduce_by_translations(z))
1050
+ True
1051
+ sage: z=UpperHalfPlaneProductElement([CC(3,1),CC(-1,1)])
1052
+ sage: P1.is_reduced_by_translations(z)
1053
+ False
1054
+ sage: P1.is_reduced_by_translations(P1.reduce_by_translations(z))
1055
+ True
1056
+ sage: b1,b2=H1.base_ring().gens(); b1+b2
1057
+ 3/2*a + 1/2
1058
+ sage: P1.is_reduced_by_translations(z,b1+b2)
1059
+ False
1060
+ sage: P1.is_reduced_by_translations(P1.reduce_by_translations(z,b1+b2),b1+b2)
1061
+ True
1062
+ sage: P1.is_reduced_by_translations(P1.reduce_by_translations(z,b1+b2))
1063
+ False
1064
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1065
+ sage: P3=HilbertPullback(H3)
1066
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(2,1),CC(10,1)])
1067
+ sage: P3.is_reduced_by_translations(z)
1068
+ False
1069
+ sage: P3.is_reduced_by_translations(P3.reduce_by_translations(z))
1070
+ True
1071
+
1072
+ """
1073
+ X = self.X(z, a)
1074
+ return all(-1/2 <= x < 1/2 for x in X)
1075
+
1076
+ def reduce(self, z, return_map=False):
1077
+ r"""
1078
+ Reduce ``z`` to a point in the fundamental domain.
1079
+
1080
+ INPUT:
1081
+
1082
+ - ``z`` -- point in the upper half-plane
1083
+ - ``return_map`` -- boolean (default False)
1084
+ Set to ``True`` to return the map which performed the reduction.
1085
+
1086
+ EXAMPLES::
1087
+
1088
+ sage: from hilbert_modgroup.all import *
1089
+ sage: H1=HilbertModularGroup(5)
1090
+ sage: P1=HilbertPullback(H1)
1091
+ sage: z = UpperHalfPlaneProductElement([1+I,1+I])
1092
+ sage: P1.reduce(z)
1093
+ [1.00000000000000*I, 1.00000000000000*I]
1094
+ sage: z = UpperHalfPlaneProductElement([0.25+I/2,1+I])
1095
+ sage: P1.reduce(z) # abs tol 1e-10
1096
+ [0.694427190999916 + 0.611145618000168*I, -0.309016994374947 + 1.30901699437495*I]
1097
+ sage: P1.reduce(z, return_map=True)[1]
1098
+ [ 1/2*a - 1/2 -1/2*a - 1/2]
1099
+ [ 1/2*a - 1/2 0]
1100
+ sage: CF = ComplexField(103)
1101
+ sage: z = UpperHalfPlaneProductElement([CF(0.5, 0.5),CF(1,1)])
1102
+ sage: P1.reduce(z)
1103
+ [1.00000000000000000000000000000 + 1.00000000000000000000000000000*I, ...
1104
+ """
1105
+ if z.norm() == 0:
1106
+ raise ValueError("Can not reduce point at the boundary of one of the half-planes.")
1107
+ c = self.find_closest_cusp(z, return_multiple=False, as_cusp=True)
1108
+ c_rep, Umu = self.group().cusp_representative(c, return_map=True)
1109
+ A = self._group.cusp_normalizing_map(c_rep)
1110
+ # Move to the cusp representative
1111
+ w = z.apply(A.inverse()*Umu)
1112
+ # Reduce in the corresponding cuspidal region (mapped to infinitY)
1113
+ w, B = self.reduce_in_cuspidal_region(w, c_rep, return_map=True)
1114
+ # Map back to the actual cuspidal region
1115
+ w = w.apply(A)
1116
+ if return_map:
1117
+ return w, A*B*A.inverse()*Umu
1118
+ return w
1119
+
1120
+ def reduce_in_cuspidal_region(self, z, cusp=None, check=True, return_map=False):
1121
+ r"""
1122
+ Reduce the point z with respect to the cuspidal region in a neighbourhood of a
1123
+ representative cusp.
1124
+
1125
+ INPUT:
1126
+
1127
+ - ``z`` -- point in the upper half-plane.
1128
+ - ``cusp`` -- cusp of the group of self.
1129
+ - ``return_map`` -- boolean (default=False)
1130
+ Return the map A such that AN^-1z is reduced where N is the
1131
+ cusp normalizing map for the cusp.
1132
+
1133
+ EXAMPLES::
1134
+
1135
+ sage: from hilbert_modgroup.all import *
1136
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
1137
+ sage: H1 = HilbertModularGroup(5)
1138
+ sage: P1 = HilbertPullback(H1)
1139
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
1140
+ sage: P1.reduce_in_cuspidal_region(z,H1.cusps()[0])
1141
+ [1.00000000000000*I, 1.00000000000000*I]
1142
+ sage: P1.reduce_in_cuspidal_region(z,H1.cusps()[0],return_map=True)
1143
+ (
1144
+ [ 1 -1]
1145
+ [1.00000000000000*I, 1.00000000000000*I], [ 0 1]
1146
+ )
1147
+ sage: P1.reduce_in_cuspidal_region(z,P1._construct_cusp(0,1),return_map=True)
1148
+ (
1149
+ [ 1 -1]
1150
+ [1.00000000000000*I, 1.00000000000000*I], [ 0 1]
1151
+ )
1152
+
1153
+ Check that if we apply a cusp-normalizing map to a point then the reduction with respect
1154
+ to that cusp is the same as the reduction of the original point with respect to infinity ::
1155
+
1156
+ sage: c=NFCusp(P1.group().base_ring().number_field(),1,2)
1157
+ sage: N=H1.cusp_normalizing_map(c)
1158
+ sage: w=z.apply(N); w
1159
+ [0.384615384615385 + 0.0769230769230769*I, 0.384615384615385 + 0.0769230769230769*I]
1160
+ sage: P1.reduce_in_cuspidal_region(w,cusp=c,return_map=True)
1161
+ (
1162
+ [0.384615384615385 + 0.0769230769230769*I, 0.384615384615385 + 0.0769230769230769*I],
1163
+ [1 0]
1164
+ [0 1]
1165
+ )
1166
+ sage: P1.reduce_in_cuspidal_region(z,cusp=c,return_map=True)
1167
+ (
1168
+ [ 1 -1]
1169
+ [1.00000000000000*I, 1.00000000000000*I], [ 0 1]
1170
+ )
1171
+
1172
+ sage: z=UpperHalfPlaneProductElement([CC(3,1),CC(-1,1)])
1173
+ sage: P1.reduce_in_cuspidal_region(z)
1174
+ [-0.236067977499790 + 1.00000000000000*I, 0.236067977499790 + 1.00000000000000*I]
1175
+ sage: P1.reduce_in_cuspidal_region(z,P1.group().cusps()[0],return_map=True)
1176
+ (
1177
+ [-0.236067977499790 + 1.00000000000000*I, 0.236067977499790 + 1.00000000000000*I],
1178
+ <BLANKLINE>
1179
+ [ 1 a - 1]
1180
+ [ 0 1]
1181
+ )
1182
+ sage: H3 = HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1183
+ sage: P3 = HilbertPullback(H3)
1184
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(2,1),CC(10,1)])
1185
+ sage: cusp = P3.group().cusps()[0]
1186
+ sage: P3.reduce_in_cuspidal_region(z, cusp, # abs tol 1e-10
1187
+ ....: return_map=True)
1188
+ (
1189
+ [2.98606258583498 + 1.0*I, -1.97222162680976 + 1.0*I, -0.0138409590252291 + 1.0*I],
1190
+ <BLANKLINE>
1191
+ [ 1 -a - 4]
1192
+ [ 0 1]
1193
+ )
1194
+ """
1195
+ self._check_upper_half_plane_element(z)
1196
+ if not cusp:
1197
+ cusp = self.group().cusps()[0]
1198
+ ideala = cusp.ideal()**-2
1199
+ # Then reduce with respect to the units, followed by reduction by translation with respect
1200
+ # to the ideal a**-2
1201
+ if return_map:
1202
+ w, A = self.reduce_by_units(z, return_map=return_map)
1203
+ w, B = self.reduce_by_translations(w, ideala, return_map=return_map)
1204
+ return w, B*A
1205
+ else:
1206
+ w = self.reduce_by_units(z, return_map=return_map)
1207
+ w = self.reduce_by_translations(w, ideala, return_map=return_map)
1208
+ return w
1209
+
1210
+ def number_field(self):
1211
+ """
1212
+ Return number field of the Hilbert modular group of self.
1213
+
1214
+ EXAMPLES::
1215
+
1216
+ sage: from hilbert_modgroup.all import *
1217
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1218
+ sage: P3=HilbertPullback(H3)
1219
+ sage: P3.number_field()
1220
+ Number Field in a with defining polynomial x^3 - 36*x - 1
1221
+
1222
+ """
1223
+ return self.group().base_ring().number_field()
1224
+
1225
+ def find_closest_cusp(self, z, return_multiple=False, as_cusp=True):
1226
+ r"""
1227
+ Find the closest cusp (rho:sigma) to z
1228
+
1229
+ INPUT:
1230
+
1231
+ - `z` -- point in the upper half-plane
1232
+ - `return_multiple` -- boolean: default False
1233
+ Set to True to return all cusps with the same minimal distance,
1234
+ otherwise just return one closest cusp.
1235
+ - ``as_cusp`` -- boolean (default True) return instance(s) of NFcusps or tuple(s)
1236
+
1237
+ EXAMPLES::
1238
+
1239
+ sage: from hilbert_modgroup.all import *
1240
+ sage: from hilbert_modgroup.upper_half_plane import UpperHalfPlaneProductElement
1241
+ sage: H1 = HilbertModularGroup(5)
1242
+ sage: P1 = HilbertPullback(H1)
1243
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
1244
+ sage: P1.find_closest_cusp(z,as_cusp=False)
1245
+ (1, 0)
1246
+ sage: P1.find_closest_cusp(z)
1247
+ Cusp Infinity of Number Field in a ... polynomial x^2 - 5 with a = 2.236067977499790?
1248
+ sage: P1.find_closest_cusp(z,return_multiple=True)
1249
+ [Cusp Infinity of Number Field in a ... polynomial x^2 - 5 with a = 2.236067977499790?,
1250
+ Cusp [-1: -1] of Number Field in a ... polynomial x^2 - 5 with a = 2.236067977499790?]
1251
+ sage: H2 = HilbertModularGroup(10)
1252
+ sage: K2 = H2.base_ring().number_field()
1253
+ sage: P2 = HilbertPullback(H2)
1254
+ sage: z=UpperHalfPlaneProductElement([CC(0,1.0),CC(0,1.0)])
1255
+ sage: P2.find_closest_cusp(z)
1256
+ Cusp Infinity of Number Field in a ... polynomial x^2 - 10 with a = 3.162277660168380?
1257
+ sage: P2.find_closest_cusp(z,return_multiple=True)
1258
+ [Cusp Infinity of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?,
1259
+ Cusp [0: 1] of Number Field in a ... polynomial x^2 - 10 with a = 3.162277660168380?]
1260
+ sage: z=UpperHalfPlaneProductElement([CC(2.58,0.5),CC(0.5,0.5)])
1261
+ sage: P2.find_closest_cusp(z)
1262
+ Cusp [-a: -a - 2] of Number Field ... polynomial x^2 - 10 with a = 3.162277660168380?
1263
+
1264
+ """
1265
+ closest_cusp = find_closest_cusp(self, z, return_multiple=return_multiple,
1266
+ use_lll=True,
1267
+ use_norm_bound=True)
1268
+ if as_cusp and return_multiple:
1269
+ return [NFCusp(self.number_field(), c[0], c[1]) for c in closest_cusp]
1270
+ if as_cusp:
1271
+ return NFCusp(self.number_field(), closest_cusp[0], closest_cusp[1])
1272
+ return closest_cusp
1273
+
1274
+ def distance_to_cusp(self, cusp, z):
1275
+ """
1276
+ Give the distance from the point z to the cusp: N(a)^-1 N(Im A^-1z)^(-1/2)
1277
+
1278
+ INPUT:
1279
+ - `cusp` -- NF cusp or tuple of integral elements in the number field.
1280
+ - `z`
1281
+
1282
+ EXAMPLES::
1283
+
1284
+ sage: from hilbert_modgroup.all import *
1285
+ sage: H1 = HilbertModularGroup(5)
1286
+ sage: P1 = HilbertPullback(H1)
1287
+ sage: z=ComplexPlaneProductElement([CC(0,1),CC(0,1)]);
1288
+ sage: P1.distance_to_cusp(H1.cusps()[0],z)
1289
+ 1.00000000000000
1290
+ sage: P1.distance_to_cusp(H1.cusps()[0],z*[2,2])
1291
+ 0.500000000000000
1292
+
1293
+ sage: H2=HilbertModularGroup(10)
1294
+ sage: P2 = HilbertPullback(H2)
1295
+ sage: z=ComplexPlaneProductElement([CC(0,1),CC(0,1)]);
1296
+ sage: P2.distance_to_cusp(H2.cusps()[0],z)
1297
+ 1.00000000000000
1298
+ sage: P2.distance_to_cusp(H2.cusps()[1],z)
1299
+ 7.00000000000000
1300
+ sage: c=NFCusp(H2.base_ring().number_field(),3,H2.base_ring().number_field().gen()+1)
1301
+ sage: P2.distance_to_cusp(c,z) # abs tol 1e-10
1302
+ 6.32455532033675
1303
+ sage: z1=ComplexPlaneProductElement([CC(-1.38,0.1),CC(0.72,0.1)]); z1
1304
+ [-1.38000000000000 + 0.100000000000000*I, 0.720000000000000 + 0.100000000000000*I]
1305
+ sage: P2.distance_to_cusp(H2.cusps()[0],z1)
1306
+ 10.0000000000000
1307
+ sage: c=NFCusp(H2.base_ring().number_field(),3,H2.base_ring().number_field().gen()+1)
1308
+ sage: P2.distance_to_cusp(c,z1) # abs tol 1e-10
1309
+ 0.300834689631305
1310
+ sage: P2.distance_to_cusp(H2.cusps()[1],z1)
1311
+ 5.01191181028675
1312
+
1313
+ """
1314
+ cusp = self._construct_cusp(cusp)
1315
+ if isinstance(z, ComplexPlaneProductElement__class):
1316
+ z = UpperHalfPlaneProductElement(z.z())
1317
+ elif not isinstance(z, UpperHalfPlaneProductElement__class):
1318
+ z = UpperHalfPlaneProductElement(z) # Try to make an upper half-plane element
1319
+ return distance_to_cusp(self, cusp.numerator(), cusp.denominator(), z)
1320
+
1321
+ def polytope_from_bounds(self, bounds, B=None):
1322
+ r"""
1323
+ Return the polytope defined by a set of bounds, either after applying the map 'B', or not.
1324
+
1325
+ Get a list of integer points in an ideal with embeddings in RR^n within a given bound.
1326
+ Reference: Algorithm 10
1327
+
1328
+ INPUT:
1329
+
1330
+ - ``a`` -- ideal or algebraic integer.
1331
+ - ``bounds`` -- list of bounds for the coordinates or scalar which then gives a cube with
1332
+ the same bound.
1333
+ Can be of the form [b1,b2,...,bn] or [(a1,b1),(a2,b2)....(an,bn)].
1334
+ In the first case the bounds are interpreted as (-b1,b1),...,(-bn,bn).
1335
+ - ``return_polyhedron`` -- boolean (default False)
1336
+ Set to True to return a polyhedron of the corresponding domain
1337
+ - ``preimage`` -- boolean (default False)
1338
+ Set to True to return the polyhedron of the pre-image
1339
+ (only used when return_polyhedron=True)
1340
+
1341
+ EXAMPLES::
1342
+
1343
+ sage: from hilbert_modgroup.all import *
1344
+ sage: H1 = HilbertModularGroup(5)
1345
+ sage: P1 = HilbertPullback(H1)
1346
+ sage: p=P1.polytope_from_bounds([(-1,1),(-1,1)])
1347
+ sage: p.vertices()
1348
+ (A vertex at (-1.0, -1.0),
1349
+ A vertex at (-1.0, 1.0),
1350
+ A vertex at (1.0, -1.0),
1351
+ A vertex at (1.0, 1.0))
1352
+ sage: p=P1.polytope_from_bounds([1,1])
1353
+ sage: p.vertices()
1354
+ (A vertex at (-1.0, -1.0),
1355
+ A vertex at (-1.0, 1.0),
1356
+ A vertex at (1.0, -1.0),
1357
+ A vertex at (1.0, 1.0))
1358
+
1359
+ """
1360
+ from sage.geometry.polyhedron.constructor import Polyhedron
1361
+ from sage.modules.free_module_element import free_module_element as vector
1362
+ if not isinstance(bounds, (list, tuple)):
1363
+ raise ValueError("Need a list of bounds!")
1364
+ if not isinstance(bounds[0], tuple):
1365
+ bounds = [(-b, b) for b in bounds]
1366
+ # Hypercube we want embeddings in
1367
+ vertices = cartesian_product([[RDF(a), RDF(b)] for a, b in bounds]).list()
1368
+ p1 = Polyhedron(vertices, base_ring=RDF)
1369
+ if not B:
1370
+ return p1
1371
+ # Hypercube containing the integral points of the coordinates wrt the integral basis
1372
+ vertices = [vector(list(B * vector(x))) for x in p1.vertices()]
1373
+ # Try to make a polyhedron of the mapped vertices.
1374
+ return Polyhedron(vertices, base_ring=RDF)
1375
+
1376
+ @cached_method
1377
+ def max_ideal_norm(self):
1378
+ r"""
1379
+ Compute the maximum of the norms of all ideal representatives in the ideal class group.
1380
+
1381
+ EXAMPLES::
1382
+
1383
+ sage: from hilbert_modgroup.all import *
1384
+ sage: H1 = HilbertModularGroup(5)
1385
+ sage: P1 = HilbertPullback(H1)
1386
+ sage: P1.max_ideal_norm()
1387
+ 1
1388
+ sage: H2 = HilbertModularGroup(10)
1389
+ sage: P2 = HilbertPullback(H2)
1390
+ sage: P2.max_ideal_norm()
1391
+ 2
1392
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1393
+ sage: P3=HilbertPullback(H3)
1394
+ sage: P3.max_ideal_norm()
1395
+ 6
1396
+ """
1397
+ return max([x.norm() for x in self.group().ideal_cusp_representatives()])
1398
+
1399
+ def _matrix_BLambda_row_sum(self, i=None):
1400
+ r"""
1401
+ Compute ``r_i(B_{\Lambda}) = sum_j |b_ij|`` or ``sum_ij |b_ij|``.
1402
+
1403
+ INPUT:
1404
+
1405
+ -`` i`` -- integer (default: None)
1406
+ If i is given then return sum of absolute values in row nr. i,
1407
+ otherwise return the sum.
1408
+
1409
+ EXAMPLES::
1410
+
1411
+ sage: from hilbert_modgroup.all import *
1412
+ sage: H1 = HilbertModularGroup(5)
1413
+ sage: P1 = HilbertPullback(H1)
1414
+ sage: P1._matrix_BLambda_row_sum() # abs tol 1e-10
1415
+ 0.962423650119207
1416
+ sage: P1._matrix_BLambda_row_sum(0) # abs tol 1e-10
1417
+ 0.481211825059603
1418
+ sage: P1._matrix_BLambda_row_sum(1) # abs tol 1e-10
1419
+ 0.481211825059603
1420
+
1421
+ sage: H2 = HilbertModularGroup(10)
1422
+ sage: P2 = HilbertPullback(H2)
1423
+ sage: P2._matrix_BLambda_row_sum() # abs tol 1e-10
1424
+ 3.63689291846413
1425
+ sage: P2._matrix_BLambda_row_sum(0) # abs tol 1e-10
1426
+ 1.81844645923207
1427
+ sage: P2._matrix_BLambda_row_sum(1) # abs tol 1e-10
1428
+ 1.81844645923207
1429
+
1430
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1431
+ sage: P3=HilbertPullback(H3)
1432
+ sage: P3._matrix_BLambda_row_sum()
1433
+ 15.7133517843634
1434
+ sage: P3._matrix_BLambda_row_sum(0)
1435
+ 6.06261225308888
1436
+ sage: P3._matrix_BLambda_row_sum(1)
1437
+ 5.37061649381160
1438
+ sage: P3._matrix_BLambda_row_sum(2)
1439
+ 4.28012303746290
1440
+
1441
+ """
1442
+ B = self.basis_matrix_logarithmic_unit_lattice()
1443
+ if i is not None:
1444
+ return sum([abs(x) for x in B[i]])
1445
+ else:
1446
+ return sum([sum([abs(x) for x in row]) for row in B])
1447
+
1448
+ @cached_method
1449
+ def _exp_matrix_BLambda_row_sum(self, i=None):
1450
+ r"""
1451
+ Compute ``exp(r_i(B_{\Lambda})) = sum_j |b_ij|`` or ``sum_ij |b_ij|``.
1452
+
1453
+ INPUT:
1454
+
1455
+ -`` i`` -- integer (default: None)
1456
+ If i is given then return sum of absolute values in row nr. i,
1457
+ otherwise return the sum.
1458
+
1459
+ EXAMPLES::
1460
+
1461
+ sage: from hilbert_modgroup.all import *
1462
+ sage: H1 = HilbertModularGroup(5)
1463
+ sage: P1 = HilbertPullback(H1)
1464
+ sage: P1._exp_matrix_BLambda_row_sum()
1465
+ 2.61803398874989
1466
+ sage: P1._exp_matrix_BLambda_row_sum(0)
1467
+ 1.61803398874989
1468
+ sage: P1._exp_matrix_BLambda_row_sum(1)
1469
+ 1.61803398874989
1470
+
1471
+ sage: H2 = HilbertModularGroup(10)
1472
+ sage: P2 = HilbertPullback(H2)
1473
+ sage: P2._exp_matrix_BLambda_row_sum() # abs tol 1e-10
1474
+ 37.9736659610102
1475
+ sage: P2._exp_matrix_BLambda_row_sum(0) # abs tol 1e-10
1476
+ 6.16227766016838
1477
+ sage: P2._exp_matrix_BLambda_row_sum(1) # abs tol 1e-10
1478
+ 6.16227766016838
1479
+
1480
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1481
+ sage: P3=HilbertPullback(H3)
1482
+ sage: P3._exp_matrix_BLambda_row_sum() # abs tol 1e-7
1483
+ 6.67147667780289e6
1484
+ sage: P3._exp_matrix_BLambda_row_sum(0) # abs tol 1e-10
1485
+ 429.495924779268
1486
+ sage: P3._exp_matrix_BLambda_row_sum(1) # abs tol 1e-10
1487
+ 214.995370171897
1488
+ sage: P3._exp_matrix_BLambda_row_sum(2) # abs tol 1e-10
1489
+ 72.2493288346009
1490
+
1491
+
1492
+ """
1493
+ return self._matrix_BLambda_row_sum(i).exp()
1494
+
1495
+ @cached_method
1496
+ def _exp_matrix_BLambda_norm(self):
1497
+ r"""
1498
+ Compute e^{|| B_{\Lambda} ||_{\infty}/2 }
1499
+
1500
+ EXAMPLES::
1501
+
1502
+ sage: from hilbert_modgroup.all import *
1503
+ sage: H1 = HilbertModularGroup(5)
1504
+ sage: P1 = HilbertPullback(H1)
1505
+ sage: P1._exp_matrix_BLambda_norm()
1506
+ 1.272019649514069
1507
+
1508
+ sage: H2 = HilbertModularGroup(10)
1509
+ sage: P2 = HilbertPullback(H2)
1510
+ sage: P2._exp_matrix_BLambda_norm()
1511
+ 2.4823935345082533
1512
+
1513
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1514
+ sage: P3=HilbertPullback(H3)
1515
+ sage: P3._exp_matrix_BLambda_norm()
1516
+ 20.72428345635302
1517
+
1518
+ """
1519
+ return self.basis_matrix_logarithmic_unit_lattice().norm(Infinity).exp().sqrt()
1520
+
1521
+ @cached_method()
1522
+ def Di(self, i=None):
1523
+ r"""
1524
+ Return the bound ``D_i`` for this Hilbert modular group.
1525
+
1526
+ INPUT:
1527
+
1528
+ -`` i`` -- integer (default: None)
1529
+ If i is given then return the bound with exp(r_i(B_Lambda))
1530
+ else use exp(sum_i r_i(B_Lambda)
1531
+
1532
+ EXAMPLES::
1533
+
1534
+ sage: from hilbert_modgroup.all import *
1535
+ sage: P1 = HilbertPullback(HilbertModularGroup(5))
1536
+ sage: P1.Di() # abs tol 1e-10
1537
+ 1.61803398874989
1538
+ sage: P1.Di(0) # abs tol 1e-10
1539
+ 1.272019649514069
1540
+ sage: P1.Di(1) # abs tol 1e-10
1541
+ 1.272019649514069
1542
+ sage: P2 = HilbertPullback(HilbertModularGroup(10))
1543
+ sage: P2.Di() # abs tol 1e-10
1544
+ 8.714776642118862
1545
+ sage: P2.Di(0) # abs tol 1e-10
1546
+ 3.510634603648856
1547
+ sage: P2.Di(1) # abs tol 1e-10
1548
+ 3.510634603648856
1549
+ sage: x = ZZ['x'].gen()
1550
+ sage: K3.<a> = NumberField(x^3-x^2-2*x+1)
1551
+ sage: P3 = HilbertPullback(HilbertModularGroup(K3))
1552
+ sage: P3.Di() # abs tol 1e-10
1553
+ 5.04891733952231
1554
+ sage: P3.Di(0) # abs tol 1e-10
1555
+ 1.67389896224499
1556
+ sage: P3.Di(1) # abs tol 1e-10
1557
+ 2.01219217261232
1558
+ sage: P3.Di(2) # abs tol 1e-10
1559
+ 1.49899286313093
1560
+ """
1561
+ n = self.group().base_ring().number_field().degree()
1562
+ return float(self.max_ideal_norm()**(1/n)*self._exp_matrix_BLambda_row_sum(i).sqrt())
1563
+
1564
+ @cached_method()
1565
+ def D(self):
1566
+ r"""
1567
+ Return a list of all bounds ``D_i`` for this Hilbert modular group.
1568
+
1569
+
1570
+ EXAMPLES::
1571
+
1572
+ sage: from hilbert_modgroup.all import *
1573
+ sage: P1 = HilbertPullback(HilbertModularGroup(5))
1574
+ sage: P1.D() # abs tol 1e-10
1575
+ [1.272019649514069, 1.272019649514069]
1576
+ sage: P2 = HilbertPullback(HilbertModularGroup(10))
1577
+ sage: P2.D() # abs tol 1e-10
1578
+ [3.510634603648856, 3.510634603648856]
1579
+ sage: x = ZZ['x'].gen()
1580
+ sage: K3.<a> = NumberField(x^3-x^2-2*x+1)
1581
+ sage: P3 = HilbertPullback(HilbertModularGroup(K3))
1582
+ sage: P3.D() # abs tol 1e-10
1583
+ [1.673898962244985, 2.012192172612324, 1.4989928631309313]
1584
+ """
1585
+ n = self.group().base_ring().number_field().degree()
1586
+ return [self.Di(i) for i in range(n)]
1587
+
1588
+ #
1589
+ # A collection of bounds necessary for finding the closest cusp.
1590
+ #
1591
+ def _bound_for_closest_cusp(self):
1592
+ """
1593
+ This is the bound such that if a cusp is closer than this then it is the closest cusp.
1594
+ Reference: Lemma XX
1595
+
1596
+ EXAMPLES::
1597
+
1598
+ sage: from hilbert_modgroup.all import *
1599
+ sage: P1 = HilbertPullback(HilbertModularGroup(5))
1600
+ sage: P1._bound_for_closest_cusp() # abs tol 1e-10
1601
+ 0.19098300562505255
1602
+ sage: P2 = HilbertPullback(HilbertModularGroup(10))
1603
+ sage: P2._bound_for_closest_cusp() # abs tol 1e-10
1604
+ 0.006583509747431002
1605
+ sage: x = ZZ['x'].gen()
1606
+ sage: K3.<a> = NumberField(x^3-x^2-2*x+1)
1607
+ sage: P3 = HilbertPullback(HilbertModularGroup(K3))
1608
+ sage: P3._bound_for_closest_cusp() # abs tol 1e-10
1609
+ 0.0138694259275406
1610
+
1611
+ """
1612
+ n = self.group().base_ring().number_field().degree()
1613
+ return self.max_ideal_norm()**(-1) * 2**(-n/2.) / \
1614
+ self._exp_matrix_BLambda_row_sum()
1615
+
1616
+ def _Dzi(self, z, i, initial_bd_d=None, use_initial_bd_d=True):
1617
+ """
1618
+ Return the constant `a_i` used to bound the embeddings of sigma in Algorithm XXX.
1619
+
1620
+ INPUT:
1621
+
1622
+ - ``z`` -- point in the upper half-plane
1623
+ - ``initial_bd_d`` -- an initial bound for the distance to nearest cusp (default None)
1624
+ - ``use_initial_bd_d`` -- boolean (default: 'True') Use the initial bound or not.
1625
+ This should only be set to False for demonstration or testing.
1626
+
1627
+ EXAMPLES::
1628
+
1629
+ sage: from hilbert_modgroup.all import *
1630
+ sage: H1 = HilbertModularGroup(5)
1631
+ sage: P1 = HilbertPullback(H1)
1632
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
1633
+ sage: P1._Dzi(z,0)
1634
+ 1.27201964951407
1635
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.5),CC(0,1)])
1636
+ sage: P1._Dzi(z,0)
1637
+ 1.06963676340867
1638
+ sage: z=UpperHalfPlaneProductElement([CC(2.2,0.5),CC(1,0.5)])
1639
+ sage: P1._Dzi(z,0)
1640
+ 1.79890743994787
1641
+ sage: H2 = HilbertModularGroup(10)
1642
+ sage: P2 = HilbertPullback(H2)
1643
+ sage: z=UpperHalfPlaneProductElement([CC(2,0.5),CC(1,0.2)])
1644
+ sage: P2._Dzi(z,0)
1645
+ 6.24288923183892
1646
+ sage: z=UpperHalfPlaneProductElement([23.3400000000000 + 0.0100000000000000*I,\
1647
+ 0.0200000000000000 + 0.0300000000000000*I])
1648
+ sage: P2._Dzi(z,0)
1649
+ 24.4704299162553
1650
+ sage: x = ZZ['x'].gen()
1651
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1652
+ sage: P3=HilbertPullback(H3)
1653
+ sage: K3=P3.number_field()
1654
+ sage: z = UpperHalfPlaneProductElement([0+0.02*I,10+0.2*I,1+0.2*I])
1655
+ sage: P3._Dzi(z,0)
1656
+ 72.7600147269406
1657
+ sage: P3._Dzi(z,1)
1658
+ 51.4787281347537
1659
+ sage: P3._Dzi(z,1)
1660
+ 51.4787281347537
1661
+
1662
+
1663
+ """
1664
+ n = self.group().base_ring().degree()
1665
+ if not use_initial_bd_d:
1666
+ return self.Di(i)*z.imag_norm() ** (-1 / (2*n))
1667
+ dist_to_infinity_bd = z.imag_norm() ** (-1 / 2)
1668
+ dist_to_zero_bd = (z.abs_square_norm()/z.imag_norm())**(0.5)
1669
+ if initial_bd_d:
1670
+ d = min(initial_bd_d, dist_to_infinity_bd, dist_to_zero_bd)
1671
+ else:
1672
+ d = min(dist_to_infinity_bd, dist_to_zero_bd)
1673
+ d = d**(1/n)
1674
+ return self.Di(i)*d
1675
+
1676
+ def _Dz(self, z, initial_bd_d=None, use_initial_bd_d=True):
1677
+ """
1678
+ Return the vector of all bounds Dzi
1679
+
1680
+ INPUT:
1681
+
1682
+ - ``z`` -- point in the upper half-plane
1683
+ - ``initial_bd_d`` -- an initial bound for the distance to nearest cusp (default None)
1684
+ - ``use_initial_bd_d`` -- boolean (default: 'True') Use the initial bound or not.
1685
+ This should only be set to False for demonstration or testing.
1686
+
1687
+ EXAMPLES::
1688
+
1689
+ sage: from hilbert_modgroup.all import *
1690
+ sage: H1 = HilbertModularGroup(5)
1691
+ sage: P1 = HilbertPullback(H1)
1692
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
1693
+ sage: P1._Dz(z)
1694
+ [1.27201964951407, 1.27201964951407]
1695
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.5),CC(0,1)])
1696
+ sage: P1._Dz(z)
1697
+ [1.06963676340867, 1.06963676340867]
1698
+ sage: z=UpperHalfPlaneProductElement([CC(2.2,0.5),CC(1,0.5)])
1699
+ sage: P1._Dz(z)
1700
+ [1.79890743994787, 1.79890743994787]
1701
+ sage: H2 = HilbertModularGroup(10)
1702
+ sage: P2 = HilbertPullback(H2)
1703
+ sage: z=UpperHalfPlaneProductElement([CC(2,0.5),CC(1,0.2)])
1704
+ sage: P2._Dz(z) # abs tol 1e-10
1705
+ [6.24288923183892, 6.24288923183892]
1706
+ sage: z=UpperHalfPlaneProductElement([23.3400000000000 + 0.0100000000000000*I,\
1707
+ 0.0200000000000000 + 0.0300000000000000*I])
1708
+ sage: P2._Dz(z) # abs tol 1e-10
1709
+ [24.4704299162553, 24.4704299162553]
1710
+ sage: x = ZZ['x'].gen()
1711
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1712
+ sage: P3=HilbertPullback(H3)
1713
+ sage: K3=P3.number_field()
1714
+ sage: z = UpperHalfPlaneProductElement([0+0.02*I,10+0.2*I,1+0.2*I])
1715
+ sage: P3._Dz(z)
1716
+ [72.7600147269406, 51.4787281347537, 29.8421537172252]
1717
+
1718
+
1719
+ """
1720
+ n = self.number_field().degree()
1721
+ return [self._Dzi(z, i, initial_bd_d=initial_bd_d, use_initial_bd_d=use_initial_bd_d)
1722
+ for i in range(n)]
1723
+
1724
+ @cached_method()
1725
+ def _bound_for_sigma_norm(self, z, dist=None):
1726
+ r"""
1727
+ Return the bound for the norm of sigma.
1728
+
1729
+ EXAMPLES::
1730
+
1731
+ sage: from hilbert_modgroup.all import *
1732
+ sage: H1 = HilbertModularGroup(5)
1733
+ sage: P1 = HilbertPullback(H1)
1734
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1735
+ sage: P1._bound_for_sigma_norm(z) # abs tol 1e-8
1736
+ 1.1111111111111112
1737
+ sage: P1._bound_for_sigma_norm(z,1) # abs tol 1e-8
1738
+ 1.0540925533894598
1739
+ """
1740
+ if not dist:
1741
+ d = z.imag_norm()**(-1)
1742
+ else:
1743
+ d = dist*z.imag_norm()**(-1/2)
1744
+ return float(d*self.max_ideal_norm())
1745
+
1746
+ def _bound_for_sigma_embeddings(self, z, initial_bd_d=None, prec=16, use_initial_bd_d=True):
1747
+ """
1748
+ Bound for the embeddings of the denominator of the closest cusp to z
1749
+ Reference: Corollary 5.
1750
+
1751
+ INPUT:
1752
+
1753
+ - ``z`` -- point in the upper half-plane
1754
+ - ``prec`` -- integer - the number of bits precision in the returned values.
1755
+ - ``initial_bd_d`` -- float - an initial bound (default None) for the distance to a cusp.
1756
+ If it is None or larger than the distance to infinity then this
1757
+ distance is used.
1758
+ - ``use_initial_bd_d`` -- boolean (default: 'True') -- Use the initial bound or not.
1759
+ This should only be set to False for demonstration or testing.
1760
+
1761
+ EXAMPLES::
1762
+
1763
+ sage: from hilbert_modgroup.all import *
1764
+ sage: H1 = HilbertModularGroup(5)
1765
+ sage: P1 = HilbertPullback(H1)
1766
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1767
+ sage: P1._bound_for_sigma_embeddings(z)
1768
+ [1.306, 1.377]
1769
+ sage: P1._bound_for_sigma_embeddings(z,use_initial_bd_d=False)
1770
+ [1.306, 1.377]
1771
+ sage: z=UpperHalfPlaneProductElement([CC(0,0.5),CC(0,0.5)])
1772
+ sage: P1._bound_for_sigma_embeddings(z)
1773
+ [1.273, 1.273]
1774
+ sage: P1._bound_for_sigma_embeddings(z,use_initial_bd_d=False)
1775
+ [2.545, 2.545]
1776
+ sage: H2 = HilbertModularGroup(10)
1777
+ sage: P2 = HilbertPullback(H2)
1778
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1779
+ sage: P2._bound_for_sigma_embeddings(z)
1780
+ [3.605, 3.800]
1781
+
1782
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1783
+ sage: P3=HilbertPullback(H3)
1784
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(1,1),CC(0,3)])
1785
+ sage: P3._bound_for_sigma_embeddings(z)
1786
+ [31.36, 22.19, 7.426]
1787
+
1788
+
1789
+ """
1790
+ self._check_upper_half_plane_element(z)
1791
+ d = self._Dz(z, initial_bd_d=initial_bd_d, use_initial_bd_d=use_initial_bd_d)
1792
+ return [upper(d[i]*y**(-1/2), prec=prec) for i, y in enumerate(z.imag())]
1793
+
1794
+ def _bound_for_sigma_coordinates(self, z, initial_bd_d=None, prec=16, use_initial_bd_d=True):
1795
+ """
1796
+ Bound `c` for the coordinates, with respect to the ring of integers,
1797
+ of the closest cusp to z.
1798
+ Reference: Lemma XXX
1799
+
1800
+ INPUT:
1801
+
1802
+ - ``z`` -- point in the upper half-plane
1803
+ - ``initial_bd_d`` -- float - an initial bound (default None) for the distance to a cusp.
1804
+ If it is None or larger than the distance to infinity
1805
+ then this distance is used.
1806
+ - ``prec`` -- the number of bits precision in the returned values.
1807
+ - ``use_initial_bd_d`` -- boolean (default: 'True') Use the initial bound or not.
1808
+ This should only be set to False for demonstration or testing.
1809
+
1810
+ EXAMPLES::
1811
+
1812
+ sage: from hilbert_modgroup.all import *
1813
+ sage: H1 = HilbertModularGroup(5)
1814
+ sage: P1 = HilbertPullback(H1)
1815
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1816
+ sage: P1._bound_for_sigma_coordinates(z)
1817
+ [1.358, 1.200]
1818
+
1819
+ sage: H2 = HilbertModularGroup(10)
1820
+ sage: P2 = HilbertPullback(H2)
1821
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1822
+ sage: P2._bound_for_sigma_coordinates(z)
1823
+ [3.702, 1.171]
1824
+
1825
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1826
+ sage: P3=HilbertPullback(H3)
1827
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(1,1),CC(0,3)])
1828
+ sage: P3._bound_for_sigma_coordinates(z)
1829
+ [27.13, 3.688, 1.919]
1830
+
1831
+
1832
+ """
1833
+ self._check_upper_half_plane_element(z)
1834
+ n = self.group().base_ring().degree()
1835
+ d = self._Dz(z, initial_bd_d=initial_bd_d, use_initial_bd_d=use_initial_bd_d)
1836
+ bounds = []
1837
+ B = self.basis_matrix_ideal().inverse()
1838
+ for i in range(n):
1839
+ bd = 0
1840
+ for j, y in enumerate(z.imag()):
1841
+ bd += B[i, j].abs()*y**(-1/2)
1842
+ bounds.append(upper(bd*d[i], prec=prec))
1843
+ return bounds
1844
+
1845
+ def _bound_for_rho_embeddings(self, z, sigma, initial_bd_d=None, use_initial_bd_d=True,
1846
+ prec=16):
1847
+ """
1848
+ Bound for the embeddings of the numerator of the closest cusp to z.
1849
+
1850
+ Reference: Corollary 5
1851
+
1852
+ ``rho_i in [sigma_i x_i - delta*y_i**0.5 , sigma_i x_i - delta*y_i**0.5]``
1853
+
1854
+ INPUT:
1855
+
1856
+ - ``z`` -- point in the upper half-plane
1857
+ - ``sigma`` -- list of complex embeddings of algebraic integer
1858
+ - ``prec`` -- the number of bits precision in the returned values.
1859
+ - ``initial_bd_d`` -- an initial bound for the distance to nearest cusp (default None)
1860
+ - ``use_initial_bd_d`` -- boolean (default: 'True') Use the initial bound or not.
1861
+ This should only be set to False for demonstration or testing.
1862
+
1863
+
1864
+ EXAMPLES::
1865
+
1866
+ sage: from hilbert_modgroup.all import *
1867
+ sage: H1 = HilbertModularGroup(5)
1868
+ sage: P1 = HilbertPullback(H1)
1869
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1870
+ sage: P1._bound_for_rho_embeddings(z, 0)
1871
+ [(-1.306, 1.306), (-1.239, 1.239)]
1872
+ sage: b1,b2=H1.base_ring().gens();
1873
+ sage: P1._bound_for_rho_embeddings(z, b1)
1874
+ [(-1.925, 0.6880), (0.3790, 2.857)]
1875
+ sage: H2 = HilbertModularGroup(10)
1876
+ sage: P2 = HilbertPullback(H2)
1877
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1878
+ sage: P2._bound_for_rho_embeddings(z, 0)
1879
+ [(-3.605, 3.605), (-3.420, 3.420)]
1880
+ sage: b1,b2=H2.base_ring().gens();
1881
+ sage: sage: P2._bound_for_rho_embeddings(z, b2)
1882
+ sage: P2._bound_for_rho_embeddings(z, b2)
1883
+ [(-6.767, 0.4421), (-0.2571, 6.582)]
1884
+
1885
+
1886
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1887
+ sage: P3=HilbertPullback(H3)
1888
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(1,1),CC(0,3)])
1889
+ sage: P3._bound_for_rho_embeddings(z, 0)
1890
+ [(-31.36, 31.36), (-22.19, 22.19), (-22.28, 22.28)]
1891
+ sage: b1,b2,b3=H3.base_ring().gens()
1892
+ sage: P3._bound_for_rho_embeddings(z, b1)
1893
+ [(-31.36, 31.36), (-21.87, 22.52), (-22.28, 22.28)]
1894
+
1895
+ """
1896
+ self._check_upper_half_plane_element(z)
1897
+ n = self.group().base_ring().degree()
1898
+ d = self._Dz(z, initial_bd_d=initial_bd_d, use_initial_bd_d=use_initial_bd_d)
1899
+ if not isinstance(sigma, list):
1900
+ sigma = self.group().base_ring().number_field()(sigma)
1901
+ sigma = sigma.complex_embeddings()
1902
+ bounds = []
1903
+ for i in range(n):
1904
+ y = z.imag()[i]
1905
+ xs = z.real()[i]*sigma[i]
1906
+ dy = d[i]*y**(0.5)
1907
+ b0 = xs - dy
1908
+ b1 = xs + dy
1909
+ bounds.append((b0, b1))
1910
+ res = []
1911
+ for b0, b1 in bounds:
1912
+ # We bound the lower bound differently depending on whether it is positive or negative
1913
+ b0 = lower(b0, prec=prec)
1914
+ b1 = upper(b1, prec=prec)
1915
+ res.append((b0, b1))
1916
+ return res
1917
+
1918
+ def _bound_for_rho_coordinates(self, z, sigma, initial_bd_d=None, use_initial_bd_d=True,
1919
+ prec=16):
1920
+ """
1921
+ Bound for the coordinates, with respect to the ring of integers,
1922
+ of the numerator of the closest cusp to z.
1923
+
1924
+ Reference: Lemma XXX
1925
+
1926
+ ``rho_i in [sigma_i x_i - delta*y_i**0.5 , sigma_i x_i - delta*y_i**0.5]``
1927
+
1928
+ INPUT:
1929
+
1930
+ - ``z`` -- point in the upper half-plane
1931
+ - ``sigma`` -- complex embeddings of algebraic integer
1932
+ - ``prec`` -- the number of bits precision in the returned values.
1933
+ - ``initial_bd_d`` -- an initial bound for the distance to nearest cusp (default None)
1934
+ - ``use_initial_bd_d`` -- boolean (default: 'True') Use the initial bound or not.
1935
+ This should only be set to False for demonstration or testing.
1936
+
1937
+
1938
+ EXAMPLES::
1939
+
1940
+ sage: from hilbert_modgroup.all import *
1941
+ sage: H1 = HilbertModularGroup(5)
1942
+ sage: P1 = HilbertPullback(H1)
1943
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1944
+ sage: P1._bound_for_rho_coordinates(z, 0)
1945
+ [2.571, 2.571]
1946
+ sage: b1,b2=H1.base_ring().gens();
1947
+ sage: P1._bound_for_rho_coordinates(z, b1)
1948
+ [5.839, 4.205]
1949
+ sage: H2 = HilbertModularGroup(10)
1950
+ sage: P2 = HilbertPullback(H2)
1951
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.9)])
1952
+ sage: P2._bound_for_rho_coordinates(z, 0)
1953
+ [7.094, 7.094]
1954
+ sage: b1,b2=H2.base_ring().gens();
1955
+ sage: P2._bound_for_rho_coordinates(z, b2)
1956
+ [20.39, 20.39]
1957
+
1958
+
1959
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
1960
+ sage: P3=HilbertPullback(H3)
1961
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(1,1),CC(0,3)])
1962
+ sage: P3._bound_for_rho_coordinates(z, 0)
1963
+ [76.58, 76.58, 76.58]
1964
+ sage: b1,b2,b3=H3.base_ring().gens()
1965
+ sage: P3._bound_for_rho_coordinates(z, b1)
1966
+ [78.54, 76.59, 78.55]
1967
+
1968
+ """
1969
+ self._check_upper_half_plane_element(z)
1970
+ n = self.group().base_ring().degree()
1971
+ d = self._Dz(z, initial_bd_d=initial_bd_d, use_initial_bd_d=use_initial_bd_d)
1972
+ if not isinstance(sigma, list):
1973
+ sigma = self.group().base_ring().number_field()(sigma)
1974
+ sigma = sigma.complex_embeddings()
1975
+ bounds = []
1976
+ factor = 1.01
1977
+ sz = [(sigma[j] * z.real()[j]).abs() for j in range(n)]
1978
+ dy = [d[j] * z.imag()[j] ** 0.5 for j in range(n)]
1979
+ for i in range(n):
1980
+ bd = 0
1981
+ for j, y in enumerate(z.imag()):
1982
+ bd += dy[j] + sz[j] * self.basis_matrix_ideal()[i, j].abs()
1983
+
1984
+ bounds.append(upper(bd*factor, prec=prec))
1985
+ return bounds
1986
+
1987
+ def _candidate_integers_sigma(self, z, domain='polytope', return_polyhedron=False,
1988
+ ideal_basis=None,
1989
+ lattice_basis=None, sorted=True,
1990
+ initial_bd_d=None,
1991
+ use_initial_bd_d=True,
1992
+ use_norm_bound=True):
1993
+ """
1994
+ Compute a list of candidates for the denominator of the closest cusp.
1995
+
1996
+ INPUT:
1997
+
1998
+ - ``z`` -- element of type UpperHalfPlaneProductelement_class
1999
+ - ``a`` -- ideal or algebraic integer (default = 1).
2000
+ If an integer is given then the ideal is the principal ideal.
2001
+ - ``domain`` -- string: 'polytope' (default), 'boundingbox',
2002
+ - ``return_polyhedron`` -- boolean
2003
+ - ``ideal_basis`` -- list or =None,
2004
+ - ``lattice_basis`` -- list of lists corresponding to a numerical basis of the lattice
2005
+ corresponding to the ring of integers.
2006
+ - ``sorted`` -- boolean -- True to return a list sorted by norm first and then
2007
+ lexicographically with respect to embeddings.
2008
+ - ``initial_bd_d`` -- positive number (default: `None`) - an initial bound for the distance
2009
+ to the nearest cusp.
2010
+ - ``use_norm_bound`` -- boolean (default: `True`) -- True if using the norm bound otherwise
2011
+ - ``use_initial_bd_d`` -- boolean (default: 'True') Use the initial bound or not.
2012
+ This should only be set to False for demonstration or testing.
2013
+
2014
+ EXAMPLES::
2015
+
2016
+ sage: from hilbert_modgroup.all import *
2017
+ sage: H1 = HilbertModularGroup(5)
2018
+ sage: P1 = HilbertPullback(H1)
2019
+ sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
2020
+ sage: P1._candidate_integers_sigma(z)
2021
+ [0, -1, 1]
2022
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
2023
+ sage: P1._candidate_integers_sigma(z)
2024
+ [0, -1, 1]
2025
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.7)])
2026
+ sage: P1._candidate_integers_sigma(z)
2027
+ [0, -1, 1]
2028
+ sage: H2 = HilbertModularGroup(10)
2029
+ sage: P2 = HilbertPullback(H2)
2030
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
2031
+ sage: P2._candidate_integers_sigma(z,use_norm_bound=False)[0:9]
2032
+ [0, -1, 1, -2, 2, -3, 3, a, -a]
2033
+ sage: P2._candidate_integers_sigma(z)
2034
+ [0, -1, 1]
2035
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
2036
+ sage: P3=HilbertPullback(H3)
2037
+ sage: z=UpperHalfPlaneProductElement([CC(0,4),CC(0,5),CC(0,4)])
2038
+ sage: P3._candidate_integers_sigma(z,use_norm_bound=False)[0:5]
2039
+ [0, -1, 1, -2, 2]
2040
+ sage: P3._candidate_integers_sigma(z)
2041
+ [0]
2042
+ """
2043
+ if return_polyhedron:
2044
+ if domain == 'boundingbox':
2045
+ bounds = self._bound_for_sigma_coordinates(z, initial_bd_d=initial_bd_d,
2046
+ use_initial_bd=use_initial_bd_d)
2047
+ B = None
2048
+ elif domain == 'preimage':
2049
+ bounds = self._bound_for_sigma_embeddings(z, initial_bd_d=initial_bd_d,
2050
+ use_initial_bd_d=use_initial_bd_d)
2051
+ B = None
2052
+ else:
2053
+ bounds = self._bound_for_sigma_embeddings(z, initial_bd_d=initial_bd_d,
2054
+ use_initial_bd_d=use_initial_bd_d)
2055
+ B = self.basis_matrix_ideal().inverse()
2056
+ return self.polytope_from_bounds(bounds, B)
2057
+ # Else we use efficient methods to find candidates.
2058
+ sigma_candidates = find_candidate_cusps(
2059
+ self, z, return_sigma_candidates=True,
2060
+ use_norm_bound=use_norm_bound,
2061
+ initial_bd_d=initial_bd_d,
2062
+ use_initial_bd_d=use_initial_bd_d)
2063
+ if sorted:
2064
+ def absort(val):
2065
+ return (sum([abs(x)**2 for x in val.complex_embeddings()]),) \
2066
+ + tuple(val.complex_embeddings())
2067
+ sigma_candidates.sort(key=absort)
2068
+ return sigma_candidates
2069
+
2070
+ def _get_lattice_and_ideal_basis(self, ideal=None):
2071
+ """
2072
+ Compute an integral basis for an ideal as well as a basis matrix for the associated lattice
2073
+ in R^n in the form of a nested list.
2074
+
2075
+ INPUT:
2076
+
2077
+ - ``ideal`` -- ideal (default: `None`) if none then (1) is used.
2078
+
2079
+ EXAMPLES::
2080
+
2081
+ sage: from hilbert_modgroup.all import *
2082
+ sage: H1 = HilbertModularGroup(5)
2083
+ sage: P1 = HilbertPullback(H1)
2084
+ sage: P1._get_lattice_and_ideal_basis()
2085
+ ([[1.00000000000000, -1.61803398874989],
2086
+ [1.00000000000000, 0.618033988749895]],
2087
+ [1, 1/2*a - 1/2])
2088
+ sage: P1._get_lattice_and_ideal_basis(H1.base_ring().fractional_ideal(2))
2089
+ ([[2.00000000000000, -3.23606797749979], [2.00000000000000, 1.23606797749979]],
2090
+ [2, a - 1])
2091
+ sage: H2=HilbertModularGroup(10)
2092
+ sage: P2 = HilbertPullback(H2)
2093
+ sage: P2._get_lattice_and_ideal_basis()
2094
+ ([[1.00000000000000, -3.16227766016838], [1.00000000000000, 3.16227766016838]],
2095
+ [1, a])
2096
+ sage: a=H2.base_ring().fractional_ideal(H2.base_ring().gen(1)+1)
2097
+ sage: P2._get_lattice_and_ideal_basis(a)
2098
+ ([[9.00000000000000, -2.16227766016838], [9.00000000000000, 4.16227766016838]],
2099
+ [9, a + 1])
2100
+ sage: x = ZZ['x'].gen()
2101
+ sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
2102
+ sage: P3=HilbertPullback(H3)
2103
+ sage: P3._get_lattice_and_ideal_basis()
2104
+ ([[1.00000000000000, -5.98606258583498, 2.28229423189948],
2105
+ [1.00000000000000, -0.0277783731902446, -7.67566891172438],
2106
+ [1.00000000000000, 6.01384095902523, 6.39337467982490]],
2107
+ [1, a, 1/3*a^2 + 1/3*a - 23/3])
2108
+ sage: c = P3.group().ideal_cusp_representatives()[1]
2109
+ sage: P3._get_lattice_and_ideal_basis(c)
2110
+ ([[2.00000000000000, -4.98606258583498, 3.28229423189948],
2111
+ [2.00000000000000, 0.972221626809755, -6.67566891172438],
2112
+ [2.00000000000000, 7.01384095902523, 7.39337467982490]],
2113
+ [2, a + 1, 1/3*a^2 + 1/3*a - 20/3])
2114
+
2115
+
2116
+
2117
+ """
2118
+ if not ideal:
2119
+ ideal = self._construct_ideal(1)
2120
+ ideal_basis = ideal.basis()
2121
+ lattice_basis = self.basis_matrix_ideal(ideal)
2122
+ n = len(lattice_basis[0])
2123
+ # Make lattice basis to a nested list
2124
+ # to avoid creation of FreeModule elements
2125
+ lattice_basis = [[lattice_basis[i][j] for j in range(n)]
2126
+ for i in range(n)]
2127
+ return lattice_basis, ideal_basis
2128
+
2129
+ def _candidate_integers_rho(self, z, sigma, a=1, domain='polytope',
2130
+ return_polyhedron=False,
2131
+ ideal_basis=None,
2132
+ lattice_basis=None, sorted=True,
2133
+ use_initial_bd_d=True):
2134
+ """
2135
+ Compute a list of candidates for the denominator of the closest cusp.
2136
+
2137
+ INPUT:
2138
+
2139
+ - ``z`` -- element of type UpperHalfPlaneProductelement_class
2140
+ - ``sigma`` -- algebraic integer
2141
+ - ``a`` -- ideal or algebraic integer (default = 1).
2142
+ If an integer is given then the ideal is the principal ideal.
2143
+ - ``algorithm`` -- string (either 'coordinates' or 'embeddings')
2144
+ - ``use_initial_bd_d`` -- boolean (default: 'True')
2145
+ Use the initial bound or not.
2146
+ This should only be set False for demonstration or testing.
2147
+
2148
+ EXAMPLES::
2149
+
2150
+ sage: from hilbert_modgroup.all import *
2151
+ sage: H1 = HilbertModularGroup(5)
2152
+ sage: P1 = HilbertPullback(H1)
2153
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
2154
+ sage: P1._candidate_integers_rho(z,1)
2155
+ [0, 1, 2]
2156
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,0.25)])
2157
+ sage: P1._candidate_integers_rho(z,1)
2158
+ [1]
2159
+ sage: H2 = HilbertModularGroup(10)
2160
+ sage: P2 = HilbertPullback(H2)
2161
+ sage: z=UpperHalfPlaneProductElement([CC(1,1),CC(1,1)])
2162
+ sage: P2._candidate_integers_rho(z,1)
2163
+ [0, -1, 1, -2, 2, 3, a + 1, -a + 1, 4]
2164
+
2165
+ # sage: H3=HilbertModularGroup(NumberField(x^3-36*x-1, names='a'))
2166
+ # sage: P3=HilbertPullback(H3)
2167
+ # sage: z=UpperHalfPlaneProductElement([CC(0,1),CC(0,1),CC(0,1)])
2168
+ # sage: sigma = H3.base_ring().gens()[1]
2169
+ # sage: P3._candidate_integers_rho(z, sigma)
2170
+
2171
+
2172
+ """
2173
+ if return_polyhedron:
2174
+ if domain == 'boundingbox':
2175
+ bounds = self._bound_for_rho_coordinates(z, sigma)
2176
+ B = None
2177
+ elif domain == 'preimage':
2178
+ bounds = self._bound_for_rho_embeddings(z, sigma)
2179
+ B = None
2180
+ else:
2181
+ bounds = self._bound_for_rho_embeddings(z, sigma)
2182
+ B = self.basis_matrix_ideal().inverse()
2183
+ return self.polytope_from_bounds(bounds, B)
2184
+ if not lattice_basis or not ideal_basis:
2185
+ lattice_basis, ideal_basis = self._get_lattice_and_ideal_basis()
2186
+ dist = None
2187
+ if use_initial_bd_d:
2188
+ candidate_cusp = self.get_heuristic_closest_cusp(z)
2189
+ if candidate_cusp:
2190
+ dist = distance_to_cusp(self, candidate_cusp[0],
2191
+ candidate_cusp[1], z)
2192
+ rho_coordinate_bounds = self._bound_for_rho_coordinates(
2193
+ z, sigma,
2194
+ initial_bd_d=dist,
2195
+ use_initial_bd_d=use_initial_bd_d)
2196
+ rho_coordinate_bounds = [(-b, b) for b in rho_coordinate_bounds]
2197
+ rho_embedding_bounds = self._bound_for_rho_embeddings(
2198
+ z, sigma,
2199
+ initial_bd_d=dist,
2200
+ use_initial_bd_d=use_initial_bd_d)
2201
+ rho_candidates_coordinates = lattice_elements_in_box(
2202
+ lattice_basis,
2203
+ rho_embedding_bounds,
2204
+ rho_coordinate_bounds)
2205
+ rho_candidates = coordinates_to_ideal_elements(rho_candidates_coordinates,
2206
+ ideal_basis)
2207
+ if sorted:
2208
+ def absort(val):
2209
+ return (sum([abs(x)**2 for x in val.complex_embeddings()]),) \
2210
+ + tuple(val.complex_embeddings())
2211
+ rho_candidates.sort(key=absort)
2212
+ return rho_candidates
2213
+
2214
+ def _candidate_closest_cusps(self, z, use_lll=True, use_norm_bound=True,
2215
+ use_initial_bd_d=True, as_cusps=False):
2216
+ r"""
2217
+ Find candidates for the closest cusp.
2218
+
2219
+ INPUT:
2220
+
2221
+ - ``z`` -- point in the upper half-plane
2222
+ - ``use_lll`` -- boolean (default: `True`)
2223
+ Use the LLL method to find a preliminary bounds
2224
+ - ``use_norm_bound`` -- boolean (default: `True`)
2225
+ Use the norm bound together with the embedding
2226
+ bounds
2227
+ - ``use_initial_bd_d`` -- boolean (default: `False`) Use initial bound
2228
+
2229
+ - ``as_cusps`` -- boolean - (default: `False`), set to True to return a
2230
+ list of cusps instead of tuples.
2231
+
2232
+
2233
+ EXAMPLES::
2234
+
2235
+ sage: from hilbert_modgroup.all import *
2236
+ sage: H1 = HilbertModularGroup(5)
2237
+ sage: P1 = HilbertPullback(H1)
2238
+ sage: z = UpperHalfPlaneProductElement([CC(0,1),CC(0,1)])
2239
+ sage: P1._candidate_closest_cusps(z)
2240
+ [(1, 0), (-1, -1), (0, 1), (1, -1)]
2241
+ """
2242
+ cusp_candidates = find_candidate_cusps(self, z, use_lll=use_lll,
2243
+ use_norm_bound=use_norm_bound,
2244
+ return_sigma_candidates=False,
2245
+ use_initial_bd_d=use_initial_bd_d)
2246
+ if as_cusps:
2247
+ # Convert to cusps
2248
+ cusps = []
2249
+ K = self.group().base_ring().number_field()
2250
+ for rho, sigma in cusp_candidates:
2251
+ c = NFCusp(K, rho, sigma)
2252
+ if c not in cusps:
2253
+ cusps.append(c)
2254
+ return cusps
2255
+ else:
2256
+ return cusp_candidates