passagemath-symbolics 10.8.1a1__cp314-cp314t-musllinux_1_2_aarch64.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.
Files changed (181) hide show
  1. passagemath_symbolics/__init__.py +3 -0
  2. passagemath_symbolics-10.8.1a1.dist-info/METADATA +186 -0
  3. passagemath_symbolics-10.8.1a1.dist-info/RECORD +181 -0
  4. passagemath_symbolics-10.8.1a1.dist-info/WHEEL +5 -0
  5. passagemath_symbolics-10.8.1a1.dist-info/top_level.txt +3 -0
  6. sage/all__sagemath_symbolics.py +17 -0
  7. sage/calculus/all.py +14 -0
  8. sage/calculus/calculus.py +2838 -0
  9. sage/calculus/desolvers.py +1864 -0
  10. sage/calculus/predefined.py +51 -0
  11. sage/calculus/tests.py +225 -0
  12. sage/calculus/var.cpython-314t-aarch64-linux-musl.so +0 -0
  13. sage/calculus/var.pyx +401 -0
  14. sage/dynamics/all__sagemath_symbolics.py +6 -0
  15. sage/dynamics/complex_dynamics/all.py +5 -0
  16. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  17. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-aarch64-linux-musl.so +0 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1034 -0
  19. sage/ext/all__sagemath_symbolics.py +1 -0
  20. sage/ext_data/kenzo/CP2.txt +45 -0
  21. sage/ext_data/kenzo/CP3.txt +349 -0
  22. sage/ext_data/kenzo/CP4.txt +4774 -0
  23. sage/ext_data/kenzo/README.txt +49 -0
  24. sage/ext_data/kenzo/S4.txt +20 -0
  25. sage/ext_data/magma/latex/latex.m +1021 -0
  26. sage/ext_data/magma/latex/latex.spec +1 -0
  27. sage/ext_data/magma/sage/basic.m +356 -0
  28. sage/ext_data/magma/sage/sage.spec +1 -0
  29. sage/ext_data/magma/spec +9 -0
  30. sage/geometry/all__sagemath_symbolics.py +8 -0
  31. sage/geometry/hyperbolic_space/all.py +5 -0
  32. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +755 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2419 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1083 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  39. sage/geometry/riemannian_manifolds/all.py +7 -0
  40. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  41. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  42. sage/interfaces/all__sagemath_symbolics.py +1 -0
  43. sage/interfaces/magma.py +2991 -0
  44. sage/interfaces/magma_free.py +90 -0
  45. sage/interfaces/maple.py +1402 -0
  46. sage/interfaces/mathematica.py +1345 -0
  47. sage/interfaces/mathics.py +1312 -0
  48. sage/interfaces/sympy.py +1398 -0
  49. sage/interfaces/sympy_wrapper.py +197 -0
  50. sage/interfaces/tides.py +938 -0
  51. sage/libs/all__sagemath_symbolics.py +6 -0
  52. sage/manifolds/all.py +7 -0
  53. sage/manifolds/calculus_method.py +553 -0
  54. sage/manifolds/catalog.py +437 -0
  55. sage/manifolds/chart.py +4010 -0
  56. sage/manifolds/chart_func.py +3416 -0
  57. sage/manifolds/continuous_map.py +2183 -0
  58. sage/manifolds/continuous_map_image.py +155 -0
  59. sage/manifolds/differentiable/affine_connection.py +2475 -0
  60. sage/manifolds/differentiable/all.py +1 -0
  61. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  62. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  63. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  64. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  65. sage/manifolds/differentiable/chart.py +1241 -0
  66. sage/manifolds/differentiable/curve.py +1028 -0
  67. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  68. sage/manifolds/differentiable/degenerate.py +559 -0
  69. sage/manifolds/differentiable/degenerate_submanifold.py +1668 -0
  70. sage/manifolds/differentiable/diff_form.py +1660 -0
  71. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  72. sage/manifolds/differentiable/diff_map.py +1315 -0
  73. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  74. sage/manifolds/differentiable/examples/all.py +1 -0
  75. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  76. sage/manifolds/differentiable/examples/real_line.py +897 -0
  77. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  78. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  79. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  80. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  81. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  82. sage/manifolds/differentiable/manifold.py +4254 -0
  83. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  84. sage/manifolds/differentiable/metric.py +3032 -0
  85. sage/manifolds/differentiable/mixed_form.py +1507 -0
  86. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  87. sage/manifolds/differentiable/multivector_module.py +800 -0
  88. sage/manifolds/differentiable/multivectorfield.py +1522 -0
  89. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  90. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  91. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  92. sage/manifolds/differentiable/scalarfield.py +1343 -0
  93. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  94. sage/manifolds/differentiable/symplectic_form.py +912 -0
  95. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  96. sage/manifolds/differentiable/tangent_space.py +412 -0
  97. sage/manifolds/differentiable/tangent_vector.py +616 -0
  98. sage/manifolds/differentiable/tensorfield.py +4665 -0
  99. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  100. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  101. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  102. sage/manifolds/differentiable/vector_bundle.py +1725 -0
  103. sage/manifolds/differentiable/vectorfield.py +1717 -0
  104. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  105. sage/manifolds/differentiable/vectorframe.py +1832 -0
  106. sage/manifolds/family.py +270 -0
  107. sage/manifolds/local_frame.py +1490 -0
  108. sage/manifolds/manifold.py +3090 -0
  109. sage/manifolds/manifold_homset.py +452 -0
  110. sage/manifolds/operators.py +359 -0
  111. sage/manifolds/point.py +994 -0
  112. sage/manifolds/scalarfield.py +3718 -0
  113. sage/manifolds/scalarfield_algebra.py +629 -0
  114. sage/manifolds/section.py +3111 -0
  115. sage/manifolds/section_module.py +831 -0
  116. sage/manifolds/structure.py +229 -0
  117. sage/manifolds/subset.py +2721 -0
  118. sage/manifolds/subsets/all.py +1 -0
  119. sage/manifolds/subsets/closure.py +131 -0
  120. sage/manifolds/subsets/pullback.py +883 -0
  121. sage/manifolds/topological_submanifold.py +891 -0
  122. sage/manifolds/trivialization.py +733 -0
  123. sage/manifolds/utilities.py +1348 -0
  124. sage/manifolds/vector_bundle.py +1347 -0
  125. sage/manifolds/vector_bundle_fiber.py +332 -0
  126. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  127. sage/matrix/all__sagemath_symbolics.py +1 -0
  128. sage/matrix/matrix_symbolic_dense.cpython-314t-aarch64-linux-musl.so +0 -0
  129. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  130. sage/matrix/matrix_symbolic_dense.pyx +1030 -0
  131. sage/matrix/matrix_symbolic_sparse.cpython-314t-aarch64-linux-musl.so +0 -0
  132. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  133. sage/matrix/matrix_symbolic_sparse.pyx +1038 -0
  134. sage/modules/all__sagemath_symbolics.py +1 -0
  135. sage/modules/vector_callable_symbolic_dense.py +105 -0
  136. sage/modules/vector_symbolic_dense.py +116 -0
  137. sage/modules/vector_symbolic_sparse.py +118 -0
  138. sage/rings/all__sagemath_symbolics.py +4 -0
  139. sage/rings/asymptotic/all.py +6 -0
  140. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  141. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  142. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4106 -0
  143. sage/rings/asymptotic/growth_group.py +5373 -0
  144. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  145. sage/rings/asymptotic/term_monoid.py +5205 -0
  146. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  147. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  148. sage/symbolic/all.py +15 -0
  149. sage/symbolic/assumptions.py +987 -0
  150. sage/symbolic/benchmark.py +93 -0
  151. sage/symbolic/callable.py +456 -0
  152. sage/symbolic/callable.pyi +66 -0
  153. sage/symbolic/comparison_impl.pyi +38 -0
  154. sage/symbolic/complexity_measures.py +35 -0
  155. sage/symbolic/constants.py +1286 -0
  156. sage/symbolic/constants_c_impl.pyi +10 -0
  157. sage/symbolic/expression_conversion_algebraic.py +310 -0
  158. sage/symbolic/expression_conversion_sympy.py +317 -0
  159. sage/symbolic/expression_conversions.py +1727 -0
  160. sage/symbolic/function_factory.py +355 -0
  161. sage/symbolic/function_factory.pyi +41 -0
  162. sage/symbolic/getitem_impl.pyi +24 -0
  163. sage/symbolic/integration/all.py +1 -0
  164. sage/symbolic/integration/external.py +271 -0
  165. sage/symbolic/integration/integral.py +1075 -0
  166. sage/symbolic/maxima_wrapper.py +162 -0
  167. sage/symbolic/operators.py +267 -0
  168. sage/symbolic/operators.pyi +61 -0
  169. sage/symbolic/pynac_constant_impl.pyi +13 -0
  170. sage/symbolic/pynac_function_impl.pyi +8 -0
  171. sage/symbolic/random_tests.py +461 -0
  172. sage/symbolic/relation.py +2062 -0
  173. sage/symbolic/ring.cpython-314t-aarch64-linux-musl.so +0 -0
  174. sage/symbolic/ring.pxd +5 -0
  175. sage/symbolic/ring.pyi +110 -0
  176. sage/symbolic/ring.pyx +1393 -0
  177. sage/symbolic/series_impl.pyi +10 -0
  178. sage/symbolic/subring.py +1025 -0
  179. sage/symbolic/symengine.py +19 -0
  180. sage/symbolic/tests.py +40 -0
  181. sage/symbolic/units.py +1468 -0
@@ -0,0 +1,1502 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ r"""
3
+ Hyperbolic Models
4
+
5
+ In this module, a hyperbolic model is a collection of data that allow
6
+ the user to implement new models of hyperbolic space with minimal effort.
7
+ The data include facts about the underlying set (such as whether the
8
+ model is bounded), facts about the metric (such as whether the model is
9
+ conformal), facts about the isometry group (such as whether it is a
10
+ linear or projective group), and more. Generally speaking, any data
11
+ or method that pertains to the model itself -- rather than the points,
12
+ geodesics, or isometries of the model -- is implemented in this module.
13
+
14
+ Abstractly, a model of hyperbolic space is a connected, simply connected
15
+ manifold equipped with a complete Riemannian metric of constant curvature
16
+ `-1`. This module records information sufficient to enable computations
17
+ in hyperbolic space without explicitly specifying the underlying set or
18
+ its Riemannian metric. Although, see the
19
+ `SageManifolds <http://sagemanifolds.obspm.fr/>`_ project if
20
+ you would like to take this approach.
21
+
22
+ This module implements the abstract base class for a model of hyperbolic
23
+ space of arbitrary dimension. It also contains the implementations of
24
+ specific models of hyperbolic geometry.
25
+
26
+ AUTHORS:
27
+
28
+ - Greg Laun (2013): Initial version.
29
+
30
+ EXAMPLES:
31
+
32
+ We illustrate how the classes in this module encode data by comparing
33
+ the upper half plane (UHP), Poincaré disk (PD) and hyperboloid (HM)
34
+ models. First we create::
35
+
36
+ sage: U = HyperbolicPlane().UHP()
37
+ sage: P = HyperbolicPlane().PD()
38
+ sage: H = HyperbolicPlane().HM()
39
+
40
+ We note that the UHP and PD models are bounded while the HM model is
41
+ not::
42
+
43
+ sage: U.is_bounded() and P.is_bounded()
44
+ True
45
+ sage: H.is_bounded()
46
+ False
47
+
48
+ The isometry groups of UHP and PD are projective, while that of HM is
49
+ linear::
50
+
51
+ sage: U.is_isometry_group_projective()
52
+ True
53
+ sage: H.is_isometry_group_projective()
54
+ False
55
+
56
+ The models are responsible for determining if the coordinates of points
57
+ and the matrix of linear maps are appropriate for constructing points
58
+ and isometries in hyperbolic space::
59
+
60
+ sage: U.point_in_model(2 + I)
61
+ True
62
+ sage: U.point_in_model(2 - I)
63
+ False
64
+ sage: U.point_in_model(2)
65
+ False
66
+ sage: U.boundary_point_in_model(2)
67
+ True
68
+ """
69
+
70
+ # ***********************************************************************
71
+ #
72
+ # Copyright (C) 2013 Greg Laun <glaun@math.umd.edu>
73
+ #
74
+ # Distributed under the terms of the GNU General Public License (GPL)
75
+ # as published by the Free Software Foundation; either version 2 of
76
+ # the License, or (at your option) any later version.
77
+ # http://www.gnu.org/licenses/
78
+ # ***********************************************************************
79
+
80
+ from sage.structure.unique_representation import UniqueRepresentation
81
+ from sage.structure.parent import Parent
82
+ from sage.misc.bindable_class import BindableClass
83
+ from sage.misc.lazy_import import lazy_import
84
+ from sage.functions.other import imag, real
85
+ from sage.misc.functional import sqrt
86
+ from sage.functions.hyperbolic import acosh as arccosh
87
+ from sage.rings.cc import CC
88
+ from sage.rings.real_double import RDF
89
+ from sage.rings.real_mpfr import RR
90
+ from sage.rings.infinity import infinity
91
+ from sage.symbolic.constants import I
92
+ from sage.matrix.constructor import matrix
93
+ from sage.categories.homset import Hom
94
+
95
+ from sage.geometry.hyperbolic_space.hyperbolic_constants import EPSILON, LORENTZ_GRAM
96
+ from sage.geometry.hyperbolic_space.hyperbolic_point import (
97
+ HyperbolicPoint, HyperbolicPointUHP)
98
+ from sage.geometry.hyperbolic_space.hyperbolic_isometry import (
99
+ HyperbolicIsometry, HyperbolicIsometryUHP,
100
+ HyperbolicIsometryPD, HyperbolicIsometryKM, moebius_transform)
101
+ from sage.geometry.hyperbolic_space.hyperbolic_geodesic import (
102
+ HyperbolicGeodesic, HyperbolicGeodesicUHP, HyperbolicGeodesicPD,
103
+ HyperbolicGeodesicKM, HyperbolicGeodesicHM)
104
+ from sage.geometry.hyperbolic_space.hyperbolic_coercion import (
105
+ CoercionUHPtoPD, CoercionUHPtoKM, CoercionUHPtoHM,
106
+ CoercionPDtoUHP, CoercionPDtoKM, CoercionPDtoHM,
107
+ CoercionKMtoUHP, CoercionKMtoPD, CoercionKMtoHM,
108
+ CoercionHMtoUHP, CoercionHMtoPD, CoercionHMtoKM)
109
+
110
+ lazy_import('sage.modules.free_module_element', 'vector')
111
+
112
+ #####################################################################
113
+ # Abstract model
114
+
115
+
116
+ class HyperbolicModel(Parent, UniqueRepresentation, BindableClass):
117
+ r"""
118
+ Abstract base class for hyperbolic models.
119
+ """
120
+ Element = HyperbolicPoint
121
+ _Geodesic = HyperbolicGeodesic
122
+ _Isometry = HyperbolicIsometry
123
+
124
+ def __init__(self, space, name, short_name, bounded, conformal,
125
+ dimension, isometry_group, isometry_group_is_projective):
126
+ """
127
+ Initialize ``self``.
128
+
129
+ EXAMPLES::
130
+
131
+ sage: UHP = HyperbolicPlane().UHP()
132
+ sage: TestSuite(UHP).run()
133
+ sage: PD = HyperbolicPlane().PD()
134
+ sage: TestSuite(PD).run()
135
+ sage: KM = HyperbolicPlane().KM()
136
+ sage: TestSuite(KM).run()
137
+ sage: HM = HyperbolicPlane().HM()
138
+ sage: TestSuite(HM).run()
139
+ """
140
+ self._name = name
141
+ self._short_name = short_name
142
+ self._bounded = bounded
143
+ self._conformal = conformal
144
+ self._dimension = dimension
145
+ self._isometry_group = isometry_group
146
+ self._isometry_group_is_projective = isometry_group_is_projective
147
+ from sage.geometry.hyperbolic_space.hyperbolic_interface import HyperbolicModels
148
+ Parent.__init__(self, category=HyperbolicModels(space))
149
+
150
+ def _repr_(self): # Abstract
151
+ """
152
+ Return a string representation of ``self``.
153
+
154
+ EXAMPLES::
155
+
156
+ sage: HyperbolicPlane().UHP()
157
+ Hyperbolic plane in the Upper Half Plane Model
158
+ """
159
+ return 'Hyperbolic plane in the {}'.format(self._name)
160
+
161
+ def _element_constructor_(self, x, is_boundary=None, **graphics_options): # Abstract
162
+ """
163
+ Construct an element of ``self``.
164
+
165
+ EXAMPLES::
166
+
167
+ sage: UHP = HyperbolicPlane().UHP()
168
+ sage: UHP(2 + I)
169
+ Point in UHP I + 2
170
+ """
171
+ return self.get_point(x, is_boundary, **graphics_options)
172
+
173
+ def name(self) -> str: # Abstract
174
+ """
175
+ Return the name of this model.
176
+
177
+ EXAMPLES::
178
+
179
+ sage: UHP = HyperbolicPlane().UHP()
180
+ sage: UHP.name()
181
+ 'Upper Half Plane Model'
182
+ """
183
+ return self._name
184
+
185
+ def short_name(self) -> str:
186
+ """
187
+ Return the short name of this model.
188
+
189
+ EXAMPLES::
190
+
191
+ sage: UHP = HyperbolicPlane().UHP()
192
+ sage: UHP.short_name()
193
+ 'UHP'
194
+ """
195
+ return self._short_name
196
+
197
+ def is_bounded(self) -> bool:
198
+ """
199
+ Return ``True`` if ``self`` is a bounded model.
200
+
201
+ EXAMPLES::
202
+
203
+ sage: HyperbolicPlane().UHP().is_bounded()
204
+ True
205
+ sage: HyperbolicPlane().PD().is_bounded()
206
+ True
207
+ sage: HyperbolicPlane().KM().is_bounded()
208
+ True
209
+ sage: HyperbolicPlane().HM().is_bounded()
210
+ False
211
+ """
212
+ return self._bounded
213
+
214
+ def is_conformal(self) -> bool:
215
+ """
216
+ Return ``True`` if ``self`` is a conformal model.
217
+
218
+ EXAMPLES::
219
+
220
+ sage: UHP = HyperbolicPlane().UHP()
221
+ sage: UHP.is_conformal()
222
+ True
223
+ """
224
+ return self._conformal
225
+
226
+ def is_isometry_group_projective(self) -> bool:
227
+ """
228
+ Return ``True`` if the isometry group of ``self`` is projective.
229
+
230
+ EXAMPLES::
231
+
232
+ sage: UHP = HyperbolicPlane().UHP()
233
+ sage: UHP.is_isometry_group_projective()
234
+ True
235
+ """
236
+ return self._isometry_group_is_projective
237
+
238
+ def point_in_model(self, p):
239
+ r"""
240
+ Return ``True`` if the point ``p`` is in the interior of the
241
+ given model and ``False`` otherwise.
242
+
243
+ INPUT:
244
+
245
+ - any object that can converted into a complex number
246
+
247
+ OUTPUT: boolean
248
+
249
+ EXAMPLES::
250
+
251
+ sage: HyperbolicPlane().UHP().point_in_model(I)
252
+ True
253
+ sage: HyperbolicPlane().UHP().point_in_model(-I)
254
+ False
255
+ """
256
+ return True
257
+
258
+ def point_test(self, p): # Abstract
259
+ r"""
260
+ Test whether a point is in the model. If the point is in the
261
+ model, do nothing. Otherwise, raise a :exc:`ValueError`.
262
+
263
+ EXAMPLES::
264
+
265
+ sage: from sage.geometry.hyperbolic_space.hyperbolic_model import HyperbolicModelUHP
266
+ sage: HyperbolicPlane().UHP().point_test(2 + I)
267
+ sage: HyperbolicPlane().UHP().point_test(2 - I)
268
+ Traceback (most recent call last):
269
+ ...
270
+ ValueError: -I + 2 is not a valid point in the UHP model
271
+ """
272
+ if not (self.point_in_model(p) or self.boundary_point_in_model(p)):
273
+ error_string = "{0} is not a valid point in the {1} model"
274
+ raise ValueError(error_string.format(p, self._short_name))
275
+
276
+ def boundary_point_in_model(self, p): # Abstract
277
+ r"""
278
+ Return ``True`` if the point is on the ideal boundary of hyperbolic
279
+ space and ``False`` otherwise.
280
+
281
+ INPUT:
282
+
283
+ - any object that can converted into a complex number
284
+
285
+ OUTPUT: boolean
286
+
287
+ EXAMPLES::
288
+
289
+ sage: HyperbolicPlane().UHP().boundary_point_in_model(I)
290
+ False
291
+ """
292
+ return True
293
+
294
+ def bdry_point_test(self, p): # Abstract
295
+ r"""
296
+ Test whether a point is in the model. If the point is in the
297
+ model, do nothing; otherwise raise a :exc:`ValueError`.
298
+
299
+ EXAMPLES::
300
+
301
+ sage: HyperbolicPlane().UHP().bdry_point_test(2)
302
+ sage: HyperbolicPlane().UHP().bdry_point_test(1 + I)
303
+ Traceback (most recent call last):
304
+ ...
305
+ ValueError: I + 1 is not a valid boundary point in the UHP model
306
+ """
307
+ if not self._bounded or not self.boundary_point_in_model(p):
308
+ error_string = "{0} is not a valid boundary point in the {1} model"
309
+ raise ValueError(error_string.format(p, self._short_name))
310
+
311
+ def isometry_in_model(self, A): # Abstract
312
+ r"""
313
+ Return ``True`` if the input matrix represents an isometry of the
314
+ given model and ``False`` otherwise.
315
+
316
+ INPUT:
317
+
318
+ - ``A`` -- a matrix that represents an isometry in the appropriate model
319
+
320
+ OUTPUT: boolean
321
+
322
+ EXAMPLES::
323
+
324
+ sage: HyperbolicPlane().UHP().isometry_in_model(identity_matrix(2))
325
+ True
326
+
327
+ sage: HyperbolicPlane().UHP().isometry_in_model(identity_matrix(3))
328
+ False
329
+ """
330
+ return True
331
+
332
+ def isometry_test(self, A): # Abstract
333
+ r"""
334
+ Test whether an isometry ``A`` is in the model.
335
+
336
+ If the isometry is in the model, do nothing. Otherwise, raise
337
+ a :exc:`ValueError`.
338
+
339
+ EXAMPLES::
340
+
341
+ sage: HyperbolicPlane().UHP().isometry_test(identity_matrix(2))
342
+ sage: HyperbolicPlane().UHP().isometry_test(matrix(2, [I,1,2,1]))
343
+ Traceback (most recent call last):
344
+ ...
345
+ ValueError:
346
+ [I 1]
347
+ [2 1] is not a valid isometry in the UHP model
348
+ """
349
+ if not self.isometry_in_model(A):
350
+ error_string = "\n{0} is not a valid isometry in the {1} model"
351
+ raise ValueError(error_string.format(A, self._short_name))
352
+
353
+ def get_point(self, coordinates, is_boundary=None, **graphics_options):
354
+ r"""
355
+ Return a point in ``self``.
356
+
357
+ Automatically determine the type of point to return given either:
358
+
359
+ #. the coordinates of a point in the interior or ideal boundary
360
+ of hyperbolic space, or
361
+ #. a :class:`~sage.geometry.hyperbolic_space.hyperbolic_point.HyperbolicPoint` object.
362
+
363
+ INPUT:
364
+
365
+ - a point in hyperbolic space or on the ideal boundary
366
+
367
+ OUTPUT: a :class:`~sage.geometry.hyperbolic_space.hyperbolic_point.HyperbolicPoint`
368
+
369
+ EXAMPLES:
370
+
371
+ We can create an interior point via the coordinates::
372
+
373
+ sage: HyperbolicPlane().UHP().get_point(2*I)
374
+ Point in UHP 2*I
375
+
376
+ Or we can create a boundary point via the coordinates::
377
+
378
+ sage: HyperbolicPlane().UHP().get_point(23)
379
+ Boundary point in UHP 23
380
+
381
+ However we cannot create points outside of our model::
382
+
383
+ sage: HyperbolicPlane().UHP().get_point(12 - I)
384
+ Traceback (most recent call last):
385
+ ...
386
+ ValueError: -I + 12 is not a valid point in the UHP model
387
+
388
+ ::
389
+
390
+ sage: HyperbolicPlane().UHP().get_point(2 + 3*I)
391
+ Point in UHP 3*I + 2
392
+
393
+ sage: HyperbolicPlane().PD().get_point(0)
394
+ Point in PD 0
395
+
396
+ sage: HyperbolicPlane().KM().get_point((0,0))
397
+ Point in KM (0, 0)
398
+
399
+ sage: HyperbolicPlane().HM().get_point((0,0,1))
400
+ Point in HM (0, 0, 1)
401
+
402
+ sage: p = HyperbolicPlane().UHP().get_point(I, color='red')
403
+ sage: p.graphics_options()
404
+ {'color': 'red'}
405
+
406
+ ::
407
+
408
+ sage: HyperbolicPlane().UHP().get_point(12)
409
+ Boundary point in UHP 12
410
+
411
+ sage: HyperbolicPlane().UHP().get_point(infinity)
412
+ Boundary point in UHP +Infinity
413
+
414
+ sage: HyperbolicPlane().PD().get_point(I)
415
+ Boundary point in PD I
416
+
417
+ sage: HyperbolicPlane().KM().get_point((0,-1))
418
+ Boundary point in KM (0, -1)
419
+ """
420
+
421
+ if isinstance(coordinates, HyperbolicPoint):
422
+ if coordinates.parent() is not self:
423
+ coordinates = self(coordinates)
424
+ coordinates.update_graphics(True, **graphics_options)
425
+ return coordinates # both Point and BdryPoint
426
+
427
+ if is_boundary is None:
428
+ is_boundary = self.boundary_point_in_model(coordinates)
429
+ return self.element_class(self, coordinates, is_boundary, **graphics_options)
430
+
431
+ def get_geodesic(self, start, end=None, **graphics_options): # Abstract
432
+ r"""
433
+ Return a geodesic in the appropriate model.
434
+
435
+ EXAMPLES::
436
+
437
+ sage: HyperbolicPlane().UHP().get_geodesic(I, 2*I)
438
+ Geodesic in UHP from I to 2*I
439
+
440
+ sage: HyperbolicPlane().PD().get_geodesic(0, I/2)
441
+ Geodesic in PD from 0 to 1/2*I
442
+
443
+ sage: HyperbolicPlane().KM().get_geodesic((1/2, 1/2), (0,0))
444
+ Geodesic in KM from (1/2, 1/2) to (0, 0)
445
+
446
+ sage: HyperbolicPlane().HM().get_geodesic((0,0,1), (1,0, sqrt(2)))
447
+ Geodesic in HM from (0, 0, 1) to (1, 0, sqrt(2))
448
+
449
+ TESTS::
450
+
451
+ sage: UHP = HyperbolicPlane().UHP()
452
+ sage: g = UHP.get_geodesic(UHP.get_point(I), UHP.get_point(2 + I))
453
+ sage: h = UHP.get_geodesic(I, 2 + I)
454
+ sage: g == h
455
+ True
456
+ """
457
+ if end is None:
458
+ if isinstance(start, HyperbolicGeodesic):
459
+ G = start
460
+ if G.model() is not self:
461
+ G = G.to_model(self)
462
+ G.update_graphics(True, **graphics_options)
463
+ return G
464
+ raise ValueError("the start and end points must be specified")
465
+ return self._Geodesic(self, self(start), self(end), **graphics_options)
466
+
467
+ def get_isometry(self, A):
468
+ r"""
469
+ Return an isometry in ``self`` from the matrix ``A`` in the
470
+ isometry group of ``self``.
471
+
472
+ EXAMPLES::
473
+
474
+ sage: HyperbolicPlane().UHP().get_isometry(identity_matrix(2))
475
+ Isometry in UHP
476
+ [1 0]
477
+ [0 1]
478
+
479
+ sage: HyperbolicPlane().PD().get_isometry(identity_matrix(2))
480
+ Isometry in PD
481
+ [1 0]
482
+ [0 1]
483
+
484
+ sage: HyperbolicPlane().KM().get_isometry(identity_matrix(3)) # needs scipy
485
+ Isometry in KM
486
+ [1 0 0]
487
+ [0 1 0]
488
+ [0 0 1]
489
+
490
+ sage: HyperbolicPlane().HM().get_isometry(identity_matrix(3)) # needs scipy
491
+ Isometry in HM
492
+ [1 0 0]
493
+ [0 1 0]
494
+ [0 0 1]
495
+ """
496
+ if isinstance(A, HyperbolicIsometry):
497
+ if A.model() is not self:
498
+ return A.to_model(self)
499
+ return A
500
+ return self._Isometry(self, A)
501
+
502
+ def random_element(self, **kwargs):
503
+ r"""
504
+ Return a random point in ``self``.
505
+
506
+ The points are uniformly distributed over the rectangle
507
+ `[-10, 10] \times [0, 10 i]` in the upper half plane model.
508
+
509
+ EXAMPLES::
510
+
511
+ sage: p = HyperbolicPlane().UHP().random_element()
512
+ sage: bool((p.coordinates().imag()) > 0)
513
+ True
514
+
515
+ sage: p = HyperbolicPlane().PD().random_element()
516
+ sage: HyperbolicPlane().PD().point_in_model(p.coordinates())
517
+ True
518
+
519
+ sage: p = HyperbolicPlane().KM().random_element()
520
+ sage: HyperbolicPlane().KM().point_in_model(p.coordinates())
521
+ True
522
+
523
+ sage: p = HyperbolicPlane().HM().random_element().coordinates()
524
+ sage: bool((p[0]**2 + p[1]**2 - p[2]**2 - 1) < 10**-8)
525
+ True
526
+ """
527
+ return self.random_point(**kwargs)
528
+
529
+ def random_point(self, **kwargs):
530
+ r"""
531
+ Return a random point of ``self``.
532
+
533
+ The points are uniformly distributed over the rectangle
534
+ `[-10, 10] \times [0, 10 i]` in the upper half plane model.
535
+
536
+ EXAMPLES::
537
+
538
+ sage: p = HyperbolicPlane().UHP().random_point()
539
+ sage: bool((p.coordinates().imag()) > 0)
540
+ True
541
+
542
+ sage: PD = HyperbolicPlane().PD()
543
+ sage: p = PD.random_point()
544
+ sage: PD.point_in_model(p.coordinates())
545
+ True
546
+ """
547
+ R = self.realization_of().a_realization()
548
+ return self(R.random_point(**kwargs))
549
+
550
+ def random_geodesic(self, **kwargs):
551
+ r"""
552
+ Return a random hyperbolic geodesic.
553
+
554
+ Return the geodesic between two random points.
555
+
556
+ EXAMPLES::
557
+
558
+ sage: h = HyperbolicPlane().PD().random_geodesic()
559
+ sage: all( e.coordinates().abs() <= 1 for e in h.endpoints() )
560
+ True
561
+ """
562
+ R = self.realization_of().a_realization()
563
+ g_ends = [R.random_point(**kwargs) for k in range(2)]
564
+ return self.get_geodesic(self(g_ends[0]), self(g_ends[1]))
565
+
566
+ def random_isometry(self, preserve_orientation=True, **kwargs):
567
+ r"""
568
+ Return a random isometry in the model of ``self``.
569
+
570
+ INPUT:
571
+
572
+ - ``preserve_orientation`` -- if ``True`` return an
573
+ orientation-preserving isometry
574
+
575
+ OUTPUT: a hyperbolic isometry
576
+
577
+ EXAMPLES::
578
+
579
+ sage: # needs scipy
580
+ sage: A = HyperbolicPlane().PD().random_isometry()
581
+ sage: A.preserves_orientation()
582
+ True
583
+ sage: B = HyperbolicPlane().PD().random_isometry(preserve_orientation=False)
584
+ sage: B.preserves_orientation()
585
+ False
586
+ """
587
+ R = self.realization_of().a_realization()
588
+ A = R.random_isometry(preserve_orientation, **kwargs)
589
+ return A.to_model(self)
590
+
591
+ ################
592
+ # Dist methods #
593
+ ################
594
+
595
+ def dist(self, a, b):
596
+ r"""
597
+ Calculate the hyperbolic distance between ``a`` and ``b``.
598
+
599
+ INPUT:
600
+
601
+ - ``a``, ``b`` -- a point or geodesic
602
+
603
+ OUTPUT: the hyperbolic distance
604
+
605
+ EXAMPLES::
606
+
607
+ sage: UHP = HyperbolicPlane().UHP()
608
+ sage: p1 = UHP.get_point(5 + 7*I)
609
+ sage: p2 = UHP.get_point(1.0 + I)
610
+ sage: UHP.dist(p1, p2)
611
+ 2.23230104635820
612
+
613
+ sage: PD = HyperbolicPlane().PD()
614
+ sage: p1 = PD.get_point(0)
615
+ sage: p2 = PD.get_point(I/2)
616
+ sage: PD.dist(p1, p2)
617
+ arccosh(5/3)
618
+
619
+ sage: UHP(p1).dist(UHP(p2))
620
+ arccosh(5/3)
621
+
622
+ sage: KM = HyperbolicPlane().KM()
623
+ sage: p1 = KM.get_point((0, 0))
624
+ sage: p2 = KM.get_point((1/2, 1/2))
625
+ sage: numerical_approx(KM.dist(p1, p2))
626
+ 0.881373587019543
627
+
628
+ sage: HM = HyperbolicPlane().HM()
629
+ sage: p1 = HM.get_point((0,0,1))
630
+ sage: p2 = HM.get_point((1,0,sqrt(2)))
631
+ sage: numerical_approx(HM.dist(p1, p2))
632
+ 0.881373587019543
633
+
634
+ Distance between a point and itself is 0::
635
+
636
+ sage: p = UHP.get_point(47 + I)
637
+ sage: UHP.dist(p, p)
638
+ 0
639
+
640
+ Points on the boundary are infinitely far from interior points::
641
+
642
+ sage: UHP.get_point(3).dist(UHP.get_point(I))
643
+ +Infinity
644
+
645
+ TESTS::
646
+
647
+ sage: UHP.dist(UHP.get_point(I), UHP.get_point(2*I))
648
+ arccosh(5/4)
649
+ sage: UHP.dist(I, 2*I)
650
+ arccosh(5/4)
651
+ """
652
+ def coords(x):
653
+ return self(x).coordinates()
654
+
655
+ if isinstance(a, HyperbolicGeodesic):
656
+ if isinstance(b, HyperbolicGeodesic):
657
+ if not a.is_parallel(b):
658
+ return 0
659
+
660
+ if a.is_ultra_parallel(b):
661
+ perp = a.common_perpendicular(b)
662
+ # Find where a and b intersect the common perp...
663
+ p = a.intersection(perp)[0]
664
+ q = b.intersection(perp)[0]
665
+ # ...and return their distance
666
+ return self._dist_points(coords(p), coords(q))
667
+
668
+ raise NotImplementedError("can only compute distance between"
669
+ " ultra-parallel and intersecting geodesics")
670
+
671
+ # If only one is a geodesic, make sure it's b to make things easier
672
+ a, b = b, a
673
+
674
+ if isinstance(b, HyperbolicGeodesic):
675
+ (p, q) = b.ideal_endpoints()
676
+ return self._dist_geod_point(coords(p), coords(q), coords(a))
677
+
678
+ return self._dist_points(coords(a), coords(b))
679
+
680
+ def _dist_points(self, p1, p2):
681
+ r"""
682
+ Compute the distance between two points.
683
+
684
+ INPUT:
685
+
686
+ - ``p1``, ``p2`` -- the coordinates of the points
687
+
688
+ EXAMPLES::
689
+
690
+ sage: HyperbolicPlane().PD()._dist_points(3/5*I, 0)
691
+ arccosh(17/8)
692
+ """
693
+ R = self.realization_of().a_realization()
694
+ phi = R.coerce_map_from(self)
695
+ return R._dist_points(phi.image_coordinates(p1), phi.image_coordinates(p2))
696
+
697
+ def _dist_geod_point(self, start, end, p):
698
+ r"""
699
+ Return the hyperbolic distance from a given hyperbolic geodesic
700
+ and a hyperbolic point.
701
+
702
+ INPUT:
703
+
704
+ - ``start`` -- the start ideal point coordinates of the geodesic
705
+ - ``end`` -- the end ideal point coordinates of the geodesic
706
+ - ``p`` -- the coordinates of the point
707
+
708
+ OUTPUT: the hyperbolic distance
709
+
710
+ EXAMPLES::
711
+
712
+ sage: HyperbolicPlane().PD()._dist_geod_point(3/5*I + 4/5, I, 0)
713
+ arccosh(1/10*sqrt(5)*((sqrt(5) - 1)^2 + 4) + 1)
714
+
715
+ If `p` is a boundary point, the distance is infinity::
716
+
717
+ sage: HyperbolicPlane().PD()._dist_geod_point(3/5*I + 4/5, I, 12/13*I + 5/13)
718
+ +Infinity
719
+ """
720
+ R = self.realization_of().a_realization()
721
+ assert R is not self
722
+
723
+ def phi(c):
724
+ return R.coerce_map_from(self).image_coordinates(c)
725
+ return R._dist_geod_point(phi(start), phi(end), phi(p))
726
+
727
+ ####################
728
+ # Isometry methods #
729
+ ####################
730
+
731
+ def isometry_from_fixed_points(self, repel, attract):
732
+ r"""
733
+ Given two fixed points ``repel`` and ``attract`` as hyperbolic
734
+ points return a hyperbolic isometry with ``repel`` as repelling
735
+ fixed point and ``attract`` as attracting fixed point.
736
+
737
+ EXAMPLES::
738
+
739
+ sage: UHP = HyperbolicPlane().UHP()
740
+ sage: PD = HyperbolicPlane().PD()
741
+ sage: PD.isometry_from_fixed_points(-i, i)
742
+ Isometry in PD
743
+ [ 3/4 1/4*I]
744
+ [-1/4*I 3/4]
745
+
746
+ ::
747
+
748
+ sage: p, q = PD.get_point(1/2 + I/2), PD.get_point(6/13 + 9/13*I)
749
+ sage: PD.isometry_from_fixed_points(p, q)
750
+ Traceback (most recent call last):
751
+ ...
752
+ ValueError: fixed points of hyperbolic elements must be ideal
753
+
754
+ sage: p, q = PD.get_point(4/5 + 3/5*I), PD.get_point(-I)
755
+ sage: PD.isometry_from_fixed_points(p, q)
756
+ Isometry in PD
757
+ [ 1/6*I - 2/3 -1/3*I - 1/6]
758
+ [ 1/3*I - 1/6 -1/6*I - 2/3]
759
+ """
760
+ R = self.realization_of().a_realization()
761
+ return R.isometry_from_fixed_points(R(self(repel)), R(self(attract))).to_model(self)
762
+
763
+
764
+ #####################################################################
765
+ # Upper half plane model
766
+
767
+ class HyperbolicModelUHP(HyperbolicModel):
768
+ r"""
769
+ Upper Half Plane model.
770
+ """
771
+ Element = HyperbolicPointUHP
772
+ _Geodesic = HyperbolicGeodesicUHP
773
+ _Isometry = HyperbolicIsometryUHP
774
+
775
+ def __init__(self, space):
776
+ """
777
+ Initialize ``self``.
778
+
779
+ EXAMPLES::
780
+
781
+ sage: UHP = HyperbolicPlane().UHP()
782
+ sage: TestSuite(UHP).run()
783
+ """
784
+ HyperbolicModel.__init__(self, space,
785
+ name="Upper Half Plane Model", short_name='UHP',
786
+ bounded=True, conformal=True, dimension=2,
787
+ isometry_group="PSL(2, \\RR)", isometry_group_is_projective=True)
788
+
789
+ def _coerce_map_from_(self, X):
790
+ """
791
+ Return if there is a coercion map from ``X`` to ``self``.
792
+
793
+ EXAMPLES::
794
+
795
+ sage: UHP = HyperbolicPlane().UHP()
796
+ sage: UHP.has_coerce_map_from(HyperbolicPlane().PD())
797
+ True
798
+ sage: UHP.has_coerce_map_from(HyperbolicPlane().KM())
799
+ True
800
+ sage: UHP.has_coerce_map_from(HyperbolicPlane().HM())
801
+ True
802
+ sage: UHP.has_coerce_map_from(QQ)
803
+ False
804
+ """
805
+ if isinstance(X, HyperbolicModelPD):
806
+ return CoercionPDtoUHP(Hom(X, self))
807
+ if isinstance(X, HyperbolicModelKM):
808
+ return CoercionKMtoUHP(Hom(X, self))
809
+ if isinstance(X, HyperbolicModelHM):
810
+ return CoercionHMtoUHP(Hom(X, self))
811
+ return super()._coerce_map_from_(X)
812
+
813
+ def point_in_model(self, p):
814
+ r"""
815
+ Check whether a complex number lies in the open upper half plane.
816
+
817
+ EXAMPLES::
818
+
819
+ sage: UHP = HyperbolicPlane().UHP()
820
+ sage: UHP.point_in_model(1 + I)
821
+ True
822
+ sage: UHP.point_in_model(infinity)
823
+ False
824
+ sage: UHP.point_in_model(CC(infinity))
825
+ False
826
+ sage: UHP.point_in_model(RR(infinity))
827
+ False
828
+ sage: UHP.point_in_model(1)
829
+ False
830
+ sage: UHP.point_in_model(12)
831
+ False
832
+ sage: UHP.point_in_model(1 - I)
833
+ False
834
+ sage: UHP.point_in_model(-2*I)
835
+ False
836
+ sage: UHP.point_in_model(I)
837
+ True
838
+ sage: UHP.point_in_model(0) # Not interior point
839
+ False
840
+ """
841
+ if isinstance(p, HyperbolicPoint):
842
+ return p.is_boundary()
843
+ if p in CC:
844
+ p = CC(p)
845
+ else:
846
+ p = CC(*p)
847
+ return bool(imag(p) > 0)
848
+
849
+ def boundary_point_in_model(self, p):
850
+ r"""
851
+ Check whether a complex number is a real number or ``\infty``.
852
+ In the ``UHP.model_name_name``, this is the ideal boundary of
853
+ hyperbolic space.
854
+
855
+ EXAMPLES::
856
+
857
+ sage: UHP = HyperbolicPlane().UHP()
858
+ sage: UHP.boundary_point_in_model(1 + I)
859
+ False
860
+ sage: UHP.boundary_point_in_model(infinity)
861
+ True
862
+ sage: UHP.boundary_point_in_model(CC(infinity))
863
+ True
864
+ sage: UHP.boundary_point_in_model(RR(infinity))
865
+ True
866
+ sage: UHP.boundary_point_in_model(1)
867
+ True
868
+ sage: UHP.boundary_point_in_model(12)
869
+ True
870
+ sage: UHP.boundary_point_in_model(1 - I)
871
+ False
872
+ sage: UHP.boundary_point_in_model(-2*I)
873
+ False
874
+ sage: UHP.boundary_point_in_model(0)
875
+ True
876
+ sage: UHP.boundary_point_in_model(I)
877
+ False
878
+ """
879
+ if isinstance(p, HyperbolicPoint):
880
+ return p.is_boundary()
881
+ if p in CC:
882
+ p = CC(p)
883
+ else:
884
+ p = CC(*p)
885
+ im = abs(imag(p).n())
886
+ return (im < EPSILON) or bool(p == infinity)
887
+
888
+ def isometry_in_model(self, A):
889
+ r"""
890
+ Check that ``A`` acts as an isometry on the upper half plane.
891
+ That is, ``A`` must be an invertible `2 \times 2` matrix with real
892
+ entries.
893
+
894
+ EXAMPLES::
895
+
896
+ sage: UHP = HyperbolicPlane().UHP()
897
+ sage: A = matrix(2,[1,2,3,4])
898
+ sage: UHP.isometry_in_model(A)
899
+ True
900
+ sage: B = matrix(2,[I,2,4,1])
901
+ sage: UHP.isometry_in_model(B)
902
+ False
903
+
904
+ An example of a matrix `A` such that `\det(A) \neq 1`, but the `A`
905
+ acts isometrically::
906
+
907
+ sage: C = matrix(2,[10,0,0,10])
908
+ sage: UHP.isometry_in_model(C)
909
+ True
910
+ """
911
+ if isinstance(A, HyperbolicIsometry):
912
+ return True
913
+ return bool(A.ncols() == 2 and A.nrows() == 2 and
914
+ sum([k in RR for k in A.list()]) == 4 and
915
+ abs(A.det()) > -EPSILON)
916
+
917
+ def get_background_graphic(self, **bdry_options):
918
+ r"""
919
+ Return a graphic object that makes the model easier to visualize.
920
+ For the upper half space, the background object is the ideal boundary.
921
+
922
+ EXAMPLES::
923
+
924
+ sage: hp = HyperbolicPlane().UHP().get_background_graphic() # needs sage.plot
925
+ """
926
+ from sage.plot.line import line
927
+ bd_min = bdry_options.get('bd_min', -5)
928
+ bd_max = bdry_options.get('bd_max', 5)
929
+ return line(((bd_min, 0), (bd_max, 0)), color='black')
930
+
931
+ ################
932
+ # Dist methods #
933
+ ################
934
+
935
+ def _dist_points(self, p1, p2):
936
+ r"""
937
+ Compute the distance between two points in the Upper Half Plane
938
+ using the hyperbolic metric.
939
+
940
+ INPUT:
941
+
942
+ - ``p1``, ``p2`` -- the coordinates of the points
943
+
944
+ EXAMPLES::
945
+
946
+ sage: HyperbolicPlane().UHP()._dist_points(4.0*I, I)
947
+ 1.38629436111989
948
+ """
949
+ num = (real(p2) - real(p1))**2 + (imag(p2) - imag(p1))**2
950
+ denom = 2 * imag(p1) * imag(p2)
951
+ if denom == 0:
952
+ return infinity
953
+ return arccosh(1 + num/denom)
954
+
955
+ def _dist_geod_point(self, start, end, p):
956
+ r"""
957
+ Return the hyperbolic distance from a given hyperbolic geodesic
958
+ and a hyperbolic point.
959
+
960
+ INPUT:
961
+
962
+ - ``start`` -- the start ideal point coordinates of the geodesic
963
+ - ``end`` -- the end ideal point coordinates of the geodesic
964
+ - ``p`` -- the coordinates of the point
965
+
966
+ OUTPUT: the hyperbolic distance
967
+
968
+ EXAMPLES::
969
+
970
+ sage: UHP = HyperbolicPlane().UHP()
971
+ sage: UHP._dist_geod_point(2, infinity, I)
972
+ arccosh(1/10*sqrt(5)*((sqrt(5) - 1)^2 + 4) + 1)
973
+
974
+ If `p` is a boundary point, the distance is infinity::
975
+
976
+ sage: HyperbolicPlane().UHP()._dist_geod_point(2, infinity, 5)
977
+ +Infinity
978
+ """
979
+ # Here is the trick for computing distance to a geodesic:
980
+ # find an isometry mapping the geodesic to the geodesic between
981
+ # 0 and infinity (so corresponding to the line imag(z) = 0.
982
+ # then any complex number is r exp(i*theta) in polar coordinates.
983
+ # the mutual perpendicular between this point and imag(z) = 0
984
+ # intersects imag(z) = 0 at ri. So we calculate the distance
985
+ # between r exp(i*theta) and ri after we transform the original
986
+ # point.
987
+ if start + end != infinity:
988
+ # Not a straight line:
989
+ # Map the endpoints to 0 and infinity and the midpoint to 1.
990
+ T = HyperbolicGeodesicUHP._crossratio_matrix(start, (start + end)/2, end)
991
+ else:
992
+ # Is a straight line:
993
+ # Map the endpoints to 0 and infinity and another endpoint to 1.
994
+ T = HyperbolicGeodesicUHP._crossratio_matrix(start, start + 1, end)
995
+ x = moebius_transform(T, p)
996
+ return self._dist_points(x, abs(x)*I)
997
+
998
+ #################
999
+ # Point Methods #
1000
+ #################
1001
+
1002
+ def random_point(self, **kwargs):
1003
+ r"""
1004
+ Return a random point in the upper half plane. The points are
1005
+ uniformly distributed over the rectangle `[-10, 10] \times [0, 10i]`.
1006
+
1007
+ EXAMPLES::
1008
+
1009
+ sage: p = HyperbolicPlane().UHP().random_point().coordinates()
1010
+ sage: bool((p.imag()) > 0)
1011
+ True
1012
+ """
1013
+ # TODO: use **kwargs to allow these to be set
1014
+ real_min = -10
1015
+ real_max = 10
1016
+ imag_min = 0
1017
+ imag_max = 10
1018
+ p = RR.random_element(min=real_min, max=real_max) \
1019
+ + I * RR.random_element(min=imag_min, max=imag_max)
1020
+ return self.get_point(p)
1021
+
1022
+ ####################
1023
+ # Isometry Methods #
1024
+ ####################
1025
+
1026
+ def isometry_from_fixed_points(self, repel, attract):
1027
+ r"""
1028
+ Given two fixed points ``repel`` and ``attract`` as complex
1029
+ numbers return a hyperbolic isometry with ``repel`` as repelling
1030
+ fixed point and ``attract`` as attracting fixed point.
1031
+
1032
+ EXAMPLES::
1033
+
1034
+ sage: UHP = HyperbolicPlane().UHP()
1035
+ sage: UHP.isometry_from_fixed_points(2 + I, 3 + I)
1036
+ Traceback (most recent call last):
1037
+ ...
1038
+ ValueError: fixed points of hyperbolic elements must be ideal
1039
+
1040
+ sage: UHP.isometry_from_fixed_points(2, 0)
1041
+ Isometry in UHP
1042
+ [ -1 0]
1043
+ [-1/3 -1/3]
1044
+
1045
+ TESTS::
1046
+
1047
+ sage: UHP = HyperbolicPlane().UHP()
1048
+ sage: UHP.isometry_from_fixed_points(0, 4)
1049
+ Isometry in UHP
1050
+ [ -1 0]
1051
+ [-1/5 -1/5]
1052
+ sage: UHP.isometry_from_fixed_points(UHP.get_point(0), UHP.get_point(4))
1053
+ Isometry in UHP
1054
+ [ -1 0]
1055
+ [-1/5 -1/5]
1056
+ """
1057
+ if isinstance(repel, HyperbolicPoint):
1058
+ repel = repel._coordinates
1059
+ if isinstance(attract, HyperbolicPoint):
1060
+ attract = attract._coordinates
1061
+
1062
+ if imag(repel) + imag(attract) > EPSILON:
1063
+ raise ValueError("fixed points of hyperbolic elements must be ideal")
1064
+ repel = real(repel)
1065
+ attract = real(attract)
1066
+ if repel == infinity:
1067
+ A = self._moebius_sending([infinity, attract, attract + 1],
1068
+ [infinity, attract, attract + 2])
1069
+ elif attract == infinity:
1070
+ A = self._moebius_sending([repel, infinity, repel + 1],
1071
+ [repel, infinity, repel + 2])
1072
+ else:
1073
+ A = self._moebius_sending([repel, attract, infinity],
1074
+ [repel, attract, max(repel, attract) + 1])
1075
+ return self.get_isometry(A)
1076
+
1077
+ def random_isometry(self, preserve_orientation=True, **kwargs):
1078
+ r"""
1079
+ Return a random isometry in the Upper Half Plane model.
1080
+
1081
+ INPUT:
1082
+
1083
+ - ``preserve_orientation`` -- if ``True`` return an
1084
+ orientation-preserving isometry
1085
+
1086
+ OUTPUT: a hyperbolic isometry
1087
+
1088
+ EXAMPLES::
1089
+
1090
+ sage: A = HyperbolicPlane().UHP().random_isometry() # needs scipy
1091
+ sage: B = HyperbolicPlane().UHP().random_isometry(preserve_orientation=False) # needs scipy
1092
+ sage: B.preserves_orientation() # needs scipy
1093
+ False
1094
+ """
1095
+ a, b, c, d = (RR.random_element() for k in range(4))
1096
+ while abs(a * d - b * c) < EPSILON:
1097
+ a, b, c, d = (RR.random_element() for k in range(4))
1098
+ M = matrix(RDF, 2, [a, b, c, d])
1099
+ M = M / (M.det()).abs().sqrt()
1100
+ if M.det() > 0:
1101
+ if not preserve_orientation:
1102
+ M = M * matrix(2, [0, 1, 1, 0])
1103
+ elif preserve_orientation:
1104
+ M = M * matrix(2, [0, 1, 1, 0])
1105
+ return self._Isometry(self, M, check=False)
1106
+
1107
+ ###################
1108
+ # Helping Methods #
1109
+ ###################
1110
+
1111
+ @staticmethod
1112
+ def _moebius_sending(z, w): # UHP
1113
+ r"""
1114
+ Given two lists ``z`` and ``w`` of three points each in
1115
+ `\mathbb{CP}^1`, return the linear fractional transformation
1116
+ taking the points in ``z`` to the points in ``w``.
1117
+
1118
+ EXAMPLES::
1119
+
1120
+ sage: from sage.geometry.hyperbolic_space.hyperbolic_model import HyperbolicModelUHP
1121
+ sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import moebius_transform
1122
+ sage: bool(abs(moebius_transform(HyperbolicModelUHP._moebius_sending([1,2,infinity],[3 - I, 5*I,-12]),1) - 3 + I) < 10^-4)
1123
+ True
1124
+ sage: bool(abs(moebius_transform(HyperbolicModelUHP._moebius_sending([1,2,infinity],[3 - I, 5*I,-12]),2) - 5*I) < 10^-4)
1125
+ True
1126
+ sage: bool(abs(moebius_transform(HyperbolicModelUHP._moebius_sending([1,2,infinity],[3 - I, 5*I,-12]),infinity) + 12) < 10^-4)
1127
+ True
1128
+ """
1129
+ if len(z) != 3 or len(w) != 3:
1130
+ raise TypeError("moebius_sending requires each list to be three points long")
1131
+ A = HyperbolicGeodesicUHP._crossratio_matrix(z[0], z[1], z[2])
1132
+ B = HyperbolicGeodesicUHP._crossratio_matrix(w[0], w[1], w[2])
1133
+ return B.inverse() * A
1134
+
1135
+ #####################################################################
1136
+ # Poincaré disk model
1137
+
1138
+
1139
+ class HyperbolicModelPD(HyperbolicModel):
1140
+ r"""
1141
+ Poincaré Disk Model.
1142
+ """
1143
+ _Geodesic = HyperbolicGeodesicPD
1144
+ _Isometry = HyperbolicIsometryPD
1145
+
1146
+ def __init__(self, space):
1147
+ """
1148
+ Initialize ``self``.
1149
+
1150
+ EXAMPLES::
1151
+
1152
+ sage: PD = HyperbolicPlane().PD()
1153
+ sage: TestSuite(PD).run()
1154
+ """
1155
+ # name should really be 'Poincaré Disk Model', but utf8 is not
1156
+ # accepted by repr
1157
+ HyperbolicModel.__init__(self, space,
1158
+ name='Poincare Disk Model', short_name='PD',
1159
+ bounded=True, conformal=True, dimension=2,
1160
+ isometry_group="PU(1, 1)",
1161
+ isometry_group_is_projective=True)
1162
+
1163
+ def _coerce_map_from_(self, X):
1164
+ """
1165
+ Return if there is a coercion map from ``X`` to ``self``.
1166
+
1167
+ EXAMPLES::
1168
+
1169
+ sage: PD = HyperbolicPlane().PD()
1170
+ sage: PD.has_coerce_map_from(HyperbolicPlane().UHP())
1171
+ True
1172
+ sage: PD.has_coerce_map_from(HyperbolicPlane().KM())
1173
+ True
1174
+ sage: PD.has_coerce_map_from(HyperbolicPlane().HM())
1175
+ True
1176
+ sage: PD.has_coerce_map_from(QQ)
1177
+ False
1178
+ """
1179
+ if isinstance(X, HyperbolicModelUHP):
1180
+ return CoercionUHPtoPD(Hom(X, self))
1181
+ if isinstance(X, HyperbolicModelKM):
1182
+ return CoercionKMtoPD(Hom(X, self))
1183
+ if isinstance(X, HyperbolicModelHM):
1184
+ return CoercionHMtoPD(Hom(X, self))
1185
+ return super()._coerce_map_from_(X)
1186
+
1187
+ def point_in_model(self, p):
1188
+ r"""
1189
+ Check whether a complex number lies in the open unit disk.
1190
+
1191
+ EXAMPLES::
1192
+
1193
+ sage: PD = HyperbolicPlane().PD()
1194
+ sage: PD.point_in_model(1.00)
1195
+ False
1196
+ sage: PD.point_in_model(1/2 + I/2)
1197
+ True
1198
+ sage: PD.point_in_model(1 + .2*I)
1199
+ False
1200
+ """
1201
+ if isinstance(p, HyperbolicPoint):
1202
+ return p.is_boundary()
1203
+ if p in CC:
1204
+ p = CC(p)
1205
+ else:
1206
+ p = CC(*p)
1207
+ return bool(abs(p) < 1)
1208
+
1209
+ def boundary_point_in_model(self, p):
1210
+ r"""
1211
+ Check whether a complex number lies in the open unit disk.
1212
+
1213
+ EXAMPLES::
1214
+
1215
+ sage: PD = HyperbolicPlane().PD()
1216
+ sage: PD.boundary_point_in_model(1.00)
1217
+ True
1218
+ sage: PD.boundary_point_in_model(1/2 + I/2)
1219
+ False
1220
+ sage: PD.boundary_point_in_model(1 + .2*I)
1221
+ False
1222
+ """
1223
+ if isinstance(p, HyperbolicPoint):
1224
+ return p.is_boundary()
1225
+ if p in CC:
1226
+ p = CC(p)
1227
+ else:
1228
+ p = CC(*p)
1229
+ return bool(abs(abs(p) - 1) < EPSILON)
1230
+
1231
+ def isometry_in_model(self, A):
1232
+ r"""
1233
+ Check if the given matrix ``A`` is in the group `U(1,1)`.
1234
+
1235
+ EXAMPLES::
1236
+
1237
+ sage: z = [CC.random_element() for k in range(2)]; z.sort(key=abs)
1238
+ sage: A = matrix(2,[z[1], z[0],z[0].conjugate(),z[1].conjugate()])
1239
+ sage: HyperbolicPlane().PD().isometry_in_model(A)
1240
+ True
1241
+ """
1242
+ if isinstance(A, HyperbolicIsometry):
1243
+ return True
1244
+ # alpha = A[0][0]
1245
+ # beta = A[0][1]
1246
+ # Orientation preserving and reversing
1247
+ return (HyperbolicIsometryPD._orientation_preserving(A) or
1248
+ HyperbolicIsometryPD._orientation_preserving(I * A))
1249
+
1250
+ def get_background_graphic(self, **bdry_options):
1251
+ r"""
1252
+ Return a graphic object that makes the model easier to visualize.
1253
+
1254
+ For the Poincaré disk, the background object is the ideal boundary.
1255
+
1256
+ EXAMPLES::
1257
+
1258
+ sage: circ = HyperbolicPlane().PD().get_background_graphic() # needs sage.plot
1259
+ """
1260
+ from sage.plot.circle import circle
1261
+ return circle((0, 0), 1, axes=False, color='black')
1262
+
1263
+
1264
+ #####################################################################
1265
+ # Klein disk model
1266
+
1267
+ class HyperbolicModelKM(HyperbolicModel):
1268
+ r"""
1269
+ Klein Model.
1270
+ """
1271
+ _Geodesic = HyperbolicGeodesicKM
1272
+ _Isometry = HyperbolicIsometryKM
1273
+
1274
+ def __init__(self, space):
1275
+ """
1276
+ Initialize ``self``.
1277
+
1278
+ EXAMPLES::
1279
+
1280
+ sage: KM = HyperbolicPlane().KM()
1281
+ sage: TestSuite(KM).run()
1282
+ """
1283
+ HyperbolicModel.__init__(self, space,
1284
+ name="Klein Disk Model", short_name='KM',
1285
+ bounded=True, conformal=False, dimension=2,
1286
+ isometry_group="PSO(2, 1)", isometry_group_is_projective=True)
1287
+
1288
+ def _coerce_map_from_(self, X):
1289
+ """
1290
+ Return if there is a coercion map from ``X`` to ``self``.
1291
+
1292
+ EXAMPLES::
1293
+
1294
+ sage: KM = HyperbolicPlane().UHP()
1295
+ sage: KM.has_coerce_map_from(HyperbolicPlane().UHP())
1296
+ True
1297
+ sage: KM.has_coerce_map_from(HyperbolicPlane().PD())
1298
+ True
1299
+ sage: KM.has_coerce_map_from(HyperbolicPlane().HM())
1300
+ True
1301
+ sage: KM.has_coerce_map_from(QQ)
1302
+ False
1303
+ """
1304
+ if isinstance(X, HyperbolicModelUHP):
1305
+ return CoercionUHPtoKM(Hom(X, self))
1306
+ if isinstance(X, HyperbolicModelPD):
1307
+ return CoercionPDtoKM(Hom(X, self))
1308
+ if isinstance(X, HyperbolicModelHM):
1309
+ return CoercionHMtoKM(Hom(X, self))
1310
+ return super()._coerce_map_from_(X)
1311
+
1312
+ def point_in_model(self, p):
1313
+ r"""
1314
+ Check whether a point lies in the open unit disk.
1315
+
1316
+ EXAMPLES::
1317
+
1318
+ sage: KM = HyperbolicPlane().KM()
1319
+ sage: KM.point_in_model((1, 0))
1320
+ False
1321
+ sage: KM.point_in_model((1/2, 1/2))
1322
+ True
1323
+ sage: KM.point_in_model((1, .2))
1324
+ False
1325
+ """
1326
+ if isinstance(p, HyperbolicPoint):
1327
+ return p.is_boundary()
1328
+ if p in CC:
1329
+ p = CC(p)
1330
+ else:
1331
+ p = CC(*p)
1332
+ # return len(p) == 2 and bool(p[0]**2 + p[1]**2 < 1)
1333
+ return bool(abs(p) < 1)
1334
+
1335
+ def boundary_point_in_model(self, p):
1336
+ r"""
1337
+ Check whether a point lies in the unit circle, which corresponds
1338
+ to the ideal boundary of the hyperbolic plane in the Klein model.
1339
+
1340
+ EXAMPLES::
1341
+
1342
+ sage: KM = HyperbolicPlane().KM()
1343
+ sage: KM.boundary_point_in_model((1, 0))
1344
+ True
1345
+ sage: KM.boundary_point_in_model((1/2, 1/2))
1346
+ False
1347
+ sage: KM.boundary_point_in_model((1, .2))
1348
+ False
1349
+ """
1350
+ if isinstance(p, HyperbolicPoint):
1351
+ return p.is_boundary()
1352
+ if p in CC:
1353
+ p = CC(p)
1354
+ else:
1355
+ p = CC(*p)
1356
+ # return len(p) == 2 and bool(abs(p[0]**2 + p[1]**2 - 1) < EPSILON)
1357
+ return bool(abs(abs(p) - 1) < EPSILON)
1358
+
1359
+ def isometry_in_model(self, A):
1360
+ r"""
1361
+ Check if the given matrix ``A`` is in the group `SO(2,1)`.
1362
+
1363
+ EXAMPLES::
1364
+
1365
+ sage: A = matrix(3, [[1, 0, 0], [0, 17/8, 15/8], [0, 15/8, 17/8]])
1366
+ sage: HyperbolicPlane().KM().isometry_in_model(A) # needs scipy
1367
+ True
1368
+ """
1369
+ if isinstance(A, HyperbolicIsometry):
1370
+ return True
1371
+ return bool((A * LORENTZ_GRAM * A.transpose() - LORENTZ_GRAM).norm()**2 <
1372
+ EPSILON)
1373
+
1374
+ def get_background_graphic(self, **bdry_options):
1375
+ r"""
1376
+ Return a graphic object that makes the model easier to visualize.
1377
+
1378
+ For the Klein model, the background object is the ideal boundary.
1379
+
1380
+ EXAMPLES::
1381
+
1382
+ sage: circ = HyperbolicPlane().KM().get_background_graphic() # needs sage.plot
1383
+ """
1384
+ from sage.plot.circle import circle
1385
+ return circle((0, 0), 1, axes=False, color='black')
1386
+
1387
+ #####################################################################
1388
+ # Hyperboloid model
1389
+
1390
+
1391
+ class HyperbolicModelHM(HyperbolicModel):
1392
+ r"""
1393
+ Hyperboloid Model.
1394
+ """
1395
+ _Geodesic = HyperbolicGeodesicHM
1396
+
1397
+ def __init__(self, space):
1398
+ """
1399
+ Initialize ``self``.
1400
+
1401
+ EXAMPLES::
1402
+
1403
+ sage: HM = HyperbolicPlane().HM()
1404
+ sage: TestSuite(HM).run()
1405
+ """
1406
+ HyperbolicModel.__init__(self, space,
1407
+ name="Hyperboloid Model", short_name='HM',
1408
+ bounded=False, conformal=True, dimension=2,
1409
+ isometry_group="SO(2, 1)", isometry_group_is_projective=False)
1410
+
1411
+ def _coerce_map_from_(self, X):
1412
+ """
1413
+ Return if there is a coercion map from ``X`` to ``self``.
1414
+
1415
+ EXAMPLES::
1416
+
1417
+ sage: HM = HyperbolicPlane().UHP()
1418
+ sage: HM.has_coerce_map_from(HyperbolicPlane().UHP())
1419
+ True
1420
+ sage: HM.has_coerce_map_from(HyperbolicPlane().PD())
1421
+ True
1422
+ sage: HM.has_coerce_map_from(HyperbolicPlane().KM())
1423
+ True
1424
+ sage: HM.has_coerce_map_from(QQ)
1425
+ False
1426
+ """
1427
+ if isinstance(X, HyperbolicModelUHP):
1428
+ return CoercionUHPtoHM(Hom(X, self))
1429
+ if isinstance(X, HyperbolicModelPD):
1430
+ return CoercionPDtoHM(Hom(X, self))
1431
+ if isinstance(X, HyperbolicModelKM):
1432
+ return CoercionKMtoHM(Hom(X, self))
1433
+ return super()._coerce_map_from_(X)
1434
+
1435
+ def point_in_model(self, p):
1436
+ r"""
1437
+ Check whether a complex number lies in the hyperboloid.
1438
+
1439
+ EXAMPLES::
1440
+
1441
+ sage: HM = HyperbolicPlane().HM()
1442
+ sage: HM.point_in_model((0,0,1))
1443
+ True
1444
+ sage: HM.point_in_model((1,0,sqrt(2)))
1445
+ True
1446
+ sage: HM.point_in_model((1,2,1))
1447
+ False
1448
+ """
1449
+ if isinstance(p, HyperbolicPoint):
1450
+ return p.is_boundary()
1451
+ return len(p) == 3 and bool(abs(p[0]**2 + p[1]**2 - p[2]**2 + 1) < EPSILON)
1452
+
1453
+ def boundary_point_in_model(self, p):
1454
+ r"""
1455
+ Return ``False`` since the Hyperboloid model has no boundary points.
1456
+
1457
+ EXAMPLES::
1458
+
1459
+ sage: HM = HyperbolicPlane().HM()
1460
+ sage: HM.boundary_point_in_model((0,0,1))
1461
+ False
1462
+ sage: HM.boundary_point_in_model((1,0,sqrt(2)))
1463
+ False
1464
+ sage: HM.boundary_point_in_model((1,2,1))
1465
+ False
1466
+ """
1467
+ return False
1468
+
1469
+ def isometry_in_model(self, A):
1470
+ r"""
1471
+ Test that the matrix ``A`` is in the group `SO(2,1)^+`.
1472
+
1473
+ EXAMPLES::
1474
+
1475
+ sage: A = diagonal_matrix([1,1,-1])
1476
+ sage: HyperbolicPlane().HM().isometry_in_model(A) # needs scipy
1477
+ True
1478
+ """
1479
+ if isinstance(A, HyperbolicIsometry):
1480
+ return True
1481
+ return bool((A * LORENTZ_GRAM * A.transpose() - LORENTZ_GRAM).norm()**2 < EPSILON)
1482
+
1483
+ def get_background_graphic(self, **bdry_options):
1484
+ r"""
1485
+ Return a graphic object that makes the model easier to visualize.
1486
+ For the hyperboloid model, the background object is the hyperboloid
1487
+ itself.
1488
+
1489
+ EXAMPLES::
1490
+
1491
+ sage: H = HyperbolicPlane().HM().get_background_graphic() # needs sage.plot
1492
+ """
1493
+ from sage.plot.plot3d.all import plot3d
1494
+ from sage.symbolic.ring import SR
1495
+ hyperboloid_opacity = bdry_options.get('hyperboloid_opacity', .1)
1496
+ z_height = bdry_options.get('z_height', 7.0)
1497
+ x_max = sqrt((z_height ** 2 - 1) / 2.0)
1498
+ x = SR.var('x')
1499
+ y = SR.var('y')
1500
+ return plot3d((1 + x ** 2 + y ** 2).sqrt(),
1501
+ (x, -x_max, x_max), (y, -x_max, x_max),
1502
+ opacity=hyperboloid_opacity, **bdry_options)