AbstractTensors.jl

Tensor algebra abstract type interoperability with vector bundle parameter
Popularity
25 Stars
Updated Last
1 Month Ago
Started In
February 2019

AbstractTensors.jl

Tensor algebra abstract type interoperability with vector bundle parameter

DOI Docs Stable Docs Dev Gitter Build Status Build status Coverage Status codecov.io

The AbstractTensors package is intended for universal interoperability of the abstract TensorAlgebra type system. All TensorAlgebra{V} subtypes have type parameter V, used to store a TensorBundle value obtained from DirectSum.jl.

For example, this is mainly used in Grassmann.jl to define various SubAlgebra, TensorGraded and TensorMixed types, each with subtypes. Externalizing the abstract type helps extend the dispatch to other packages. By itself, this package does not impose any specifications or structure on the TensorAlgebra{V} subtypes and elements, aside from requiring V to be a Manifold. This means that different packages can create tensor types having a common underlying TensorBundle structure.

Interoperability

Since TensorBundle choices are fundamental to TensorAlgebra operations, the universal interoperability between TensorAlgebra{V} elements with different associated TensorBundle choices is naturally realized by applying the union morphism to operations.

function op(::TensorAlgebra{V},::TensorAlgebra{V}) where V
    # well defined operations if V is shared
end # but what if V ≠ W in the input types?

function op(a::TensorAlgebra{V},b::TensorAlgebra{W}) where {V,W}
    VW = V  W        # VectorSpace type union
    op(VW(a),VW(b))   # makes call well-defined
end # this option is automatic with interop(a,b)

# alternatively for evaluation of forms, VW(a)(VW(b))

Some of operations like +,-,*,⊗,⊛,⊙,⊠,⨼,⨽,⋆ and postfix operators ⁻¹,ǂ,₊,₋,ˣ for TensorAlgebra elements are shared across different packages, some of the interoperability is taken care of in this package. Additionally, a universal unit volume element can be specified in terms of LinearAlgebra.UniformScaling, which is independent of V and has its interpretation only instantiated by the context of the TensorAlgebra{V} element being operated on.

Utility methods such as scalar, involute, norm, norm2, unit, even, odd are also defined.

Example with a new subtype

Suppose we are dealing with a new subtype in another project, such as

using AbstractTensors, DirectSum
struct SpecialTensor{V} <: TensorAlgebra{V} end
a = SpecialTensor{ℝ}()
b = SpecialTensor{ℝ'}()

To define additional specialized interoperability for further methods, it is necessary to define dispatch that catches well-defined operations for equal TensorBundle choices and a fallback method for interoperability, along with a Manifold morphism:

(W::Signature)(s::SpecialTensor{V}) where V = SpecialTensor{W}() # conversions
op(a::SpecialTensor{V},b::SpecialTensor{V}) where V = a # do some kind of operation
op(a::TensorAlgebra{V},b::TensorAlgebra{W}) where {V,W} = interop(op,a,b) # compat

which should satisfy (using the operation as defined in DirectSum)

julia> op(a,b) |> Manifold == Manifold(a)  Manifold(b)
true

Thus, interoperability is simply a matter of defining one additional fallback method for the operation and also a new form TensorBundle compatibility morphism.

UniformScaling pseudoscalar

The universal interoperability of LinearAlgebra.UniformScaling as a pseudoscalar element which takes on the TensorBundle form of any other TensorAlgebra element is handled globally by defining the dispatch:

(W::Signature)(s::UniformScaling) = ones(ndims(W)) # interpret a unit pseudoscalar
op(a::TensorAlgebra{V},b::UniformScaling) where V = op(a,V(b)) # right pseudoscalar
op(a::UniformScaling,b::TensorAlgebra{V}) where V = op(V(a),b) # left pseudoscalar

This enables the usage of I from LinearAlgebra as a universal pseudoscalar element.

Tensor evaluation

To support a generalized interface for TensorAlgebra element evaluation, a similar compatibility interface is constructible.

(a::SpecialTensor{V})(b::SpecialTensor{V}) where V = a # conversion of some form
(a::SpecialTensor{W})(b::SpecialTensor{V}) where {V,W} = interform(a,b) # compat

which should satisfy (using the operation as defined in DirectSum)

julia> b(a) |> Manifold == Manifold(a)  Manifold(b)
true

The purpose of the interop and interform methods is to help unify the interoperability of TensorAlgebra elements.

Deployed applications

The key to making the whole interoperability work is that each TensorAlgebra subtype shares a TensorBundle parameter (with all isbitstype parameters), which contains all the info needed at compile time to make decisions about conversions. So other packages need only use the vector space information to decide on how to convert based on the implementation of a type. If external methods are needed, they can be loaded by Requires when making a separate package with TensorAlgebra interoperability.

Required Packages