hilbert-modular-group 0.1.3__cp312-cp312-macosx_15_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of hilbert-modular-group might be problematic. Click here for more details.
- hilbert_modgroup/__init__.py +1 -0
- hilbert_modgroup/all.py +9 -0
- hilbert_modgroup/hilbert_modular_group_class.py +987 -0
- hilbert_modgroup/hilbert_modular_group_element.cpython-312-darwin.so +0 -0
- hilbert_modgroup/hilbert_modular_group_element.pyx +623 -0
- hilbert_modgroup/pullback.py +2256 -0
- hilbert_modgroup/pullback_cython.cpython-312-darwin.so +0 -0
- hilbert_modgroup/pullback_cython.pyx +411 -0
- hilbert_modgroup/upper_half_plane.cpython-312-darwin.so +0 -0
- hilbert_modgroup/upper_half_plane.pxd +59 -0
- hilbert_modgroup/upper_half_plane.pyx +1828 -0
- hilbert_modgroup/utils.py +66 -0
- hilbert_modgroup/version.py +21 -0
- hilbert_modular_group-0.1.3.dist-info/METADATA +891 -0
- hilbert_modular_group-0.1.3.dist-info/RECORD +18 -0
- hilbert_modular_group-0.1.3.dist-info/WHEEL +5 -0
- hilbert_modular_group-0.1.3.dist-info/licenses/LICENSE +674 -0
- hilbert_modular_group-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,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
|