# 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 :
version()

Out:
'SageMath version 9.4.rc1, Release Date: 2021-08-08'

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

In :
%display latex


## Mixed Differential Forms¶

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

In :
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 :
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 :
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 :
print(Omega.category())

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 :
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 :
A.set_expr(x^2)
A[:] = y, 2*x
A[0,1] = 4*x^3

In :
A.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}A = {A}_{0} + {A}_{1} + {A}_{2}$
In :
A.display_expansion()

Out:
$\newcommand{\Bold}{\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 :
A.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}\begin{array}{llcl} {A}_{0}:& \mathbb{R}^2 & \longrightarrow & \mathbb{R} \\ & \left(x, y\right) & \longmapsto & x^{2} \end{array}$
In :
A.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}{A}_{1} = y \mathrm{d} x + 2 \, x \mathrm{d} y$
In :
A.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}{A}_{2} = 4 \, x^{3} \mathrm{d} x\wedge \mathrm{d} y$

The category framework of Sage captures the entire setup:

In :
all([A in Omega0,
A in Omega,
A in Omega1,
A in Omega,
A in Omega2,
A in Omega])

Out:
$\newcommand{\Bold}{\mathbf{#1}}\mathrm{True}$

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

In :
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 :
B = M.mixed_form([f,eta,0], name='B'); B.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}B = f + \eta + 0$
In :
B.display_expansion()

Out:
$\newcommand{\Bold}{\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 :
B == f

Out:
$\newcommand{\Bold}{\mathbf{#1}}\mathrm{True}$
In :
B is f

Out:
$\newcommand{\Bold}{\mathbf{#1}}\mathrm{False}$

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

In :
B = xi

In :
B.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}B = f + \eta + \xi$

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

In :
B.set_immutable()

In :
try:
B = 0
except ValueError as ve:
print(ve)

the components of an immutable element cannot be changed


The multiplication is executed degree wise:

In :
all((A * B)[k] == sum(A[j].wedge(B[k - j])
for j in range(k + 1))
for k in Omega.irange())

Out:
$\newcommand{\Bold}{\mathbf{#1}}\mathrm{True}$
In :
(A * B).display_expansion()

Out:
$\newcommand{\Bold}{\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 :
(B * A).display_expansion()

Out:
$\newcommand{\Bold}{\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 :
dA = A.exterior_derivative(); dA.display_expansion()

Out:
$\newcommand{\Bold}{\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 :
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 :
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 :
nab = E.bundle_connection('nabla^E', latex_name=r'\nabla^E')
A = function('A')


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

In :
omega = M.one_form(name='omega', latex_name=r'\omega')
omega = I*A(t)
omega.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}\omega = i \, A\left(t\right) \mathrm{d} x$

Let us put this into the connection:

In :
nab[0,0] = omega


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

In :
nab.set_immutable()


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

In :
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 :
ch_form = ch.get_form(nab)
ch_form.display_expansion()

Out:
$\newcommand{\Bold}{\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 :
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 :
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))
cart_to_comp.display()

Out:
$\newcommand{\Bold}{\mathbf{#1}}\left\{\begin{array}{lcl} {z} & = & x + i \, y \\ {\bar{z}} & = & x - i \, y \end{array}\right.$
In :
comp_to_cart = cart_to_comp.inverse()
comp_to_cart.display()

Out:
$\newcommand{\Bold}{\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 :
E = M.vector_bundle(1, 'gamma_1',
latex_name=r'\gamma_1',
field='complex')


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

In :
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 :
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
nab.set_immutable()


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

In :
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 :
c_form = c.get_form(nab)
c_form.display_expansion(c_comp.frame(), chart=c_comp)

Out:
$\newcommand{\Bold}{\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 :
integrate(integrate(c_form[[1,2]].expr(c_cart), x, -infinity, infinity).full_simplify(), y, -infinity, infinity)

Out:
$\newcommand{\Bold}{\mathbf{#1}}1$

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 :
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)),
intersection_name='W',
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 :
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 :
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 :
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
h.display()

Out:
$\newcommand{\Bold}{\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 :
iota = M.diff_map(E, {(stereoN, cart):
[2*x/(1+x^2+y^2), 2*y/(1+x^2+y^2),
(1-x^2-y^2)/(1+x^2+y^2)],
(stereoS, cart):
[2*xp/(1+xp^2+yp^2), 2*yp/(1+xp^2+yp^2),
(xp^2+yp^2-1)/(1+xp^2+yp^2)]},
name='iota', latex_name=r'\iota')
iota.display()

Out:
$\newcommand{\Bold}{\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 :
g = M.metric()
g.set(iota.pullback(h))
g[1,1].factor(); g[2,2].factor() # simplifications
g.display()

Out:
$\newcommand{\Bold}{\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 :
nab = g.connection()
nab.set_immutable()


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 :
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 :
for i in range(TM.rank()):
for j in range(TM.rank()):
show(cmatrix_U[i][j].display())

$\newcommand{\Bold}{\mathbf{#1}}\Omega^1_{\ \, 1} = 0$
$\newcommand{\Bold}{\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}{\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}{\mathbf{#1}}\Omega^2_{\ \, 2} = 0$
In :
for i in range(TM.rank()):
for j in range(TM.rank()):
show(cmatrix_V[i][j].display())

$\newcommand{\Bold}{\mathbf{#1}}\Omega^1_{\ \, 1} = 0$
$\newcommand{\Bold}{\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}{\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}{\mathbf{#1}}\Omega^2_{\ \, 2} = 0$

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

In :
cmatrices = {eU: cmatrix_U, eV: cmatrix_V}
e_class_form = e_class.get_form(nab, cmatrices)
e_class_form.display_expansion()

Out:
$\newcommand{\Bold}{\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 :
integrate(integrate(e_class_form[[1,2]].expr(), x, -infinity, infinity).simplify_full(), y, -infinity, infinity)

Out:
$\newcommand{\Bold}{\mathbf{#1}}2$

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 :
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 :
U = M.open_subset('U'); V = M.open_subset('V')
M.declare_union(U,V)


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

In :
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),
y/(x^2+y^2+z^2),
z/(x^2+y^2+z^2)),
intersection_name='W',
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()
N_to_S.display()

Out:
$\newcommand{\Bold}{\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 :
E = M.vector_frame('E', latex_symbol=r'\varepsilon')
E_U = E.restrict(U); E_U

Out:
$\newcommand{\Bold}{\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 :
E_U[:] = [1,0,0,0]
E_U[:] = [0, (x^2-y^2-z^2+1)/2, x*y+z, x*z-y]
E_U[:] = [0, x*y-z, (1-x^2+y^2-z^2)/2, x+y*z]
E_U[:] = [0, x*z+y, y*z-x, (1-x^2-y^2+z^2)/2]

In :
for i in M.irange():
show(E_U[i].display())

$\newcommand{\Bold}{\mathbf{#1}}\varepsilon_{0} = \frac{\partial}{\partial t }$
$\newcommand{\Bold}{\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}{\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}{\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 :
P = U.automorphism_field()
for i in M.irange():
for j in M.irange():
P[j,i] = E_U[i][j]

In :
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 :
for i in M.irange():


And again, we declare the change of frame:

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

In :
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 :
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 :
A = TU.characteristic_class('AHat'); A

Out:
$\newcommand{\Bold}{\mathbf{#1}}\hat{A}(TU)$

Its holomorphic function is given by:

In :
A.function()

Out:
$\newcommand{\Bold}{\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 :
a = function('a')

In :
g = U.metric()
g.display(E_U)

Out:
$\newcommand{\Bold}{\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 :
nab = g.connection(); nab
nab.set_immutable()


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

In :
A_form = A.get_form(nab)  # long time
A_form.display_expansion(E_U, stereoN)

Out:
$\newcommand{\Bold}{\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 [ ]: