The calculation of reciprocal basis for non-orthogonal basis in GAlgebra deviate from standard definition and caused a bug when calculating inverse metric.

By definition,

$$e_{i} \cdot e^{j}=\delta_{i}^{j}$$

where $\delta_{k}^{j}$ is the kronecker delta.

But GAlgebra, intentionally makes it:

$$e_{i} \cdot e^{j}={E_n}^{2}\delta_{i}^{j}$$

where

$$ E_{n} = e_{1}\wedge \dots \wedge e_{n} $$

In the docstring of Ga.build_reciprocal_basis(), it explicitly states:

For non-orthogonal basis $e^{j}$ is not normalized and must be divided by ${E_n}^{2}$ (self.e_sq) in any relevant calculations.

The implementation of Ga.build_reciprocal_basis() claims to use formula 4.94 from GA4P by Doran and Lasenby:

$$e^{i}=(-1)^{i-1}\left(e_{1} \wedge \ldots \wedge \breve{e}_{i} \wedge \ldots \wedge e_{n}\right) E_{n}^{-1}$$

But it looks like this:

# Take all (n-1)-blades
duals = list(self.blades_lst[-(self.n + 1):-1])
# After reverse, the j-th of them is exactly e_{1}^...e_{j-1}^e_{j+1}^...^e_{n}
duals.reverse()

sgn = 1
self.r_basis = []
for dual in duals:
    dual_base_rep = self.blade_to_base_rep(dual)
    # {E_n}^{-1} = \frac{E_n}{{E_n}^{2}}
    # r_basis_j = sgn * duals[j] * E_n so it's not normalized, missing a factor of {E_n}^{-2}
    r_basis_j = collect(expand(self.base_to_blade_rep(self.mul(sgn * dual_base_rep, self.e_obj))), self.blades_lst)
    self.r_basis.append(r_basis_j)
    # sgn = (-1)**{j-1}
    sgn = -sgn

Let's verify this logic step by step using Kerr-Debney Metric:


In [1]:
from sympy import *

In [2]:
import sys
from galgebra.printer import Format, GaLatexPrinter

Format()
from galgebra.ga import Ga
from galgebra.mv import ONE, ZERO, HALF

W = symbols("W")
c = symbols("c")

g4coords = (u, x, y, z) = symbols("u x y z")
g = [
    [0, 0, -exp(-z), 0],
    [0, HALF * u ** 2 * exp(4 * z), 0, 0],
    [-exp(-z), 0, 12 * exp(-2 * z), u * exp(-z)],
    [0, 0, u * exp(-z), HALF * u ** 2],
]

g4 = Ga("e_u e_x e_y e_z", g=g, coords=g4coords, norm=False)  # Create g4
(e_u, e_x, e_y, e_z) = g4.mv()

In [3]:
from IPython.display import display, Math

def show(x):
    display(Math(GaLatexPrinter.latex(x)))

In [29]:
g4.g


' \\left [ \\begin{array}{cccc} 0 & 0 & - e^{- z} & 0  \\\\ 0 & \\frac{u^{2} e^{4 z}}{2} & 0 & 0  \\\\ - e^{- z} & 0 & 12 e^{- 2 z} & u e^{- z}  \\\\ 0 & 0 & u e^{- z} & \\frac{u^{2}}{2}  \\end{array}\\right ] '
Out[29]:
$$\left[\begin{matrix}0 & 0 & - e^{- z} & 0\\0 & \frac{u^{2} e^{4 z}}{2} & 0 & 0\\- e^{- z} & 0 & 12 e^{- 2 z} & u e^{- z}\\0 & 0 & u e^{- z} & \frac{u^{2}}{2}\end{matrix}\right]$$

In [4]:
g4.basis


Out[4]:
$$\left [ e_{u}, \quad e_{x}, \quad e_{y}, \quad e_{z}\right ]$$

In [5]:
for blade in g4.blades_lst:
    show(blade)


