#!/usr/bin/env python # coding: utf-8 # # Real projective plane $\mathbb{RP}^2$ # # This notebook demonstrates some capabilities of SageMath about differentiable manifolds on the example of real projective plane. The corresponding tools have been developed within # the [SageManifolds](https://sagemanifolds.obspm.fr) project (version 1.3, as included in SageMath 8.3). # # Click [here](https://raw.githubusercontent.com/sagemanifolds/SageManifolds/master/Worksheets/v1.3/SM_projective_plane.ipynb) to download the notebook 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() # First we set up the notebook to display mathematical objects using LaTeX rendering: # In[2]: get_ipython().run_line_magic('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) # ## Constructing the manifold # We start by declaring the real projective plane as a 2-dimensional differentiable manifold: # In[4]: RP2 = Manifold(2, 'RP^2', r'\mathbb{RP}^2') ; RP2 #
Then we provide $\mathbb{RP}^2$ with some atlas. A minimal atlas on $\mathbb{RP}^2$ must have at least three charts. Such an atlas is easy to infer from the common interpretation of $\mathbb{RP}^2$ as the set of lines of $\mathbb{R}^3$ passing through the origin $(x,y,z)=(0,0,0)$. Let $U_1$ be the subset of lines that are not contained in the plane $z=0$; this is an open set of $\mathbb{RP}^2$, so that we declare it as:
# In[5]: U1 = RP2.open_subset('U_1') ; U1 #Any line in $U_1$ is uniquely determined by its intersection with the plane $z=1$. The Cartesian coordinates $(x,y,1)$ of the intersection point lead to an obvious coordinate system $(x_1,y_1)$ on $U_1$ by setting $(x_1,y_1)=(x,y)$:
# In[6]: X1.Note that since we have not specified any coordinate range in the arguments of chart(), the range of $(x_1,y_1)$ is $\mathbb{R}^2$.
#Similarly, let $U_2$ be the set of lines through the origin of $\mathbb{R}^3$ that are not contained in the plane $x=0$. Any line in $U_2$ is uniquely determined by its intersection $(1,y,z)$ with the plane $x=1$, leading to coordinates $(x_2,y_2)=(y,z)$ on $U_2$:
# In[7]: U2 = RP2.open_subset('U_2') X2.Finally, let $U_3$ be the set of lines through the origin of $\mathbb{R}^3$ that are not contained in the plane $y=0$. Any line in $U_3$ is uniquely determined by its intersection $(x,1,z)$ with the plane $y=1$, leading to coordinates $(x_3,y_3)=(z,x)$ on $U_3$:
# In[8]: U3 = RP2.open_subset('U_3') X3.We declare that the union of the three (overlapping) open domains $U_1$, $U_2$ and $U_3$ is $\mathbb{RP}^2$:
# In[9]: RP2.declare_union(U1.union(U2), U3) U1.union(U2).union(U3) #At this stage, three open covers of $\mathbb{RP}^2$ have been constructed:
# In[10]: RP2.open_covers() #Finally, to fully specify the manifold $\mathbb{RP}^2$, we give the transition maps between the various charts; the transition map between the charts X1=$(U_1,(x_1,y_1))$ and X2=$(U_2,(x_2,y_2))$ is defined on the set $U_{12} := U_1 \cap U_2$ of lines through the origin of $\mathbb{R}^3$ that are neither contained in the plane $x=0$ ($x_1=0$ in $U_1$) nor contained in the plane $z=0$ ($y_2=0$ in $U_2$):
# In[11]: X1_to_X2 = X1.transition_map(X2, (y1/x1, 1/x1), intersection_name='U_{12}', restrictions1= x1!=0, restrictions2= y2!=0) X1_to_X2.display() #The inverse of this transition map is easily computed by Sage:
# In[12]: X2_to_X1 = X1_to_X2.inverse() X2_to_X1.display() #The transition map between the charts X1=$(U_1,(x_1,y_1))$ and X3=$(U_3,(x_3,y_3))$ is defined on the set $U_{13} := U_1 \cap U_3$ of lines through the origin of $\mathbb{R}^3$ that are neither contained in the plane $y=0$ ($y_1=0$ in $U_1$) nor contained in the plane $z=0$ ($x_3=0$ in $U_3$):
# In[13]: X1_to_X3 = X1.transition_map(X3, (1/y1, x1/y1), intersection_name='U_{13}', restrictions1= y1!=0, restrictions2= x3!=0) X1_to_X3.display() # In[14]: X3_to_X1 = X1_to_X3.inverse() X3_to_X1.display() #Finally, the transition map between the charts X2=$(U_2,(x_2,y_2))$ and X3=$(U_3,(x_3,y_3))$ is defined on the set $U_{23} := U_2 \cap U_3$ of lines through the origin of $\mathbb{R}^3$ that are neither contained in the plane $y=0$ ($x_2=0$ in $U_2$) nor contained in the plane $x=0$ ($y_3=0$ in $U_3$):
# In[15]: X2_to_X3 = X2.transition_map(X3, (y2/x2, 1/x2), intersection_name='U_{23}', restrictions1= x2!=0, restrictions2= y3!=0) X2_to_X3.display() # In[16]: X3_to_X2 = X2_to_X3.inverse() X3_to_X2.display() #At this stage, the manifold $\mathbb{RP}^2$ is fully constructed. It has been provided with the following atlas:
# In[17]: RP2.atlas() #Note that, in addition to the three chart we have defined, the atlas comprises subcharts on the intersection domains $U_{12}$, $U_{13}$ and $U_{23}$. These charts can be obtained by the method restrict():
# In[18]: U12 = U1.intersection(U2) U13 = U1.intersection(U3) U23 = U2.intersection(U3) X1.restrict(U12) # In[19]: X1.restrict(U12) is RP2.atlas()[3] #It is well known that $\mathbb{RP}^2$ is not an orientable manifold. To illustrate this, let us make an attempt to construct a global non-vanishing 2-form $\epsilon$ on $\mathbb{RP}^2$. If we succeed, this would provide a volume form and $\mathbb{RP}^2$ would be orientable. We start by declaring $\epsilon$ as a 2-form on $\mathbb{RP}^2$:
# In[20]: eps = RP2.diff_form(2, name='eps', latex_name=r'\epsilon') print(eps) #We set the value of $\epsilon$ on domain $U_1$ to be $\mathrm{d}x_1 \wedge \mathrm{d}y_1$ by demanding that the component $\epsilon_{01}$ of $\epsilon$ with respect to coordinates $(x_1,y_1)$ is one, as follows:
# In[21]: e1 = X1.frame() ; e1 # In[22]: eps[e1,0,1] = 1 eps.display(e1) #If we ask for the expression of $\epsilon$ in terms of the coframe $(\mathrm{d}x_2, \mathrm{d}y_2)$ associated with the chart X2 on $U_{12} = U_1\cap U_2$, we get
# In[23]: eps.display(X2.frame().restrict(U12), chart=X2.restrict(U12)) #Now, the complement of $U_{12}$ in $U_2$ is defined by $y_2=0$. The above expression shows that it is not possible to extend smoothly $\epsilon$ to the whole domain $U_2$. We conclude that starting from $\mathrm{d}x_1\wedge\mathrm{d}y_1$ on $U_1$, it is not possible to get a regular non-vanishing 2-form on $\mathbb{RP}^2$. This of course follows from the fact that $\mathbb{RP}^2$ is not orientable.
# #Let us first define $\mathbb{R}^3$ as a 3-dimensional manifold, with a single-chart atlas (Cartesian coordinates Y):
# In[24]: R3 = Manifold(3, 'R^3', r'\mathbb{R}^3') Y.The Steiner map is a map $\mathbb{RP}^2 \rightarrow \mathbb{R}^3$ defined as follows:
# In[25]: Phi = RP2.diff_map(R3, {(X1,Y): [y1/(1+x1^2+y1^2), x1/(1+x1^2+y1^2), x1*y1/(1+x1^2+y1^2)], (X2,Y): [x2*y2/(1+x2^2+y2^2), y2/(1+x2^2+y2^2), x2/(1+x2^2+y2^2)], (X3,Y): [x3/(1+x3^2+y3^2), x3*y3/(1+x3^2+y3^2), y3/(1+x3^2+y3^2)]}, name='Phi', latex_name=r'\Phi') Phi.display() #$\Phi$ is a topological immersion of $\mathbb{RP}^2$ into $\mathbb{R}^3$, but it is not a smooth immersion (contrary to the Apéry map below): its differential is not injective at $(x_1,y_1)=(0,1)$ and $(x_1,y_1)=(1,0)$. The image of $\Phi$ is a self-intersecting surface of $\mathbb{R}^3$, called the Roman surface:
# In[26]: g1 = parametric_plot3d(Phi.expr(X1,Y), (x1,-10,10), (y1,-10,10), plot_points=[100,100]) g2 = parametric_plot3d(Phi.expr(X2,Y), (x2,-10,10), (y2,-10,10), plot_points=[100,100]) g3 = parametric_plot3d(Phi.expr(X3,Y), (x3,-10,10), (y3,-10,10), plot_points=[100,100]) show(g1+g2+g3, viewer=viewer3D, online=True) # In[27]: gX1 = X1.plot(Y, mapping=Phi, max_range=16, number_values=24, plot_points=100, label_axes=False) gX2 = X2.plot(Y, mapping=Phi, max_range=16, number_values=24, plot_points=100, label_axes=False, color='green') gX3 = X3.plot(Y, mapping=Phi, max_range=16, number_values=24, plot_points=100, label_axes=False, color='blue') show(gX1, viewer=viewer3D, online=True) # In[28]: show(gX2, viewer=viewer3D, online=True) # In[29]: show(gX3, viewer=viewer3D, online=True) # In[30]: show(gX1+gX2+gX3, viewer=viewer3D, online=True) #The Apéry map [Apéry, Adv. Math. 61, 185 (1986)] is a smooth immersion $\Psi: \mathbb{RP}^2 \rightarrow \mathbb{R}^3$. In terms of the charts X1, X2, X3 introduced above, it is defined as follows:
# In[31]: fx = ((2*x^2-y^2-z^2)*(x^2+y^2+z^2)+2*y*z*(y^2-z^2)+z*x*(x^2-z^2)+x*y*(y^2-z^2))/2 ; fx # In[32]: fy = sqrt(3)/2*((y^2-z^2)*(x^2+y^2+z^2)+z*x*(z^2-x^2)+x*y*(y^2-x^2)) ; fy # In[33]: fz = (x+y+z)*((x+y+z)^3/4+(y-x)*(z-y)*(x-z)) ; fz # In[34]: a = sqrt(1+x1^2+y1^2) fx1 = fx.subs(x=x1/a, y=y1/a, z=1/a).simplify_full() fy1 = fy.subs(x=x1/a, y=y1/a, z=1/a).simplify_full() fz1 = fz.subs(x=x1/a, y=y1/a, z=1/a).simplify_full() # In[35]: a = sqrt(1+x2^2+y2^2) fx2 = fx.subs(x=1/a, y=x2/a, z=y2/a).simplify_full() fy2 = fy.subs(x=1/a, y=x2/a, z=y2/a).simplify_full() fz2 = fz.subs(x=1/a, y=x2/a, z=y2/a).simplify_full() # In[36]: a = sqrt(1+x3^2+y3^2) fx3 = fx.subs(x=y3/a, y=1/a, z=x3/a).simplify_full() fy3 = fy.subs(x=y3/a, y=1/a, z=x3/a).simplify_full() fz3 = fz.subs(x=y3/a, y=1/a, z=x3/a).simplify_full() # In[37]: Psi = RP2.diff_map(R3, {(X1,Y): [fx1, fy1, fz1], (X2,Y): [fx2, fy2, fz2], (X3,Y): [fx3, fy3, fz3]}, name='Psi', latex_name=r'\Psi') Psi.display() #The image of $\Psi$ is a self-intersecting surface of $\mathbb{R}^3$, called the Boy surface, after Werner Boy (1879-1914):
# In[38]: g1 = parametric_plot3d(Psi.expr(X1,Y), (x1,-10,10), (y1,-10,10), plot_points=[100,100]) g2 = parametric_plot3d(Psi.expr(X2,Y), (x2,-10,10), (y2,-10,10), plot_points=[100,100]) g3 = parametric_plot3d(Psi.expr(X3,Y), (x3,-10,10), (y3,-10,10), plot_points=[100,100]) show(g1+g2+g3, viewer=viewer3D, online=True) # In[39]: gX1 = X1.plot(Y, mapping=Psi, number_values=40, plot_points=100, label_axes=False) gX2 = X2.plot(Y, mapping=Psi, number_values=40, plot_points=100, label_axes=False, color='green') gX3 = X3.plot(Y, mapping=Psi, number_values=40, plot_points=100, label_axes=False, color='blue') # In[40]: show(gX1, viewer=viewer3D, online=True) # In[41]: show(gX2, viewer=viewer3D, online=True) # In[42]: show(gX3, viewer=viewer3D, online=True) # In[43]: show(gX1+gX2+gX3, viewer=viewer3D, online=True)