Quaternionic change of basis

A rotation can be described in two equivalent ways: as a change of basis or as a linear transformation (see dynamical pictures for cool examples from quantum mechanics). Rotations of bases are usually described using rotation matrices, whereas the preferred way to describe rotations of vectors is by using unit quaternions (a.k.a. versors). This raises a natural question: “Can one describe rotations of bases using quaternions and is it any better than using rotation matrices?” The answer is in both cases yes (thanks to Rudolf Lioutikov for convincing me of that), and moreover, the formulas for vector and versor coordinate transformations based on unit quaternions are truly elegant and simple.

Using matrices

To get started, let’s recall the matrix formulas that relate coordinates of vectors and rotation matrices in different bases. Assume there are two coordinate frames with a common origin: a base frame $B$ and a free frame $F$, and let $T$ be the transformation matrix containing the basis vectors of $F$ in basis $B$ as columns.

Given coordinates $\xi'$ of a vector $v$ in a free frame $F$, its coordinates $\xi$ in a base frame $B$ are given by \begin{equation} \label{mat_vec} \xi = T \xi'. \end{equation}
Given matrix coordinates $Q'$ of a rotation $R$ in a free frame $F$, its matrix coordinates $Q$ in a base frame $B$ are given by \begin{equation} Q = T Q' T^{-1}. \end{equation}

To get an intuition of how the formula for transformation of rotations works, imagine that $R$ stands for “get beer”, $T$ for “close fridge”, and $T^{-1}$ for “open fridge”. Beer is awaiting in a fridge, but you only know how to “get beer when fridge open”, which corresponds to $Q’$. What you want is to “get beer when fridge closed”, i.e., $Q$. The formula above provides a sequence of actions (from right to left) you need to take to “get beer when fridge closed”. Read it out loud: “Get beer when fridge closed = open fridge, get beer when fridge open, close fridge”. I think Hilbert would rejoice in this explanation.

Using quaternions

Unit quaternions rotate vectors by conjugation $v \mapsto q v q^{-1}$. Since change of basis is nothing else but a rotation of basis vectors, we can rotate each basis vector separately to describe the full basis rotation. Let versor $t$ represent rotation of basis $B$ into basis $F$, i.e., if $\left\{ e_1, e_2, e_3 \right\}$ are the basis vectors of $B$ and $\left\{ e’_1, e’_2, e’_3 \right\}$ are the basis vectors of $F$, then $e’_i = t e_i t^{-1}$ for $i \in \{ 1, 2, 3 \}$.

Given coordinates $\xi'$ of a vector $v$ in a free frame $F$, its coordinates $\xi$ in a base frame $B$ are given by \begin{equation} \label{quat_vec} \xi = t \xi' t^{-1}. \end{equation}

Formula \eqref{quat_vec} is straightforward to derive. Without loss of generality, we can identify basis $B$ with the standard basis in $\mathbb{R}^3$. Then $T = \begin{pmatrix}e’_1 & e’_2 & e’_3\end{pmatrix}$ and \eqref{mat_vec} can be written as $\xi = e’_i \xi’_i$ (summation assumed). Substituting $e’_i = t e_i t^{-1}$ and pulling $\xi’_i$ towards $e_i$, we obtain $\xi = t \left( e_i \xi’_i \right) t^{-1} = t \xi’ t^{-1}$.

Some confusion may arise at this point because formulas on Wikipedia have primes on the left-hand side. That is because they use active transformations whereas we use passive ones. See active vs passive transformations for more details.

Given versor coordinates $q'$ of a rotation $R$ in a free frame $F$, its versor coordinates $q$ in a base frame $B$ are given by \begin{equation} \label{quat_quat} q = t q' t^{-1}. \end{equation}

To arrive at \eqref{quat_quat}, recall the derivation with matrices \begin{equation*} \left. \begin{array}{l} \left. \begin{array}{l} \eta = A \xi \\ \xi = T \xi^\prime \end{array} \right\} \Rightarrow \eta = A T \xi^\prime \\ \left. \begin{array}{l} \eta = T \eta^\prime \\ \eta^\prime = A^\prime \xi^\prime \end{array} \right\} \Rightarrow \eta = T A^\prime \xi^\prime \end{array} \right\} \Rightarrow A = T A^\prime T^{-1} \end{equation*} and convince yourself that the same derivation also works with quaternions.

Implementation

We can save quite some computation on Formula \eqref{quat_quat} if we notice that the scalar part of a unit quaternion is invariant under change of basis. Indeed, if $q’ = \left(\cos(\theta/2), u\sin(\theta/2)\right)$ with a unit vector $u \in \mathbb{R}^3$, the scalar part of $q’$ corresponds to the rotation angle around axis $u$. Rotation of a basis rotates $u$ but it cannot affect $\theta$, therefore we can simplify computation by just copying the scalar part of the quaternion and rotating the vector part by conjugation. Thanks to Ralf Grosse-Kunstleve for pointing that out.

import numpy as np
π = np.pi
e1, e2, e3 = np.eye(3)

def qmul(p, q):
    a1, b1, c1, d1 = p
    a2, b2, c2, d2 = q
    a3 = a1*a2 - b1*b2 - c1*c2 - d1*d2
    b3 = a1*b2 + b1*a2 + c1*d2 - d1*c2
    c3 = a1*c2 - b1*d2 + c1*a2 + d1*b2
    d3 = a1*d2 + b1*c2 - c1*b2 + d1*a2
    return np.r_[a3, b3, c3, d3]

quat = lambda θ, u: np.r_[np.cos(θ/2), np.sin(θ/2)*u/np.linalg.norm(u)]
conj = lambda q: np.r_[q[0], -q[1:]]
rotv = lambda q, v: qmul(qmul(q, np.r_[0, v]), conj(q))[1:]
rotq = lambda q, p: np.r_[p[0], rotv(q, p[1:])]
rmat = lambda q: np.c_[rotv(q, e1), rotv(q, e2), rotv(q, e3)]

print("=== Change of vector coordinates under change of basis ===")
ξ_prime = np.array([2, 1, 3])
t = quat(π/5, np.array([2, -3, 1]))
T = rmat(t)
print(f"Matr: ξ = T @ ξ' = {T @ ξ_prime}")
print(f"Quat: ξ = t @ ξ' @ t^(-1) = {rotv(t, ξ_prime)}")

print("\n=== Change of rotation coordinates under change of basis ===")
q_prime = quat(π/4, np.array([1, 1, 0]))
print(f"Matr: Q = T @ Q' @ T^(-1) =\n{T @ rmat(q_prime) @ T.T}")
print(f"Quat: q = t @ q' @ t^(-1) => Matr\n{rmat(rotq(t, q_prime))}")

Conclusion

Quaternions are fast, beautiful, and singularity-free. Use them for a great good!