Lorentz transformations

Quaternions with complex components represent proper orthochronous Lorentz transformations — more precisely, elements of their double cover $\mathrm{Spin}^+(3,1)$ — through the isomorphism between the complex quaternions and the even subalgebra of the spacetime algebra. The theory, including the sign conventions, is described on the Quaternions and the Spacetime Algebra page; this page describes how to use it.

The Lorentz type is simply an alias for Rotor{Complex{T}}. The Boost function constructs a pure boost from a rapidity and a direction, or from a velocity (in units where $c = 1$), and Lorentz(R) converts a real Rotor R into the corresponding pure rotation. Lorentz transformations are composed with * and inverted with inv, and calling one on a Minkowski 4-vector $[t, x, y, z]$ (with signature ${-}{+}{+}{+}$) transforms it:

julia> using Quaternionic

julia> B = Boost([0.6, 0.0, 0.0]);  # A boost with velocity 0.6 along x

julia> B([1, 0, 0, 0]) ≈ [1.25, 0.75, 0, 0]
true

julia> Boost(atanh(0.6), [1, 0, 0]) ≈ B  # The same boost, given by its rapidity
true

The action is active: Λ(v) is the transformed vector, expressed in the original frame. Above, B maps the 4-velocity of a particle at rest to the 4-velocity $\gamma\,(1, \vec{v})$ of a particle moving with velocity $\vec{v}$. The components of a fixed vector as seen by an observer moving with velocity $\vec{v}$ are given instead by the passive transformation inv(Boost(v⃗)). Composition acts from right to left:

julia> R = Lorentz(exp(π/8 * imz));  # A rotation by π/4 about z

julia> v = [1.0, 2.0, 3.0, 4.0];

julia> (R * B)(v) ≈ R(B(v))
true

julia> inv(B)(B(v)) ≈ v
true

Every Lorentz transformation can be factored into simpler pieces. The functions RB and BR factor it into a rotation and a boost (in either order), Rv and vR do the same but return the boost's velocity, and KAN computes the Iwasawa decomposition described in Iwasawa's $KA\,N$ decomposition:

julia> K, A, N = Quaternionic.KAN(R * B);

julia> K * A * N ≈ R * B
true

The functions ℂreal, ℂimag, ℂreim, and ℂconj operate on the real and imaginary parts of the complex components. These functions are public but not exported, so they must be qualified with the module name, or imported explicitly.

Many generic quaternion functions, such as exp, log, sqrt, and integer powers, also accept complex quaternions, and abs and abs2 compute the complex spinor norm. Functions that are specific to rotations in three dimensions, such as distance, to_euler_angles, and from_rotation_matrix, do not support Lorentz input.

Reference

The entry Lorentz(::AbstractVector) below documents the action Λ(v) of a Lorentz transformation Λ on a 4-vector v.

Quaternionic.Lorentz — Type
Lorentz{T<:Real}

A proper orthochronous Lorentz transformation, an element of Spin⁺(3,1) — the double cover of the restricted Lorentz group SO⁺(3,1).

We work in the spacetime algebra $Cl(3,1)$ with metric signature ${−}{+}{+}{+}$ and basis vectors 𝐭, 𝐱, 𝐲, 𝐳 satisfying 𝐭² = −1, 𝐱² = 𝐲² = 𝐳² = +1, all mutually anticommuting. The pseudoscalar $𝐈 = 𝐭𝐱𝐲𝐳$ satisfies $𝐈² = −𝟏$.

Elements $𝐑 ∈ \mathrm{Spin}⁺(3,1)$ live in the even subalgebra (grades 0, 2, 4) and satisfy $𝐑 𝐑̃ = 𝟏$, where $𝐑̃$ is the GA reverse. In particular, note that that product could include a term proportional to the pseudoscalar; it is zero: $𝐑 𝐑̃ = 𝟏 + 0 𝐈$. This subalgebra splits into two parts:

  • Rotation subgroup: generated by the spatial bivectors 𝐲𝐳, 𝐱𝐳, 𝐱𝐲.
  • Boost sector: generated by the timelike bivectors 𝐭𝐱, 𝐭𝐲, 𝐭𝐳.

All elements of this algebra commute with the pseudoscalar $𝐈$, which acts as the complex unit for this subalgebra. Note that $i𝐢 = 𝐭𝐱$, $i𝐣 = 𝐭𝐲$, $i𝐤 = 𝐭𝐳$, relating spatial-rotation generators to boost generators (all with positive signs). A boost along $\hat{𝐧} = n^x\,𝐢 + n^y\,𝐣 + n^z\,𝐤$ (a unit QuatVec) with rapidity $η$ is thus

