Mixed Forms and Characteristic Classes

This Jupyter notebook illustrates SageMath's functionalities regarding mixed differential forms and characteristic classes. The involved tools have been developed through the SageManifolds project.

Author: Michael Jung

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

In [1]:
'SageMath version 9.4.rc1, Release Date: 2021-08-08'

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

In [2]:
%display latex

Mixed Differential Forms

We briefly demonstrate the capabilities of our implementation, and start by declaring the manifold $M=\mathbb{R}^2$:

In [3]:
M = Manifold(2, 'R^2', latex_name=r'\mathbb{R}^2')
X.<x,y> = M.chart()

We define the corresponding spaces of differential forms:

In [4]:
Omega0 = M.diff_form_module(0); print(Omega0)
Omega1 = M.diff_form_module(1); print(Omega1)
Omega2 = M.diff_form_module(2); print(Omega2)
Algebra of differentiable scalar fields on the 2-dimensional differentiable manifold R^2
Free module Omega^1(R^2) of 1-forms on the 2-dimensional differentiable manifold R^2
Free module Omega^2(R^2) of 2-forms on the 2-dimensional differentiable manifold R^2

The algebra of mixed forms is returned by a simple command:

In [5]:
Omega = M.mixed_form_algebra(); print(Omega)
Graded algebra Omega^*(R^2) of mixed differential forms on the 2-dimensional differentiable manifold R^2

It belongs to the category of graded algebras over the symbolic ring:

In [6]:
Join of Category of graded algebras over Symbolic Ring and Category of chain complexes over Symbolic Ring

Let us define a mixed differential form:

In [7]:
A = M.mixed_form(name='A'); print(A)
Mixed differential form A on the 2-dimensional differentiable manifold R^2

The homogeneous components of A can be accessed and altered using the A[i] syntax:

In [8]:
A[1][:] = y, 2*x
A[2][0,1] = 4*x^3
In [9]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}A = {A}_{0} + {A}_{1} + {A}_{2}\]
In [10]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}A = x^{2} + y \mathrm{d} x + 2 \, x \mathrm{d} y + 4 \, x^{3} \mathrm{d} x\wedge \mathrm{d} y\]
In [11]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}\begin{array}{llcl} {A}_{0}:& \mathbb{R}^2 & \longrightarrow & \mathbb{R} \\ & \left(x, y\right) & \longmapsto & x^{2} \end{array}\]
In [12]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}{A}_{1} = y \mathrm{d} x + 2 \, x \mathrm{d} y\]
In [13]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}{A}_{2} = 4 \, x^{3} \mathrm{d} x\wedge \mathrm{d} y\]

The category framework of Sage captures the entire setup:

In [14]:
all([A[0] in Omega0,
     A[0] in Omega,
     A[1] in Omega1,
     A[1] in Omega,
     A[2] in Omega2,
     A[2] in Omega])

Let us perform some computations and define another mixed form. This time, we predefine some differential forms:

In [15]:
f = M.scalar_field(2, name='f')

eta = M.diff_form(1, name='eta', latex_name=r'\eta')
eta[:] = x, y

xi = M.diff_form(2, name='xi', latex_name=r'\xi')
xi[0,1] = y^2
In [16]:
B = M.mixed_form([f,eta,0], name='B'); B.display()
\[\newcommand{\Bold}[1]{\mathbf{#1}}B = f + \eta + 0\]
In [17]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}B = 2 + x \mathrm{d} x + y \mathrm{d} y\]

Though the homogeneous components share the same name, they are independent instances:

In [18]:
B[0] == f
In [19]:
B[0] is f

As long as B is mutable, the homogeneous components can still be changed:

In [20]:
B[2] = xi
In [21]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}B = f + \eta + \xi\]

But if we set the mixed form immutable, this is no longer possible:

In [22]:
In [23]:
    B[1] = 0
except ValueError as ve:
the components of an immutable element cannot be changed

The multiplication is executed degree wise:

In [24]:
all((A * B)[k] == sum(A[j].wedge(B[k - j])
                      for j in range(k + 1))
    for k in Omega.irange())