$\displaystyle \boldsymbol{e}_{u}$
$\displaystyle \boldsymbol{e}_{x}$
$\displaystyle \boldsymbol{e}_{y}$
$\displaystyle \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{y}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}$
$\displaystyle \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$

Take all (n-1)-blades:


In [6]:
duals = list(g4.blades_lst[-(g4.n + 1):-1])

In [7]:
for dual in duals:
    show(dual)


$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$

After reverse, the i-th of them is exactly $e_{1} \wedge \ldots \wedge \breve{e}_{i} \wedge \ldots \wedge e_{n}$


In [8]:
duals.reverse()

In [9]:
for dual in duals:
    show(dual)


$\displaystyle \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}$

Turn them into base reprsentation:


In [10]:
for dual in duals:
    show(g4.blade_to_base_rep(dual))


$\displaystyle - u e^{- z} \boldsymbol{e}_{x} + \boldsymbol{e}_{x}\boldsymbol{e}_{y}\boldsymbol{e}_{z}$
$\displaystyle - u e^{- z} \boldsymbol{e}_{u} + \boldsymbol{e}_{u}\boldsymbol{e}_{y}\boldsymbol{e}_{z} + e^{- z} \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\boldsymbol{e}_{x}\boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\boldsymbol{e}_{x}\boldsymbol{e}_{y} - e^{- z} \boldsymbol{e}_{x}$

In [11]:
show(g4.e_obj)


$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$

In [12]:
for i,dual in enumerate(duals):
    show(S(-1)**(i-1)*g4.base_to_blade_rep(g4.blade_to_base_rep(dual) * g4.e_obj))


$\displaystyle - (- u e^{- z} \boldsymbol{e}_{x} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} + \boldsymbol{e}_{x}\boldsymbol{e}_{y}\boldsymbol{e}_{z} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z})$
$\displaystyle - u e^{- z} \boldsymbol{e}_{u} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} + \boldsymbol{e}_{u}\boldsymbol{e}_{y}\boldsymbol{e}_{z} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} + e^{- z} \boldsymbol{e}_{z} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle - \boldsymbol{e}_{u}\boldsymbol{e}_{x}\boldsymbol{e}_{z} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\boldsymbol{e}_{x}\boldsymbol{e}_{y} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} - e^{- z} \boldsymbol{e}_{x} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$

In [13]:
for i,dual in enumerate(duals):
    show(collect(expand(S(-1)**(i-1)*g4.base_to_blade_rep(g4.blade_to_base_rep(dual) * g4.e_obj)),g4.blades_lst))


$\displaystyle u e^{- z} \boldsymbol{e}_{x} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} - \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} \boldsymbol{e}_{x}\boldsymbol{e}_{y}\boldsymbol{e}_{z}$
$\displaystyle - u e^{- z} \boldsymbol{e}_{u} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} + \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} \boldsymbol{e}_{u}\boldsymbol{e}_{y}\boldsymbol{e}_{z} + e^{- z} \boldsymbol{e}_{z} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle - \boldsymbol{e}_{u}\boldsymbol{e}_{x}\boldsymbol{e}_{z} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$
$\displaystyle \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} \boldsymbol{e}_{u}\boldsymbol{e}_{x}\boldsymbol{e}_{y} - e^{- z} \boldsymbol{e}_{x} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z}$

So it turns out the formula used is actually

$$e^{i}=(-1)^{i-1}\left(e_{1} \wedge \ldots \wedge \breve{e}_{i} \wedge \ldots \wedge e_{n}\right) E_{n}$$

it's different from

$$e^{i}=(-1)^{i-1}\left(e_{1} \wedge \ldots \wedge \breve{e}_{i} \wedge \ldots \wedge e_{n}\right) E_{n}^{-1}$$

by a factor of $E_{n}^{-2}$ which is a scalar.

$E_n$ is


In [14]:
g4.e


Out[14]:
\begin{equation*} \boldsymbol{e}_{u}\wedge \boldsymbol{e}_{x}\wedge \boldsymbol{e}_{y}\wedge \boldsymbol{e}_{z} \end{equation*}

