---
title: "Quaternionic arithmetic with Clifford algebra"
author: "Robin K. S. Hankin"
output: html_vignette
bibliography: clifford.bib
link-citations: true
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
@hankin2025_clifford_rmd.  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
