The hardware and bandwidth for this mirror is donated by dogado GmbH, the Webhosting and Full Service-Cloud Provider. Check out our Wordpress Tutorial.
If you wish to report a bug, or if you are interested in having us mirror your free-software or open-source project, please feel free to contact us at mirror[@]dogado.de.

The hodge() function in the stokes package

Robin K. S. Hankin

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 \(\beta\), function hodge() returns its Hodge dual \(\star\beta\). Formally, if \(V={\mathbb R}^n\), and \(\Lambda^k(V)\) is the space of alternating linear maps from \(V^k\) to \({\mathbb R}\), then \(\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 \(\beta\in\Lambda^k(V)\) we define \(\star\beta\) to be the (unique) \(n-k\)-form satisfying the fundamental relation:

\[ \alpha\wedge\left(\star\beta\right)=\left\langle\alpha,\beta\right\rangle\omega,\]

for every \(\alpha\in\Lambda^k(V)\). Here \(\omega=e_1\wedge e_2\wedge\cdots\wedge e_n\) is the unit \(n\)-vector of \(\Lambda^n(V)\). Taking determinants of this relation shows the following. If we use multi-index notation so \(e_I=e_{i_1}\wedge\cdots\wedge e_{i_k}\) with \(I=\left\lbrace i_1,\cdots,i_k\right\rbrace\), then

\[\star e_I=(-1)^{\sigma(I)}e_J\]

where \(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 \(I\), and \((-1)^{\sigma(I)}\) is the sign of the permutation \(\sigma(I)=i_1\cdots i_kj_1\cdots j_{n-k}\). We extend to the whole of \(\Lambda^k(V)\) using linearity. Package idiom for calculating the Hodge dual is straightforward, being simply hodge().

The Hodge dual on basis elements of \(\Lambda^k(V)\)

We start by demonstrating hodge() on basis elements of \(\Lambda^k(V)\). Recall that if \(\left\lbrace e_1,\ldots,e_n\right\rbrace\) is a basis of vector space \(V=\mathbb{R}^n\), then \(\left\lbrace\omega_1,\ldots,\omega_k\right\rbrace\) is a basis of \(\Lambda^1(V)\), where \(\omega_i(e_j)=\delta_{ij}\). A basis of \(\Lambda^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 \(\omega_2\wedge\omega_6\wedge\omega_7\). f \(V=\mathbb{R}^9\), what is \(\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 \(\star a\) has index entries 1-9 except \(2,6,7\) (from \(a\)). The (numerical) sign is negative because the permution 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 \(i_k\) 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=\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
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 \(\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 TRUE 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 \(\alpha=\alpha_1\wedge\cdots\wedge\alpha_k\) and \(\beta=\beta_1\wedge\cdots\wedge\beta_k\) as

\[\left\langle\alpha,\beta\right\rangle= \det\left(\left\langle\alpha_i,\beta_j\right\rangle_{i,j}\right)\]

where \(\left\langle\alpha_i,\beta_j\right\rangle=\pm\delta_{ij}\) is an inner product on \(\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\).

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.

These binaries (installable software) and packages are in development.
They may not be fully stable and should be used with caution. We make no claims about them.
Health stats visible at Monitor.