$E_n^2$ is


In [15]:
g4.e * g4.e


Out[15]:
\begin{equation*} - \frac{u^{4} e^{2 z}}{4} \end{equation*}

In [16]:
g4.e_sq


Out[16]:
$$- \frac{u^{4} e^{2 z}}{4}$$

$E_n^{-2}$ is


In [17]:
1 / g4.e_sq


Out[17]:
$$- \frac{4 e^{- 2 z}}{u^{4}}$$

It's clear that $$e_{i} \cdot e^{j}={E_n}^{2}\delta_{i}^{j}$$


In [18]:
for i,base in enumerate(g4.basis):
    show(g4.dot(g4.basis[i], g4.r_basis[i]))


$\displaystyle - \frac{u^{4} e^{2 z}}{4}$
$\displaystyle - \frac{u^{4} e^{2 z}}{4}$
$\displaystyle - \frac{u^{4} e^{2 z}}{4}$
$\displaystyle - \frac{u^{4} e^{2 z}}{4}$

And g_inv is implemented like this:

# Calculate inverse of metric tensor, g^{ij}

        for i in self.n_range:
            rx_i = self.r_symbols[i]
            for j in self.n_range:
                rx_j = self.r_symbols[j]
                if j >= i:
                    g_inv[i, j] = self.dot(self.r_basis_dict[rx_i], self.r_basis_dict[rx_j])
                    if not self.is_ortho:
                        g_inv[i, j] /= self.e_sq
                else:
                    g_inv[i, j] = g_inv[j, i]

which divide the dot product by $E_n^2$ and it's not enough and caused:

$$g^{il} g_{lk} \neq \delta^i_k $$

In [21]:
g4.g*g4.g_inv


' \\left [ \\begin{array}{cccc} - \\frac{u^{4} e^{2 z}}{4} & 0 & 0 & 0  \\\\ 0 & - \\frac{u^{4} e^{2 z}}{4} & 0 & 0  \\\\ 0 & 0 & - \\frac{u^{4} e^{2 z}}{4} & 0  \\\\ 0 & 0 & 0 & - \\frac{u^{4} e^{2 z}}{4}  \\end{array}\\right ] '
Out[21]:
$$\left[\begin{matrix}- \frac{u^{4} e^{2 z}}{4} & 0 & 0 & 0\\0 & - \frac{u^{4} e^{2 z}}{4} & 0 & 0\\0 & 0 & - \frac{u^{4} e^{2 z}}{4} & 0\\0 & 0 & 0 & - \frac{u^{4} e^{2 z}}{4}\end{matrix}\right]$$

The reason is that g_inv can be seen as:


In [24]:
g_inv = eye(g4.n)
g4.dot_mode = '|'
for i in g4.n_range:
    rx_i = g4.r_symbols[i]
    for j in g4.n_range:
        rx_j = g4.r_symbols[j]
        if j >= i:
            if g4.is_ortho:
                g_inv[i, j] = g4.dot(g4.r_basis_dict[rx_i], g4.r_basis_dict[rx_j])
            else:
                # NOTE: both reciprocal basis vectors should be devided by E_n^2
                g_inv[i, j] = g4.dot(g4.r_basis_dict[rx_i] / g4.e_sq, g4.r_basis_dict[rx_j] / g4.e_sq)
        else:
            g_inv[i, j] = g_inv[j, i]

Now g_inv is correct and $$g^{il} g_{lk} = \delta^i_k $$


In [28]:
g4.g*g_inv


' \\left [ \\begin{array}{cccc} 1 & 0 & 0 & 0  \\\\ 0 & 1 & 0 & 0  \\\\ 0 & 0 & 1 & 0  \\\\ 0 & 0 & 0 & 1  \\end{array}\\right ] '
Out[28]:
$$\left[\begin{matrix}1 & 0 & 0 & 0\\0 & 1 & 0 & 0\\0 & 0 & 1 & 0\\0 & 0 & 0 & 1\end{matrix}\right]$$

In [ ]: