Clifford numbers

Supertype and associated functions

CliffordNumbers.AbstractCliffordNumberType
AbstractCliffordNumber{Q,T} <: Number

An element of a Clifford algebra, often referred to as a multivector, with quadratic form Q and element type T. These are statically size and therefore should be able to be stored inline in arrays or other data structures.

Interface

Required implementation

All subtypes C of AbstractCliffordNumber{Q} must implement the following functions:

  • CliffordNumbers.similar_type(::Type{C}, ::Type{T}, ::Type{Q}) where {C,T,Q} should construct a new type similar to C which subtypes AbstractCliffordNumber{Q,T} that may serve as a constructor.
  • Base.getindex(x::C, b::BitIndex{Q}) should allow one to recover the coefficients associated with each basis blade represented by C.
  • nblades(::Type{C}) should be defined to return the number of basis blades represented by the type. By default, nblades(x::AbstractCliffordNumber) = nblades(typeof(x)).
  • Base.Tuple(x::C) should return the tuple used to construct x. The fallback is getfield(x, :data)::Tuple, so any type declared with a NTuple field named data should have this defined automatically.
source
CliffordNumbers.nbladesFunction
nblades(::Type{<:Number}) -> Int
nblades(x::Number)

Returns the number of blades represented by a Number subtype or instance. For subtypes of Number that are not AbstractCliffordNumber, this is always 1.

This function is separate from Base.length since AbstractCliffordNumber is a scalar type for which collect() returns a zero-dimensional array. For consistency, length(x) should always equal length(collect(x)).

source
CliffordNumbers.scalar_typeFunction
scalar_type(::Type{<:AbstractCliffordNumber{Q,T}}) = T
scalar_type(T::Type{<:Union{Real,Complex}}) = T
scalar_type(x) = scalar_type(typeof(x))

Returns the numeric type associated with an AbstractCliffordNumber instance. For subtypes of Real and Complex, or their instances, this simply returns the input type or instance type.

Why not define eltype?

AbstractCliffordNumber instances behave like numbers, not arrays. If collect() is called on a Clifford number of type T, it should not construct a vector of coefficients; instead it should return an Array{T,0}. Similarly, a broadcasted multiplication should return the same result as normal multiplication, as is the case with complex numbers.

For subtypes T of Number, eltype(T) === T, and this is true for AbstractCliffordNumber.

source
CliffordNumbers.similar_typeFunction
CliffordNumbers.similar_type(
    C::Type{<:AbstractCliffordNumber},
    [N::Type{<:BaseNumber} = scalar_type(C)],
    [Q::Val = Val(signature(C))]
) -> Type{<:AbstractCliffordNumber{Q,N}}

Constructs a type similar to T but with numeric type N and quadratic form Q. The quadratic form must be wrapped in a Val to preserve type stability.

This function must be defined with all its arguments for each concrete type subtyping AbstractCliffordNumber.

Note on function export

This function is nearly identical in semantics to StaticArraysCore.similar_type. However, since this package does not depend on StaticArraysCore, this function is not exported to avoid name conflicts whenever any package exporting StaticArraysCore.similar_type is loaded.

If a package exports StaticArraysCore.similar_type, that function will have methods added which match the methods in this package with the same signature. This may be triggered explicitly if desired with a using or import directive.

source

Concrete types

CliffordNumbers.CliffordNumberType
CliffordNumber{Q,T,L} <: AbstractCliffordNumber{Q,T}

A dense multivector (or Clifford number), with quadratic form Q, element type T, and length L (which depends entirely on Q).

The coefficients are ordered by taking advantage of the natural binary structure of the basis. The grade of an element is given by the Hamming weight of its index. For the algebra of physical space, the order is: 1, e₁, e₂, e₁₂, e₃, e₁₃, e₂₃, e₁₂₃ = i. This order allows for more aggressive SIMD optimization when calculating the geometric product.

source
CliffordNumbers.Z2CliffordNumberType
CliffordNumbers.Z2CliffordNumber{P,Q,T,L} <: AbstractCliffordNumber{Q,T}

A Clifford number whose only nonzero grades are even or odd. Clifford numbers of this form naturally arise as versors, the geometric product of 1-vectors.

The type parameter P is constrained to be a Bool: true for odd grade Clifford numbers, and false for even grade Clifford numbers, corresponding to the Boolean result of each grade modulo 2.

Type aliases

This type is not exported, and usually you will want to refer to the following aliases:

const EvenCliffordNumber{Q,T,L} = Z2CliffordNumber{false,Q,T,L}
const OddCliffordNumber{Q,T,L} = Z2CliffordNumber{true,Q,T,L}
source
CliffordNumbers.EvenCliffordNumberType
EvenCliffordNumber{P,Q,T,L} (alias for CliffordNumbers.Z2CliffordNumber{false,Q,T,L})

A Clifford number whose only nonzero grades are even. These are the natural choice of representation for rotors and motors (Euclidean isometries preserving orientation, or "proper" isometries), as well as their composition with dilations.

source
CliffordNumbers.OddCliffordNumberType
OddCliffordNumber{P,Q,T,L} (alias for CliffordNumbers.Z2CliffordNumber{true,Q,T,L})

A Clifford number whose only nonzero grades are odd. These are the natural choice of representation for reflections, as well as their compositions with rotors and motors (Euclidean isometries preserving orientation, or "proper" isometries), as well as their composition with dilations.

source
CliffordNumbers.KVectorType
KVector{K,Q,T,L} <: AbstractCliffordNumber{Q,T}

A multivector consisting only linear combinations of basis blades of grade K - in other words, a k-vector.

k-vectors have binomial(dimension(Q), K) components.

source
CliffordNumbers.gradeMethod
grade(::Type{<:KVector{K}}) = K
grade(x::KVector{K}) = k

Returns the grade represented by a KVector{K}, which is K.

source

Promotion and conversion

CliffordNumbers.scalar_convertFunction
scalar_convert(T::Type{<:Union{Real,Complex}}, x::AbstractCliffordNumber) -> T
scalar_convert(T::Type{<:Union{Real,Complex}}, x::Union{Real,Complex}) -> T

If x is an AbstractCliffordNumber, converts the scalars of x to type T.

If x is a Real or Complex, converts x to T.

Examples

julia> scalar_convert(Float32, KVector{1,APS}(1, 2, 3))
3-element KVector{1, VGA(3), Float32}:
1.0σ₁ + 2.0σ₂ + 3.0σ

julia> scalar_convert(Float32, 2)
2.0f0
source
CliffordNumbers.scalar_promoteFunction
scalar_promote(x::AbstractCliffordNumber, y::AbstractCliffordNumber)

Promotes the scalar types of x and y to a common type. This does not increase the number of represented grades of either x or y.

source
Base.widenMethod
widen(C::Type{<:AbstractCliffordNumber})
widen(x::AbstractCliffordNumber)

Construct a new type whose scalar type is widened. This behavior matches that of widen(C::Type{Complex{T}}), which results in widening of its scalar type T.

For obtaining a representation of a Clifford number with an increased number of nonzero grades, use widen_grade(T).

source
CliffordNumbers.widen_gradeFunction
widen_grade(C::Type{<:AbstractCliffordNumber})
widen_grade(x::AbstractCliffordNumber)

For type arguments, construct the next largest type that can hold all of the grades of C. KVector{K,Q,T} widens to EvenCliffordNumber{Q,T} or OddCliffordNumber{Q,T}, and EvenCliffordNumber{Q,T} and OddCliffordNumber{Q,T} widen to CliffordNumber{Q,T}, which is the widest type.

For AbstractCliffordNumber arguments, the argument is converted to the result of widen_grade(typeof(x)).

For widening the scalar type of an AbstractCliffordNumber, use Base.widen(T).

source

Real and complex algebras

Base.realMethod
real(x::AbstractCliffordNumber{Q,T})

Gets the real portion of each coefficient of x. For T<:Real this operation does nothing; for T<:Complex{S} this an AbstractCliffordNumber{Q,S}.

Note that this does not return the scalar (grade 0) coefficient of x. Use scalar(x) to obtain this result in general, or real(scalar(x)) if only the real portion is desired.

source
Base.complexMethod
complex(x::AbstractCliffordNumber, [y::AbstractCliffordNumber = zero(typeof(x))])

For a single argument x, converts the type of each coefficient to a suitable complex type.

For two arguments x and y, which are both real Clifford numbers, performs the sum x + y*im, constructing a complex Clifford number.

Note that this operation does not isolate a scalar (grade 0) coefficient of x or y. Use complex(scalar(x), [scalar(y)]) to obtain this result.

source

Scalar and pseudoscalar components

CliffordNumbers.isscalarFunction
isscalar(x::AbstractCliffordNumber)

Determines whether the Clifford number x is a scalar, meaning that all of its blades of nonzero grade are zero.

source
CliffordNumbers.ispseudoscalarFunction
ispseudoscalar(m::AbstractCliffordNumber)

Determines whether the Clifford number x is a pseudoscalar, meaning that all of its blades with grades below the dimension of the space are zero.

source
CliffordNumbers.scalarFunction
scalar(x::AbstractCliffordNumber{Q,T}) -> T

Returns the scalar portion of x as its scalar type. This is equivalent to x[scalar_index(x)].

To retain Clifford number semantics, use the KVector{0} constructor.

source
CliffordNumbers.pseudoscalarFunction
pseudoscalar(C::Type{<:AbstractCliffordNumber{Q,T}}) -> KVector{dimension(Q),Q,T}
pseudoscalar(x::AbstractCliffordNumber{Q})

Returns the pseudoscalar associated with the signature Q of the argument. The result will have the same scalar type as the input if such information is given; otherwise it will use Bool as the scalar type so as to promote to any other numeric type.

source

Defining basis variables

CliffordNumbers.@basis_varsMacro
@basis_vars(Q, ::Type{T}; prefix = Metrics.blade_symbol(Q))

Generates variables in the global scope representing the 1-blade basis elements of Q.

Warning

The default prefix for most algebras is 'e', which can cause problems with multiplying through juxtaposition: Julia interprets 2e0 as scientific notation. For the sake of clarity, use explicit multiplication (2*e0) or change the prefix.

Lorentzian geometric algebras default to γ as the default prefix and do not have this problem.

Examples

julia> @basis_vars(VGA(3), prefix = :σ)
source