# Hyperbolic plane $\mathbb{H}^2$¶

This notebook illustrates some differential geometry capabilities of SageMath on the example of the hyperbolic plane. The corresponding tools have been developed within the SageManifolds project (version 1.3, as included in SageMath 8.3).

Click here to download the notebook file (ipynb format). To run it, you must start SageMath with the Jupyter notebook interface, 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 8.3, Release Date: 2018-08-03'

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)


Finally, we tell Maxima, which is used by SageMath for simplifications of symbolic expressions, that all computations involve real variables:

In [4]:
maxima_calculus.eval("domain: real;")

Out[4]:

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

In [5]:
H2 = Manifold(2, 'H2', latex_name=r'\mathbb{H}^2', start_index=1)
print(H2)
H2

2-dimensional differentiable manifold H2

Out[5]:

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 [6]:
R3 = Manifold(3, 'R3', latex_name=r'\mathbb{R}^3', start_index=1)
X3.<X,Y,Z> = R3.chart()
X3

Out[6]:

## 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 [7]:
X_hyp.<X,Y> = H2.chart()
X_hyp

Out[7]:

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

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

Out[8]:

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 [9]:
show(X_hyp.plot(X3, mapping=Phi1, number_values=15, color='blue'), aspect_ratio=1,
viewer=viewer3D, online=True, 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 [10]:
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 [11]:
X_pol.<r,ph> = U.chart(r'r:(0,+oo) ph:(0,2*pi):\varphi')
X_pol

Out[11]:
In [12]:
X_pol.coord_range()

Out[12]:

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 [13]:
pol_to_hyp = X_pol.transition_map(X_hyp, [r*cos(ph), r*sin(ph)])
pol_to_hyp

Out[13]:
In [14]:
pol_to_hyp.display()

Out[14]:
In [15]:
pol_to_hyp.set_inverse(sqrt(X^2+Y^2), atan2(Y, X))

In [16]:
pol_to_hyp.inverse().display()

Out[16]:

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

In [17]:
Phi1.restrict(U).display()

Out[17]:
In [18]:
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, online=True, figsize=7)

In [19]:
Phi1._coord_expression

Out[19]:

## 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 [20]:
eta = R3.lorentzian_metric('eta', latex_name=r'\eta')
eta[1,1] = 1 ; eta[2,2] = 1 ; eta[3,3] = -1
eta.display()

Out[20]:
In [21]:
g = H2.metric('g')
g.set( Phi1.pullback(eta) )
g.display()

Out[21]:

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

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

Out[22]:

The Riemann curvature tensor associated with $g$ is

In [23]:
Riem = g.riemann()
print(Riem)

Tensor field Riem(g) of type (1,3) on the 2-dimensional differentiable manifold H2

In [24]:
Riem.display(X_pol.frame(), X_pol)

Out[24]:

The Ricci tensor and the Ricci scalar:

In [25]:
Ric = g.ricci()
print(Ric)

Field of symmetric bilinear forms Ric(g) on the 2-dimensional differentiable manifold H2

In [26]:
Ric.display(X_pol.frame(), X_pol)

Out[26]:
In [27]:
Rscal = g.ricci_scalar()
print(Rscal)

Scalar field r(g) on the 2-dimensional differentiable manifold H2

In [28]:
Rscal.display()

Out[28]:

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 [29]:
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[29]:

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

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

Out[30]:

## 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 [31]:
X_Pdisk.<R,ph> = U.chart(r'R:(0,1) ph:(0,2*pi):\varphi')
X_Pdisk

Out[31]:
In [32]:
X_Pdisk.coord_range()

Out[32]:

and relate it to the hyperboloid polar chart by

In [33]:
pol_to_Pdisk = X_pol.transition_map(X_Pdisk, [r/(1+sqrt(1+r^2)), ph])
pol_to_Pdisk

Out[33]:
In [34]:
pol_to_Pdisk.display()

Out[34]:
In [35]:
pol_to_Pdisk.set_inverse(2*R/(1-R^2), ph)
pol_to_Pdisk.inverse().display()

Out[35]:

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

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


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

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

Out[37]:

We may factorize each metric component:

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

Out[38]:

### 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 [39]:
X_Pdisk_cart.<u,v> = H2.chart('u:(-1,1) v:(-1,1)')
X_Pdisk_cart

Out[39]:

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

In [40]:
Pdisk_to_Pdisk_cart = X_Pdisk.transition_map(X_Pdisk_cart, [R*cos(ph), R*sin(ph)])
Pdisk_to_Pdisk_cart

Out[40]:
In [41]:
Pdisk_to_Pdisk_cart.display()

Out[41]:
In [42]:
Pdisk_to_Pdisk_cart.set_inverse(sqrt(u^2+v^2), atan2(v, u))
Pdisk_to_Pdisk_cart.inverse().display()

Out[42]:

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

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

Out[43]:

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

In [44]:
graph_disk_uv = X_Pdisk_cart.plot(X3, mapping=Phi2, number_values=15)
show(graph_disk_uv, viewer=viewer3D, online=True, 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 [45]:
pol_to_Pdisk_cart = Pdisk_to_Pdisk_cart * pol_to_Pdisk
pol_to_Pdisk_cart

Out[45]:
In [46]:
pol_to_Pdisk_cart.display()

Out[46]:

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 [47]:
hyp_to_Pdisk_cart_U = pol_to_Pdisk_cart * pol_to_hyp.inverse()
hyp_to_Pdisk_cart_U

Out[47]:
In [48]:
hyp_to_Pdisk_cart_U.display()

Out[48]:

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 [49]:
hyp_to_Pdisk_cart = X_hyp.transition_map(X_Pdisk_cart, hyp_to_Pdisk_cart_U(X,Y))
hyp_to_Pdisk_cart

Out[49]:
In [50]:
hyp_to_Pdisk_cart.display()

Out[50]:
In [51]:
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[51]:
In [52]:
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, online=True, figsize=7)

In [53]:
X_pol.plot(X_Pdisk_cart, ranges={r: (0, 20)}, number_values=15)

Out[53]: