diff --git a/docs/src/api/clifford.md b/docs/src/api/clifford.md index e905ccd..f89c405 100644 --- a/docs/src/api/clifford.md +++ b/docs/src/api/clifford.md @@ -4,6 +4,7 @@ ```@docs CliffordNumbers.AbstractCliffordNumber +CliffordNumbers.nblades CliffordNumbers.scalar_type CliffordNumbers.similar_type ``` diff --git a/src/CliffordNumbers.jl b/src/CliffordNumbers.jl index 75cbd96..7226608 100644 --- a/src/CliffordNumbers.jl +++ b/src/CliffordNumbers.jl @@ -32,7 +32,7 @@ export VGA2D, VGA3D, PGA2D, PGA3D, CGA2D, CGA3D, STA, STAEast, STAWest, STAP, ST # Abstract supertype for all Clifford numbers include("abstract.jl") export AbstractCliffordNumber -export signature, scalar_type +export nblades, signature, scalar_type # Working with the grades represented by an AbstractCliffordNumber subtype include("grades.jl") export nonzero_grades, has_grades_of diff --git a/src/abstract.jl b/src/abstract.jl index 2290ed2..8fa2e09 100644 --- a/src/abstract.jl +++ b/src/abstract.jl @@ -3,22 +3,21 @@ AbstractCliffordNumber{Q,T} <: Number An element of a Clifford algebra, often referred to as a multivector, with quadratic form `Q` and -element type `T`. +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: - * `Base.length(x::C)` should return the number of nonzero basis elements represented by `x`. * `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`. - -## Required implementation for static types - * `Base.length(::Type{C})` should be defined, with `Base.length(x::C) = length(typeof(x))`. + * `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. @@ -30,6 +29,21 @@ end (::Type{T})(x::Vararg{BaseNumber}) where {Q,T<:AbstractCliffordNumber{Q}} = T(x) +#---Number of blades-------------------------------------------------------------------------------# +""" + 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))`. +""" +nblades(::Type{<:Number}) = 1 +nblades(x::Number) = nblades(typeof(x)) + #---Get type parameters----------------------------------------------------------------------------# """ signature(T::Type{<:AbstractCliffordNumber{Q}}) = Q @@ -76,13 +90,13 @@ zero_tuple(::Type{T}, ::Val{L}) where {T,L} = ntuple(Returns(zero(T)), Val(L)) """ CliffordNumbers.zero_tuple(::Type{C<:AbstractCliffordNumber}) - -> NTuple{length(C),scalar_type(C)} + -> NTuple{nblades(C),scalar_type(C)} Generates a `Tuple` that can be used to construct `zero(C)`. """ -zero_tuple(::Type{C}) where C<:AbstractCliffordNumber = zero_tuple(scalar_type(C), Val(length(C))) +zero_tuple(::Type{C}) where C<:AbstractCliffordNumber = zero_tuple(scalar_type(C), Val(nblades(C))) -zero(::Type{C}) where C<:AbstractCliffordNumber = C(zero_tuple(Bool, Val(length(C)))) +zero(::Type{C}) where C<:AbstractCliffordNumber = C(zero_tuple(Bool, Val(nblades(C)))) zero(x::AbstractCliffordNumber) = zero(typeof(x)) # The default defintion assumes oneunit(T) = T(one(x)) @@ -193,11 +207,11 @@ short_typename(::Type{C}) where C<:AbstractCliffordNumber = C short_typename(x::AbstractCliffordNumber) = short_typename(typeof(x)) function show(io::IO, x::AbstractCliffordNumber) - print(io, short_typename(x), (isone(length(x)) ? string('(', only(Tuple(x)), ')') : Tuple(x))) + print(io, short_typename(x), (isone(nblades(x)) ? string('(', only(Tuple(x)), ')') : Tuple(x))) end function summary(io::IO, x::AbstractCliffordNumber) - println(io, length(x), "-element ", short_typename(x), ":") + println(io, nblades(x), "-element ", short_typename(x), ":") end #---Algebra mismatch errors------------------------------------------------------------------------# diff --git a/src/bitindices.jl b/src/bitindices.jl index 09ce6ad..2496fac 100644 --- a/src/bitindices.jl +++ b/src/bitindices.jl @@ -8,10 +8,10 @@ by `C`. abstract type AbstractBitIndices{Q,C<:AbstractCliffordNumber{Q}} <: AbstractVector{BitIndex{Q}} end -size(::Type{<:AbstractBitIndices{Q,C}}) where {Q,C} = tuple(length(C)) +size(::Type{<:AbstractBitIndices{Q,C}}) where {Q,C} = tuple(nblades(C)) size(b::AbstractBitIndices) = size(typeof(b)) -length(::Type{<:AbstractBitIndices{Q,C}}) where {Q,C} = length(C) +length(::Type{<:AbstractBitIndices{Q,C}}) where {Q,C} = nblades(C) length(::T) where T<:AbstractBitIndices = length(T) # Conversion to tuple diff --git a/src/cliffordnumber.jl b/src/cliffordnumber.jl index 0f865da..5fc87be 100644 --- a/src/cliffordnumber.jl +++ b/src/cliffordnumber.jl @@ -27,13 +27,11 @@ CliffordNumber{Q}(x::Tuple{Vararg{T}}) where {Q,T<:BaseNumber} = CliffordNumber{ CliffordNumber{Q}(x::Tuple{Vararg{BaseNumber}}) where Q = CliffordNumber{Q}(promote(x...)) # Convert real/complex numbers to CliffordNumber -(::Type{T})(x::BaseNumber) where T<:CliffordNumber = T(ntuple(i -> x*isone(i), Val(length(T)))) +(::Type{T})(x::BaseNumber) where T<:CliffordNumber = T(ntuple(i -> x*isone(i), Val(nblades(T)))) #---Number of elements-----------------------------------------------------------------------------# -length(::Type{<:CliffordNumber{Q}}) where Q = blade_count(Q) -length(m::CliffordNumber) = length(typeof(m)) - +nblades(::Type{<:CliffordNumber{Q}}) where Q = blade_count(Q) nonzero_grades(::Type{<:CliffordNumber{Q}}) where Q = 0:dimension(Q) #---Default BitIndices construction should include all possible BitIndex objects-------------------# @@ -54,7 +52,7 @@ end #---Multiplicative identity------------------------------------------------------------------------# -one(C::Type{<:CliffordNumber{Q}}) where Q = C(ntuple(isone, Val(length(C)))) +one(C::Type{<:CliffordNumber{Q}}) where Q = C(ntuple(isone, Val(nblades(C)))) #---Similar types----------------------------------------------------------------------------------# diff --git a/src/even.jl b/src/even.jl index 8e18845..21afde3 100644 --- a/src/even.jl +++ b/src/even.jl @@ -55,13 +55,11 @@ function Z2CliffordNumber{P,Q}(x::Tuple{Vararg{BaseNumber}}) where {P,Q} end # Convert real/complex numbers to CliffordNumber -(::Type{T})(x::BaseNumber) where {T<:Z2CliffordNumber} = T(ntuple(i -> x*isone(i), Val(length(T)))) +(::Type{T})(x::BaseNumber) where {T<:Z2CliffordNumber} = T(ntuple(i -> x*isone(i), Val(nblades(T)))) #---Number of elements-----------------------------------------------------------------------------# -length(::Type{<:Z2CliffordNumber{P,Q}}) where {P,Q} = div(blade_count(Q), 2) -length(x::Z2CliffordNumber) = length(typeof(x)) - +nblades(::Type{<:Z2CliffordNumber{P,Q}}) where {P,Q} = div(blade_count(Q), 2) nonzero_grades(::Type{<:Z2CliffordNumber{P,Q}}) where {P,Q} = P:2:dimension(Q) #---Indexing---------------------------------------------------------------------------------------# @@ -83,7 +81,7 @@ end #---Multiplicative identity------------------------------------------------------------------------# -one(C::Type{<:EvenCliffordNumber{Q}}) where Q = C(ntuple(isone, Val(length(C)))) +one(C::Type{<:EvenCliffordNumber{Q}}) where Q = C(ntuple(isone, Val(nblades(C)))) #---Similar types----------------------------------------------------------------------------------# diff --git a/src/kvector.jl b/src/kvector.jl index 8917ff0..d7fba94 100644 --- a/src/kvector.jl +++ b/src/kvector.jl @@ -25,9 +25,7 @@ KVector{K,Q}(x::Tuple{Vararg{BaseNumber}}) where {K,Q} = KVector{K,Q}(promote(x. #---Number of elements-----------------------------------------------------------------------------# -length(::Type{KVector{K,Q,T,L}}) where {K,Q,T,L} = L -length(::Type{<:KVector{K,Q}}) where {K,Q} = binomial(dimension(Q), K) -length(x::KVector) = length(typeof(x)) +nblades(::Type{<:KVector{K,Q}}) where {K,Q} = binomial(dimension(Q), K) #---Indexing---------------------------------------------------------------------------------------# @@ -52,7 +50,7 @@ end @inline function to_index(C::Type{<:KVector{K,Q}}, b::BitIndex{Q}) where {K,Q} # Default to 1 as a valid index for any KVector instance i = 1 - for n in 1:length(C) + for n in 1:nblades(C) is_same_blade(b, (@inbounds BitIndices(C)[n])) && (i = n) end return i diff --git a/src/math.jl b/src/math.jl index 0608881..9e511e0 100644 --- a/src/math.jl +++ b/src/math.jl @@ -96,7 +96,7 @@ conj(x::T) where T<:KVector = T(x.data .* Int8(-1)^!iszero((grade(x) + 1) & 2)) function +(x::AbstractCliffordNumber, y::BaseNumber) T = promote_type(typeof(x), typeof(y)) b = BitIndices(T) - data = ntuple(i -> x[b[i]] + (iszero(grade(b[i])) * y), Val(length(T))) + data = ntuple(i -> x[b[i]] + (iszero(grade(b[i])) * y), Val(nblades(T))) return T(data) end @@ -112,14 +112,14 @@ end function -(x::AbstractCliffordNumber, y::BaseNumber) T = promote_type(typeof(x), typeof(y)) b = BitIndices(T) - data = ntuple(i -> x[b[i]] - (iszero(grade(b[i])) * y), Val(length(T))) + data = ntuple(i -> x[b[i]] - (iszero(grade(b[i])) * y), Val(nblades(T))) return T(data) end function -(x::BaseNumber, y::AbstractCliffordNumber) T = promote_type(typeof(x), typeof(y)) b = BitIndices(T) - data = ntuple(i -> (iszero(grade(b[i])) * x) - y[b[i]], Val(length(T))) + data = ntuple(i -> (iszero(grade(b[i])) * x) - y[b[i]], Val(nblades(T))) return T(data) end diff --git a/src/multiply.jl b/src/multiply.jl index f037274..1bfe900 100644 --- a/src/multiply.jl +++ b/src/multiply.jl @@ -219,7 +219,7 @@ kernel just returns the geometric product. x_mask = mul_mask(F(), a, inds) # Filter out indexing operations that automatically go to zero # This must be done manually since we want to work directly with tuples - y_mask = map(in, grade.(inds), ntuple(Returns(nonzero_grades(y)), Val(length(C)))) + y_mask = map(in, grade.(inds), ntuple(Returns(nonzero_grades(y)), Val(nblades(C)))) # Don't append operations that won't actually do anything if any(x_mask) && any(y_mask) # Resolve BitIndex to an integer here to avoid having to call Base.to_index at runtime diff --git a/test/indexing.jl b/test/indexing.jl index 2718bae..85699f9 100644 --- a/test/indexing.jl +++ b/test/indexing.jl @@ -123,10 +123,10 @@ end end @testset "Type lengths" begin - @test length(KVector{2,STA}) === length(KVector{2,STA,Int,6}) - @test length(zero(KVector{2,STA})) === length(KVector{2,STA,Int,6}) - @test length(CliffordNumber{STA}) === length(CliffordNumber{STA,Int,16}) - @test length(zero(CliffordNumber{STA})) === length(CliffordNumber{STA,Int,16}) - @test length(EvenCliffordNumber{STA}) === length(EvenCliffordNumber{STA,Int,8}) - @test length(zero(EvenCliffordNumber{STA})) === length(EvenCliffordNumber{STA,Int,8}) + @test nblades(KVector{2,STA}) === nblades(KVector{2,STA,Int,6}) + @test nblades(zero(KVector{2,STA})) === nblades(KVector{2,STA,Int,6}) + @test nblades(CliffordNumber{STA}) === nblades(CliffordNumber{STA,Int,16}) + @test nblades(zero(CliffordNumber{STA})) === nblades(CliffordNumber{STA,Int,16}) + @test nblades(EvenCliffordNumber{STA}) === nblades(EvenCliffordNumber{STA,Int,8}) + @test nblades(zero(EvenCliffordNumber{STA})) === nblades(EvenCliffordNumber{STA,Int,8}) end