Dual quaternions via Clifford algebra
Robin K. S. Hankin
Source:vignettes/dual_quaternion_clifford.Rmd
dual_quaternion_clifford.Rmd
To cite the clifford
package in publications please use
Hankin (2022). This short document shows
how dual quaternions may be implemented using Clifford algebra. The dual
quaternions are an interesting and useful mathematical object which
finds applications in solid-body mechanics. Consider the following
multiplication table:
## 1 i j k ε εi εj εk
## 1 1 i j k ε εi εj εk
## i i -1 k -j εi -ε εk -εj
## j j -k -1 i εj -εk -ε εi
## k k j -i -1 εk εj -εi -ε
## ε ε εi εj εk 0 0 0 0
## εi εi -ε εk -εj 0 0 0 0
## εj εj -εk -ε εi 0 0 0 0
## εk εk εj -εi -ε 0 0 0 0
Thus, for example, \(ij=k\) (not
\(-k\)). Here, \(i,j,k\) are the unit quaternion basis
elements (so \(i^2=j^2=k^2=ijk=-1\))
and \(\varepsilon\) is the dual unit
that commutes with \(i,j,k\) and
satisfies \(\varepsilon^2=0\). We can
implement this in a rough-and-ready way with the onion
package (Hankin 2006) by observing that a
dual quaternion may be represented as \(A+\varepsilon B\), where \(A\) and \(B\) are quaternions. Then
\[ (A+\varepsilon B)(C+\varepsilon D)== AC + \varepsilon(AD+BC)\]
(note the absence of a \(BD\) term,
as \(\varepsilon^2=0\)). Very crude R
idiom for this would be to define a DQ
object as a list of
quaternions, as in:
DQ_example <- list(as.quaternion(c(5,8,-3,3),single=TRUE), as.quaternion(c(-1,2,1,12),single=TRUE))
DQ_example
## [[1]]
## [1]
## Re 5
## i 8
## j -3
## k 3
##
## [[2]]
## [1]
## Re -1
## i 2
## j 1
## k 12
Then the product can be implemented as follows:
DQ_prod_DQ <- function(DQ1,DQ2){
A <- DQ1[[1]] ; B <- DQ1[[2]] ; C <- DQ2[[1]] ; D <- DQ2[[2]]
list(A*C, A*D+B*C)
}
We will use follow the coercion used in
inst/quaternion_clifford.Rmd
(not the alternative
mapping, which requires a different signature) to coerce from cliffords
to quaternions. We may map the dual quaternions to Clifford objects by
additionally identifying \(\varepsilon\) with \(e_4\); to ensure \(e_4^2=0\) we work with \(Cl(3,0)\) which in package idiom is set by
executing signature(3,0)
.
## Element of a Clifford algebra, equal to
## the zero clifford element (0)
\[ \mathbf{i}\leftrightarrow -e_{12}\\ \mathbf{j}\leftrightarrow -e_{13}\\ \mathbf{k}\leftrightarrow -e_{23}\\ \varepsilon\leftrightarrow e_4 \]
Conversion functions would be
`cliff_to_DQ` <- function(C){ # terms such as e_3 and e_123 and e_34 are silently discarded
quat <- getcoeffs(C,list(numeric(0),c(1,2),c(1,3),c(2,3)))
quat[-1] <- -quat[-1]
epsi <- getcoeffs(C,list(4,c(1,2,4),c(1,3,4),c(2,3,4)))
epsi[-1] <- -epsi[-1]
return(list(as.quaternion(quat,single=TRUE),as.quaternion(epsi,single=TRUE)))
}
`DQ_to_cliff` <- function(DQ){ # DQ is a two-element list of quaternions
jj1 <- c(as.matrix(DQ[[1]]))
jj1[-1] <- -jj1[-1]
jj2 <- c(as.matrix(DQ[[2]]))
jj2[-1] <- -jj2[-1]
clifford(list(numeric(0),c(1,2),c(1,3),c(2,3),4,c(1,2,4),c(1,3,4),c(2,3,4)),c(jj1,jj2))
}
Check that DQ_to_cliff()
is indeed a homomorphism:
DQ1 <- list(as.quaternion(c(5,6,2,-7),single=TRUE),as.quaternion(c(-3,1,4,8),single=TRUE))
DQ2 <- list(as.quaternion(c(-1,3,1,4),single=TRUE),as.quaternion(c(1,9,-7,4),single=TRUE))
LHS <- DQ_to_cliff(DQ1) * DQ_to_cliff(DQ2)
RHS <- DQ_to_cliff(DQ_prod_DQ(DQ1,DQ2))
LHS == RHS
## [1] TRUE
Checking that cliff_to_DQ()
is a homomorphism follows
the same line of reasoning:
C1 <- clifford(list(numeric(0),c(1,3),c(1,2,4),c(1,3,4)),c(3,7,11,13))
C2 <- clifford(list(numeric(0),c(1,2),c(1,3),c(1,3,4)),c(2,5,6,17))
LHS <- cliff_to_DQ(C1*C2)
RHS <- DQ_prod_DQ(cliff_to_DQ(C1),cliff_to_DQ(C2))
identical(LHS,RHS)
## [1] TRUE
The final verification is to check that functions
cliff_to_DQ()
and DQ_to_cliff()
are
isomorphisms:
identical(DQ1,cliff_to_DQ(DQ_to_cliff(DQ1)))
## [1] TRUE
identical(C1,DQ_to_cliff(cliff_to_DQ(C1)))
## [1] TRUE