--- title: "Quaternionic arithmetic with Clifford algebra" author: "Robin K. S. Hankin" output: html_vignette bibliography: clifford.bib vignette: > %\VignetteIndexEntry{Quaternionic arithmetic with Clifford algebra} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r setup, include=FALSE} knitr::opts_chunk$set(echo = TRUE) library("clifford") library("onion") ``` ```{r out.width='15%', out.extra='style="float:right; padding:10px"',echo=FALSE} knitr::include_graphics(system.file("help/figures/clifford.png", package = "clifford")) knitr::include_graphics(system.file("help/figures/onion.png", package = "onion")) ``` To cite the `clifford` package in publications please use @hankin2022_clifford. This short document shows how quaternionic arithmetic may be implemented as a special case of Clifford algebras. This is done for illustrative purposes only; to manipulate quaternions in R the `onion` package [@hankin2006_onion] is much more efficient and includes more transparent idiom. Hamilton's Broome Bridge insight: \[ \mathbf{i}^2= \mathbf{j}^2= \mathbf{k}^2= \mathbf{i}\mathbf{j}\mathbf{k}=-1 \] The BBI and associativity together imply \[ \mathbf{j}\mathbf{k}=-\mathbf{k}\mathbf{j}=\mathbf{i}\qquad \mathbf{k}\mathbf{i}=-\mathbf{i}\mathbf{k}=\mathbf{j}\qquad \mathbf{i}\mathbf{j}=-\mathbf{j}\mathbf{i}=\mathbf{k}\qquad \] and if we require a distributive algebra we get the quaternions. A general quaternion is of the form $a+\mathbf{i}b+\mathbf{j}c+\mathbf{k}d$; addition is componentwise and multiplication follows from the above. To express quaternionic multiplication using Clifford algebra we make the following identifications: \[ \mathbf{i}\leftrightarrow -e_{12}\\ \mathbf{j}\leftrightarrow -e_{13}\\ \mathbf{k}\leftrightarrow -e_{23}\\ \] Thus, for example, $\mathbf{ii}=(-e_{12})(-e_{12})=+e_{1212}=-e_{1122}=-1$ and $\mathbf{ij}=(-e_{12})(-e_{13})=+e_{1213}=-e_{1123}=-e_{23}=\mathbf{k}$. The default signature [in which $e_i^2=+1$] is fine here, but as a safety measure we can set `maxdim` to 3: ```{r} options(maxdim=3) # paranoid safety measure ``` We might wish to multiply $q_1=1+2\mathbf{i}+3\mathbf{j}+4\mathbf{k}$ by $q_2=-2+\mathbf{i}-2\mathbf{j}+\mathbf{k}$: ```{r} q1 <- +1 + 2* -e(c(1,2)) + 3*-e(c(1,3)) + 4*-e(c(2,3)) q1 q2 <- -2 + 1* -e(c(1,2)) - 2*-e(c(1,3)) + 1*-e(c(2,3)) q2 q1*q2 ``` The product would correspond to $-2+8\mathbf{i} -6\mathbf{j} -14\mathbf{k}$. Note that the "`*`" in "`q1*q2`" is a _clifford_ product. It is possible to leverage the `onion` package and coerce between `clifford` objects and quaternions (but don't actually do it, you crazy fool): ```{r clifftoquat} `clifford_to_quaternion` <- function(C){ C <- as.clifford(C) tC <- disordR::elements(terms(C)) stopifnot(all(c(tC,recursive=TRUE) <= 3)) jj <- unlist(lapply(tC,length)) stopifnot(all(jj <= 2)) # safety check stopifnot(all(jj%%2 == 0)) # safety check out <- matrix(c(const(C), -getcoeffs(C,list(c(1,2),c(1,3),c(2,3)) ))) as.quaternion(out) } ``` ```{r defineqtoc} `quaternion_to_clifford` <- function(Q){ Q <- as.numeric(Q) stopifnot(length(Q)==4) clifford(list(numeric(0),c(1,2),c(1,3),c(2,3)),c(Q[1],-Q[2:4])) } ``` We may verify that these maps behave properly by defining some random-ish quaternions and Clifford objects: ```{r defqh} q1 <- +1 + 2* -e(c(1,2)) + 3*-e(c(1,3)) + 4*-e(c(2,3)) q2 <- -2 + 1* -e(c(1,2)) - 2*-e(c(1,3)) + 1*-e(c(2,3)) H1 <- as.quaternion(c(3,-5,2,1),single=TRUE) H2 <- as.quaternion(c(1,2,-2,2),single=TRUE) ``` First, check that they are inverses of one another: ```{r verifyinverse} c( # check they are inverses of one another q1 == quaternion_to_clifford(clifford_to_quaternion(q1)), q2 == quaternion_to_clifford(clifford_to_quaternion(q2)), H1 == clifford_to_quaternion(quaternion_to_clifford(H1)), H2 == clifford_to_quaternion(quaternion_to_clifford(H2)) ) ``` Next, verify that they are homomorphisms: ```{r} c( q1*q2 == quaternion_to_clifford(clifford_to_quaternion(q1)*clifford_to_quaternion(q2)), H1*H2 == clifford_to_quaternion(quaternion_to_clifford(H1)*quaternion_to_clifford(H2)) ) ``` Note that in package idiom the asterisk, "`*`" represents either Clifford geometric product or Hamilton's quaternionic multiplication depending on its arguments. ## Alternative mapping Alternatively we might consider the even subalgebra of $\operatorname{Cl}(0,3)$ with general element $q_0 + q_1e_{23} -q_2e_{13} + q_3e_{12}$ (note change of sign for $q_2$). Thus \[ \mathbf{i}\leftrightarrow e_{23}\\ \mathbf{j}\leftrightarrow -e_{13}\\ \mathbf{k}\leftrightarrow e_{12}\\ \] A quick-and-dirty R function might be ```{r} signature(0,3) cliff2quat <- function(C){ out <- getcoeffs(C,list(numeric(0),c(2,3),c(1,3),c(1,2))) out[2] <- -out[2] as.quaternion(out,single=TRUE) } quat2cliff <- function(H){ jj <- c(as.matrix(H)) jj[2] <- -jj[2] clifford(list(numeric(0),c(2,3),c(1,3),c(1,2)),jj) } ``` Then verification is straightforward: ```{r} c( cliff2quat(quat2cliff(H1)) == H1, cliff2quat(quat2cliff(H2)) == H2, quat2cliff(cliff2quat(q1)) == q1, quat2cliff(cliff2quat(q2)) == q2, cliff2quat(q1*q2) == cliff2quat(q1) * cliff2quat(q2), quat2cliff(H1*H2) == quat2cliff(H1) * quat2cliff(H2) ) ``` ```{r,echo=FALSE} # restore default options(maxdim = NULL) signature(Inf) ``` ## References