Ask Your Question
0

Vertices of hyperbolic triangle with given angles

asked 2024-03-21 14:12:25 +0100

Rowing0914 gravatar image

How can we compute the vertex positions in a hyperbolic space (e.g., Poincare Disk) with given angles? I can't seem to find any reference... So, any reference or algorithm would be really appreciated.

edit retag flag offensive close merge delete

Comments

FYI: Although I've implemented the following algorithm, it didn't work well so I'm asking for help as well. https://mathoverflow.net/questions/46...

Rowing0914 gravatar imageRowing0914 ( 2024-03-21 14:13:08 +0100 )edit

1 Answer

Sort by » oldest newest most voted
2

answered 2024-03-23 04:04:39 +0100

dan_fulea gravatar image

Here is the same code as posted on MO. However, here i is the place to say some words about the sage part of the implementation.

UHP = HyperbolicPlane().UHP()    # UHP is the upper half plane IH
HM  = HyperbolicPlane().HM()     # HM  is the hyperboloid model
a1, a2, a3 = pi/4, pi/4, pi/4    # given angles, we draw a hyperbolic triangle with these angles

def c(a1, a2, a3):
    return (cos(a1) + cos(a2)*cos(a3)) / sin(a2) / sin(a3)

c1, c2, c3 = c(a1, a2, a3), c(a2, a3, a1), c(a3, a1, a2)    # algebraic in the given example

def s(v, w):
    """v, w are vectors with three entries, we return the Minkowski product with signature ++-"""
    return v*diagonal_matrix([1, 1, -1])*w

# a, b, p, q, r are used in the following coordinates in Hyperboloid model
# as a parametrization of points
myvars = var("a b p q r");
a, b, p, q, r = myvars

V1 = vector([0, 0, 1])
V2 = vector([0, a, b])
V3 = vector([p, q, r])

sols = solve([ s(V2, V2) == -1, s(V3, V3) == -1,
               s(V1, V2) == -c3, s(V2, V3) == -c1, s(V3, V1) == -c2 ]
             , myvars, solution_dict=True)
sols = [sol for sol in sols if sol[a] > 0 and sol[q] > 0]    # so V2, V3 maps to IH
sol  = sols[0]                                               # first solution

a0, b0, p0, q0, r0 = [sol[v].simplify_full() for v in myvars]

S1, S2, S3 = vector([0, 0, 1]), vector([0, a0, b0]), vector([p0, q0, r0])
M1, M2, M3 = HM.get_point(S1), HM.get_point(S2), HM.get_point(S3)
H1, H2, H3 = UHP(M1), UHP(M2), UHP(M3)    # using the coercion from HM to UHP
Q1, Q2, Q3 = H1.coordinates(), H2.coordinates(), H3.coordinates()

p = hyperbolic_polygon(pts=[Q1, Q2, Q3], model="UHP", fill=True, alpha=0.3)

g = Graphics()
g += p.plot()
g.show(axes=True, aspect_ratio=1)

It was possible to perform "exact arithmetics". Sage comes with some models of the HyperbolicPlane - and here they are:

sage: HP = HyperbolicPlane()
sage: HP.HM()
Hyperbolic plane in the Hyperboloid Model
sage: HP.Hyperboloid()
Hyperbolic plane in the Hyperboloid Model
sage: # same

sage: HP.KM()
Hyperbolic plane in the Klein Disk Model
sage: HP.KleinDisk()
Hyperbolic plane in the Klein Disk Model
sage: # same

sage: HP.PoincareDisk()
Hyperbolic plane in the Poincare Disk Model
sage: HP.PD()
Hyperbolic plane in the Poincare Disk Model
sage: # same

sage: HP.UHP()
Hyperbolic plane in the Upper Half Plane Model

sage: HP.realizations()
[Hyperbolic plane in the Upper Half Plane Model,
 Hyperbolic plane in the Hyperboloid Model,
 Hyperbolic plane in the Klein Disk Model,
 Hyperbolic plane in the Poincare Disk Model]

In the implementation, we start with the given angles, use the hyperbolic laws of sine and cosine, see also the short pdf, solve a system in the HM, then move it in the world of the upper half plane $\Bbb H$ by simple coercion, and there is for $\Bbb H=$UHP a coercion from HM:

UHP = HyperbolicPlane().UHP()    # UHP is the upper half plane IH
HM  = HyperbolicPlane().HM()     # HM  is the hyperboloid model

and now:

sage: UHP.has_coerce_map_from(HM)
True

After solving the system, your idea to use this framework, we obtain the vectors V1, V2, V3, each with three coordinates, that should be considered now in HM. It turns out that we have the right order of components.

For instance the solution S2 (instead of V2) is:

sage: S2
(0, sqrt(2)*sqrt(sqrt(2) + 1), sqrt(2) + 1)
sage: s(S2, S2)
-(sqrt(2) + 1)^2 + 2*sqrt(2) + 2
sage: _.simplify_full()
-1

And the line is accepted:

sage: HM(S2)
Point in HM (0, sqrt(2)*sqrt(sqrt(2) + 1), sqrt(2) + 1)

so the bigger coordinate is at the last place. The point HM(S2) becomes now M2. Similarly, we have after solving the system and mapping the vectors to HM also the other points M1, M3. Sage gives us the chance to compute the distance between these points in the given model:

sage: # HM.dist(M1, M2) # arccosh( a long expression )
sage: HM.dist(M1, M2).n()
1.52857091948100
sage: arccosh(c3).n()
1.52857091948100

So the distance corresponds to the one stated in the linked pdf, second cosine rule in hyperbolic geometry. The distance between each two points is the same. For instance, we have symbolically the True in the lines:

sage: bool( HM.dist(M1, M2) == HM.dist(M2, M3) )
True

sage: bool( HM.dist(M2, M3) == HM.dist(M3, M1) )
True

From here, we can pass to the upper half-plane $\Bbb H=$UHP, and the points are also exact, to work with them as complex number we take the "coordinates", and we can convert them now to numbers in some exact field we prefer, for instance in $\bar{\Bbb Q}$ (or with some work in some cyclotomic field). Here are some test lines, that illustrate the structure, using

S1, S2, S3 = vector([0, 0, 1]), vector([0, a0, b0]), vector([p0, q0, r0])
M1, M2, M3 = HM.get_point(S1), HM.get_point(S2), HM.get_point(S3)
H1, H2, H3 = UHP(M1), UHP(M2), UHP(M3)    # using the coercion from HM to UHP
Q1, Q2, Q3 = H1.coordinates(), H2.coordinates(), H3.coordinates()
x1, x2, x3 = QQbar(Q1), QQbar(Q2), QQbar(Q3)

we have:

sage: UHP.dist(H1, H2).n()
1.52857091948100
sage: arccosh(c3).n()    # same distance in HM and UHP
1.52857091948100

sage: x1, x2, x3
(I, 4.611581789308715?*I, -1.805790894654358? + 1.162196641748775?*I)

sage: x1.minpoly(), x2.minpoly(), x3.minpoly()
(x^2 + 1, x^8 + 20*x^6 - 26*x^4 + 20*x^2 + 1, x^8 - 4*x^6 + 22*x^4 - 4*x^2 + 1)

sage: x2.minpoly().galois_group()
Transitive group number 4 of degree 8
sage: x2.minpoly().galois_group().structure_description()
'D4'
sage: x3.minpoly().galois_group()
Transitive group number 4 of degree 8
sage: x3.minpoly().galois_group().structure_description()
'D4'

This may seem less important for drawing one triangle, but may become important when trying to draw a full tesselation starting from one triangle and applying Möbius transformations, details in a deep level can be made visible (also in an animation with zoom-in features)...

edit flag offensive delete link more

Comments

Thank you for your detailed answer!! I'm getting no-solution (thus run-time error) when I use pi/5, pi/5, pi/4 or pi/8, pi/8, pi/4. Do you have any idea? For instance, https://sagecell.sagemath.org/?q=tqgvce

Rowing0914 gravatar imageRowing0914 ( 2024-03-28 01:47:02 +0100 )edit

Your Answer

Please start posting anonymously - your entry will be published after you log in or create a new account.

Add Answer

Question Tools

1 follower

Stats

Asked: 2024-03-21 14:12:25 +0100

Seen: 238 times

Last updated: Mar 23