`TensorAlgebra`

design, `Manifold`

code generation

Mathematical foundations and definitions specific to the Grassmann.jl implementation provide an extensible platform for computing with geometric algebra at high dimensions, along with the accompanying support packages. The design is based on the `TensorAlgebra`

abstract type interoperability from AbstractTensors.jl with a `TensorBundle`

parameter from DirectSum.jl. Abstract tangent vector space type operations happen at compile-time, resulting in a differential conformal geometric algebra of hyper-dual multivector forms.

The nature of the geometric algebra code generation enables one to easily extend the abstract product operations to any specific number field type (including differential operators with Leibniz.jl or symbolic coefficients with Reduce.jl), by making use of Julia's type system. Mixed tensor products with their coefficients are constructed from these operations to work with bivector elements of Lie groups.

**DirectSum.jl**: Abstract tangent bundle vector space types (unions, intersections, sums, etc.)**AbstractTensors.jl**: Tensor algebra abstract type interoperability with vector bundle parameter**Grassmann.jl**: ⟨Leibniz-Grassmann-Clifford-Hestenes⟩ differential geometric algebra of multivector forms**Leibniz.jl**: Derivation operator algebras for tensor fields**Reduce.jl**: Symbolic parser generator for Julia expressions using REDUCE algebra term rewriter

Mathematics of `Grassmann`

can be used to study unitary groups used in quantum computing by building efficient computational representations of their algebras. Applicability of the Grassmann computational package not only maps to quantum computing, but has the potential of impacting countless other engineering and scientific computing applications. It can be used to work with automatic differentiation and differential geometry, algebraic forms and invariant theory, electric circuits and wave scattering, spacetime geometry and relativity, computer graphics and photogrammetry, and much more.

Thus, computations involving fully general rotational algebras and Lie bivector groups are possible with a full trigonometric suite. Conformal geometric algebra is possible with the Minkowski plane $v_{\infty\emptyset}$, based on the null-basis. In general, multivalued quantum logic is enabled by the $\wedge,\vee,\star$ Grassmann lattice. Mixed-symmetry algebra with *Leibniz.jl* and *Grassmann.jl*, having the geometric algebraic product chain rule, yields automatic differentiation and Hodge-DeRahm co/homology as unveiled by Grassmann. Most importantly, the Dirac-Clifford product yields generalized Hodge-Laplacian and the Betti numbers with Euler characteristic $\chi$.

Due to the abstract generality of the product algebra code generation, it is possible to extend the `Grassmann`

library to include additional high performance products with few extra definitions. Operations on ultra-sparse representations for very high dimensional algebras will be gaining further performance enhancements in future updates, along with hybrid optimizations for low-dimensional algebra code generation. Thanks to the design of the product algebra code generation, any additional optimizations to the type stability will automatically enhance all the different products simultaneously. Likewise, any new product formulas will be able to quickly gain from the setup of all of the existing optimizations.

The *Grassmann.jl* package and its accompanying support packages provide an extensible platform for high performance computing with geometric algebra at high dimensions. This enables the usage of many different types of `TensorAlgebra`

along with various `TensorBundle`

parameters and interoperability for a wide range of scientific and research applications.

## DirectSum yields `TensorBundle`

parametric type polymorphism

The *DirectSum.jl* package is a work in progress providing the necessary tools to work with an arbitrary `Manifold`

specified by an encoding. Due to the parametric type system for the generating `TensorBundle`

, the Julia compiler can fully preallocate and often cache values efficiently ahead of run-time. Although intended for use with the *Grassmann.jl* package, `DirectSum`

can be used independently.

Let $M = T^\mu V$ be a `TensorBundle{n}<:Manifold{n}`

of rank $n$,

\[T^\mu V = (n,\mathbb P,g,\nu,\mu), \qquad \mathbb P \subseteq\langle v_\infty,v_\emptyset\rangle, \qquad g :V\times V\rightarrow\mathbb K\]

The type `TensorBundle{n,ℙ,g,ν,μ}`

uses *byte-encoded* data available at pre-compilation, where $\mathbb P$ specifies the basis for up and down projection, $g$ is a bilinear form that specifies the metric of the space, and $\mu$ is an integer specifying the order of the tangent bundle (i.e. multiplicity limit of Leibniz-Taylor monomials). Lastly, $\nu$ is the number of tangent variables. The dual space functor $'$ is an involution which toggles a dual vector space with inverted signature with property $V' = \text{Hom}(V,\mathbb K)$ and having `SubManifold`

generators

