Basics
Basic operations for quaternions
Quaternions can be defined with the Quaternion constructor or quat function. Note that the order of the arguments is $w+xi+yj+zk$, not $xi+yj+zk+w$.
julia> using Quaternionsjulia> q1 = Quaternion(1,2,3,4)Quaternion{Int64}(1, 2, 3, 4)julia> q2 = quat(5,6,7,8)Quaternion{Int64}(5, 6, 7, 8)julia> q3 = quat(9)Quaternion{Int64}(9, 0, 0, 0)
The multiplication is not commutative.
julia> q1 * q2Quaternion{Int64}(-60, 12, 30, 24)julia> q2 * q1Quaternion{Int64}(-60, 20, 14, 32)
The multiplicative inverse can be calculated with Base.inv.
julia> inv(q1)QuaternionF64(0.03333333333333333, -0.06666666666666667, -0.1, -0.13333333333333333)julia> inv(q1) * q1QuaternionF64(1.0, 0.0, 0.0, 0.0)
The division is also not commutative.
julia> q1 / q2 # Same as `q1*inv(q2)` mathematically.QuaternionF64(0.40229885057471265, 0.04597701149425287, 0.0, 0.09195402298850575)julia> q2 \ q1 # Same as `inv(q2)*q1` mathematically.QuaternionF64(0.40229885057471265, -0.0, 0.09195402298850575, 0.04597701149425287)
A conjugate of a quaternion can be calculated with Base.conj. But Base.imag(::Quaternion) is not defined because it should return three real values which is not consistent with imag(::Complex) and imag(::Real). Instead, the imag_part function can be used to obtain the imaginary part of a quaternion. See issue#61 for more discussion.
julia> conj(q1)Quaternion{Int64}(1, -2, -3, -4)julia> imag(q1) # Not supported.ERROR: MethodError: no method matching imag(::Quaternion{Int64}) The function `imag` exists, but no method is defined for this combination of argument types. Closest candidates are: imag(::Missing) @ Base missing.jl:101 imag(::LinearAlgebra.Symmetric) @ LinearAlgebra /usr/local/julia1.11.6/share/julia/stdlib/v1.11/LinearAlgebra/src/symmetric.jl:420 imag(::Real) @ Base complex.jl:89 ...julia> imag_part(q1) # Use this instead.(2, 3, 4)
Unit quaternions can be obtained with sign.
julia> sign(q1)QuaternionF64(0.18257418583505536, 0.3651483716701107, 0.5477225575051661, 0.7302967433402214)julia> sign(q2)QuaternionF64(0.3790490217894517, 0.454858826147342, 0.5306686305052324, 0.6064784348631227)julia> sign(q3)QuaternionF64(1.0, 0.0, 0.0, 0.0)julia> sign(quat(0)) # Zero-quaternion will not be normalized.QuaternionF64(0.0, 0.0, 0.0, 0.0)
Quaternion vs quat
The general rule is that quat is to Quaternion as complex is to Complex. Complex and Quaternion are both constructors so should return an object of the corresponding type, whereas quat and complex both can operate on types and arrays.
julia> Quaternion(1,2,3,4)Quaternion{Int64}(1, 2, 3, 4)julia> quat(1,2,3,4)Quaternion{Int64}(1, 2, 3, 4)julia> Quaternion(Int) # Similar to `Complex(Int)`.ERROR: MethodError: no method matching Quaternion(::Type{Int64}) The type `Quaternion` exists, but no method is defined for this combination of argument types when trying to construct it. Closest candidates are: Quaternion(::T, ::T, ::T, ::T) where T<:Real @ Quaternions ~/work/Quaternions.jl/Quaternions.jl/src/Quaternion.jl:12 Quaternion(::Real, ::Real, ::Real, ::Real) @ Quaternions ~/work/Quaternions.jl/Quaternions.jl/src/Quaternion.jl:24 Quaternion(::Real) @ Quaternions ~/work/Quaternions.jl/Quaternions.jl/src/Quaternion.jl:25 ...julia> quat(Int) # Similar to `complex(Int)`.Quaternion{Int64}
Compatibility with Complex
There is no natural embedding $\mathbb{C}\to\mathbb{H}$. Thus, quat(w,x,0,0) is not equal to complex(w,x), i.e.
\[\mathbb{C} \ni w+ix \ne w+ix+0j+0k \in \mathbb{H}.\]
julia> 1 + complex(1,2) # `Complex` is compatible with `Real`2 + 2imjulia> 1 + quat(1,2,3,4) # `Quaternion` is compatible with `Real`Quaternion{Int64}(2, 2, 3, 4)julia> 1 + complex(1,2) + quat(1,2,3,4) # no compatibilityERROR: promotion of types Complex{Int64} and Quaternion{Int64} failed to change any argumentsjulia> complex(1,2) == quat(1,2,0,0) # no compatibilityERROR: promotion of types Complex{Int64} and Quaternion{Int64} failed to change any argumentsjulia> complex(1) == quat(1) # no compatibilityERROR: promotion of types Complex{Int64} and Quaternion{Int64} failed to change any argumentsjulia> complex(1) == 1 == quat(1) # Both `quat(1)` and `complex(1)` are equal to `1`.true
See issue#62 for more discussion.