-
Notifications
You must be signed in to change notification settings - Fork 37
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementing matrix functions with quaternion eltypes #89
Comments
Here's a minimal example. We'd need to think more carefully about indexing for a real implementation, which might mean we restrict inputs to julia> using Quaternions, LinearAlgebra
julia> function quaternion_to_complex_matrix!(z::AbstractMatrix, q::Quaternion)
w = complex(q.s, q.v1)
y = complex(q.v2, q.v3)
z[begin, begin] = w
z[begin + 1, begin] = -conj(y)
z[begin, begin + 1] = y
z[end, end] = conj(w)
return z
end
quaternion_to_complex_matrix! (generic function with 1 method)
julia> function quaternion_matrix_to_complex(Q::AbstractMatrix{<:Quaternion})
m, n = size(Q)
Z = similar(Q, complex(real(eltype(Q))), 2m, 2n)
for j in axes(Q, 2), i in axes(Q, 1)
z = @views Z[2i-1:2i, 2j-1:2j]
quaternion_to_complex_matrix!(z, Q[i, j])
end
return Z
end
quaternion_matrix_to_complex (generic function with 1 method)
julia> function complex_matrix_to_quaternion(z::AbstractMatrix)
w = (z[begin, begin] + conj(z[begin + 1, begin + 1])) / 2
y = (-conj(z[begin+1, begin]) + z[begin, begin+1]) / 2
return Quaternion(reim(w)..., reim(y)...)
end
complex_matrix_to_quaternion (generic function with 1 method)
julia> function complex_matrix_to_quaternion_matrix(Z::AbstractMatrix{<:Complex})
m, n = map(x -> div(x, 2), size(Z))
Q = similar(Z, Quaternion{real(eltype(Z))}, m, n)
for j in axes(Q, 2), i in axes(Q, 1)
z = @views Z[2i-1:2i, 2j-1:2j]
Q[i, j] = complex_matrix_to_quaternion(z)
end
return Q
end
complex_matrix_to_quaternion_matrix (generic function with 1 method)
julia> q = randn(QuaternionF64, 1, 1);
julia> function matfun(f, Q::AbstractMatrix{<:Quaternion})
Z = quaternion_matrix_to_complex(Q)
fZ = f(Z)
fQ = complex_matrix_to_quaternion_matrix(fZ)
return fQ
end
matfun (generic function with 1 method)
julia> matfun(exp, q)[1, 1] ≈ exp(q[1, 1])
true
julia> matfun(log, q)[1, 1] ≈ log(q[1, 1])
true
julia> matfun(cosh, q)[1, 1] ≈ cosh(q[1, 1])
true
julia> q = randn(QuaternionF64, 5, 5);
julia> matfun(log, matfun(exp, q)) ≈ q
true
julia> matfun(inv, q) * q ≈ one(q)
true
julia> matfun(sin, matfun(asin, q)) ≈ q
true Why do this? Only reason I know of is for the matrix exp/log, which one can use for the Unitary group on quaternions, see e.g. JuliaManifolds/Manifolds.jl#382 (comment). I don't think this would be commonly used though, but it's nice to support operations like this generically here when we can for first-class Quaternion support. |
Quaternions can be represented as$2 \times 2$ complex matrices whose matrix product preserves the Hamilton product. This means that one can map an $n \times n$ quaternion matrix to a $2n \times 2n$ complex matrix with $2 \times 2$ blocks from quaternions to perform any complicated operations that only consist of matrix addition and multiplication.
Matrix functions such as the matrix exponential satisfy this property. We could generically support these matrix functions for quaternion eltypes by explicitly generating these complex matrices, dispatching to the complex matrix functions, and then mapping back to a matrix of quaternions.
This would generalize #46 and extend #56 to matrices.
The text was updated successfully, but these errors were encountered: