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); this function monograph discusses hodge(). Given a kk-form β\beta, function hodge() returns its Hodge dual ⋆β\star\beta. Formally, if V=ℝnV={\mathbb R}^n, and Λk(V)\Lambda^k(V) is the space of alternating linear maps from VkV^k to ℝ{\mathbb R}, then ⋆:Λk(V)→Λn−k(V)\star\colon\Lambda^k(V)\longrightarrow\Lambda^{n-k}(V). To define the Hodge dual, we need an inner product ⟨⋅,⋅⟩\left\langle\cdot,\cdot\right\rangle [function kinner() in the package] and, given this and β∈Λk(V)\beta\in\Lambda^k(V) we define ⋆β\star\beta to be the (unique) n−kn-k-form satisfying the fundamental relation:

α∧(⋆β)=⟨α,β⟩ω, \alpha\wedge\left(\star\beta\right)=\left\langle\alpha,\beta\right\rangle\omega,

for every α∈Λk(V)\alpha\in\Lambda^k(V). Here ω=e1∧e2∧⋯∧en\omega=e_1\wedge e_2\wedge\cdots\wedge e_n is the unit nn-vector of Λn(V)\Lambda^n(V). Taking determinants of this relation shows the following. If we use multi-index notation so eI=ei1∧⋯∧eike_I=e_{i_1}\wedge\cdots\wedge e_{i_k} with I={i1,⋯,ik}I=\left\lbrace i_1,\cdots,i_k\right\rbrace, then

⋆eI=(−1)σ(I)eJ\star e_I=(-1)^{\sigma(I)}e_J

where J={ji,…,jk}=[n]\{i1,…,ik}J=\left\lbrace j_i,\ldots,j_k\right\rbrace=[n]\setminus\left\lbrace i_1,\ldots,i_k\right\rbrace is the complement of II, and (−1)σ(I)(-1)^{\sigma(I)} is the sign of the permutation σ(I)=i1⋯ikj1⋯jn−k\sigma(I)=i_1\cdots i_kj_1\cdots j_{n-k}. We extend to the whole of Λk(V)\Lambda^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)\Lambda^k(V)

We start by demonstrating hodge() on basis elements of Λk(V)\Lambda^k(V). Recall that if {e1,…,en}\left\lbrace e_1,\ldots,e_n\right\rbrace is a basis of vector space V=ℝnV=\mathbb{R}^n, then {ω1,…,ωk}\left\lbrace\omega_1,\ldots,\omega_k\right\rbrace is a basis of Λ1(V)\Lambda^1(V), where ωi(ej)=δij\omega_i(e_j)=\delta_{ij}. A basis of Λk(V)\Lambda^k(V) is given by the set

⋃1≤i1<⋯<ik≤n⋀j=1kωij={ωi1∧⋯∧ωik|1≤i1<⋯<ik≤n}. \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\omega_2\wedge\omega_6\wedge\omega_7. If V=ℝ9V=\mathbb{R}^9, what is ⋆ω2∧ω6∧ω7\star\omega_2\wedge\omega_6\wedge\omega_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\star a has index entries 1-9 except 2,6,72,6,7 (from aa). 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 iki_k of the first argument is less than nn; the default value is indeed nn. In the example above, this is 77:

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=ℝ7V=\mathbb{R}^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
## 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 kk-forms:

## [1] TRUE

Or even

## [1] TRUE

Small-dimensional vector spaces

We can work in three dimensions in which case we have three linearly independent 11-forms: dxdx, dydy, and dzdz. 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 𝐮×𝐯=det⁡(ijku1u2u3v1v2v3)\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 𝐮×𝐯=⋆(𝐮∧𝐯)\mathbf{u}\times\mathbf{v}=\star{\left(\mathbf{u}\wedge\mathbf{v}\right)}. 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 FALSE  TRUE

Above, note the use of the hodge operator to define triple vector cross products. For example we have 𝐮×(𝐯×𝐰)=⋆(𝐮∧⋆(𝐯∧𝐰))\mathbf{u}\times\left(\mathbf{v}\times\mathbf{w}\right)= \star\left(\mathbf{u}\wedge\star\left(\mathbf{v}\wedge\mathbf{w}\right)\right).

Non positive-definite metrics

The inner product ⟨α,β⟩\left\langle\alpha,\beta\right\rangle above may be generalized by defining it on decomposable vectors α=α1∧⋯∧αk\alpha=\alpha_1\wedge\cdots\wedge\alpha_k and β=β1∧⋯∧βk\beta=\beta_1\wedge\cdots\wedge\beta_k as

⟨α,β⟩=det⁡(⟨αi,βj⟩i,j)\left\langle\alpha,\beta\right\rangle= \det\left(\left\langle\alpha_i,\beta_j\right\rangle_{i,j}\right)

where ⟨αi,βj⟩=±δij\left\langle\alpha_i,\beta_j\right\rangle=\pm\delta_{ij} is an inner product on Λ1(V)\Lambda^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-1,1,1,1.

The standard print method is not particularly suitable for working with the Minkowski metric:

options(kform_symbolic_print = NULL)  # default print method
(o <- kform_general(4,2,1:6))
## An alternating linear map from V^2 to R with V=R^4:
##          val
##  3 4  =    6
##  2 4  =    5
##  1 4  =    4
##  2 3  =    3
##  1 3  =    2
##  1 2  =    1

Above we see an unhelpful representation. To work with 22-forms in relativistic physics, it is often preferable to use bespoke print method txyz:

options(kform_symbolic_print = "txyz")
o
## An alternating linear map from V^2 to R with V=R^4:
##  +6 dy^dz +5 dx^dz +4 dt^dz +3 dx^dy +2 dt^dy + dt^dx

Specifying the Minkowski metric

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

## 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.