# Simple Vector Bundles¶

This Jupyter notebook illustrates some basic features and functionalities regarding vector bundles within SageMath. The involved tools have been developed through the SageManifolds project.

Author: Michael Jung

A version of SageMath at least equal to 9.0 is required to run this notebook:

In [1]:
version()

Out[1]:
'SageMath version 9.1.beta6, Release Date: 2020-03-01'

First we set up the notebook to display math formulas using LaTeX formatting:

In [2]:
%display latex


## The Möbius Bundle¶

In this section we want to introduce a non-trivial line bundle and explain how it can be applied within Sage making use of our implementation.

In [3]:
M = Manifold(1, 'RP1',
latex_name=r'\mathbb{RP}^1',
start_index=1,
structure='topological')
U = M.open_subset('U'); hu.<u> = U.chart()
V = M.open_subset('V'); hv.<v> = V.chart()
M.declare_union(U, V)


The change of coordinates is given by:

In [4]:
u_to_v = hu.transition_map(hv, 1/u,
intersection_name='W',
restrictions1= u!=0,
restrictions2= v!=0)
u_to_v.display()

Out[4]:
In [5]:
W = U.intersection(V)
v_to_u = u_to_v.inverse()
v_to_u.display()

Out[5]:

We define the Möbius bundle in Sage:

In [6]:
E = M.vector_bundle(1, 'E'); print(E)

Topological real vector bundle E -> RP1 of rank 1 over the base space 1-dimensional topological manifold RP1


Let us state the two trivializations:

In [7]:
psiU = E.trivialization('psiU', latex_name=r'\psi_U', domain=U)
psiU

Out[7]:
In [8]:
psiV = E.trivialization('psiV', latex_name=r'\psi_V', domain=V)
psiV

Out[8]:

Next we declare the transition map between $\psi_U$ and $\psi_V$:

In [9]:
transf = psiU.transition_map(psiV, [[u]]); transf

Out[9]:

Of course each trivialization induces a local frame which we can get by the following command:

In [10]:
eU = psiU.frame(); eU

Out[10]:

The notation $\left(\psi_U^* e_{ 1 }\right)$ stands for the local section on $U$ given by $p \mapsto \psi^{-1}_U(p,e_1)$, where $e_1$ is the standard basis of $\mathbb{R}$. Similarly, we have for $\psi_V$:

In [11]:
eV = psiV.frame(); eV

Out[11]:

The corresponding bundle automorphism $\psi_U^{-1} \circ \psi_V:\left. E \right|_{W} \to \left. E \right|_{W}$ translating $\left(\psi_V^* e_{ 1 }\right)$ into $\left(\psi_U^* e_{ 1 }\right)$ can be easily returned:

In [12]:
transf.automorphism()

Out[12]:

This is an instance of FreeModuleAutomorphism:

In [13]:
from sage.tensor.modules.free_module_automorphism import FreeModuleAutomorphism
isinstance(transf.automorphism(), FreeModuleAutomorphism)

Out[13]:

We can even get its determinant which is a scalar field on the intersection $W = U \cap V$:

In [14]:
transf.det().display()

Out[14]:

We can see that the determinant is negative if $u<0$. It is therefore reasonable to suspect that the vector bundle $E$ is not orientable. This is indeed true. As a consequence, $E$ is not trivial and each global section must vanish somewhere. To exemplify this, we define the corresponding section module over $\mathbb{RP}^1$:

In [15]:
C0 = E.section_module(); C0

Out[15]:

We can see that Sage rejects $C^{0}(\mathbb{RP}^1;E)$ as a free module:

In [16]:
from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule
isinstance(C0, FiniteRankFreeModule)

Out[16]:

This is because there is no global frame that Sage knows about:

In [17]:
E.is_manifestly_trivial()

Out[17]:

On the contrary, the section module over $U$ must be free:

In [18]:
C0U = E.section_module(domain=U); C0U

Out[18]:

And indeed, it is:

In [19]:
from sage.tensor.modules.finite_rank_free_module import FiniteRankFreeModule
isinstance(C0U, FiniteRankFreeModule)

Out[19]:

We start with some concrete computations and therefore define a section on $U$:

In [20]:
sU = E.section(name='sigma', latex_name=r'\sigma', domain=U)
sU[eU,1] = (1-u)/(1+u^2)
sU.display()

Out[20]:

This local section lives in the free module $C^0(U;E)$:

In [21]:
sU in C0U

Out[21]:

We can perform a change of frame on the subset $W$:

In [22]:
sU.display(eV.restrict(W), hv.restrict(W))

Out[22]:

This expression is obviously well-defined on the whole subset $V$. Hence, we can continue this section onto $\mathbb{RP}^1$:

In [23]:
s = E.section(name='sigma', latex_name=r'\sigma')
s.set_restriction(sU)
s.display(eV)

Out[23]:

The corresponding continuation is indeed an element of $C^{0}(\mathbb{RP}^1;E)$:

In [24]:
s in C0

Out[24]:

Let us define another global section in $E$:

In [25]:
t = E.section(name='tau', latex_name=r'\tau')
t[eV,1] = (3-v^2)/(1+v^4)
t.display(eU)

Out[25]:

Now, $\sigma$ and $\tau$ can be added pointwise:

In [26]:
r = (s + t); r.display(eU)

Out[26]:
In [27]:
r.display(eV)

Out[27]:

Since $\sigma + \tau$ is again a well-defined continuous section on $E$, it must vanish at some point. We want to check this by solving an equation:

In [28]:
sol = solve(r[eU,1,hu].expr() == 0, u, solution_dict=True)
sol

Out[28]:

Let us investigate what happens at this particular point $p\in \mathbb{RP}^1$ determined by $u=-1$:

In [29]:
p = M.point([sol[0][u]], name='p', chart=hu); print(p)

Point p on the 1-dimensional topological manifold RP1


The corresponding section $\sigma$ evaluated at $p$ is an element of the fiber $E_p$:

In [30]:
print(s.at(p))

Vector sigma in the fiber of E at Point p on the 1-dimensional topological manifold RP1


Concretely we have:

In [31]:
s.at(p).display(basis=eU.at(p))

Out[31]:

For $\tau$ we similarly obtain:

In [32]:
t.at(p).display(basis=eU.at(p))

Out[32]:

As expected, the sum vanishes at $p$:

In [33]:
r.at(p).display(basis=eU.at(p))

Out[33]:

## Tensor Bundles over $\mathbb{S}^2$¶

We define $U \subset \mathbb{S}^2$ to be the complement of the meridian lying in the upper $x$-$z$-plane for $x\geq 0$. Similarly $V \subset \mathbb{S}^2$ defines the complement of the meridian going through the $x$-$y$-plane for $x \leq 0$. In the language of Sage we write:

In [34]:
M = Manifold(2, name='S^2', latex_name=r'\mathbb{S}^2')
U = M.open_subset('U'); V = M.open_subset('V')
M.declare_union(U,V) # M is the union of U and V


The corresponding tangent bundle can be returned by the following command:

In [35]:
TM = M.tangent_bundle(); TM

Out[35]:

However for now, we are interested in the parallelizable subset $U \subset \mathbb{S}^2$:

In [36]:
TU = U.tangent_bundle(); print(TU)

Tangent bundle TU over the Open subset U of the 2-dimensional differentiable manifold S^2


Trivializations entirely fall back on the class DiffChart at this stage. To demonstrate how it is done, we introduce spherical coordinates on the subset $U$:

In [37]:
c_spher.<th,ph> = TU.trivialization(r'th:(0,pi):\theta ph:(0,2*pi):\phi')


To demonstrate the pullback of tensor bundles, we define the Euclidean space $\mathbb{R}^3$ and introduce a differential map $\varphi: \mathbb{S}^2 \to \mathbb{R}^3$ given by the embedding of $\mathbb{S}^2$ into $\mathbb{R}^3$:

In [38]:
R = Manifold(3, 'R^3', r'\mathbb{R}^3')
c_cart.<x,y,z> = R.chart()  # Cartesian coord. on R^3
phi = U.diff_map(R, (sin(th)*cos(ph), sin(th)*sin(ph), cos(th)),
name='phi', latex_name=r'\varphi'); print(phi)

Differentiable map phi from the Open subset U of the 2-dimensional differentiable manifold S^2 to the 3-dimensional differentiable manifold R^3


Let us fix the point $p$ in $U \subset \mathbb{S}^2$ determined by $(\frac{\pi}{2},\pi)$ in spherical coordinates:

In [39]:
p = U.point((pi/2, pi), name='p'); print(p)

Point p on the 2-dimensional differentiable manifold S^2


We can evaluate $\varphi$ at this point $p$:

In [40]:
phi(p).coord(c_cart)

Out[40]:

We get the corresponding pullback tensor bundles by stating $\varphi$ as the destination map:

In [41]:
phiT11U = U.tensor_bundle(1,1, dest_map=phi); phiT11U

Out[41]:

More precisely:

In [42]:
print(phiT11U)

Tensor bundle phi^*T^(1,1)R^3 over the Open subset U of the 2-dimensional differentiable manifold S^2 along the Differentiable map phi from the Open subset U of the 2-dimensional differentiable manifold S^2 to the 3-dimensional differentiable manifold R^3


We see that sections completely fall back on the preexisting implementation of tensor fields:

In [43]:
phiT11U.section_module() is U.tensor_field_module((1,1), dest_map=phi)

Out[43]:

The fiber at $p$ is given by the space of $(1,1)$-tensors of the tangent space over $\mathbb{R}^3$ at $\varphi(p)$:

In [44]:
phiT11U.fiber(p)

Out[44]:

Since $\mathbb{R}^3$ is parallelizable, the pullback tensor bundle $\varphi^* T^{(1,1)}\mathbb{R}^3\to U$ must be trivial:

In [45]:
phiT11U.is_manifestly_trivial()

Out[45]:

Hence it comes with a frame naturally induced by the pullback:

In [46]:
phiT11U.frames()

Out[46]:

Strictly speaking, this is a frame of $\varphi^* T\mathbb{R}^3$ rather than $\varphi^* T^{(1,1)}\mathbb{R}^3$. But remember that all frames in the tensor bundle can be retrieved from frames in the tangent bundle. Thus, there is no loss of generality here. We can extract our frame by applying index operations on the returned list:

In [47]:
print(phiT11U.frames()[0])

Vector frame (U, (d/dx,d/dy,d/dz)) with values on the 3-dimensional differentiable manifold R^3

In [ ]: