Hyperbolic plane $\mathbb{H}^2$

This worksheet illustrates some features of SageManifolds (v1.0, as included in SageMath 7.5) on computations regarding the hyperbolic plane.

Click here to download the worksheet file (ipynb format). To run it, you must start SageMath with the Jupyter notebook, via the command sage -n jupyter

NB: a version of SageMath at least equal to 7.5 is required to run this worksheet:

In [1]:
version()
Out[1]:
'SageMath version 7.5, Release Date: 2017-01-11'

First we set up the notebook to display mathematical objects using LaTeX formatting:

In [2]:
%display latex

We also define a viewer for 3D plots (use 'threejs' or 'jmol' for interactive 3D graphics):

In [3]:
viewer3D = 'threejs' # must be 'threejs', jmol', 'tachyon' or None (default)

We declare $\mathbb{H}^2$ as a 2-dimensional differentiable manifold:

In [4]:
H2 = Manifold(2, 'H2', latex_name=r'\mathbb{H}^2', start_index=1)
print(H2)
H2
2-dimensional differentiable manifold H2
Out[4]:

We shall introduce charts on $\mathbb{H}^2$ that are related to various models of the hyperbolic plane as submanifolds of $\mathbb{R}^3$. Therefore, we start by declaring $\mathbb{R}^3$ as a 3-dimensional manifold equiped with a global chart: the chart of Cartesian coordinates $(X,Y,Z)$:

In [5]:
R3 = Manifold(3, 'R3', latex_name=r'\mathbb{R}^3', start_index=1)
X3.<X,Y,Z> = R3.chart()
X3
Out[5]:

Hyperboloid model

The first chart we introduce is related to the hyperboloid model of $\mathbb{H}^2$, namely to the representation of $\mathbb{H}^2$ as the upper sheet ($Z>0$) of the hyperboloid of two sheets defined in $\mathbb{R}^3$ by the equation $X^2 + Y^2 - Z^2 = -1$:

In [6]:
X_hyp.<X,Y> = H2.chart()
X_hyp
Out[6]:

The corresponding embedding of $\mathbb{H}^2$ in $\mathbb{R}^3$ is

In [7]:
Phi1 = H2.diff_map(R3, [X, Y, sqrt(1+X^2+Y^2)], name='Phi_1', latex_name=r'\Phi_1')
Phi1.display()
Out[7]:

By plotting the chart $\left(\mathbb{H}^2,(X,Y)\right)$ in terms of the Cartesian coordinates of $\mathbb{R}^3$, we get a graphical view of $\Phi_1(\mathbb{H}^2)$:

In [8]:
show(X_hyp.plot(X3, mapping=Phi1, number_values=15, color='blue'), aspect_ratio=1, 
     viewer=viewer3D, figsize=7)

A second chart is obtained from the polar coordinates $(r,\varphi)$ associated with $(X,Y)$. Contrary to $(X,Y)$, the polar chart is not defined on the whole $\mathbb{H}^2$, but on the complement $U$ of the segment $\{Y=0, x\geq 0\}$:

In [9]:
U = H2.open_subset('U', coord_def={X_hyp: (Y!=0, X<0)})
print(U)
Open subset U of the 2-dimensional differentiable manifold H2

Note that (y!=0, x<0) stands for $y\not=0$ OR $x<0$; the condition $y\not=0$ AND $x<0$ would have been written [y!=0, x<0] instead.

In [10]:
X_pol.<r,ph> = U.chart(r'r:(0,+oo) ph:(0,2*pi):\varphi')
X_pol
Out[10]:
In [11]:
X_pol.coord_range()
Out[11]:

We specify the transition map between the charts $\left(U,(r,\varphi)\right)$ and $\left(\mathbb{H}^2,(X,Y)\right)$ as $X=r\cos\varphi$, $Y=r\sin\varphi$:

In [12]:
pol_to_hyp = X_pol.transition_map(X_hyp, [r*cos(ph), r*sin(ph)])
pol_to_hyp
Out[12]:
In [13]:
pol_to_hyp.display()
Out[13]:
In [14]:
pol_to_hyp.set_inverse(sqrt(X^2+Y^2), atan2(Y, X)) 
In [15]:
pol_to_hyp.inverse().display()
Out[15]:

The restriction of the embedding $\Phi_1$ to $U$ has then two coordinate expressions:

In [16]:
Phi1.restrict(U).display()
Out[16]:
In [17]:
graph_hyp = X_pol.plot(X3, mapping=Phi1.restrict(U), number_values=15, ranges={r: (0,3)}, 
                       color='blue')
show(graph_hyp, aspect_ratio=1, viewer=viewer3D, figsize=7)
In [18]:
Phi1._coord_expression
Out[18]:

Metric and curvature

The metric on $\mathbb{H}^2$ is that induced by the Minkowksy metric on $\mathbb{R}^3$: $$ \eta = \mathrm{d}X\otimes\mathrm{d}X + \mathrm{d}Y\otimes\mathrm{d}Y

  • \mathrm{d}Z\otimes\mathrm{d}Z $$
In [19]:
eta = R3.lorentzian_metric('eta', latex_name=r'\eta')
eta[1,1] = 1 ; eta[2,2] = 1 ; eta[3,3] = -1
eta.display()
Out[19]:
In [20]:
g = H2.metric('g')
g.set( Phi1.pullback(eta) )
g.display() 
Out[20]:

The expression of the metric tensor in terms of the polar coordinates is

In [21]:
g.display(X_pol.frame(), X_pol)
Out[21]:

The Riemann curvature tensor associated with $g$ is

In [22]:
Riem = g.riemann()
print(Riem)
Tensor field Riem(g) of type (1,3) on the 2-dimensional differentiable manifold H2
In [23]:
Riem.display(X_pol.frame(), X_pol)
Out[23]:

The Ricci tensor and the Ricci scalar:

In [24]:
Ric = g.ricci()
print(Ric)
Field of symmetric bilinear forms Ric(g) on the 2-dimensional differentiable manifold H2
In [25]:
Ric.display(X_pol.frame(), X_pol)
Out[25]:
In [26]:
Rscal = g.ricci_scalar()
print(Rscal)
Scalar field r(g) on the 2-dimensional differentiable manifold H2
In [27]:
Rscal.display()
Out[27]:

Hence we recover the fact that $(\mathbb{H}^2,g)$ is a space of constant negative curvature.

In dimension 2, the Riemann curvature tensor is entirely determined by the Ricci scalar $R$ according to

$$R^i_{\ \, jlk} = \frac{R}{2} \left( \delta^i_{\ \, k} g_{jl} - \delta^i_{\ \, l} g_{jk} \right)$$

Let us check this formula here, under the form $R^i_{\ \, jlk} = -R g_{j[k} \delta^i_{\ \, l]}$:

In [28]:
delta = H2.tangent_identity_field()
Riem == - Rscal*(g*delta).antisymmetrize(2,3)  # 2,3 = last positions of the type-(1,3) tensor g*delta 
Out[28]:

Similarly the relation $\mathrm{Ric} = (R/2)\; g$ must hold:

In [29]:
Ric == (Rscal/2)*g
Out[29]:

Poincaré disk model

The Poincaré disk model of $\mathbb{H}^2$ is obtained by stereographic projection from the point $S=(0,0,-1)$ of the hyperboloid model to the plane $Z=0$. The radial coordinate $R$ of the image of a point of polar coordinate $(r,\varphi)$ is $$ R = \frac{r}{1+\sqrt{1+r^2}}.$$ Hence we define the Poincaré disk chart on $\mathbb{H}^2$ by

In [30]:
X_Pdisk.<R,ph> = U.chart(r'R:(0,1) ph:(0,2*pi):\varphi')
X_Pdisk
Out[30]:
In [31]:
X_Pdisk.coord_range()
Out[31]:

and relate it to the hyperboloid polar chart by

In [32]:
pol_to_Pdisk = X_pol.transition_map(X_Pdisk, [r/(1+sqrt(1+r^2)), ph])
pol_to_Pdisk
Out[32]:
In [33]:
pol_to_Pdisk.display()
Out[33]:
In [34]:
pol_to_Pdisk.set_inverse(2*R/(1-R^2), ph)
pol_to_Pdisk.inverse().display()
Out[34]:

A view of the Poincaré disk chart via the embedding $\Phi_1$:

In [35]:
show(X_Pdisk.plot(X3, mapping=Phi1.restrict(U), ranges={R: (0,0.9)}, color='blue',
                  number_values=15), aspect_ratio=1, viewer=viewer3D, figsize=7)

The expression of the metric tensor in terms of coordinates $(R,\varphi)$:

In [36]:
g.display(X_Pdisk.frame(), X_Pdisk)
Out[36]:

We may factorize each metric component:

In [37]:
for i in [1,2]:
    g[X_Pdisk.frame(), i, i, X_Pdisk].factor()
g.display(X_Pdisk.frame(), X_Pdisk)
Out[37]:

Cartesian coordinates on the Poincaré disk

Let us introduce Cartesian coordinates $(u,v)$ on the Poincaré disk; since the latter has a unit radius, this amounts to define the following chart on $\mathbb{H}^2$:

In [38]:
X_Pdisk_cart.<u,v> = H2.chart('u:(-1,1) v:(-1,1)')
X_Pdisk_cart.add_restrictions(u^2+v^2 < 1)
X_Pdisk_cart
Out[38]:

On $U$, the Cartesian coordinates $(u,v)$ are related to the polar coordinates $(R,\varphi)$ by the standard formulas:

In [39]:
Pdisk_to_Pdisk_cart = X_Pdisk.transition_map(X_Pdisk_cart, [R*cos(ph), R*sin(ph)])
Pdisk_to_Pdisk_cart
Out[39]:
In [40]:
Pdisk_to_Pdisk_cart.display()
Out[40]:
In [41]:
Pdisk_to_Pdisk_cart.set_inverse(sqrt(u^2+v^2), atan2(v, u)) 
Pdisk_to_Pdisk_cart.inverse().display()
Out[41]:

The embedding of $\mathbb{H}^2$ in $\mathbb{R}^3$ associated with the Poincaré disk model is naturally defined as

In [42]:
Phi2 = H2.diff_map(R3, {(X_Pdisk_cart, X3): [u, v, 0]},
                   name='Phi_2', latex_name=r'\Phi_2')
Phi2.display()
Out[42]:

Let us use it to draw the Poincaré disk in $\mathbb{R}^3$:

In [43]:
graph_disk_uv = X_Pdisk_cart.plot(X3, mapping=Phi2, number_values=15)
show(graph_disk_uv, viewer=viewer3D, figsize=7)

On $U$, the change of coordinates $(r,\varphi) \rightarrow (u,v)$ is obtained by combining the changes $(r,\varphi) \rightarrow (R,\varphi)$ and $(R,\varphi) \rightarrow (u,v)$:

In [44]:
pol_to_Pdisk_cart = Pdisk_to_Pdisk_cart * pol_to_Pdisk
pol_to_Pdisk_cart
Out[44]:
In [45]:
pol_to_Pdisk_cart.display()
Out[45]:

Still on $U$, the change of coordinates $(X,Y) \rightarrow (u,v)$ is obtained by combining the changes $(X,Y) \rightarrow (r,\varphi)$ with $(r,\varphi) \rightarrow (u,v)$:

In [46]:
hyp_to_Pdisk_cart_U = pol_to_Pdisk_cart * pol_to_hyp.inverse()
hyp_to_Pdisk_cart_U
Out[46]:
In [47]:
hyp_to_Pdisk_cart_U.display()
Out[47]:

We use the above expression to extend the change of coordinates $(X,Y) \rightarrow (u,v)$ from $U$ to the whole manifold $\mathbb{H}^2$:

In [48]:
hyp_to_Pdisk_cart = X_hyp.transition_map(X_Pdisk_cart, hyp_to_Pdisk_cart_U(X,Y))
hyp_to_Pdisk_cart
Out[48]:
In [49]:
hyp_to_Pdisk_cart.display()
Out[49]:
In [50]:
hyp_to_Pdisk_cart.set_inverse(2*u/(1-u^2-v^2), 2*v/(1-u^2-v^2))
hyp_to_Pdisk_cart.inverse().display()
Out[50]:
In [51]:
graph_Pdisk = X_pol.plot(X3, mapping=Phi2.restrict(U), ranges={r: (0, 20)}, number_values=15, 
                         label_axes=False)
show(graph_hyp + graph_Pdisk, aspect_ratio=1, viewer=viewer3D, figsize=7)
In [52]:
X_pol.plot(X_Pdisk_cart, ranges={r: (0, 20)}, number_values=15)
Out[52]:

Metric tensor in Poincaré disk coordinates $(u,v)$

From now on, we are using the Poincaré disk chart $(\mathbb{H}^2,(u,v))$ as the default one on $\mathbb{H}^2$:

In [53]:
H2.set_default_chart(X_Pdisk_cart)
H2.set_default_frame(X_Pdisk_cart.frame())
In [54]:
g.display(X_hyp.frame())
Out[54]:
In [55]:
g.display()
Out[55]:
In [56]:
g[1,1].factor() ; g[2,2].factor()
g.display()
Out[56]:

Hemispherical model

The hemispherical model of $\mathbb{H}^2$ is obtained by the inverse stereographic projection from the point $S = (0,0,-1)$ of the Poincaré disk to the unit sphere $X^2+Y^2+Z^2=1$. This induces a spherical coordinate chart on $U$:

In [57]:
X_spher.<th,ph> = U.chart(r'th:(0,pi/2):\theta ph:(0,2*pi):\varphi')
X_spher
Out[57]:

From the stereographic projection from $S$, we obtain that \begin{equation} \sin\theta = \frac{2R}{1+R^2} \end{equation} Hence the transition map:

In [58]:
Pdisk_to_spher = X_Pdisk.transition_map(X_spher, [arcsin(2*R/(1+R^2)), ph])
Pdisk_to_spher
Out[58]:
In [59]:
Pdisk_to_spher.display()
Out[59]:
In [60]:
Pdisk_to_spher.set_inverse(sin(th)/(1+cos(th)), ph)
Pdisk_to_spher.inverse().display()
Out[60]:

In the spherical coordinates $(\theta,\varphi)$, the metric takes the following form:

In [61]:
g.display(X_spher.frame(), X_spher)
Out[61]:

The embedding of $\mathbb{H}^2$ in $\mathbb{R}^3$ associated with the hemispherical model is naturally:

In [62]:
Phi3 = H2.diff_map(R3, {(X_spher, X3): [sin(th)*cos(ph), sin(th)*sin(ph), cos(th)]},
                   name='Phi_3', latex_name=r'\Phi_3')
Phi3.display()
Out[62]:
In [63]:
graph_spher = X_pol.plot(X3, mapping=Phi3, ranges={r: (0, 20)}, number_values=15, color='orange', 
                         label_axes=False)
show(graph_hyp + graph_Pdisk + graph_spher, aspect_ratio=1, viewer=viewer3D, figsize=7)