\[𝐑 = \cosh(η/2)·𝟏 + \sinh(η/2)·(nˣ 𝐭𝐱 + nʸ 𝐭𝐲 + nᶻ 𝐭𝐳)\]

The two elements $±R ∈ \mathrm{Spin}⁺(3,1)$ represent the same Lorentz transformation. The real GA components and their encoding are described in ga_components; as usual, you can get the plain (complex) coefficients with components.

Operations

Compose with *, invert with inv, and obtain the identity with one. Apply Λ to a Minkowski 4-vector v = [vᵗ, vˣ, vʸ, vᶻ] by calling Λ(v), and access the GA components via ga_components.

The action is active: Λ(v) is the transformed vector, expressed in the original frame. For example, Boost(v⃗)([1, 0, 0, 0]) is γ .* [1; v⃗], the 4-velocity of a particle moving with velocity v⃗. The components of a fixed vector as seen by an observer moving with velocity v⃗ are given by the passive transformation inv(Boost(v⃗)). Composition acts from right to left, so that (Λ₁ * Λ₂)(v) ≈ Λ₁(Λ₂(v)).

Calling Λ on a 4-vector given as an AbstractVector is different from calling it on a QuatVec. Λ(v::QuatVec) is the generic rotor sandwich Λ * v * conj(Λ), which, in this encoding, transforms a spatial bivector (such as an electromagnetic field) rather than a spatial vector, and it returns a complex QuatVec even for real input.

Many generic quaternion functions, such as exp, log, sqrt, and integer powers, also accept complex quaternions. Functions that are specific to rotations in three dimensions, such as distance, to_euler_angles, and from_rotation_matrix, do not support Lorentz input.

Constructors

Use the named constructor Boost for a pure boost, and Lorentz(R) for the pure rotation given by a real Rotor R. More generally, the forms

Lorentz(q::AbstractQuaternion)
Lorentz(w, x, y, z)
Lorentz(v::AbstractVector)
Lorentz(w::Number)

convert the given (possibly complex) components to complex numbers and normalize the result by the complex spinor norm √(w² + x² + y² + z²), so that w² + x² + y² + z² = 1 in complex arithmetic. The vector form accepts 1, 3, or 4 components, like rotor. Note that this normalization is not the same as normalization by the Euclidean norm. For example, Lorentz(QuatVec(0, im, 0, 0)) (the bivector 𝐭𝐱, whose spinor norm is im) returns the rotation 𝐢. For a boost with large rapidity, the spinor norm is computed as a difference of large numbers, so the normalization itself loses accuracy. The type constructors Lorentz{T}(w, x, y, z) and Lorentz{T}(q::AbstractQuaternion) do not normalize, and are the better choice when the input is already known to have unit spinor norm. Finally, Lorentz(T) returns the type Lorentz{T} for a real number type T.

source
Quaternionic.Lorentz — Method
(Λ::Lorentz{T})(v::AbstractVector) → AbstractVector{S}

Apply Λ to the Minkowski 4-vector v = [vᵗ, vˣ, vʸ, vᶻ] (signature −+++) and return the transformed vector.

The element type S of the result is obtained by promoting T and eltype(v) through the arithmetic, so that Lorentz{Float64} acting on a vector of Int or Float32 returns Float64 elements, and dual numbers from automatic differentiation propagate. An SVector{4} input returns an SVector{4, S}; any other input returns a new container similar(v, S), which, for example, is a Vector{S} for a Vector or a view.

The action is the Spin⁺(3,1) sandwich V′ = R·V·R̃ in Cl(3,1), where R̃ is the GA reverse. This is an active transformation, and (Λ₁ * Λ₂)(v) ≈ Λ₁(Λ₂(v)). The eight real GA components (R¹, Rᶻʸ, …, Rᵗˣʸᶻ) are extracted via ga_components(::Lorentz), and the bilinear expansion of the grade-1 projection of R·V·R̃ is applied directly.

source
Quaternionic.Boost — Function
Boost(η, n̂::AbstractVector) → Lorentz{S}
Boost(η, n̂::QuatVec) → Lorentz{S}
Boost(v⃗::QuatVec) → Lorentz{S}
Boost(v⃗::AbstractVector) → Lorentz{S}

Construct the pure boost with rapidity η along the unit direction n̂, or equivalently from a velocity vector v⃗ whose magnitude β = norm(v⃗) encodes the boost speed (in units where c = 1) and whose direction gives n̂.

The first two forms take an explicit rapidity and unit direction. The last two forms accept a velocity vector v⃗, which must satisfy β < 1, and are equivalent to Boost(atanh(β), v⃗ / β). The direction n̂ may be either a 3-element AbstractVector [nˣ, nʸ, nᶻ] or a QuatVec (whose x, y, z components are used as the direction), and the velocity v⃗ may likewise be a 3-element AbstractVector or a QuatVec.

The element type S of the result is the floating-point type obtained by promoting the types of all the inputs, so that, for example, an integer rapidity, a Float32 rapidity with a Float64 direction, and automatic differentiation with respect to the direction all work as expected.

The direction n̂ is neither checked nor normalized; if it is not a unit vector, the result does not have unit spinor norm, and is not a Lorentz transformation. The velocity forms throw a DomainError if β ≥ 1. Note that the velocity parametrization cannot represent large rapidities accurately, because β = tanh(η) rounds to 1 for η ≳ 19 in Float64; the rapidity forms have no such limitation.

In the even subalgebra of Cl(3,1) the rotor is

\[𝐑 = \cosh(η/2)·𝟏 + \sinh(η/2)·(nˣ\,𝐭𝐱 + nʸ\,𝐭𝐲 + nᶻ\,𝐭𝐳).\]

This is an active transformation: Boost(v⃗) maps the 4-velocity [1, 0, 0, 0] of a particle at rest to γ .* [1; v⃗], the 4-velocity of a particle moving with velocity v⃗.

See ga_components(::Lorentz) for the correspondence between this GA form and the quaternion storage.

source
Quaternionic.ga_components — Function
ga_components(Λ::Lorentz{T}) → SVector{8, T}

Return the eight real geometric-algebra components of Λ in Cl(3,1), ordered as in the even-subalgebra expansion of a Spin⁺(3,1) rotor:

\[𝐑 = R^1 + R^{𝐳𝐲}\,𝐳𝐲 + R^{𝐱𝐳}\,𝐱𝐳 + R^{𝐲𝐱}\,𝐲𝐱 + R^{𝐭𝐱}\,𝐭𝐱 + R^{𝐭𝐲}\,𝐭𝐲 + R^{𝐭𝐳}\,𝐭𝐳 + R^{𝐭𝐱𝐲𝐳}\,𝐈\]

where $𝐈 = 𝐭𝐱𝐲𝐳$ is the pseudoscalar of $\mathrm{Cl}(3,1)$.

Storage mapping

Lorentz{T} stores a unit quaternion $𝐑 = w𝟏 + x𝐢 + y𝐣 + z𝐤$ with complex coefficients $w, x, y, z ∈ ℂ$, where the quaternionic basis elements 𝐢, 𝐣, 𝐤 correspond to spatial bivectors according to

\[\begin{gather} 𝐢 = 𝐳𝐲 = -𝐲𝐳, \\ 𝐣 = 𝐱𝐳 = -𝐳𝐱, \\ 𝐤 = 𝐲𝐱 = -𝐱𝐲, \end{gather}\]

and the complex unit $i ∈ ℂ$ maps to the pseudoscalar $𝐈 = 𝐭𝐱𝐲𝐳$, so that

\[\begin{gather} i𝐢 = 𝐭𝐱𝐲𝐳𝐳𝐲 = 𝐭𝐱, \\ i𝐣 = 𝐭𝐱𝐲𝐳𝐱𝐳 = 𝐭𝐲, \\ i𝐤 = 𝐭𝐱𝐲𝐳𝐲𝐱 = 𝐭𝐳, \end{gather}\]

The eight real GA components read off from w, x, y, z as:

GA componentquaternion expressionCl(3,1) basis
R¹$\Re(w)$$𝟏$
Rᶻʸ$\Re(x)$$𝐳𝐲 = 𝐢$
Rˣᶻ$\Re(y)$$𝐱𝐳 = 𝐣$
Rʸˣ$\Re(z)$$𝐲𝐱 = 𝐤$
Rᵗˣ$\Im(x)$$𝐭𝐱$
Rᵗʸ$\Im(y)$$𝐭𝐲$
Rᵗᶻ$\Im(z)$$𝐭𝐳$
Rᵗˣʸᶻ$\Im(w)$$𝐭𝐱𝐲𝐳$

The unit-norm condition $𝐑𝐑̃ = 𝟏$ — i.e., $w² + x² + y² + z² = 1$ with complex arithmetic — translates to two real conditions on the GA components:

\[\begin{gather} (R¹)² + (Rᶻʸ)² + (Rˣᶻ)² + (Rʸˣ)² - (Rᵗˣ)² - (Rᵗʸ)² - (Rᵗᶻ)² - (Rᵗˣʸᶻ)² = 1, \\ R¹ Rᵗˣʸᶻ + Rᶻʸ Rᵗˣ + Rˣᶻ Rᵗʸ + Rʸˣ Rᵗᶻ = 0. \end{gather}\]

source
Quaternionic.RB — Function
RB(Λ::Lorentz{T}) → (R::Rotor{T}, B::Lorentz{T})

Polar decomposition Λ = R * B into a pure rotation R and a pure boost B. When Λ acts on a vector, the boost B is applied first, and then the rotation R, so that Λ(v) ≈ Lorentz(R)(B(v)).

Double cover

Since Spin⁺(3,1) is a double cover of SO⁺(3,1), both (R, B) and (-R, -B) represent the same Lorentz transformation. The scalar part of the returned R has the same sign as the scalar part of Λ.

The general Lorentz transformation Λ can be expressed as the product of a pure rotation R and a pure boost B in either order. For this function, we assume Λ = R * B. The boost is, in turn, determined by a rapidity η ≥ 0 and a unit direction n̂, so that B = cosh(η/2) + im*sinh(η/2)*n̂ in the quaternionic encoding. Therefore, we have

Λ = R * B = cosh(η/2)*R + im*sinh(η/2)*(R*n̂).

Since cosh(η/2) is 1 for no boost, and grows smoothly with η, we can find R very simply by normalizing the complex-real part of Λ. And since the norm of R is 1, we can find cosh(η/2) as the magnitude of the complex-real part of Λ. Then, we find sinh(η/2)*n̂ simply by multiplying the complex-imaginary part of Λ by inv(R), and reconstruct B simply by adding the result to cosh(η/2). The boost B is constructed directly from these components, without renormalization by the spinor norm, which would introduce relative errors of order eps(T) * cosh(η) at large rapidity.

See also BR, vR, and Rv for similar forms, and KAN for the Iwasawa decomposition.

source
Quaternionic.BR — Function
BR(Λ::Lorentz{T}) → (B::Lorentz{T}, R::Rotor{T})

Polar decomposition Λ = B * R into a pure boost B and a pure rotation R. When Λ acts on a vector, the rotation R is applied first, and then the boost B, so that Λ(v) ≈ B(Lorentz(R)(v)).

The algorithm here is the same as for RB, but with the order of multiplication by inv(R) reversed.

See also vR and Rv for similar forms, and KAN for the Iwasawa decomposition.

source
Quaternionic.Rv — Function
Rv(Λ::Lorentz{T}) → (R::Rotor{T}, v⃗::QuatVec{T})

Return the pure rotation R and (vectorial) boost velocity v⃗ such that Λ = R * Boost(η, v̂) where η = atanh(β) is the associated rapidity with β = norm(v⃗) as the boost parameter. When Λ acts on a vector, the boost is applied first, and then the rotation.

The boost spinor is B = cosh(η/2) + im*v̂*sinh(η/2) in the quaternionic encoding. Note that v̂ is the unit vector in the direction of v⃗. We can immediately obtain the values of those cosh and sinh factors by taking the scalar part and the norm of the vector part. Using that information, the stable way to compute the velocity vector is to take the second term (the complex-imaginary part of B), and multiply by β / sinh(η/2). We can use half-angle formulas to show that

\[\frac{β}{\sinh(η/2)} = \frac{\tanh(η)}{\sinh(η/2)} = \frac{2 \cosh(η/2)}{\cosh^2(η/2) + \sinh^2(η/2)} = \frac{2 \cosh(η/2)}{2\cosh^2(η/2) - 1},\]

where the last equality uses the double-angle identity $\cosh(η) = 2\cosh^2(η/2) - 1$. The last form is made up entirely of the scalar component of B, does not involve any cancellation or division by small numbers, and avoids computing the norm of the vector part.

Large rapidities

The velocity cannot represent large rapidities accurately, because β = tanh(η) approaches 1 exponentially quickly. The rapidity reconstructed from v⃗ loses relative accuracy roughly like eps(T) * exp(2η), and in Float64, β rounds to 1 for η ≳ 19, so that Boost(v⃗) is inaccurate or throws. For large boosts, use RB or BR, which return the boost as a Lorentz rotor.

See also vR, RB, and BR for similar forms, and KAN for the Iwasawa decomposition.

source
Quaternionic.vR — Function
vR(Λ::Lorentz{T}) → (v⃗::QuatVec{T}, R::Rotor{T})

