# Taking Quaternions Seriously

**URL:** <https://discourse.julialang.org/t/taking-quaternions-seriously/44834>\
**Category:** Numerics\
**Created:** [August 12, 2020, 10:45pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834 "2020-08-12T22:45:16Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [August 12, 2020, 10:45pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/1 "2020-08-12T22:45:16Z")

</div>

> There are at least 4 separate implementations of quaternions in Julia. Some of them are \<:AbstractVector, some are \<:AbstractMatrix (😐), some are \<:Number, some follow sijk convention, some follow ijks convention, etc. It would be no problem, but I’m constantly switching between them because different packages that I need to use together use different ones. - @jonniedie

> It would be good to unify these with whichever choice is best all around. - @StefanKarpinski

**Absatively**

---

<div class="post-metadata">

**Author:** ![Zach\_Christensen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/zach_christensen/32/7220_2.png) [@Zach\_Christensen](https://discourse.julialang.org/u/Zach_Christensen)\
**Post date:** [August 12, 2020, 10:49pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/2 "2020-08-12T22:49:01Z")

</div>

I’m sure the are specifics of each implementation that are good but I really like what’s in Rotations.jl.

---

<div class="post-metadata">

**Author:** ![anon37204545](https://avatars.discourse-cdn.com/v4/letter/a/439d5e/32.png) [@anon37204545](https://discourse.julialang.org/u/anon37204545)\
**Post date:** [August 12, 2020, 10:55pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/3 "2020-08-12T22:55:32Z")

</div>

Are there any advantages of using \<: AbstractVector / \<: AbstractMatrix over \<: Number for quaternions?

---

<div class="post-metadata">

**Author:** ![jonniedie](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jonniedie/32/12842_2.png) [@jonniedie](https://discourse.julialang.org/u/jonniedie)\
**Post date:** [August 12, 2020, 11:58pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/4 "2020-08-12T23:58:23Z")

</div>

Vectors are nice for differential equations

---

<div class="post-metadata">

**Author:** ![Ronis\_BR](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/ronis_br/32/50999_2.png) [@Ronis\_BR](https://discourse.julialang.org/u/Ronis_BR)\
**Post date:** [August 13, 2020, 1:12am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/5 "2020-08-13T01:12:35Z")

</div>

My package ReferenceFrameRotations.jl also has quaternion algebra. The difference from Rotations.jl is that we treat all the rotations as passive (here we rotate reference systems, not vectors, which is what we usually do when working with satellite dynamics).

It is a structure that is also an AbstractVector. This is good because when you are propagating attitude, you compute a product between a matrix and a vector with the quaternion components. In this case, it is good that a quaternion can behave as a 4x1 vector.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [August 13, 2020, 3:07am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/6 "2020-08-13T03:07:44Z")

</div>

Treating it as a number type can make it easily portable to new applications. A lot of problems can just run with a Vector{\<:Number}, so you can expect a lot of Julia to just work. That said, the array of structs formulation might not be efficient for some calculations.

---

<div class="post-metadata">

**Author:** ![stevengj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/stevengj/32/71_2.png) [@stevengj](https://discourse.julialang.org/u/stevengj)\
**Post date:** [August 13, 2020, 3:16am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/7 "2020-08-13T03:16:38Z")

</div>

> [@jonniedie](#):
>
> Vectors are nice for differential equations

Numbers should be fine too (or any abstract vector space over the reals, for that matter — numbers are a vector space, and so are matrices … not all vector spaces are described by `AbstractVector`!).

If you don’t define a `Quaternion` as a `Number` type, many of the LinearAlgebra functions may not work for matrices of quaternions. And broadcasting should clearly treat a quaternion as a scalar, not as an `AbstractVector` (container). And so forth…

> [@Ronis\_BR](#):
>
> It is a structure that is also an AbstractVector. This is good because when you are propagating attitude, you compute a product between a matrix and a vector with the quaternion components. In this case, it is good that a quaternion can behave as a 4x1 vector.

You’ll want to unroll/inline such a small matrix-vector product anyway (similar to `StaticArrays`), and hence you’ll probably want to define your own quaternion-operator type (similar to `StaticMatrix`) rather than using `AbstractMatrix`.

You could get the best of both worlds by defining it as

```julia
struct Quaternion{T<:Real} <: Number
    v::SVector{4,T}
end

```

and then you can define efficient operations on `q::Quaternion` by calling methods from StaticArrays.jl on `q.v` (and using `q.v` any time you need to iterate over components). For example

```julia
Base.+(q1::Quaternion, q2::Quaternion) = Quaternion(q1.v + q2.v)

```

and similarly defining your quaternion-operator type as an object encapsulating an `SMatrix`.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [August 13, 2020, 3:31am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/8 "2020-08-13T03:31:41Z")

</div>

FWIW, I think someone did this once and reported back that the boundary value problem solvers just worked, even with ForwardDiff involved, so this style of implementation is what I’d suggest too since it’s fairly robust.

---

<div class="post-metadata">

**Author:** ![JeffreySarnoff](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jeffreysarnoff/32/1980_2.png) [@JeffreySarnoff](https://discourse.julialang.org/u/JeffreySarnoff)\
**Post date:** [August 13, 2020, 4:33am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/9 "2020-08-13T04:33:52Z")

</div>

The kinematics of special relativity can be described using Quaternions with complex elements.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [August 13, 2020, 4:38am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/10 "2020-08-13T04:38:13Z")

</div>

A unit complex number is a parameterization for SO(2) loosely the same way that unit quaternions [double] parameterizing SO(3).

But `Complex` is not defined to be a `<:AbstractArray` (not a `AbstractVector` and not a rotation matrix).

I don’t think the underlying “field” should be `<:Real`. Along the same lines, I don’t see a good reason for `Complex{<:Real}` either. For example, a [dual quaternion](https://en.wikipedia.org/wiki/Dual_quaternion) could be represented by `Quaternion{Dual{Float64}}` (or isomorphically `Dual{Quaternion{Float64}}`).

---

<div class="post-metadata">

**Author:** ![jondeuce](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jondeuce/32/16378_2.png) [@jondeuce](https://discourse.julialang.org/u/jondeuce)\
**Post date:** [August 13, 2020, 6:23am UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/11 "2020-08-13T06:23:12Z")

</div>

Not that I disagree with your point, but note that in most implementations `Dual <: Real` (e.g. [ForwardDiff.jl](https://github.com/JuliaDiff/ForwardDiff.jl/blob/58202dd784b7203d1c8d30a98dd24e1bbcccab9f/src/dual.jl#L14)’s) for convenience. In my experience, `Real` in Julia often really means Abelian or similar.

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [August 13, 2020, 1:49pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/12 "2020-08-13T13:49:27Z")

</div>

I’ll just mention that my package [Grassmann.jl](https://github.com/chakravala/Grassmann.jl) supports quaternions and unifies them with vectors and other types of tensors. This package is my attempt ar unifying quaternions with linear algebra and tensor algebra. This is going to be my default algebra for everything, and I will rarely if ever use other ones.

```julia
AbstractTensors.TensorAlgebra <: Number

```

They are all subtype of `Number`, and it also supports Minkowski space and dual quaternions and even higher dimensions than just 3D.

I’ve been using it to develop a mesh interface for partial differential equations, similar to `GeometryBasics` but more flexible and generalized.

It also has faster performance than `SMatrix` for certain matrix operations and allows the construction of matrix like tensor elements too.

It’s still a work in progress and I plan on making more optimizations for quaternions specifically, but there are many possible things to focus on, so I first focused on the most general unification features and then specialized specific features as I go along.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [August 13, 2020, 2:16pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/13 "2020-08-13T14:16:37Z")

</div>

Yeah, I think that arises in part from wanting to operate with other types and methods that force `<:Real`. I personally have yet to understand the benefit of this structure. Are there examples of two algorithms, one which assumes commutativity and one that doesn’t, and dispatch chooses the right one? I certainly could imagine that situation, and think it would be better served by a more general mechanism like using traits so that e.g. `LinearAlgebra.Diagonal` could be designated Abelian.

I could see even `<:Number` causing problems, since square matrices behave a lot like numbers. Indeed, [complex](https://en.wikipedia.org/wiki/Complex_number#Generalizations_and_related_notions), [dual](https://en.wikipedia.org/wiki/Dual_number#Linear_representation), [quaternion]([Quaternion - Wikipedia](https://en.wikipedia.org/wiki/Quaternion#Matrix_representations), [split-complex](https://en.wikipedia.org/wiki/Split-complex_number#Matrix_representations) numbers all have matrix representations. Perhaps the answer there is to wrap small matrices into a type that is `<:Number`.

This is probably outside the scope of “taking quaternions seriously”, but I think it would be very cool and useful to encode those matrix representations in Julia. In sloppy notation, `matrep(Complex{Dual}) === kron(matrep(Complex), matrep(Dual))`. It’s useful because you can fall back on many functions that are defined for square matrices (sqrt, exp, …). You also can then choose to use some operations, like `\`, which might do pivoting for better numerical accuracy (at the cost of performance).

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [August 13, 2020, 2:34pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/14 "2020-08-13T14:34:27Z")

</div>

> [@goretkin](#):
>
> Perhaps the answer there is to wrap small matrices into a type that is `<:Number` .

Grassmann.jl already does that.

> [@goretkin](#):
>
> think it would be very cool and useful to encode those matrix representations in Julia. In sloppy notation, `matrep(Complex{Dual}) === kron(matrep(Complex), matrep(Dual))` . It’s useful because you can fall back on many functions that are defined for square matrices (sqrt, exp, …)

Again, this is something I am working on in Grassmann.jl but am still working on finalizing.

Most of the issues discussed here can be addressed with my packages, which have started with the most general possible foundation and are now working towards optimizing specialized cases.

---

<div class="post-metadata">

**Author:** ![goretkin](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/goretkin/32/167_2.png) [@goretkin](https://discourse.julialang.org/u/goretkin)\
**Post date:** [August 13, 2020, 2:45pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/15 "2020-08-13T14:45:14Z")

</div>

Can you give an example of, say, `Complex{Float64}` interoperating with a complex number as `Grassmann.jl` represents it?

---

<div class="post-metadata">

**Author:** ![chakravala](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chakravala/32/6832_2.png) [@chakravala](https://discourse.julialang.org/u/chakravala)\
**Post date:** [August 13, 2020, 2:52pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/16 "2020-08-13T14:52:47Z")

</div>

Grassmann.jl is not designed to be automatically interoperable with the Julia’s `Complex` type currently. This is because in Grassmann.jl you can have a higher dimensional manifold with multiple imaginary units, much more than just 3 imaginary units (quaternions). So it would be ambiguous in general to know which imaginary basis unit it is supposed to convert to. Therfore, the user must make this choice personally.

Complex numbers are flat land, and live in a single plane, but in Grassmann.jl the choice of plane is ambiguous from a Julia `Complex` type, so you must personally choose how to interpret it.

Also, Julia’s `Complex` can be used as a coefficient type in the Grassmann algebra (as with `Real`), so automatic conversion would cause confusion.

The Grassmann algebra is designed to resolve this higher dimensional ambiguity by relying on combinatorics and “quantum computing.” But this requires an additional layer of context, which the Julia’s `Complex` lacks, so you personally choose how to interpret it in that case.

---

<div class="post-metadata">

**Author:** ![schnetter](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/schnetter/32/45_2.png) [@schnetter](https://discourse.julialang.org/u/schnetter)\
**Post date:** [August 13, 2020, 8:01pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/17 "2020-08-13T20:01:51Z")

</div>

Use the same convention as for complex numbers here. Complex numbers could be described as vectors, but that’s not useful in practice, hence they are numbers. Same with quaternions.

---

<div class="post-metadata">

**Author:** ![Mason](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mason/32/2423_2.png) [@Mason](https://discourse.julialang.org/u/Mason)\
**Post date:** [August 13, 2020, 10:08pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/18 "2020-08-13T22:08:34Z")

</div>

For what it’s worth, I think that while there may be some tantalizing historical reasons to think of Quaternions as being `<: Vector` (e.g. the word vector even comes from quaternions), it’s the least sensible of the supertype options `Number`, `AbstractVector`, `AbstractMatrix`.

Unlike vectors, a quaternion may be sensibly multiplied by another quaternion, and you can also divide by quaternions.

Algebraically, one can show that the reason people keep on finding a deep connection between quaternions and rotations is because the quaternions are actually a set of unit bivectors – oriented planes, rather than a set of unit vectors (oriented lines), further suggesting that `Number` or `AbstractMatrix` make more sense.

I’d definitely lean towards `<: Number`, but there are also distinct advantages to treating them as square matrices.

---

<div class="post-metadata">

**Author:** ![oxinabox](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/oxinabox/32/206603_2.png) [@oxinabox](https://discourse.julialang.org/u/oxinabox)\
**Post date:** [August 13, 2020, 10:13pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/19 "2020-08-13T22:13:42Z")

</div>

Julia’s `Number` type acts a lot like a matrix anyway.

```julia
julia> 1'
1

julia> 2[1]
2

```

---

<div class="post-metadata">

**Author:** ![juliohm](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/juliohm/32/215266_2.png) [@juliohm](https://discourse.julialang.org/u/juliohm)\
**Post date:** [August 13, 2020, 10:33pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/20 "2020-08-13T22:33:03Z")

</div>

> [@Ronis\_BR](#):
>
> The difference from Rotations.jl is that we treat all the rotations as passive (here we rotate reference systems, not vectors, which is what we usually do when working with satellite dynamics).

@Ronis_BR do you have some reference that you could share? We are recently considering implementing some coordinate reference system rotations in GeoStats.jl and it would be really useful to understand the benefits of this approach.

[Next page](https://discourse.julialang.org/t/taking-quaternions-seriously/44834.md?page=2)