\[\langle v_1,\dots,v_{n-\nu},\partial_1,\dots,\partial_\nu\rangle=M\leftrightarrow M' = \langle w_1,\dots,w_{n-\nu},\epsilon_1,\dots,\epsilon_\nu\rangle\]

where $v_i,w_i$ are a basis for the vectors and covectors, while $\partial_j,\epsilon_j$ are a basis for differential operators and tensor fields.

The metric signature of the `SubManifold{V,1}`

elements of a vector space $V$ can be specified with the `V"..."`

constructor by using $+$ and $-$ to specify whether the `SubManifold{V,1}`

element of the corresponding index squares to $+1$ or $-1$. For example, `S"+++"`

constructs a positive definite 3-dimensional `TensorBundle`

.

```
julia> ℝ^3 == V"+++" == Manifold(3)
true
```

It is also possible to specify an arbitrary `DiagonalForm`

having numerical values for the basis with degeneracy `D"1,1,1,0"`

, although the `Signature`

format has a more compact representation. Further development will result in more metric types.

Declaring an additional plane at infinity is done by specifying it in the string constructor with $\infty$ at the first index (i.e. Riemann sphere `S"∞+++"`

). The hyperbolic geometry can be declared by $\emptyset$ subsequently (i.e. Minkowski spacetime `S"∅+++"`

). Additionally, the *null-basis* based on the projective split for confromal geometric algebra would be specified with `∞∅`

initially (i.e. 5D CGA `S"∞∅+++"`

). These two declared basis elements are interpreted in the type system.

The index number $n$ of the `TensorBundle`

corresponds to the total number of generator elements. However, even though `V"∞∅+++"`

is of type `TensorBundle{5,3}`

with $5$ generator elements, it can be internally recognized in the direct sum algebra as being an embedding of a 3-index `TensorBundle{3,0}`

with additional encoding of the null-basis (origin and point at infinity) in the parameter $\mathbb P$ of the `TensorBundle{n,ℙ}`

type.

The `tangent`

map takes $V$ to its tangent space and can be applied repeatedly for higher orders, such that `tangent(V,μ,ν)`

can be used to specify $\mu$ and $\nu$.

```
julia> V = tangent(ℝ^3)
T¹⟨+++₁⟩
julia> tangent(V')
T²⟨----¹⟩'
julia> V⊕V'
T¹⟨+++---₁¹⟩*
```

The direct sum operator $\oplus$ can be used to join spaces (alternatively $+$), and the dual space functor $'$ is an involution which toggles a dual vector space with inverted signature.

```
julia> V = ℝ'⊕ℝ^3
⟨-+++⟩
julia> V'
⟨+---⟩'
julia> W = V⊕V'
⟨-++++---⟩*
```

The direct sum of a `TensorBundle`

and its dual $V\oplus V'$ represents the full mother space $V*$.

```
julia> collect(V) # all SubManifold vector basis elements
DirectSum.Basis{⟨-+++⟩,16}(⟨____⟩, ⟨-___⟩, ⟨_+__⟩, ⟨__+_⟩, ⟨___+⟩, ⟨-+__⟩, ⟨-_+_⟩, ⟨-__+⟩, ⟨_++_⟩, ⟨_+_+⟩, ⟨__++⟩, ⟨-++_⟩, ⟨-+_+⟩, ⟨-_++⟩, ⟨_+++⟩, ⟨-+++⟩)
julia> collect(SubManifold(V')) # all covector basis elements
DirectSum.Basis{⟨+---⟩',16}(w, w¹, w², w³, w⁴, w¹², w¹³, w¹⁴, w²³, w²⁴, w³⁴, w¹²³, w¹²⁴, w¹³⁴, w²³⁴, w¹²³⁴)
julia> collect(SubManifold(W)) # all mixed basis elements
DirectSum.Basis{⟨-++++---⟩*,256}(v, v₁, v₂, v₃, v₄, w¹, w², w³, w⁴, v₁₂, v₁₃, v₁₄, v₁w¹, v₁w², v₁w³, v₁w⁴, v₂₃, v₂₄, v₂w¹, v₂w², v₂w³, v₂w⁴, v₃₄, v₃w¹, v₃w², v₃w³, v₃w⁴, v₄w¹, v₄w², v₄w³, v₄w⁴, w¹², w¹³, w¹⁴, w²³, w²⁴, w³⁴, v₁₂₃, v₁₂₄, v₁₂w¹, v₁₂w², v₁₂w³, v₁₂w⁴, v₁₃₄, v₁₃w¹, v₁₃w², v₁₃w³, v₁₃w⁴, v₁₄w¹, v₁₄w², v₁₄w³, v₁₄w⁴, v₁w¹², v₁w¹³, v₁w¹⁴, v₁w²³, v₁w²⁴, v₁w³⁴, v₂₃₄, v₂₃w¹, v₂₃w², v₂₃w³, v₂₃w⁴, v₂₄w¹, v₂₄w², v₂₄w³, v₂₄w⁴, v₂w¹², v₂w¹³, v₂w¹⁴, v₂w²³, v₂w²⁴, v₂w³⁴, v₃₄w¹, v₃₄w², v₃₄w³, v₃₄w⁴, v₃w¹², v₃w¹³, v₃w¹⁴, v₃w²³, v₃w²⁴, v₃w³⁴, v₄w¹², v₄w¹³, v₄w¹⁴, v₄w²³, v₄w²⁴, v₄w³⁴, w¹²³, w¹²⁴, w¹³⁴, w²³⁴, v₁₂₃₄, v₁₂₃w¹, v₁₂₃w², v₁₂₃w³, v₁₂₃w⁴, v₁₂₄w¹, v₁₂₄w², v₁₂₄w³, v₁₂₄w⁴, v₁₂w¹², v₁₂w¹³, v₁₂w¹⁴, v₁₂w²³, v₁₂w²⁴, v₁₂w³⁴, v₁₃₄w¹, v₁₃₄w², v₁₃₄w³, v₁₃₄w⁴, v₁₃w¹², v₁₃w¹³, v₁₃w¹⁴, v₁₃w²³, v₁₃w²⁴, v₁₃w³⁴, v₁₄w¹², v₁₄w¹³, v₁₄w¹⁴, v₁₄w²³, v₁₄w²⁴, v₁₄w³⁴, v₁w¹²³, v₁w¹²⁴, v₁w¹³⁴, v₁w²³⁴, v₂₃₄w¹, v₂₃₄w², v₂₃₄w³, v₂₃₄w⁴, v₂₃w¹², v₂₃w¹³, v₂₃w¹⁴, v₂₃w²³, v₂₃w²⁴, v₂₃w³⁴, v₂₄w¹², v₂₄w¹³, v₂₄w¹⁴, v₂₄w²³, v₂₄w²⁴, v₂₄w³⁴, v₂w¹²³, v₂w¹²⁴, v₂w¹³⁴, v₂w²³⁴, v₃₄w¹², v₃₄w¹³, v₃₄w¹⁴, v₃₄w²³, v₃₄w²⁴, v₃₄w³⁴, v₃w¹²³, v₃w¹²⁴, v₃w¹³⁴, v₃w²³⁴, v₄w¹²³, v₄w¹²⁴, v₄w¹³⁴, v₄w²³⁴, w¹²³⁴, v₁₂₃₄w¹, v₁₂₃₄w², v₁₂₃₄w³, v₁₂₃₄w⁴, v₁₂₃w¹², v₁₂₃w¹³, v₁₂₃w¹⁴, v₁₂₃w²³, v₁₂₃w²⁴, v₁₂₃w³⁴, v₁₂₄w¹², v₁₂₄w¹³, v₁₂₄w¹⁴, v₁₂₄w²³, v₁₂₄w²⁴, v₁₂₄w³⁴, v₁₂w¹²³, v₁₂w¹²⁴, v₁₂w¹³⁴, v₁₂w²³⁴, v₁₃₄w¹², v₁₃₄w¹³, v₁₃₄w¹⁴, v₁₃₄w²³, v₁₃₄w²⁴, v₁₃₄w³⁴, v₁₃w¹²³, v₁₃w¹²⁴, v₁₃w¹³⁴, v₁₃w²³⁴, v₁₄w¹²³, v₁₄w¹²⁴, v₁₄w¹³⁴, v₁₄w²³⁴, v₁w¹²³⁴, v₂₃₄w¹², v₂₃₄w¹³, v₂₃₄w¹⁴, v₂₃₄w²³, v₂₃₄w²⁴, v₂₃₄w³⁴, v₂₃w¹²³, v₂₃w¹²⁴, v₂₃w¹³⁴, v₂₃w²³⁴, v₂₄w¹²³, v₂₄w¹²⁴, v₂₄w¹³⁴, v₂₄w²³⁴, v₂w¹²³⁴, v₃₄w¹²³, v₃₄w¹²⁴, v₃₄w¹³⁴, v₃₄w²³⁴, v₃w¹²³⁴, v₄w¹²³⁴, v₁₂₃₄w¹², v₁₂₃₄w¹³, v₁₂₃₄w¹⁴, v₁₂₃₄w²³, v₁₂₃₄w²⁴, v₁₂₃₄w³⁴, v₁₂₃w¹²³, v₁₂₃w¹²⁴, v₁₂₃w¹³⁴, v₁₂₃w²³⁴, v₁₂₄w¹²³, v₁₂₄w¹²⁴, v₁₂₄w¹³⁴, v₁₂₄w²³⁴, v₁₂w¹²³⁴, v₁₃₄w¹²³, v₁₃₄w¹²⁴, v₁₃₄w¹³⁴, v₁₃₄w²³⁴, v₁₃w¹²³⁴, v₁₄w¹²³⁴, v₂₃₄w¹²³, v₂₃₄w¹²⁴, v₂₃₄w¹³⁴, v₂₃₄w²³⁴, v₂₃w¹²³⁴, v₂₄w¹²³⁴, v₃₄w¹²³⁴, v₁₂₃₄w¹²³, v₁₂₃₄w¹²⁴, v₁₂₃₄w¹³⁴, v₁₂₃₄w²³⁴, v₁₂₃w¹²³⁴, v₁₂₄w¹²³⁴, v₁₃₄w¹²³⁴, v₂₃₄w¹²³⁴, v₁₂₃₄w¹²³⁴)
```

In addition to the direct-sum operation, several other operations are supported, such as $\cup,\cap,\subseteq,\supseteq$ for set operations. Due to the design of the `TensorBundle`

dispatch, these operations enable code optimizations at compile-time provided by the bit parameters.

```
julia> ℝ⊕ℝ' ⊇ Manifold(1)
true
julia> ℝ ∩ ℝ' == Manifold(0)
true
julia> ℝ ∪ ℝ' == ℝ⊕ℝ'
true
```

**Remark**. Although some of the operations like $\cup$ and $\oplus$ are similar and sometimes result in the same values, the `union`

and `⊕`

are entirely different operations in general.

\[\bigcup T^{\mu_i}V_i = \left(|\mathbb P|+\max\{n_i-|\mathbb P_i|\}_i,\, \bigcup \mathbb P_i,\, \cup g_i,\, \max\{\mu_i\}_i\right)\]

\[\bigoplus T^{\mu_i}V_i = \left(|\mathbb P|+\sum (n_i-|\mathbb P_i|),\, \bigcup \mathbb P_i,\, \oplus_i g_i,\,\max\{\mu_i\}_i\right)\]

Calling manifolds with sets of indices constructs the subspace representations. Given `M(s::Int...)`

one can encode `SubManifold{length(s),M,s}`

with induced orthogonal space $Z$, such that computing unions of submanifolds is done by inspecting the parameter $s\in V\subseteq W$ and $s\notin Z$.

```
julia> (ℝ^5)(3,5)
⟨__+_+⟩
julia> dump(ans)
SubManifold{⟨+++++⟩, 2, 0x0000000000000014} ⟨__+_+⟩
```

Here, calling a `Manifold`

with a set of indices produces a `SubManifold`

representation.

\[T^eV \subset T^\mu W \iff \exists Z\in\text{Vect}_{\mathbb K}(T^e(V\oplus Z) = T^{e\leq \mu}W,\,V\perp Z).\]

Operations on `Manifold`

types is automatically handled at compile time.

To help provide a commonly shared and readable indexing to the user, some extended dual index print methods with full alphanumeric characters (62+2) are provided:

```
julia> DirectSum.printindices(stdout,DirectSum.indices(UInt(2^62-1)),false,"v")
v₁₂₃₄₅₆₇₈₉₀abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ
julia> DirectSum.printindices(stdout,DirectSum.indices(UInt(2^62-1)),false,"w")
w¹²³⁴⁵⁶⁷⁸⁹⁰ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
```

An application of this is in the `Grasmann`

package, where dual indexing is used.

More information about `DirectSum`

is available at https://github.com/chakravala/DirectSum.jl

## Approaching ∞ dimensions with `SparseBasis`

and `ExtendedBasis`

In order to work with a `TensorAlgebra{V}`

, it is necessary for some computations to be cached. This is usually done automatically when accessed.

```
julia> Λ(7) ⊕ Λ(7)'
DirectSum.SparseBasis{⟨+++++++-------⟩*,16384}(v, ..., v₁₂₃₄₅₆₇w¹²³⁴⁵⁶⁷)
```

One way of declaring the cache for all 3 combinations of a `TensorBundle{N}`

and its dual is to ask for the sum `Λ(V) + Λ(V)'`

, which is equivalent to `Λ(V⊕V')`

, but this does not initialize the cache of all 3 combinations unlike the former.

Staging of precompilation and caching is designed so that a user can smoothly transition between very high dimensional and low dimensional algebras in a single session, with varying levels of extra caching and optimizations. The parametric type formalism in `Grassmann`

is highly expressive and enables pre-allocation of geometric algebra computations involving specific sparse subalgebras, including the representation of rotational groups.

It is possible to reach `Simplex`

elements with up to $N=62$ vertices from a `TensorAlgebra`

having higher maximum dimensions than supported by Julia natively.

```
julia> Λ(62)
DirectSum.ExtendedBasis{⟨××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××××⟩,4611686018427387904}(v, ..., v₁₂₃₄₅₆₇₈₉₀abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ)
julia> Λ(62).v32a87Ng
-1v₂₃₇₈agN
```

The 62 indices require full alpha-numeric labeling with lower-case and capital letters. This now allows you to reach up to $4,611,686,018,427,387,904$ dimensions with Julia `using Grassmann`

. Then the volume element is

v₁₂₃₄₅₆₇₈₉₀abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ

Full `MultiVector`

allocations are only possible for $N\leq22$, but sparse operations are also available at higher dimensions. While `DirectSum.Basis{V}`

is a container for the `TensorAlgebra`

generators of $V$, the `Basis`

is only cached for $N\leq8$. For the range of dimensions $8<N\leq22$, the `SparseBasis`

type is used.

```
julia> Λ(22)
DirectSum.SparseBasis{⟨++++++++++++++++++++++⟩,4194304}(v, ..., v₁₂₃₄₅₆₇₈₉₀abcdefghijkl)
```

This is the largest `SparseBasis`

that can be generated with Julia, due to array size limitations.

To reach higher dimensions with $N>22$, the `DirectSum.ExtendedBasis`

type is used. It is suficient to work with a 64-bit representation (which is the default). And it turns out that with 62 standard keyboard characters, this fits.

```
julia> V = ℝ^22
⟨++++++++++++++++++++++⟩
julia> Λ(V+V')
DirectSum.ExtendedBasis{⟨++++++++++++++++++++++----------------------⟩*,17592186044416}(v, ..., v₁₂₃₄₅₆₇₈₉₀abcdefghijklw¹²³⁴⁵⁶⁷⁸⁹⁰ABCDEFGHIJKL)
```

At 22 dimensions and lower there is better caching, with further extra caching for 8 dimensions or less. Thus, the largest Hilbert space that is fully reachable has 4,194,304 dimensions, but we can still reach out to 4,611,686,018,427,387,904 dimensions with the `ExtendedBasis`

built in. It is still feasible to extend to a further super-extended 128-bit representation using the `UInt128`

type (but this will require further modifications of internals and helper functions. To reach into infinity even further, it is theoretically possible to construct ultra-extensions also using dictionaries. Full `MultiVector`

elements are not representable when `ExtendedBasis`

is used, but the performance of the `Basis`

and sparse elements should be just as fast as for lower dimensions for the current `SubAlgebra`

and `TensorAlgebra`

types. The sparse representations are a work in progress to be improved with time.

## Interoperability for `TensorAlgebra{V}`

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*. 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 `TensorBundle`

. This means that different packages can create tensor types having a common underlying `TensorBundle`

structure. For example, this is mainly used in *Grassmann.jl* to define various `SubAlgebra`

, `TensorTerm`

and `TensorMixed`

types, each with subtypes. Externalizing the abstract type helps extend the dispatch to other packages.

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.

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, e.g. $\bigwedge :\Lambda^{p_1}V_1\times\dots\times\Lambda^{p_g}V_g \rightarrow \Lambda^{\sum_kp_k}\bigcup_k V_k$. Some of the method names like $+,-,*,\otimes,\circledast,\odot,\boxtimes,\star$ for `TensorAlgebra`

elements are shared across different packages, with interoperability.

```
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))
```

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 `TensorBundle`

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 $\cup$ 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.

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. The universal interoperability of `LinearAlgebra.UniformScaling`

as a pseudoscalar element which takes on the `TensorBundle`

form of any other `TensorAlgebra`

element is handled globally. This enables the usage of $I$ from `LinearAlgebra`

as a universal pseudoscalar element.

```
(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
```

Utility methods such as `scalar, involute, norm, norm2, unit, even, odd`

are also defined.

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 $\cup$ 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.

More information about `DirectSum`

is available at https://github.com/chakravala/AbstractTensors.jl