Rotations and conversions
It can sometimes be useful to convert between quaternions and other representations. Most of these functions are named to_<representation> and have a corresponding from_<representation> function. Furthermore, most convert to/from representations of rotations. While rotations are not the only useful application of quaternions, they are probably the most common. The only conversions that are not specifically related to rotations are to_float_array and from_float_array.
Quaternionic.dominant_eigenvector — Method
dominant_eigenvector(M::Symmetric)Return the unit eigenvector of the real symmetric matrix M that belongs to its largest eigenvalue.
This is the computation at the heart of from_rotation_matrix and align, each of which builds a symmetric 4×4 matrix whose dominant eigenvector is the optimal rotor. The sign of the result is arbitrary, as for any eigenvector; those functions choose it afterward.
The result is a vector with the element type of M. Float16, Float32, and Float64 matrices are handled by LAPACK, and other real element types (such as BigFloat) by a generic eigendecomposition, which needs a package such as GenericLinearAlgebra (loaded by this package). The function can be differentiated by the AD packages that this package supports. For 4×4 matrices whose elements are the number types of AD packages (such as ForwardDiff's dual numbers), the eigenvector of the values is refined by Newton steps, so that higher derivatives are also correct; they have been checked through fourth order. Derivatives assume that the largest eigenvalue is simple. Complex matrices are not supported.
Quaternionic.from_euler_angles — Method
from_euler_angles(α, β, γ)
from_euler_angles(αβγ)Come over from the dark side.
Return the Rotor corresponding to the Euler angles α, β, and γ, which may also be given as a single collection αβγ of three angles.
Assumes the Euler angles correspond to the quaternion R via
R = exp(α𝐤/2) * exp(β𝐣/2) * exp(γ𝐤/2)where 𝐣 and 𝐤 rotate about the fixed $y$ and $z$ axes, respectively, so this represents an initial rotation (in the positive sense) through an angle $γ$ about the axis $z$, followed by a rotation through $β$ about the axis $y$, and a final rotation through $α$ about the axis $z$. This is equivalent to performing an initial rotation through $α$ about the axis $z$, followed by a rotation through $β$ about the rotated axis $y'$, followed by a rotation through $γ$ about the twice-rotated axis $z''$. The angles are naturally assumed to be in radians.
NOTE: Before opening an issue reporting something "wrong" with this function, be sure to read all of this page, especially the very last section about opening issues or pull requests.
See Also
to_euler_angles: Convert quaternion to Euler anglesto_euler_phases: Convert quaternion to Euler phasesfrom_euler_phases: Create quaternion from Euler phases
Quaternionic.from_euler_phases — Method
from_euler_phases(zₐ, zᵦ, zᵧ)
from_euler_phases(z)Return the Rotor corresponding to these Euler phases.
The complex Euler phases are defined in terms of the Euler angles (α, β, γ) as
zₐ ≔ exp(i*α)
zᵦ ≔ exp(i*β)
zᵧ ≔ exp(i*γ)These are more useful geometric quantities than the angles themselves — being involved in computing spherical harmonics and Wigner's 𝔇 matrices — and can be computed from the components of the corresponding quaternion algebraically (without the use of transcendental functions).
Parameters
z: a collection (such as aVectororSVector) of three complex numbers, representing the complex phases (zₐ, zᵦ, zᵧ) in that order.
Returns
R::Rotor{T}
See Also
to_euler_phases: Convert quaternion to Euler phasesto_euler_angles: Convert quaternion to Euler anglesfrom_euler_angles: Create quaternion from Euler angles
Quaternionic.from_float_array — Method
from_float_array(A)Reinterpret a float array as an array of quaternions
The input array must have an initial dimension whose size is 4, because successive indices in that dimension will be considered successive components of the output quaternion.
Note that this returns a view of the original data [via reinterpret(reshape,...)] only if the base type of the input array isbitstype; otherwise, a new array of Quaternions must be created, and the memory copied.
See also to_float_array.
Quaternionic.from_rotation_matrix — Method
from_rotation_matrix(ℛ)Convert 3x3 rotation matrix to quaternion.
Assuming the 3x3 matrix ℛ rotates a vector v according to
v' = ℛ * v,we can also express this rotation in terms of a quaternion R such that
v' = R * v * R⁻¹.This function returns that quaternion, using Bar-Itzhack's algorithm (version 3) to allow for non-orthogonal matrices. J. Guidance, Vol. 23, No. 6, p. 1085
Since R and -R represent the same rotation, the sign of the result is fixed by convention: its scalar part is positive, or, if the scalar part is zero, its first nonzero component is positive. The result is therefore discontinuous at rotations through an angle of π, where the scalar part changes sign. See unflip for making a sequence of rotors continuous.
This works for any real element type, and for float input the result has the same element type as the input. It computes the eigen-decomposition of a 4×4 symmetric matrix built from ℛ, which LAPACK provides only for Float16, Float32, and Float64; for anything else we rely on GenericLinearAlgebra, which Quaternionic depends on directly. The eigen-decomposition makes this function far more expensive than to_rotation_matrix. Matrices with complex elements are not supported.
Quaternionic.from_spherical_coordinates — Method
from_spherical_coordinates(θ, ϕ)
from_spherical_coordinates(θϕ)Return a rotor corresponding to these spherical coordinates, which may also be given as a single collection θϕ of two angles.
Considering (θ, ϕ) as a point $n̂$ on the sphere, we can also construct a quaternion that rotates the $z$ axis onto that point. Here, we use the convention commonly used in physics: θ represents the "polar angle" between the $z$ axis and the direction $n̂$, while ϕ represents the "azimuthal angle" between the $x$ axis and the projection of $n̂$ into the $x$-$y$ plane. Both angles must be given in radians.
Quaternionic.to_euler_angles — Method
to_euler_angles(R)Open Pandora's Box.
If somebody is trying to make you use Euler angles, tell them no, and walk away, and go and tell your mum.
You don't want to use Euler angles. They are awful. Stay away. It's one thing to convert from Euler angles to quaternions; at least you're moving in the right direction. But to go the other way?! It's just not right.
Assumes the Euler angles correspond to the quaternion R via
R = exp(α𝐤/2) * exp(β𝐣/2) * exp(γ𝐤/2)where 𝐣 and 𝐤 rotate about the fixed $y$ and $z$ axes, respectively, so this represents an initial rotation (in the positive sense) through an angle $γ$ about the axis $z$, followed by a rotation through $β$ about the axis $y$, and a final rotation through $α$ about the axis $z$. This is equivalent to performing an initial rotation through $α$ about the axis $z$, followed by a rotation through $β$ about the rotated axis $y'$, followed by a rotation through $γ$ about the twice-rotated axis $z''$. The angles are naturally assumed to be in radians.
The outputs from this function are in these ranges:
- α ∈ (-2π, 2π]
- β ∈ [0, π]
- γ ∈ (-2π, 2π]
This is redundant, and inconsistent with more standard conventions for Euler angles on $\mathrm{Spin}(3)$:
- α ∈ [0, 2π)
- β ∈ [0, π]
- γ ∈ [0, 4π)
But since these angles will usually be fed into periodic functions, it usually won't matter. If you really need angles in those ranges, you can always post-process the output of this function.
NOTE: Before opening an issue reporting something "wrong" with this function, be sure to read all of this page, especially the very last section about opening issues or pull requests.
The Euler angles are not differentiable functions of the quaternion components at the Euler singularities (β=0 or β=π), where only α+γ or α-γ, respectively, is defined. Automatic differentiation of this function at exactly those points returns NaN for some of the derivatives, and those NaNs propagate into the derivatives of anything computed from the angles, even when that result is itself smooth.
Quaternions with complex components (Lorentz rotors) are not supported, and raise an ArgumentError.
Returns
αβγ::SVector{3,T}
Raises
AllHellif you try to actually use Euler angles, when you could have been using quaternions like a sensible person.
See Also
from_euler_angles: Create quaternion from Euler anglesto_euler_phases: Convert quaternion to Euler phasesfrom_euler_phases: Create quaternion from Euler phases
Quaternionic.to_euler_phases! — Method
to_euler_phases!(z, R)Store the Euler phases of the quaternion R in the vector z, and return z.
This is equivalent to z[:] = to_euler_phases(R), and is provided for backwards compatibility. See to_euler_phases for details.
Quaternionic.to_euler_phases — Method
to_euler_phases(q)Convert input quaternion to complex phases of Euler angles
Interpreting the input quaternion as a rotation (though its normalization scales out), we can define the complex Euler phases from the Euler angles (α, β, γ) as
zₐ ≔ exp(i*α)
zᵦ ≔ exp(i*β)
zᵧ ≔ exp(i*γ)These are more useful geometric quantities than the angles themselves — being involved in computing spherical harmonics and Wigner's 𝔇 matrices — and can be computed from the components of the corresponding quaternion algebraically (without the use of transcendental functions).
Note that to_euler_phases!(z, q) is supported for backwards compatibility, but because this function returns an SVector, there is probably no advantage to the in-place approach.
Integer quaternions are converted to a float type first. The normalization of the input scales out, as long as the squares of its components neither overflow nor underflow. Quaternions with complex components (Lorentz rotors) are not supported, and raise an ArgumentError.
Note that the derivatives of these phases with respect to the quaternion components are ill-defined at the Euler singularities (specifically, when β=0 or β=π). The problematic cases will just return 1 for the phase factor, which will autodiff to zero. Fortunately, this should not pollute downstream results if they are grounded in geometric quantities. Specifically, Wigner's 𝔇 matrices can be computed from these phases without any issues, because the $d$ factors should be zero anyway, and the product rule will save us.
Returns
z::SVector{Complex{T}}: complex phases (zₐ, zᵦ, zᵧ) in that order.
See Also
from_euler_phases: Create quaternion from Euler phasesto_euler_angles: Convert quaternion to Euler anglesfrom_euler_angles: Create quaternion from Euler angles
Quaternionic.to_float_array — Method
to_float_array(A)
to_float_array(q)View a quaternion array as an array of numbers
The output array will have an extra initial dimension whose size is 4, because successive indices in that dimension correspond to successive components of the quaternion. The element type of the output is the base type of the input quaternions; it is not converted to a float type.
Note that this returns a view of the original data only if the base type of the input array isbitstype; otherwise, a new array of that type must be created, and the memory copied.
When the argument is a single quaternion q, the result is a new Vector of its four components, converted to a float type as by float(q).
See also from_float_array.
Quaternionic.to_rotation_matrix — Method
to_rotation_matrix(q)Convert quaternion to 3x3 rotation matrix.
Assuming the quaternion R rotates a vector v according to
v' = R * v * R⁻¹,we can also express this rotation in terms of a 3x3 matrix ℛ such that
v' = ℛ * v.This function returns that matrix, as an SMatrix{3,3}. The normalization of q scales out, as long as abs2(q) neither overflows nor underflows.
For a Lorentz rotor (a quaternion with complex components), the result is the complex-orthogonal matrix in SO(3,ℂ) that represents the action of the rotor on bivectors.
Quaternionic.to_spherical_coordinates — Method
to_spherical_coordinates(q)Return the spherical coordinates corresponding to this quaternion.
We can treat the quaternion as a transformation taking the $z$ axis to some direction $n̂$. This direction can be described in terms of spherical coordinates (θ, ϕ). Here, we use the convention commonly used in physics: θ represents the "polar angle" between the $z$ axis and the direction $n̂$, while ϕ represents the "azimuthal angle" between the $x$ axis and the projection of $n̂$ into the $x$-$y$ plane. Both angles are given in radians, and the result is an SVector of (θ, ϕ).
The outputs are in these ranges:
- θ ∈ [0, π]
- ϕ ∈ (-2π, 2π]
As for to_euler_angles, ϕ is not reduced to a range of length 2π, so q and -q may give values of ϕ that differ by 2π. Quaternions with complex components (Lorentz rotors) are not supported, and raise an ArgumentError.