# 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:** 10
**Page:** 4

<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: [September 3, 2020, 7:39pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/61 "2020-09-03T19:39:35Z")

</div>

Given `q = one(Quaternion{Float64})`, it seems to me that  
`eltype(q) === Float64` makes the most sense. Given the second half of the help text:

> Determine the type of the elements generated by iterating a collection of the given type. For dictionary types, this will be a Pair{KeyType,ValType}. The definition eltype(x) = eltype(typeof(x)) is provided for convenience so that instances can be passed instead of types. However the form that accepts a type argument should be defined for new types.

`Base.eltype(::Type{Quaternion{T}) where {T<:Number} = T` makes sense imo. Also `Base.eltype(::Type{QuaternionAsVector{T}}) = T`.

---

<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: [September 3, 2020, 7:45pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/62 "2020-09-03T19:45:34Z")

</div>

> [@Orbots](#):
>
> Usually this would be called an imaginary quaternion, I believe . Implementing Base.imag in place of pure would be good.

My understanding has been that a `pure` quaternion is a quaternion. Implementing `real(q)` and `imag(q)` makes sense. Would you have `imag(q)` return a vector or a tuple? Do you agree that `real(q)` should return a scalar?

---

<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: [September 3, 2020, 7:58pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/63 "2020-09-03T19:58:02Z")

</div>

In `Grassmann` you would get a bivector with `imag` of a quaternion, so a 2-form (neither vector nor tuple).

Of course, `real` would return a scalar, although that scalar might be wrapped in a 0-form, to preserve vector space the scalar exists in.

---

<div class="post-metadata">

### Author: ![Orbots](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/orbots/32/3392_2.png) [@Orbots](https://discourse.julialang.org/u/Orbots)
#### Post date: [September 3, 2020, 7:59pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/64 "2020-09-03T19:59:54Z")

</div>

> [@JeffreySarnoff](#):
>
> Would you have `imag(q)` return a vector or a tuple? Do you agree that `real(q)` should return a scalar?

Good questions. For the application I have in mind I actually would want both to end up as quaternions, since I’m feeding everything into a sparse linear system and the uniformity of dimensions helps book-keeping. But that’s related to my specific use case/implementation. Constructors with 1 and 3 airty could be used to easily convert.

---

<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: [September 3, 2020, 8:39pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/65 "2020-09-03T20:39:07Z")

</div>

`eltype(Quaternion{Float64}) === Float64` is only okay if and only if `collect(q::Quaternion{Float64}) == [q.w, q.x, q.y, q.z]` (modulo the field order and field access), and I believe also iff `1 .+ q == [1 + q.w, 1+q.x, 1+q.y, 1+q.z]` (to say something about broadcasting).

I don’t think it should be done, and that the Quaternion type (or any type that implements the quaternion interface) should behave like `Complex` and all the types defined in `ColorTypes.jl`. That is, they should be treated like scalars for the purpose of iteration, indexing, and broadcasting.

Note that `Base` defines `iterate(x::Number) = (x, nothing)` and `getindex(x::Number) = x` (which is questionable, but I think outside the scope of this conversation), and I think it would basically be incorrect for a `<:Number` to define anything else.

---

<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: [September 3, 2020, 9:30pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/66 "2020-09-03T21:30:46Z")

</div>

> [@goretkin](#):
>
> That is, they should be treated like scalars for the purpose of iteration, indexing, and broadcasting.

Exactly in agreement with my experiences. This is why I have `valuetype` instead of `eltype`.

However, I am willing to change it, if a convincing argument can be made for something else.

The situation should be similar to `Complex`.

---

<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: [September 3, 2020, 9:49pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/67 "2020-09-03T21:49:15Z")

</div>

I have become convinced (thank you @goretkin). Whether we call it `valuetype` or `innertype` or ?? … it should be distinct from `eltype`.

---

<div class="post-metadata">

### Author: ![elsuizo](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/elsuizo/32/89_2.png) [@elsuizo](https://discourse.julialang.org/u/elsuizo)
#### Post date: [September 4, 2020, 12:08pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/68 "2020-09-04T12:08:52Z")

</div>

Hi, in some literature they define the Quaternion in term of a scalar part and a 3D vector

```julia
using StaticArrays
using LinearAlgebra

import Base.*

struct Quaternion{T<:Real} <: Number
   q₀::T
   q::SVector{3, T}
end

(*)(a::Quaternion{T}, b::Quaternion{T}) where T<:Real = Quaternion(a.q₀ * b.q₀ - dot(a.q, b.q), a.q₀ * b.q + b.q₀ * a.q + cross(a.q, b.q))

```

and they define the product in terms of `dot` and `cross` products

---

<div class="post-metadata">

### Author: ![yashi](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/yashi/32/19448_2.png) [@yashi](https://discourse.julialang.org/u/yashi)
#### Post date: [April 1, 2022, 12:50pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/69 "2022-04-01T12:50:08Z")

</div>

So, does anyone working on this?

I’ve been trying to use `Makie.Quaternion` with `ReferenceFrameRotations.Quaternion` and I am beaten.

---

<div class="post-metadata">

### Author: ![hyrodium](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/hyrodium/32/27015_2.png) [@hyrodium](https://discourse.julialang.org/u/hyrodium)
#### Post date: [November 24, 2023, 4:00pm UTC](https://discourse.julialang.org/t/taking-quaternions-seriously/44834/70 "2023-11-24T16:00:16Z")

</div>

Update: Note that the [Quaternions.jl](https://github.com/JuliaGeometry/Quaternions.jl) package is now actively maintained, and Rotations.jl depends on Quaternions.jl ([Rotations.jl#175](https://github.com/JuliaGeometry/Rotations.jl/pull/175)).  
See [[ANN] Quaternions.jl v0.7.0](https://discourse.julialang.org/t/ann-quaternions-jl-v0-7-0/91368) for more information.

> I’ve been trying to use `Makie.Quaternion` with `ReferenceFrameRotations.Quaternion` and I am beaten.

- Makie.jl should now use `Quaternions.Quaternion` ([Makie.jl/src/utilities  
/quaternions.jl](https://github.com/MakieOrg/Makie.jl/blob/f2970dcd77bc16f311f8bb3226ef7d716395b369/src/utilities/quaternions.jl#L1-L5)).
- There was an issue in ReferenceFrameRotations.jl. to be compatible with Quaternions.jl ([ReferenceFrameRotations.jl#25](https://github.com/JuliaSpace/ReferenceFrameRotations.jl/issues/25)).

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