Return the boost velocity v⃗ and pure rotation R such that Λ = Boost(η, v̂) * R. When Λ acts on a vector, the rotation is applied first, and then the boost. See Rv for more details, including the loss of accuracy at large rapidities.

See also BR and RB for similar forms, and KAN for the Iwasawa decomposition.

source
Quaternionic.KAN — Function
KAN(Λ::Lorentz{T}) → (Rₖ::Rotor{T}, Rₐ::Lorentz{T}, Rₙ::Lorentz{T})

Compute the Iwasawa $KAN$ decomposition of the Lorentz transformation Λ, returning the three factors such that Λ = Rₖ * Rₐ * Rₙ, where

  • Rₖ ∈ K is a pure rotation (the maximal compact factor),
  • Rₐ ∈ A is a boost along the $𝐳$ axis, Rₐ = cosh(φₐ/2) + sinh(φₐ/2) 𝐭𝐳, and
  • Rₙ ∈ N is a null rotation fixing the null vector $ℓ = (𝐭+𝐳)/√2$.

As with RB and BR, the rotation Rₖ is returned as a real Rotor{T}, and can be converted with Lorentz(Rₖ), while Rₐ and Rₙ are returned as Lorentz{T} rotors. When Λ acts on a vector, Rₙ is applied first, then Rₐ, and then Rₖ.

Conditioning

The decomposition is intrinsically ill-conditioned when the rapidity $φₐ$ of the $A$ factor is large and negative (a large boost toward $-𝐳$). In that case, the errors in Rₖ grow roughly like eps(T) * exp(|φₐ|), and those in Rₙ like eps(T) * exp(2|φₐ|). This sensitivity is a property of the decomposition itself, rather than of the algorithm used here. More generally, when the total rapidity η of Λ is large, rounding its components to T leaves a spinor norm that differs from 1 by about `eps(T)

  • exp(η). No exact factorization of such aΛexists, so the productRₖ * Rₐ * Rₙ`

can differ from Λ by a relative error of that order.

See the KAN decomposition section of the spacetime-algebra documentation for a full discussion and derivation of the method used in this function.

See also BR, RB, vR, and Rv for polar decompositions.

source
Quaternionic.ℂreal — Function
ℂreal(Λ::Lorentz{T}) → Quaternion{T}
ℂreal(q::AbstractQuaternion{Complex{T}}) → Quaternion{T}

Complex-real part of Λ: Quaternion(real(Λ[1]), real(Λ[2]), real(Λ[3]), real(Λ[4])).

For Λ = R*B where R is a pure rotation and B is a pure boost with rapidity η, this equals cosh(η/2)*R. Its Euclidean norm is cosh(η/2) ≥ 1.

This is distinct from the GA "real" part, given by real(Λ), which is constructed from the reverse, rather than the complex-conjugate. That will just be the scalar part of Λ, but may be complex; this function returns a full quaternion, with all components real.

source
Quaternionic.ℂimag — Function
ℂimag(Λ::Lorentz{T}) → Quaternion{T}
ℂimag(q::AbstractQuaternion{Complex{T}}) → Quaternion{T}

Complex-imaginary part of Λ: Quaternion(imag(Λ[1]), imag(Λ[2]), imag(Λ[3]), imag(Λ[4])).

For Λ = R*B where R is a pure rotation and B is a pure boost with rapidity η in direction n̂, this equals sinh(η/2)*R*n̂.

This is distinct from the GA "imaginary" part, given by imag(Λ), which is constructed from the reverse, rather than the complex-conjugate. That will just be the quaternion "vector" part of Λ, but may be complex; this function returns a full quaternion, with all components real.

source
Quaternionic.ℂreim — Function
ℂreim(Λ::Lorentz{T}) → (Quaternion{T}, Quaternion{T})
ℂreim(q::AbstractQuaternion{Complex{T}}) → (Quaternion{T}, Quaternion{T})

Return (ℂreal(Λ), ℂimag(Λ)) as a single call.

source
Quaternionic.ℂconj — Function
ℂconj(Λ::Lorentz{T}) → Lorentz{T}
ℂconj(q::AbstractQuaternion{Complex{T}}) → typeof(q)

Component-wise complex conjugate of Λ: conjugate each complex coefficient (w, x, y, z) → (w̄, x̄, ȳ, z̄). The result has the same type as the input, and is not renormalized.

This is distinct from the GA reverse, which is conj(Λ) and just negates the quaternion "vector" part. For Λ = R*B with R a pure rotation and B a pure boost, ℂconj(Λ) = R * B⁻¹, since ℂconj(B) = B⁻¹ for pure boosts and ℂconj(R) = R for real rotors.

source