The hodge() function in the stokes package

hodge
function (K, n = dovs(K), g, lose = TRUE) 
{
    if (missing(g)) {
        g <- rep(1, n)
    }
    if (is.empty(K)) {
        if (missing(n)) {
            stop("'K' is zero but no value of 'n' is supplied")
        }
        else {
            return(kform(spray(matrix(1, 0, n - arity(K)), 1)))
        }
    }
    else if (is.volume(K, n)) {
        return(scalar(coeffs(K), lose = lose))
    }
    else if (is.scalar(K)) {
        if (missing(n)) {
            stop("'K' is scalar but no value of 'n' is supplied")
        }
        else {
            return(volume(n) * coeffs(K))
        }
    }
    stopifnot(n >= dovs(K))
    f1 <- function(o) {
        seq_len(n)[!seq_len(n) %in% o]
    }
    f2 <- function(x) {
        permutations::sgn(permutations::as.word(x))
    }
    f3 <- function(v) {
        prod(g[v])
    }
    iK <- index(K)
    jj <- apply(iK, 1, f1)
    if (is.matrix(jj)) {
        newindex <- t(jj)
    }
    else {
        newindex <- as.matrix(jj)
    }
    x_coeffs <- elements(coeffs(K))
    x_metric <- apply(iK, 1, f3)
    x_sign <- apply(cbind(iK, newindex), 1, f2)
    as.kform(newindex, x_metric * x_coeffs * x_sign)
}

To cite the stokes package in publications, please use Hankin (2022). Given a k-form β, function hodge() returns its Hodge dual β. Formally, if V = ℝn, and Λk(V) is the space of alternating linear maps from Vk to , then ⋆: Λk(V) → Λn − k(V). To define the Hodge dual, we need an inner product ⟨⋅, ⋅⟩ [function kinner() in the package] and, given this and β ∈ Λk(V) we define β to be the (unique) n − k-form satisfying the fundamental relation:

α ∧ (⋆β) = ⟨α, βω,

for every α ∈ Λk(V). Here ω = e1 ∧ e2 ∧ ⋯ ∧ en is the unit n-vector of Λn(V). Taking determinants of this relation shows the following. If we use multi-index notation so eI = ei1 ∧ ⋯ ∧ eik with I = {i1, ⋯, ik}, then

eI = (−1)σ(I)eJ

where J = {ji, …, jk} = [n] \ {i1, …, ik} is the complement of I, and (−1)σ(I) is the sign of the permutation σ(I) = i1ikj1jn − k. We extend to the whole of Λk(V) using linearity. Package idiom for calculating the Hodge dual is straightforward, being simply hodge().

The Hodge dual on basis elements of Λk(V)

We start by demonstrating hodge() on basis elements of Λk(V). Recall that if {e1, …, en} is a basis of vector space V = ℝn, then {ω1, …, ωk} is a basis of Λ1(V), where ωi(ej) = δij. A basis of Λk(V) is given by the set

$$ \bigcup_{1\leqslant i_1 < \cdots < i_k\leqslant n} \bigwedge_{j=1}^k\omega_{i_j} = \left\lbrace \left.\omega_{i_1}\wedge\cdots\wedge\omega_{i_k} \right|1\leqslant i_1 < \cdots < i_k\leqslant n \right\rbrace. $$

This means that basis elements are things like ω2 ∧ ω6 ∧ ω7. If V = ℝ9, what is ω2 ∧ ω6 ∧ ω7?

(a <- d(2) ^ d(6) ^ d(7))
## An alternating linear map from V^3 to R with V=R^7:
##            val
##  2 6 7  =    1
hodge(a,9)
## An alternating linear map from V^6 to R with V=R^9:
##                  val
##  1 3 4 5 8 9  =   -1

See how a has index entries 1-9 except 2, 6, 7 (from a). The (numerical) sign is negative because the permutation has negative (permutational) sign. We can verify this using the permutations package:

p <- c(2,6,7,  1,3,4,5,8,9)
(pw <- as.word(p))
## [1] (1264)(375)
## [coerced from word form]
print_word(pw)
##     1 2 3 4 5 6 7 8 9
## [1] 2 6 7 1 3 4 5 . .
sgn(pw)
## [1] -1

Above we see the sign of the permutation is negative. More succinct idiom would be

hodge(d(c(2,6,7)),9)
## An alternating linear map from V^6 to R with V=R^9:
##                  val
##  1 3 4 5 8 9  =   -1

The second argument to hodge() is needed if the largest index ik of the first argument is less than n; the default value is indeed n. In the example above, this is 7:

hodge(d(c(2,6,7)))
## An alternating linear map from V^4 to R with V=R^5:
##              val
##  1 3 4 5  =   -1

Above we see the result if V = ℝ7.

More complicated examples

The hodge operator is linear and it is interesting to verify this.

(o <- rform())
## An alternating linear map from V^3 to R with V=R^7:
##            val
##  2 6 7  =    6
##  2 5 7  =    5
##  5 6 7  =   -9
##  1 3 7  =    4
##  1 5 7  =    7
##  2 3 5  =   -3
##  1 5 6  =   -8
##  1 2 7  =    2
##  1 4 6  =    1
hodge(o)
## An alternating linear map from V^4 to R with V=R^7:
##              val
##  2 3 5 7  =   -1
##  3 4 5 6  =    2
##  2 3 4 7  =   -8
##  1 4 6 7  =   -3
##  2 3 4 6  =   -7
##  2 4 5 6  =   -4
##  1 2 3 4  =   -9
##  1 3 4 6  =    5
##  1 3 4 5  =   -6

We verify that the fundamental relation holds by direct inspection:

o ^ hodge(o)
## An alternating linear map from V^7 to R with V=R^7:
##                    val
##  1 2 3 4 5 6 7  =  285
kinner(o,o)*volume(dovs(o))
## An alternating linear map from V^7 to R with V=R^7:
##                    val
##  1 2 3 4 5 6 7  =  285

showing agreement (above, we use function volume() in lieu of calculating the permutation’s sign explicitly. See the volume vignette for more details). We may work more formally by defining a function that returns TRUE if the left and right hand sides match

diff <- function(a,b){a^hodge(b) ==  kinner(a,b)*volume(dovs(a))}

and call it with random k-forms:

diff(rform(),rform())
## [1] TRUE

Or even

all(replicate(10,diff(rform(),rform())))
## [1] TRUE

Small-dimensional vector spaces

We can work in three dimensions in which case we have three linearly independent 1-forms: dx, dy, and dz. To work in this system it is better to use dx print method:

options(kform_symbolic_print = "dx")
hodge(dx,3)
## An alternating linear map from V^2 to R with V=R^3:
##  + dy^dz

This is further discussed in the dovs vignette.

Vector cross product identities

The three dimensional vector cross product $\mathbf{u}\times\mathbf{v}=\det\begin{pmatrix} i & j & k \\ u_1&u_2&u_3\\ v_1&v_2&v_3 \end{pmatrix}$ is a standard part of elementary vector calculus. In the package the idiom is as follows:

vcp3
## function (u, v) 
## {
##     hodge(as.1form(u)^as.1form(v))
## }

revealing the formal definition of cross product as u × v = ⋆(u ∧ v). There are several elementary identities that are satisfied by the cross product:

$$ \begin{aligned} \mathbf{u}\times(\mathbf{v}\times\mathbf{w}) &= \mathbf{v}(\mathbf{w}\cdot\mathbf{u})-\mathbf{w}(\mathbf{u}\cdot\mathbf{v})\\ (\mathbf{u}\times\mathbf{v})\times\mathbf{w} &= \mathbf{v}(\mathbf{w}\cdot\mathbf{u})-\mathbf{u}(\mathbf{v}\cdot\mathbf{w})\\ (\mathbf{u}\times\mathbf{v})\times(\mathbf{u}\times\mathbf{w}) &= (\mathbf{u}\cdot(\mathbf{v}\times\mathbf{w}))\mathbf{u} \\ (\mathbf{u}\times\mathbf{v})\cdot(\mathbf{w}\times\mathbf{x}) &= (\mathbf{u}\cdot\mathbf{w})(\mathbf{v}\cdot\mathbf{x}) - (\mathbf{u}\cdot\mathbf{x})(\mathbf{v}\cdot\mathbf{w}) \end{aligned} $$

We may verify all four together:

u <- c(1,4,2)
v <- c(2,1,5)
w <- c(1,-3,2)
x <- c(-6,5,7)
c(
  hodge(as.1form(u) ^ vcp3(v,w))        == as.1form(v*sum(w*u) - w*sum(u*v)),
  hodge(vcp3(u,v) ^ as.1form(w))        == as.1form(v*sum(w*u) - u*sum(v*w)),
  as.1form(as.function(vcp3(v,w))(u)*u) == hodge(vcp3(u,v) ^ vcp3(u,w))     ,
  hodge(hodge(vcp3(u,v)) ^ vcp3(w,x))   == sum(u*w)*sum(v*x) - sum(u*x)*sum(v*w)
)         
## [1] TRUE TRUE TRUE TRUE

Above, note the use of the hodge operator to define triple vector cross products. For example we have u × (v × w) = ⋆(u ∧ ⋆(v ∧ w)).

Non positive-definite metrics

The inner product α, β above may be generalized by defining it on decomposable vectors α = α1 ∧ ⋯ ∧ αk and β = β1 ∧ ⋯ ∧ βk as

α, β⟩ = det (⟨αi, βji, j)

where αi, βj⟩ = ±δij is an inner product on Λ1(V) [the inner product is given by kinner()]. The resulting Hodge star operator is implemented in the package and one can specify the metric. For example, if we consider the Minkowski metric this would be −1, 1, 1, 1.

Specifying the Minkowski metric

Function hodge() takes a g argument to specify the metric:

hodge(o)
## An alternating linear map from V^2 to R with V=R^4:
##  + dy^dz -2 dx^dz +3 dt^dz +4 dx^dy -5 dt^dy +6 dt^dx
hodge(o,g=c(-1,1,1,1))
## An alternating linear map from V^2 to R with V=R^4:
##  - dy^dz +2 dx^dz +3 dt^dz -4 dx^dy -5 dt^dy +6 dt^dx
hodge(o)-hodge(o,g=c(-1,1,1,1))
## An alternating linear map from V^2 to R with V=R^4:
##  +8 dx^dy -4 dx^dz +2 dy^dz

References

Hankin, R. K. S. 2022. “Stokes’s Theorem in R.” arXiv. https://doi.org/10.48550/ARXIV.2210.17008.