In [25]:
(A * B).display_expansion()
\[\newcommand{\Bold}[1]{\mathbf{#1}}A\wedge B = 2 \, x^{2} + \left( x^{3} + 2 \, y \right) \mathrm{d} x + \left( x^{2} y + 4 \, x \right) \mathrm{d} y + \left( 8 \, x^{3} + {\left(x^{2} + 1\right)} y^{2} - 2 \, x^{2} \right) \mathrm{d} x\wedge \mathrm{d} y\]

This particular example is also convenient to demonstrate that the multiplication given by the wedge product is in general neither commutative nor anticommutative:

In [26]:
(B * A).display_expansion()
\[\newcommand{\Bold}[1]{\mathbf{#1}}B\wedge A = 2 \, x^{2} + \left( x^{3} + 2 \, y \right) \mathrm{d} x + \left( x^{2} y + 4 \, x \right) \mathrm{d} y + \left( 8 \, x^{3} + {\left(x^{2} - 1\right)} y^{2} + 2 \, x^{2} \right) \mathrm{d} x\wedge \mathrm{d} y\]

Finally, let us compute the exterior derivative:

In [27]:
dA = A.exterior_derivative(); dA.display_expansion()
\[\newcommand{\Bold}[1]{\mathbf{#1}}\mathrm{d}A = 2 \, x \mathrm{d} x +\mathrm{d} x\wedge \mathrm{d} y\]

Characteristic Classes

Chern Character over Minkowski Space

We want to exemplify the usage of characteristic classes within Sage by computing the Chern character form $\mathrm{ch}(E, \nabla^E)$ on a complex trivial line bundle $E$ over the 2-dimensional Minkowski space $M$ equipped with a bundle connection $\nabla^E$. We start with the general setup:

In [28]:
M = Manifold(2, 'M', structure='Lorentzian')
X.<t,x> = M.chart()
E = M.vector_bundle(1, 'E', field='complex'); print(E)
Differentiable complex vector bundle E -> M of rank 1 over the base space 2-dimensional Lorentzian manifold M

To trivialize the vector bundle $E$, we fix a global frame $e$:

In [29]:
e = E.local_frame('e') # trivialize

Let us declare an $\mathrm{U}(1)$-connection $\nabla^E$ on $E$ given by an electromagnetic potential $A(t)$:

In [30]:
nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E')
A = function('A')

The corresponding connection form $\omega$ turns out as:

In [31]:
omega = M.one_form(name='omega', latex_name=r'\omega')
omega[1] = I*A(t)
\[\newcommand{\Bold}[1]{\mathbf{#1}}\omega = i \, A\left(t\right) \mathrm{d} x\]

Let us put this into the connection:

In [32]:
nab[0,0] = omega

For the following, nab must be hashable. Thus we have to make it immutable first:

In [33]:

Notice that the Chern character $\mathrm{ch}(E)$ is already predefined in the system. We can get it by the following command:

In [34]:
ch = E.characteristic_class('ChernChar'); print(ch)
Characteristic class ch of additive type associated to e^x on the Differentiable complex vector bundle E -> M of rank 1 over the base space 2-dimensional Lorentzian manifold M

The computation of the corresponding Chern character form $\mathrm{ch}(E,\nabla^E)$ can be invoked by the method get_form:

In [35]:
ch_form = ch.get_form(nab)
\[\newcommand{\Bold}[1]{\mathbf{#1}}\mathrm{ch}(E, \nabla^E) = 1 + \frac{\frac{\partial\,A}{\partial t}}{2 \, \pi} \mathrm{d} t\wedge \mathrm{d} x\]

We can see that the resulting 2-form coincides with the Faraday tensor divided by $2 \pi$ as expected.

Example: Chern Class of the Tautological Line Bundle

We start our computation by initializing the complex projective space as 2-dimensional real manifold with coordinates on $U:= \mathbb{CP}^1 \setminus \left\{ [1:0] \right\}$:

In [36]:
M = Manifold(2, 'CP^1', start_index=1)
U = M.open_subset('U')
c_cart.<x,y> = U.chart() # [1:x+I*y]

For the sake of convenience, we additionally declare the complex coordinates $z$ and $\bar{z}$ on $U$:

In [37]:
c_comp.<z, zbar> = U.chart(r'z:z zbar:\bar{z}')
cart_to_comp = c_cart.transition_map(c_comp, (x+I*y, x-I*y))
\[\newcommand{\Bold}[1]{\mathbf{#1}}\left\{\begin{array}{lcl} {z} & = & x + i \, y \\ {\bar{z}} & = & x - i \, y \end{array}\right.\]
In [38]:
comp_to_cart = cart_to_comp.inverse()
\[\newcommand{\Bold}[1]{\mathbf{#1}}\left\{\begin{array}{lcl} x & = & \frac{1}{2} \, {z} + \frac{1}{2} \, {\bar{z}} \\ y & = & -\frac{1}{2} i \, {z} + \frac{1}{2} i \, {\bar{z}} \end{array}\right.\]

Now, we are ready to construct the tautological line bundle $\gamma_1$:

In [39]:
E = M.vector_bundle(1, 'gamma_1',

Furthermore we declare a local frame $e$ on $U$ naturally given by $[z:1] \mapsto \left(\begin{smallmatrix} z \\ 1 \end{smallmatrix}\right)$:

In [40]:
e = E.local_frame('e', domain=U)

To compute the Chern class, we still need a connection. The tautological line bundle inherits a Hermitian metric from the overlying trivial bundle $\mathbb{C}^2 \times \mathbb{CP}^1$:

In [41]:
nab = E.bundle_connection('nabla', latex_name=r'\nabla')
omega = U.one_form(name='omega')
omega[c_comp.frame(), 1, c_comp] = zbar/(1+z*zbar)
nab[e, 1, 1] = omega

It is time to initialize $c(\gamma_1)$. Fortunately Sage already knows the Chern class:

In [42]:
c = E.characteristic_class('Chern'); print(c)
Characteristic class c of multiplicative type associated to x + 1 on the Differentiable complex vector bundle gamma_1 -> CP^1 of rank 1 over the base space 2-dimensional differentiable manifold CP^1

Let the machinery do its work:

In [43]:
c_form = c.get_form(nab)
c_form.display_expansion(c_comp.frame(), chart=c_comp)
\[\newcommand{\Bold}[1]{\mathbf{#1}}c(\gamma_1, \nabla) = 1 + \frac{i}{2 \, {\left(\pi + \pi {z}^{2} {\bar{z}}^{2} + 2 \, \pi {z} {\bar{z}}\right)}} \mathrm{d} {z}\wedge \mathrm{d} {\bar{z}}\]

Since this particular representation is defined outside a set of measure zero, we can compute its integral over $\mathbb{CP}^1$ in real coordinates:

In [44]:
integrate(integrate(c_form[2][[1,2]].expr(c_cart), x, -infinity, infinity).full_simplify(), y, -infinity, infinity)

The result shows that $c_1(\gamma_1)$ generates the second integer cohomology $H^2(\mathbb{CP}^1, \mathbb{Z})$.

Euler Class of $\mathbb{S}^2$

In this example, we want to compute the Euler class of the 2-sphere $\mathbb{S}^2 \subset \mathbb{R}^3$. As usual, we cover $\mathbb{S}^2$ by two parallelizable open subsets $U:=\mathbb{S}^2 \setminus \{(0,0,1)\}$ and $V:=\mathbb{S}^2 \setminus \{(0,0,-1)\}$ for which the point $(0,0,1)$ is identified with the north pole. We state stereographic coordinates in Sage:

In [45]:
M = Manifold(2, name='S^2', latex_name=r'\mathbb{S}^2',
             structure='Riemannian', start_index=1)
U = M.open_subset('U') ; V = M.open_subset('V')
M.declare_union(U,V)   # M is the union of U and V
stereoN.<x,y> = U.chart()
stereoS.<xp,yp> = V.chart("xp:x' yp:y'")
N_to_S = stereoN.transition_map(stereoS,
                                (x/(x^2+y^2), y/(x^2+y^2)),
                                restrictions1= x^2+y^2!=0,
                                restrictions2= xp^2+yp^2!=0)
S_to_N = N_to_S.inverse()

Next we define the tangent bundle and its local frames induced by the charts:

In [46]:
eU = stereoN.frame(); eV = stereoS.frame()
TM = M.tangent_bundle()

The Euler class is also one of the predefined classes. Thus, we can easily get it from the tangent bundle's instance:

In [47]:
e_class = TM.characteristic_class('Euler'); print(e_class)
Characteristic class e of Pfaffian type associated to x on the Tangent bundle TS^2 over the 2-dimensional Riemannian manifold S^2

To compute a form representing the Euler class, we need to state a suitable connection first. Here we want to use the Levi-Civita connection induced by the standard metric. This is simply given by the pullback of the Euclidean scalar product of the ambient space $\mathbb{R}^3$ along the canonical embedding $\iota: \mathbb{S}^2 \hookrightarrow \mathbb{R}^3$. Let us define the ambient space $\mathbb{R}^3$ and its Euclidean scalar product $h$:

In [48]:
E = Manifold(3, 'R^3', latex_name=r'\mathbb{R}^3', start_index=1)
cart.<X,Y,Z> = E.chart()
h = E.metric('h')
h[1,1], h[2,2], h[3, 3] = 1, 1, 1
\[\newcommand{\Bold}[1]{\mathbf{#1}}h = \mathrm{d} X\otimes \mathrm{d} X+\mathrm{d} Y\otimes \mathrm{d} Y+\mathrm{d} Z\otimes \mathrm{d} Z\]

On that account, we declare the embedding $\iota: \mathbb{S}^2 \hookrightarrow \mathbb{R}^3$ in stereographic coordinates when one considers its projection from the north pole $(0, 0, 1)$ to the equatorial plane $Z=0$:

In [49]:
iota = M.diff_map(E, {(stereoN, cart):
                      [2*x/(1+x^2+y^2), 2*y/(1+x^2+y^2),
                      (stereoS, cart):
                      [2*xp/(1+xp^2+yp^2), 2*yp/(1+xp^2+yp^2),
                  name='iota', latex_name=r'\iota')
\[\newcommand{\Bold}[1]{\mathbf{#1}}\begin{array}{llcl} \iota:& \mathbb{S}^2 & \longrightarrow & \mathbb{R}^3 \\ \mbox{on}\ U : & \left(x, y\right) & \longmapsto & \left(X, Y, Z\right) = \left(\frac{2 \, x}{x^{2} + y^{2} + 1}, \frac{2 \, y}{x^{2} + y^{2} + 1}, -\frac{x^{2} + y^{2} - 1}{x^{2} + y^{2} + 1}\right) \\ \mbox{on}\ V : & \left({x'}, {y'}\right) & \longmapsto & \left(X, Y, Z\right) = \left(\frac{2 \, {x'}}{{x'}^{2} + {y'}^{2} + 1}, \frac{2 \, {y'}}{{x'}^{2} + {y'}^{2} + 1}, \frac{{x'}^{2} + {y'}^{2} - 1}{{x'}^{2} + {y'}^{2} + 1}\right) \end{array}\]

We can define the standard metric $g$ on $\mathbb{S}^2$ by setting it as the pullback metric $\iota^*h$:

In [50]:
g = M.metric()
g[1,1].factor(); g[2,2].factor() # simplifications
\[\newcommand{\Bold}[1]{\mathbf{#1}}g = \frac{4}{{\left(x^{2} + y^{2} + 1\right)}^{2}} \mathrm{d} x\otimes \mathrm{d} x + \frac{4}{{\left(x^{2} + y^{2} + 1\right)}^{2}} \mathrm{d} y\otimes \mathrm{d} y\]

The corresponding Levi-Civita connection is computed automatically:

In [51]:
nab = g.connection()

Since we have found the desired Levi-Civita connection, we want to compute the associated curvature forms and store them in a Python list:

In [52]:
cmatrix_U = [[nab.curvature_form(i,j,eU) for j in TM.irange()]
              for i in TM.irange()]
cmatrix_V = [[nab.curvature_form(i,j,eV) for j in TM.irange()]
              for i in TM.irange()]

Fortunately, the curvature form matrices are already skew-symmetric:

In [53]:
for i in range(TM.rank()):
    for j in range(TM.rank()):
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^1_{\ \, 1} = 0\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^1_{\ \, 2} = \left( \frac{4}{x^{4} + y^{4} + 2 \, {\left(x^{2} + 1\right)} y^{2} + 2 \, x^{2} + 1} \right) \mathrm{d} x\wedge \mathrm{d} y\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^2_{\ \, 1} = \left( -\frac{4}{x^{4} + y^{4} + 2 \, {\left(x^{2} + 1\right)} y^{2} + 2 \, x^{2} + 1} \right) \mathrm{d} x\wedge \mathrm{d} y\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^2_{\ \, 2} = 0\]
In [54]:
for i in range(TM.rank()):
    for j in range(TM.rank()):
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^1_{\ \, 1} = 0\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^1_{\ \, 2} = \left( \frac{4}{{x'}^{4} + {y'}^{4} + 2 \, {\left({x'}^{2} + 1\right)} {y'}^{2} + 2 \, {x'}^{2} + 1} \right) \mathrm{d} {x'}\wedge \mathrm{d} {y'}\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^2_{\ \, 1} = \left( -\frac{4}{{x'}^{4} + {y'}^{4} + 2 \, {\left({x'}^{2} + 1\right)} {y'}^{2} + 2 \, {x'}^{2} + 1} \right) \mathrm{d} {x'}\wedge \mathrm{d} {y'}\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\Omega^2_{\ \, 2} = 0\]

Hence we can put them into a dictionary and apply the algorithm:

In [55]:
cmatrices = {eU: cmatrix_U, eV: cmatrix_V}
e_class_form = e_class.get_form(nab, cmatrices)
\[\newcommand{\Bold}[1]{\mathbf{#1}}e(T\mathbb{S}^2, \nabla_{g}) = \left( \frac{2}{\pi + \pi x^{4} + \pi y^{4} + 2 \, \pi x^{2} + 2 \, {\left(\pi + \pi x^{2}\right)} y^{2}} \right) \mathrm{d} x\wedge \mathrm{d} y\]

We want to compute the Euler characteristic of $\mathbb{S}^2$ now. This can be achieved by integrating the top form over $\mathbb{S}^2$. But since $U$ and $\mathbb{S}^2$ differ only by a point and therefore a set of measure zero, it is enough to integrate over the subset $U$:

In [56]:
integrate(integrate(e_class_form[2][[1,2]].expr(), x, -infinity, infinity).simplify_full(), y, -infinity, infinity)

We have eventually obtained the Euler characteristic of $\mathbb{S}^2$.

$\hat{A}$-Class of Lorentzian Foliation of Berger Spheres

We consider the space of unit quaternions $\mathbb{S}^3 \subset \mathbb{R}^4 \cong \mathbb{H}$, where $\mathbb{H}$ is endowed with the canonical basis $(\mathbf{1}, \mathbf{i}, \mathbf{j}, \mathbf{k})$. It turns out that $\mathbb{S}^3$ admits a global frame $(\varepsilon_1, \varepsilon_2, \varepsilon_3)$ so that we observe $\mathbb{S}^3$ to be a parallelizable manifold.

We introduce the following smooth family of so-called Berger metrics: \begin{align*} g_t = a(t)^{2} \; \varepsilon^{1}\otimes \varepsilon^{1} +\varepsilon^{2}\otimes \varepsilon^{2} +\varepsilon^{3}\otimes \varepsilon^{3}. \end{align*}

This family can be used to define a globally hyperbolic manifold $M=\mathbb{R} \times \mathbb{S}^3$ equipped with the Lorentzian metric $g = - {\mathrm{d} t}^2 + g_t$ and hence foliated by Berger spheres.

In the following we compute the $\hat{A}$-form of the corresponding Levi-Civita connection $\nabla_g$. We start the computation by declaring the Lorentzian manifold first:

In [57]:
M = Manifold(4, 'M', structure='Lorentzian'); print(M)
4-dimensional Lorentzian manifold M

We cover $M$ by two open subsets defined as $U := \mathbb{R} \times \left( \mathbb{S}^3 \setminus \{ -\mathbf{1} \} \right)$ and $V := \mathbb{R} \times \left( \mathbb{S}^3 \setminus \{ \mathbf{1} \} \right)$:

In [58]:
U = M.open_subset('U'); V = M.open_subset('V')

We need to impose coordinates on $M$ and use stereographic projections with respect to the foliated 3-sphere:

In [59]:
stereoN.<t,x,y,z> = U.chart()
stereoS.<tp,xp,yp,zp> = V.chart("tp:t' xp:x' yp:y' zp:z'")
N_to_S = stereoN.transition_map(stereoS,
                                (t, x/(x^2+y^2+z^2),
                                restrictions1= x^2+y^2+z^2!=0,
                                restrictions2= xp^2+yp^2+zp^2!=0)
W = U.intersection(V)
S_to_N = N_to_S.inverse()
\[\newcommand{\Bold}[1]{\mathbf{#1}}\left\{\begin{array}{lcl} {t'} & = & t \\ {x'} & = & \frac{x}{x^{2} + y^{2} + {z}^{2}} \\ {y'} & = & \frac{y}{x^{2} + y^{2} + {z}^{2}} \\ {z'} & = & \frac{{z}}{x^{2} + y^{2} + {z}^{2}} \end{array}\right.\]

From above, we know that $M$ admits a global frame $(\varepsilon_0, \varepsilon_1, \varepsilon_2, \varepsilon_3)$:

In [60]:
E = M.vector_frame('E', latex_symbol=r'\varepsilon')
E_U = E.restrict(U); E_U
\[\newcommand{\Bold}[1]{\mathbf{#1}}\left(U, \left(\varepsilon_{0},\varepsilon_{1},\varepsilon_{2},\varepsilon_{3}\right)\right)\]

The vector field $\varepsilon_0$ is simply given by $\frac{\partial}{\partial t}$. To obtain the global vector frame $(\varepsilon_1, \varepsilon_2, \varepsilon_3)$ on $\mathbb{S}^3$ in stereographic coordinates, a computation within Sage is performed in the this Jupyter-Notebook. This is done by embedding $\mathbb{S}^3$ into $\mathbb{R}^4 \cong \mathbb{H}$, endowing it with the quaternionic structure. This eventually leads to:

In [61]:
E_U[0][:] = [1,0,0,0]
E_U[1][:] = [0, (x^2-y^2-z^2+1)/2, x*y+z, x*z-y]
E_U[2][:] = [0, x*y-z, (1-x^2+y^2-z^2)/2, x+y*z]  
E_U[3][:] = [0, x*z+y, y*z-x, (1-x^2-y^2+z^2)/2]
In [62]:
for i in M.irange():
\[\newcommand{\Bold}[1]{\mathbf{#1}}\varepsilon_{0} = \frac{\partial}{\partial t }\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\varepsilon_{1} = \left( \frac{1}{2} \, x^{2} - \frac{1}{2} \, y^{2} - \frac{1}{2} \, {z}^{2} + \frac{1}{2} \right) \frac{\partial}{\partial x } + \left( x y + {z} \right) \frac{\partial}{\partial y } + \left( x {z} - y \right) \frac{\partial}{\partial {z} }\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\varepsilon_{2} = \left( x y - {z} \right) \frac{\partial}{\partial x } + \left( -\frac{1}{2} \, x^{2} + \frac{1}{2} \, y^{2} - \frac{1}{2} \, {z}^{2} + \frac{1}{2} \right) \frac{\partial}{\partial y } + \left( y {z} + x \right) \frac{\partial}{\partial {z} }\]
\[\newcommand{\Bold}[1]{\mathbf{#1}}\varepsilon_{3} = \left( x {z} + y \right) \frac{\partial}{\partial x } + \left( y {z} - x \right) \frac{\partial}{\partial y } + \left( -\frac{1}{2} \, x^{2} - \frac{1}{2} \, y^{2} + \frac{1}{2} \, {z}^{2} + \frac{1}{2} \right) \frac{\partial}{\partial {z} }\]

To ensure evaluations in this particular frame, we must communicate the change-of-frame formula to Sage:

In [63]:
P = U.automorphism_field()
for i in M.irange():
    for j in M.irange():
        P[j,i] = E_U[i][j]
In [64]:
U.set_change_of_frame(stereoN.frame(), E_U, P)

The subset $U$ differs from $M$ only by a one dimensional slit, and is therefore dense in $M$. As we know that $\left(\varepsilon_{0},\varepsilon_{1},\varepsilon_{2},\varepsilon_{3}\right)$ defines a \emph{global} frame, its components can be easily and uniquely extended to all of $M$. For this, we use the method add_comp_by_continuation:

In [65]:
for i in M.irange():
    E[i].add_comp_by_continuation(stereoS.frame(), W)

And again, we declare the change of frame:

In [66]:
P = V.automorphism_field()
for i in M.irange():
    for j in M.irange():
        P[j,i] = E.restrict(V)[i][j]
In [67]:
V.set_change_of_frame(stereoS.frame(), E.restrict(V), P)

In order to reduce the computation time, we examine the $\hat{A}$-class on the open subset $U \subset M$ first. The final result can be obtained by continuation. For this purpose we define the tangent bundle over $U$:

In [68]:
TU = U.tangent_bundle(); print(TU)
Tangent bundle TU over the Open subset U of the 4-dimensional Lorentzian manifold M

Notice that the $\hat{A}$-class is already predefined:

In [69]:
A = TU.characteristic_class('AHat'); A

Its holomorphic function is given by:

In [70]:
\[\newcommand{\Bold}[1]{\mathbf{#1}}\frac{\sqrt{x}}{2 \, \sinh\left(\frac{1}{2} \, \sqrt{x}\right)}\]

We are ready to define the Berger metric, at least on the subset $U$:

In [71]:
a = function('a')
In [72]:
g = U.metric()
g.add_comp(E_U)[0, 0] = - 1
g.add_comp(E_U)[1, 1] = a(t)^2
g.add_comp(E_U)[2, 2] = 1
g.add_comp(E_U)[3, 3] = 1
\[\newcommand{\Bold}[1]{\mathbf{#1}}g = -\varepsilon^{0}\otimes \varepsilon^{0} + a\left(t\right)^{2} \varepsilon^{1}\otimes \varepsilon^{1} +\varepsilon^{2}\otimes \varepsilon^{2} +\varepsilon^{3}\otimes \varepsilon^{3}\]

The corresponding connection is automatically computed by Sage:

In [73]:
nab = g.connection(); nab

Finally, we perform the computation of the $\hat{A}$-form with respect to this connection $\nabla_g$:

In [74]:
A_form = A.get_form(nab)  # long time
A_form.display_expansion(E_U, stereoN)
\[\newcommand{\Bold}[1]{\mathbf{#1}}\hat{A}(TU, \nabla_{g}) = 1 + \left( \frac{4 \, {\left(a\left(t\right)^{3} - a\left(t\right)\right)} \frac{\partial\,a}{\partial t} - \frac{\partial\,a}{\partial t} \frac{\partial^2\,a}{\partial t ^ 2}}{24 \, \pi^{2}} \right) \varepsilon^{0}\wedge \varepsilon^{1}\wedge \varepsilon^{2}\wedge \varepsilon^{3}\]

To attain $\hat{A}(TM, \nabla_g)$ in all given coordinates, we still have to extend the result onto $M$. With respect to the global frame $(\varepsilon_0, \varepsilon_1,\varepsilon_2,\varepsilon_3)$, the form $\hat{A}(TU, \nabla_g)$ only depends on the global coordinate $t$. This makes the continuation trivial. Besides, for the most part, one is interested in characteristic forms outside a set of measure zero. Hence, we terminate our calculation at this point.

In [ ]: