Hyperbolic Geodesics¶
This module implements the abstract base class for geodesics in hyperbolic space of arbitrary dimension. It also contains the implementations for specific models of hyperbolic geometry.
AUTHORS:
- Greg Laun (2013): initial version
EXAMPLES:
We can construct geodesics in the upper half plane model, abbreviated UHP for convenience:
sage: HyperbolicPlane().UHP().get_geodesic(2, 3)
Geodesic in UHP from 2 to 3
sage: g = HyperbolicPlane().UHP().get_geodesic(I, 3 + I)
sage: g.length()
arccosh(11/2)
Geodesics are oriented, which means that two geodesics with the same graph will only be equal if their starting and ending points are the same:
sage: HyperbolicPlane().UHP().get_geodesic(1,2) == HyperbolicPlane().UHP().get_geodesic(2,1)
False
Todo
Implement a parent for all geodesics of the hyperbolic plane?
-
class
sage.geometry.hyperbolic_space.hyperbolic_geodesic.
HyperbolicGeodesic
(model, start, end, **graphics_options)¶ Bases:
sage.structure.sage_object.SageObject
Abstract base class for oriented geodesics that are not necessarily complete.
INPUT:
start
– a HyperbolicPoint or coordinates of a point in hyperbolic space representing the start of the geodesicend
– a HyperbolicPoint or coordinates of a point in hyperbolic space representing the end of the geodesic
EXAMPLES:
sage: HyperbolicPlane().UHP().get_geodesic(1, 0) Geodesic in UHP from 1 to 0 sage: HyperbolicPlane().PD().get_geodesic(1, 0) Geodesic in PD from 1 to 0 sage: HyperbolicPlane().KM().get_geodesic((0,1/2), (1/2, 0)) Geodesic in KM from (0, 1/2) to (1/2, 0) sage: HyperbolicPlane().HM().get_geodesic((0,0,1), (0,1, sqrt(2))) Geodesic in HM from (0, 0, 1) to (0, 1, sqrt(2))
-
angle
(other)¶ Return the angle between any two given geodesics if they intersect.
INPUT:
other
– a hyperbolic geodesic in the same model asself
OUTPUT:
- the angle in radians between the two given geodesics
EXAMPLES:
sage: PD = HyperbolicPlane().PD() sage: g = PD.get_geodesic(3/5*I + 4/5, 15/17*I + 8/17) sage: h = PD.get_geodesic(4/5*I + 3/5, 9/13*I + 6/13) sage: g.angle(h) 1/2*pi
-
common_perpendicula
(other)¶ Return the unique hyperbolic geodesic perpendicular to two given geodesics, if such a geodesic exists. If none exists, raise a
ValueError
.INPUT:
other
– a hyperbolic geodesic in the same model asself
OUTPUT:
- a hyperbolic geodesic
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(2,3) sage: h = HyperbolicPlane().UHP().get_geodesic(4,5) sage: g.common_perpendicular(h) Geodesic in UHP from 1/2*sqrt(3) + 7/2 to -1/2*sqrt(3) + 7/2
It is an error to ask for the common perpendicular of two intersecting geodesics:
sage: g = HyperbolicPlane().UHP().get_geodesic(2,4) sage: h = HyperbolicPlane().UHP().get_geodesic(3, infinity) sage: g.common_perpendicular(h) Traceback (most recent call last): ... ValueError: geodesics intersect; no common perpendicular exists
-
complete
()¶ Return the geodesic with ideal endpoints in bounded models. Raise a
NotImplementedError
in models that are not bounded.EXAMPLES:
sage: H = HyperbolicPlane() sage: H.UHP().get_geodesic(1 + I, 1 + 3*I).complete() Geodesic in UHP from 1 to +Infinity sage: H.PD().get_geodesic(0, I/2).complete() Geodesic in PD from -I to I sage: H.KM().get_geodesic((0,0), (0, 1/2)).complete() Geodesic in KM from (0, -1) to (0, 1) sage: H.HM().get_geodesic((0,0,1), (1, 0, sqrt(2))).complete() Geodesic in HM from (0, 0, 1) to (1, 0, sqrt(2)) sage: H.HM().get_geodesic((0,0,1), (1, 0, sqrt(2))).complete().is_complete() True
-
dist
(other)¶ Return the hyperbolic distance from a given hyperbolic geodesic to another geodesic or point.
INPUT:
other
– a hyperbolic geodesic or hyperbolic point in the same model
OUTPUT:
- the hyperbolic distance
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(2, 4.0) sage: h = HyperbolicPlane().UHP().get_geodesic(5, 7.0) sage: bool(abs(g.dist(h).n() - 1.92484730023841) < 10**-9) True
If the second object is a geodesic ultraparallel to the first, or if it is a point on the boundary that is not one of the first object’s endpoints, then return +infinity:
sage: g = HyperbolicPlane().UHP().get_geodesic(2, 2+I) sage: p = HyperbolicPlane().UHP().get_point(5) sage: g.dist(p) +Infinity
-
end
()¶ Return the starting point of the geodesic.
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(I, 3*I) sage: g.end() Point in UHP 3*I
-
endpoints
()¶ Return a list containing the start and endpoints.
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(I, 3*I) sage: g.endpoints() [Point in UHP I, Point in UHP 3*I]
-
graphics_options
()¶ Return the graphics options of
self
.EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(I, 2*I, color="red") sage: g.graphics_options() {'color': 'red'}
-
ideal_endpoints
()¶ Return the ideal endpoints in bounded models. Raise a
NotImplementedError
in models that are not bounded.EXAMPLES:
sage: H = HyperbolicPlane() sage: H.UHP().get_geodesic(1 + I, 1 + 3*I).ideal_endpoints() [Boundary point in UHP 1, Boundary point in UHP +Infinity] sage: H.PD().get_geodesic(0, I/2).ideal_endpoints() [Boundary point in PD -I, Boundary point in PD I] sage: H.KM().get_geodesic((0,0), (0, 1/2)).ideal_endpoints() [Boundary point in KM (0, -1), Boundary point in KM (0, 1)] sage: H.HM().get_geodesic((0,0,1), (1, 0, sqrt(2))).ideal_endpoints() Traceback (most recent call last): ... NotImplementedError: boundary points are not implemented in the HM model
-
intersection
(other)¶ Return the point of intersection of two geodesics (if such a point exists).
INPUT:
other
– a hyperbolic geodesic in the same model asself
OUTPUT:
- a hyperbolic point or geodesic
EXAMPLES:
sage: PD = HyperbolicPlane().PD()
-
is_asymptotically_parallel
(other)¶ Return
True
ifself
andother
are asymptotically parallel andFalse
otherwise.INPUT:
other
– a hyperbolic geodesic
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: h = HyperbolicPlane().UHP().get_geodesic(-2,4) sage: g.is_asymptotically_parallel(h) True
Ultraparallel geodesics are not asymptotically parallel:
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: h = HyperbolicPlane().UHP().get_geodesic(-1,4) sage: g.is_asymptotically_parallel(h) False
No hyperbolic geodesic is asymptotically parallel to itself:
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: g.is_asymptotically_parallel(g) False
-
is_complete
()¶ Return
True
ifself
is a complete geodesic (that is, both endpoints are on the ideal boundary) andFalse
otherwise.EXAMPLES:
sage: HyperbolicPlane().UHP().get_geodesic(I, 2*I).is_complete() False sage: HyperbolicPlane().UHP().get_geodesic(0, I).is_complete() False sage: HyperbolicPlane().UHP().get_geodesic(0, infinity).is_complete() True
-
is_parallel
(other)¶ Return
True
if the two given hyperbolic geodesics are either ultra parallel or asymptotically parallel and``False`` otherwise.INPUT:
other
– a hyperbolic geodesic in any model
OUTPUT:
True
if the given geodesics are either ultra parallel or asymptotically parallel,False
if not.EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: h = HyperbolicPlane().UHP().get_geodesic(5,12) sage: g.is_parallel(h) True
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: h = HyperbolicPlane().UHP().get_geodesic(-2,4) sage: g.is_parallel(h) True
No hyperbolic geodesic is either ultra parallel or asymptotically parallel to itself:
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: g.is_parallel(g) False
-
is_ultra_parallel
(other)¶ Return
True
ifself
andother
are ultra parallel andFalse
otherwise.INPUT:
other
– a hyperbolic geodesic
EXAMPLES:
sage: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import * sage: g = HyperbolicPlane().UHP().get_geodesic(0,1) sage: h = HyperbolicPlane().UHP().get_geodesic(-3,3) sage: g.is_ultra_parallel(h) True
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: h = HyperbolicPlane().UHP().get_geodesic(2,6) sage: g.is_ultra_parallel(h) False
sage: g = HyperbolicPlane().UHP().get_geodesic(-2,5) sage: g.is_ultra_parallel(g) False
-
length
()¶ Return the Hyperbolic length of the hyperbolic line segment.
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(2 + I, 3 + I/2) sage: g.length() arccosh(9/4)
-
midpoint
()¶ Return the (hyperbolic) midpoint of a hyperbolic line segment.
EXAMPLES:
sage: g = HyperbolicPlane().UHP().random_geodesic() sage: m = g.midpoint() sage: end1, end2 = g.endpoints() sage: bool(abs(m.dist(end1) - m.dist(end2)) < 10**-9) True
Complete geodesics have no midpoint:
sage: HyperbolicPlane().UHP().get_geodesic(0,2).midpoint() Traceback (most recent call last): ... ValueError: the length must be finite
-
model
()¶ Return the model to which the
HyperbolicGeodesic
belongs.EXAMPLES:
sage: HyperbolicPlane().UHP().get_geodesic(I, 2*I).model() Hyperbolic plane in the Upper Half Plane Model model sage: HyperbolicPlane().PD().get_geodesic(0, I/2).model() Hyperbolic plane in the Poincare Disk Model model sage: HyperbolicPlane().KM().get_geodesic((0, 0), (0, 1/2)).model() Hyperbolic plane in the Klein Disk Model model sage: HyperbolicPlane().HM().get_geodesic((0, 0, 1), (0, 1, sqrt(2))).model() Hyperbolic plane in the Hyperboloid Model model
-
perpendicular_bisector
()¶ Return the perpendicular bisector of
self
ifself
has finite length. Here distance is hyperbolic distance.EXAMPLES:
sage: g = HyperbolicPlane().PD().random_geodesic() sage: h = g.perpendicular_bisector() sage: bool(h.intersection(g)[0].coordinates() - g.midpoint().coordinates() < 10**-9) True
Complete geodesics cannot be bisected:
sage: g = HyperbolicPlane().PD().get_geodesic(0, 1) sage: g.perpendicular_bisector() Traceback (most recent call last): ... ValueError: the length must be finite
-
reflection_involution
()¶ Return the involution fixing
self
.EXAMPLES:
sage: H = HyperbolicPlane() sage: gU = H.UHP().get_geodesic(2,4) sage: RU = gU.reflection_involution(); RU Isometry in UHP [ 3 -8] [ 1 -3] sage: RU*gU == gU True sage: gP = H.PD().get_geodesic(0, I) sage: RP = gP.reflection_involution(); RP Isometry in PD [ 1 0] [ 0 -1] sage: RP*gP == gP True sage: gK = H.KM().get_geodesic((0,0), (0,1)) sage: RK = gK.reflection_involution(); RK Isometry in KM [-1 0 0] [ 0 1 0] [ 0 0 1] sage: RK*gK == gK True sage: A = H.HM().get_geodesic((0,0,1), (1,0, n(sqrt(2)))).reflection_involution() sage: B = diagonal_matrix([1, -1, 1]) sage: bool((B - A.matrix()).norm() < 10**-9) True
The above tests go through the Upper Half Plane. It remains to test that the matrices in the models do what we intend.
sage: from sage.geometry.hyperbolic_space.hyperbolic_isometry import mobius_transform sage: R = H.PD().get_geodesic(-1,1).reflection_involution() sage: bool(mobius_transform(R.matrix(), 0) == 0) True
-
start
()¶ Return the starting point of the geodesic.
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(I, 3*I) sage: g.start() Point in UHP I
-
to_model
(model)¶ Convert the current object to image in another model.
INPUT:
model
– the image model
EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: PD = HyperbolicPlane().PD() sage: UHP.get_geodesic(I, 2*I).to_model(PD) Geodesic in PD from 0 to 1/3*I sage: UHP.get_geodesic(I, 2*I).to_model('PD') Geodesic in PD from 0 to 1/3*I
-
update_graphics
(update=False, **options)¶ Update the graphics options of
self
.INPUT:
update
– ifTrue
, the original option are updated rather than overwritten
EXAMPLES:
sage: g = HyperbolicPlane().UHP().get_geodesic(I, 2*I) sage: g.graphics_options() {} sage: g.update_graphics(color = "red"); g.graphics_options() {'color': 'red'} sage: g.update_graphics(color = "blue"); g.graphics_options() {'color': 'blue'} sage: g.update_graphics(True, size = 20); g.graphics_options() {'color': 'blue', 'size': 20}
-
class
sage.geometry.hyperbolic_space.hyperbolic_geodesic.
HyperbolicGeodesicHM
(model, start, end, **graphics_options)¶ Bases:
sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesic
A geodesic in the hyperboloid model.
INPUT:
start
– aHyperbolicPoint
in hyperbolic space representing the start of the geodesicend
– aHyperbolicPoint
in hyperbolic space representing the end of the geodesic
EXAMPLES:
sage: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import * sage: HM = HyperbolicPlane().HM() sage: g = HM.get_geodesic(HM.get_point((0, 0, 1)), HM.get_point((0,1,sqrt(2)))) sage: g = HM.get_geodesic((0, 0, 1), (0, 1, sqrt(2)))
-
show
(show_hyperboloid=True, **graphics_options)¶ Plot
self
.EXAMPLES:
sage: from sage.geometry.hyperbolic_space.hyperbolic_geodesic import * sage: g = HyperbolicPlane().HM().random_geodesic() sage: g.show() Graphics3d Object
-
class
sage.geometry.hyperbolic_space.hyperbolic_geodesic.
HyperbolicGeodesicKM
(model, start, end, **graphics_options)¶ Bases:
sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesic
A geodesic in the Klein disk model.
INPUT:
start
– aHyperbolicPoint
in hyperbolic space representing the start of the geodesicend
– aHyperbolicPoint
in hyperbolic space representing the end of the geodesic
EXAMPLES:
sage: KM = HyperbolicPlane().KM() sage: g = KM.get_geodesic(KM.get_point((0,1)), KM.get_point((0,1/2))) sage: g = KM.get_geodesic((0,1), (0,1/2))
-
show
(boundary=True, **options)¶ Plot
self
.EXAMPLES:
sage: HyperbolicPlane().KM().get_geodesic((0,0), (1,0)).show() Graphics object consisting of 2 graphics primitives
-
class
sage.geometry.hyperbolic_space.hyperbolic_geodesic.
HyperbolicGeodesicPD
(model, start, end, **graphics_options)¶ Bases:
sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesic
A geodesic in the Poincaré disk model.
INPUT:
start
– aHyperbolicPoint
in hyperbolic space representing the start of the geodesicend
– aHyperbolicPoint
in hyperbolic space representing the end of the geodesic
EXAMPLES:
sage: PD = HyperbolicPlane().PD() sage: g = PD.get_geodesic(PD.get_point(I), PD.get_point(I/2)) sage: g = PD.get_geodesic(I, I/2)
-
show
(boundary=True, **options)¶ Plot
self
.EXAMPLES:
First some lines:
sage: PD = HyperbolicPlane().PD() sage: PD.get_geodesic(0, 1).show() Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(0, 0.3+0.8*I).show() Graphics object consisting of 2 graphics primitives
Then some generic geodesics:
sage: PD.get_geodesic(-0.5, 0.3+0.4*I).show() Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(-1, exp(3*I*pi/7)).show(linestyle="dashed", color="red") Graphics object consisting of 2 graphics primitives sage: PD.get_geodesic(exp(2*I*pi/11), exp(1*I*pi/11)).show(thickness=6, color="orange") Graphics object consisting of 2 graphics primitives
-
class
sage.geometry.hyperbolic_space.hyperbolic_geodesic.
HyperbolicGeodesicUHP
(model, start, end, **graphics_options)¶ Bases:
sage.geometry.hyperbolic_space.hyperbolic_geodesic.HyperbolicGeodesic
Create a geodesic in the upper half plane model.
INPUT:
start
– aHyperbolicPoint
in hyperbolic space representing the start of the geodesicend
– aHyperbolicPoint
in hyperbolic space representing the end of the geodesic
EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.get_geodesic(UHP.get_point(I), UHP.get_point(2 + I)) sage: g = UHP.get_geodesic(I, 2 + I)
-
angle
(other)¶ Return the angle between any two given completed geodesics if they intersect.
INPUT:
other
– a hyperbolic geodesic in the UHP model
OUTPUT:
- the angle in radians between the two given geodesics
EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.get_geodesic(2, 4) sage: h = UHP.get_geodesic(3, 3 + I) sage: g.angle(h) 1/2*pi sage: numerical_approx(g.angle(h)) 1.57079632679490
If the geodesics are identical, return angle 0:
sage: g.angle(g) 0
It is an error to ask for the angle of two geodesics that do not intersect:
sage: g = UHP.get_geodesic(2, 4) sage: h = UHP.get_geodesic(5, 7) sage: g.angle(h) Traceback (most recent call last): ... ValueError: geodesics do not intersect
-
common_perpendicular
(other)¶ Return the unique hyperbolic geodesic perpendicular to
self
andother
, if such a geodesic exists; otherwise raise aValueError
.INPUT:
other
– a hyperbolic geodesic in current model
OUTPUT:
- a hyperbolic geodesic
EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.get_geodesic(2, 3) sage: h = UHP.get_geodesic(4, 5) sage: g.common_perpendicular(h) Geodesic in UHP from 1/2*sqrt(3) + 7/2 to -1/2*sqrt(3) + 7/2
It is an error to ask for the common perpendicular of two intersecting geodesics:
sage: g = UHP.get_geodesic(2, 4) sage: h = UHP.get_geodesic(3, infinity) sage: g.common_perpendicular(h) Traceback (most recent call last): ... ValueError: geodesics intersect; no common perpendicular exists
-
ideal_endpoints
()¶ Determine the ideal (boundary) endpoints of the complete hyperbolic geodesic corresponding to
self
.OUTPUT:
- a list of 2 boundary points
EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: UHP.get_geodesic(I, 2*I).ideal_endpoints() [Boundary point in UHP 0, Boundary point in UHP +Infinity] sage: UHP.get_geodesic(1 + I, 2 + 4*I).ideal_endpoints() [Boundary point in UHP -sqrt(65) + 9, Boundary point in UHP sqrt(65) + 9]
-
intersection
(other)¶ Return the point of intersection of
self
andother
(if such a point exists).INPUT:
other
– a hyperbolic geodesic in the current model
OUTPUT:
- a list of hyperbolic points or a hyperbolic geodesic
EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.get_geodesic(3, 5) sage: h = UHP.get_geodesic(4, 7) sage: g.intersection(h) [Point in UHP 2/3*sqrt(-2) + 13/3]
If the given geodesics do not intersect, the function returns an empty list:
sage: g = UHP.get_geodesic(4, 5) sage: h = UHP.get_geodesic(5, 7) sage: g.intersection(h) []
If the given geodesics are identical, return that geodesic:
sage: g = UHP.get_geodesic(4 + I, 18*I) sage: h = UHP.get_geodesic(4 + I, 18*I) sage: g.intersection(h) [Boundary point in UHP -1/8*sqrt(114985) - 307/8, Boundary point in UHP 1/8*sqrt(114985) - 307/8]
-
midpoint
()¶ Return the (hyperbolic) midpoint of
self
if it exists.EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.random_geodesic() sage: m = g.midpoint() sage: d1 = UHP.dist(m, g.start()) sage: d2 = UHP.dist(m, g.end()) sage: bool(abs(d1 - d2) < 10**-9) True
Infinite geodesics have no midpoint:
sage: UHP.get_geodesic(0, 2).midpoint() Traceback (most recent call last): ... ValueError: the length must be finite
-
perpendicular_bisector
()¶ Return the perpendicular bisector of the hyperbolic geodesic
self
if that geodesic has finite length.EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g = UHP.random_geodesic() sage: h = g.perpendicular_bisector() sage: c = lambda x: x.coordinates() sage: bool(c(g.intersection(h)[0]) - c(g.midpoint()) < 10**-9) True
Infinite geodesics cannot be bisected:
sage: UHP.get_geodesic(0, 1).perpendicular_bisector() Traceback (most recent call last): ... ValueError: the length must be finite
-
reflection_involution
()¶ Return the isometry of the involution fixing the geodesic
self
.EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: g1 = UHP.get_geodesic(0, 1) sage: g1.reflection_involution() Isometry in UHP [ 1 0] [ 2 -1] sage: UHP.get_geodesic(I, 2*I).reflection_involution() Isometry in UHP [ 1 0] [ 0 -1]
-
show
(boundary=True, **options)¶ Plot
self
.EXAMPLES:
sage: UHP = HyperbolicPlane().UHP() sage: UHP.get_geodesic(0, 1).show() Graphics object consisting of 2 graphics primitives sage: UHP.get_geodesic(I, 3+4*I).show(linestyle="dashed", color="red") Graphics object consisting of 2 graphics primitives