TensorToolbox.jl
Note: the contents of this README might currently not reflect the actual status of the code
Introduction
TensorToolbox.jl is a Julia package for working with tensors, which are consistently treated as the elements of a tensor product of vector spaces or some subspace thereof. While tensors can typically be represented as multidimensional arrays with respect to a chosen basis, they have a richer mathematical structure depending on the type of vector spaces used in the tensor product construction.
Henceforth, we represent a tensor using index notation and refer to the different "dimensons" as indices. The tensor $T^{i_1;;;\overline{\imath}4}{;i_2\overline{\imath}_3}
- contravariant index $i_1$ : normal vector space $V_1$
- contravariant index $i_2$ : dual space $V_2^{\ast}$
- barred or dotted covariant index $\overline{\imath}_3$ : complex conjugate space $\overline{V}_3$
- barred or dotted contravariant index $\overline{\imath}_4$ : complex conjugate of the dual space $\overline{V}_4^{\ast}$ , which is equivalent to the dual of the complex conjugate space
The four different vector spaces
Vector spaces
Elementary vector spaces
Tensors in TensorToolbox.jl are treated as elements of a tensor product of a homogeneous family of elementary vector spaces, which we also refer to as index spaces and can be user defined. We thus define a type hierarchy for representing a hierarchy of common vector spaces:
abstract VectorSpace
abstract ElementarySpace{F} <: VectorSpace
const IndexSpace = ElementarySpace
where the parameter F
can be used to represent a field over which the vector space is defined. In particular, we define a unicode shorthand for two common fields, which we take from the Julia Number
type hierarchy:
const ℝ=Real
const ℂ=Complex{Real}
Every ElementarySpace
should implement the following methods
-
dim(::ElementarySpace) -> ::Int
returns the dimension of the space as anInt
-
dual{S<:ElementarySpace}(::S) -> ::S
returns the dual spacedual(V)
, preferably using an instance of the same concrete type (i.e. not via type parameters) to combine well with the way tensors are defined; this should satisfydual(dual(V)==V
-
conj{S<:ElementarySpace}(::S) -> ::S
returns the complex conjugate spaceconj(V)
, preferably using an instance of the same concrete type (i.e. not via type parameters) to combine well with the way tensors are defined; this should satisfyconj(conj(V))==V
and we automatically haveconj{F<:Real}(V::ElementarySpace{F}) = V
.
In particular, there is concrete type GeneralSpace
which is completely characterized by its field F
, its dimension and whether its the dual and/or complex conjugate of
We furthermore define the abstract type
abstract InnerProductSpace{F} <: ElementarySpace{F}
to contain all vector spaces V
which have an inner product and thus a natural mapping from dual(V)
to V
(for F<:Real
) or from dual(V)
to conj(V)
(for F<:Complex
). This mapping is provided by the metric, but no further support for working with metrics is currently implemented.
Finally there is
abstract EuclideanSpace{F} <: InnerProductSpace{F}
to contain all spaces V
with a standard Euclidean inner product (i.e. where the metric is the identity). These spaces have the natural isomorphisms dual(V)==V
(for F<:Real
) or dual(V)==conj(V)
(for F<:Complex
). In particular, we have two concrete types
immutable CartesianSpace <: EuclideanSpace{ℝ}
d::Int
end
immutable ComplexSpace <: EuclideanSpace{ℂ}
d::Int
dual::Bool
end
to represent the Euclidean spaces ℝ^d
and ℂ^d
.
WIP: Graded spaces, superselection sectors and braiding
If tensors are used to describe system with a certain symmetry corresponding to a group
The implementation of graded vector spaces is currently limited to those cases where
abstract UnitaryRepresentationSpace{G<:Sector} <: EuclideanSpace{ℂ}
Here, Sector
is an abstract type. A subtype of Sector
corresponds to a particular fusion category and the possible objects correspond to the different labels, i.e. the different charges or superselection sectors. Sector
objects should support the functionality to map objects (labels) to the corresponding conjugate label (anticharge), to create the trivial object (identity, zero charge) and to determine the outcome of the fusion product. So far, only abelian categories are implemented, corresponding to the representations of abelian groups:
abstract Sector
abstract Abelian <: Sector
abstract NonAbelian <: Sector
immutable Parity <: Abelian
charge::Bool
end
immutable ZNCharge{N} <: Abelian
charge::Int
ZNCharge(charge::Int)=new(mod(charge,N))
end
immutable U1Charge <: Abelian
charge::Int
end
TODO: Braiding
When the different superselection sectors correspond to e.g. different fermion or anyon occupation numbers, a natural action will arise when changing the order of the corresponding vectors in a tensor product. The graded vector space thus becomes a braided vector space. The simplest example is that of a vector space V
graded by fermion parity. An element of V1⊗V2
can be mapped to one of V2⊗V1
by permuting the two tensor indices and adding a phase -1
in the sector where both indices have an odd fermion number. More generally, a complete braiding tensor
ElementarySpace methods
Composite spaces
Composite spaces are built out of elementary vector spaces of a homogeneous type S
. The most relevant case is the abstract family TensorSpace{S,N}
used to denote certain subspaces in the tensor product space of N
vector spaces of type S
. These spaces will be used to define rank-N
tensors, where the different tensor indices
abstract CompositeSpace{S<:ElementarySpace} <: VectorSpace
abstract TensorSpace{S<:ElementarySpace,N} <: CompositeSpace{S}
The homogenity restriction is the only sensible way of defining tensor product spaces, since there is no point in defining i.e. a tensor with a group action on some indices and not on other indices and it is even impossible to define the tensor product space of vector spaces over different fields. It is thus not possible to construct tensor product spaces of e.g.
ProductSpace
The complete tensor product space is represented by the concrete type ProductSpace{S,N}
. This corresponds to the definitions
immutable ProductSpace{S<:ElementarySpace,N} <: TensorSpace{S,N}
spaces::NTuple{N, S}
end
The ProductSpace
of a set of elementary spaces V1
, V2
, ... of type S
can be created as V1 ⊗ V2 ⊗ ...
. Product spaces can be iterated over and indexed in order to extract the elementary spaces, or the tensor product of a subset of them. The dual and conjugate spaces are defined by mapping these actions to the respective elementary vector spaces V1
, V2
, ... For convience, we also define the transpose
of a ProductSpace
by reversing the factors V1
to VN
, and the ctranspose
by reversing the conjugated spaces. While there is no such thing as the transpose of a vector space, this definition is convenient because it is compatible with the way (c)transpose
is defined for tensors. Finally, the dim
of a ProductSpace
is given by the product of the dim
of its constituents.
WIP: InvariantSpace
A InvariantSpace
corresponds to the subspace of the tensor product of some UnitaryRepresentationSpaces
that fuses to the identity (i.e. total 'charge' zero). In the case of irreducible representations of groups, it corresponds to the invariant subspace, i.e. the subspace of the tensor product that couples to the trivial representation. The different sectors of an InvariantSpace
are labelled not only by the set of sectors of the individual elementary spaces (under the constraint that they have a fusion channel to trivial charge), but also by the intermediate fusion sectors. This gives rise to the concept of a fusion tree.
In order to describe and manipulate the trivial sector in the tensor product of UnitaryRepresentationSpace
s, one thus needs to be able to store and manipulate fusion trees using recouplings (F-moves) or braidings (R-moves). So far, this has only been implemented for spaces with Abelian fusion rules and trivial braiding.
-
TODO: develop interface to work with fusion trees and braidings
-
TODO: implement some non-trivial cases (SU(2) symmetry, fermions, ...)
References:
- General theory of anyons and unitary fusion categoies
- General treatment of symmetries in tensors
- U1 symmetric tensors
- SU2 symmetric tensors
- Anyonic tensors 1 , Anyonic tensors 2
TODO: Symmetric and antisymmetric vector spaces (Fock space)
For the tensor product of N
identical copies of a given vector space V
, we can also consider the symmetric or antisymmetric subspace of N
particle boson or fermion Fock space corresponding to a single particle Hilbert Space V
. This has of course also other applications and can be extended to tensors with (anti)symmetry in subsets of indices.
Tensors
The most important elements in TensorToolbox.jl
are of course tensors. A rank N
tensor is interpreted as the element of (a subspace of) the tensor product of some N
elementary vector spaces, represented as a TensorSpace{S,N}
object V
. A tensor needs to store its components as a list of numbers of type T<:Number
. The following observations are in order:
- The element type
T
must not be the same as the field of the vector space, i.e. a tensor in a (tensor product) ofComplexSpace
s can have real components, but a tensor in the product space ofCartesianSpace
s should not have complex entries. However, this is not strictly enforced. - The components represent the tensor with respect to a canonical choice of basis in
V
; so far there is no support to represent different basis choices and the transformations between them. This might change in the future. - The number of (independent) components of a tensor is given by
dim V
. WhenV
is a proper subspace ofV1 ⊗ V2 ⊗ ... ⊗ VN
, thendim V
is not just the product of the dimensions of the elementary spaces and the independent components cannot simply be represent as aN
-dimensional array.
Different tensor types
The only difference between tensors (so far) is how their independent components are stored. All other characteristics are encoded in the type of vector space.
DenseTensor
We start the type hierarchy with an abstract type and currently have a single concrete tensor type, DenseTensor
, that stores its components using a Vector{T}
, corresponding to the following definitions
abstract AbstractTensor{S,T,N}
immutable DenseTensor{S,T,N,P} <: AbstractTensor{S,T,N}
data::Vector{T}
space::P
end
Here, we should have P<:TensorSpace{S,N}
. With the current Julia type system, this cannot be enforced in the type but only in its constructor (which also checks that length(data)=dim(space)
. This might change with the type system redesign.
We can then define some useful type aliasses for e.g. the standard tensor living in the full tensor product space
typealias Tensor{S,T,N} DenseTensor{S,T,N,ProductSpace{S,N}}
typealias CartesianTensor{T,N} Tensor{CartesianSpace,T,N}
typealias ComplexTensor{T,N} Tensor{ComplexSpace,T,N}
typealias InvariantTensor{S,T,N} DenseTensor{S,T,N,InvariantSpace{S,N}}
typealias U1Tensor{T,N} ...
and so on.
A tensor can be created from a set of components as
tensor(data, space)
where data
can be an arbitrary Vector{T}
.
TODO: DiagonalTensor
For the specific case of a rank N=2
tensor in V ⊗ dual(V)
, it is often useful to have an explicit diagonal representation, e.g. to store the eigenvalues or singular values corresponding to a given tensor factorization (see below).
Other tensors ?
Should there be sparse tensors?
Tensor properties
The basic tensor methods allow to construct tensors and query their characteristics
-
space(t)
returns the vector space of a tensort
. -
eltype(t)
returns the element typeT
of the coefficient vector. -
numind(t)=order(t)
returns the number of tensor indicesN
, i.e. the number of elementary vector spaces inspace(t)
. -
in(t,V)
can be used to check ifspace(t)
is a subspace ofV
. -
vec(t)
returns the coefficient vectordata
which allows to modify the tensor components -
full(t)
returns anArray{T,N}
representation of a rankN
tensor. Only whenspace(t)
is aProductSpace
is this isomorphic to the vector of coefficients, otherwise zeros or repeated coefficients might appear. Therefore,full(t)
does not share data with the tensor and cannot be used to modified its contens.
Constructing and converting tensors
-
tensor(data,V)
can be used to construct aDenseTensor
. Here,data
represents an arbitraryArray{T,N}
. If the vector spaceV
is provided, the multidimensional characteristics ofdata
are ignored. Onlyvec(data)
is used and the only requirement is thatlength(data)
equalsdim(V)
. IfV
is absent, thentensor(data)
creates aCartesianTensor
ifT<:Real
withV=ProductSpace(map(CartesianSpace,size(data)))
. IfT<:Complex
, aComplexTensor
is constructed, even though there it is already ambiguous whether the normal complex Euclidean space or the dual space should be constructed for every index. -
zeros(T,V)
creates a tensor inV
filled with zero coefficients, which is equivalent to the zero vector. IfT
is omitted, it is given the default valueT=Float64
. -
rand(T,V)
creates a tensor inV
filled with random coefficients. A default value ofT=Float64
is asssumed whenT
is omitted. -
similar(t,T,V)
constructs an unitialized tensor similar tot
, but with element typeT
and for different spaceV
(of the same type ofspace(t)
). -
complex(t)
convertst
to a tensor with complex-valued coefficients; it does nothing ifeltype(t)<:Complex
. -
real(t)
andimag(t)
returns a tensor with the real and imaginary parts of the coefficients; this is a basis-dependent operations and refers to the canonical basis with respect to which the coefficients are stored. -
float32
,float64
,complex64
andcomplex128
can be used to cast the tensor coefficients into a specific format.
Basic linear algebra methods
The following methods allow to perform basic linear algebra (corresponding to their interpretation as elements in a vector space):
- arithmetic: tensors in the same vector space can be added, subtracted en multiplied with scalars. There are also mutating methods such as
scale!
andaxpy!
. conj(t)
conjugates the tensor in the canonical basis. Note that this also maps the tensor to the spaceconj(V)
which is different fromV
. Therefore,conj!
is not an inplace operation but can be used to store the result of conjugating the tensorsrc
in a preallocated tensordst
inconj(V)
usingconj!(dst,src)
.transpose(t)
implements an isomorphism fromV=V1 ⊗ V2 ⊗ ... ⊗ VN
toreverse(V) = VN ⊗ ... ⊗ V2 ⊗ V1
, i.e. it reverses the order of the indices. For a tensor withN=1
, this has no effect. For a tensor withN=2
, this corresponds to the most general definition of the transpose of a linear map. A linear map $f:V\to W$ can be identified with a tensor inW ⊗ dual(V)
. The transpose of this tensor lives indual(V) ⊗ W
, which can be identified with a linear map fromdual(W)
todual(V)
, in accordance with the aforementioned definition. Only for real Euclidean vector spaces isdual(V) == V
and does this correspond to a map fromW
toV
. ForN>2
, there is no standard definition of transpose, but reversing all indices corresponds to the convention used in the Penrose graphical notation, where transposing corresponds to mirroring the diagrammatic representation of the tensor. There is again a mutating versiontranspose!(dst,src)
that allows to store the result of transposingsrc
in the preallocated tensordst
.ctranspose(t)
is equivalent toconj(transpose(t))
but performs this operation in a single step. In particular, forN=2
, it maps a tensor inW ⊗ dual(V)
to a tensor indual(conj(V)) ⊗ conj(W)
. For complex Euclidean spaces (wheredual(V)=conj(V)
) or real Euclidean spaces (wheredual(V)=V
andconj(V)=V
), the conjugate transpose of a tensor inW ⊗ dual(V)
is a tensor inV ⊗ dual(W)
, which can be interpreted as a linear map fromW
toV
, according to the definition of the adjoint map. As before,ctranpose!(dst,src)
stores the result in the preallocated destination tensor.dot(t1,t2)
computers the inner product between two tensorst1
andt2
. This is only possible ifspace(t1)==space(t2)
and if this space is the tensor product of elementary vector spaces with an inner product, i.e.S<:InnerProductSpace
. However, the interface for specifying general inner products still needs to be developed, and thus so fardot(t1,t2)
only works ifS<:EuclideanSpace
. We choose the canonical basis of euclidean spaces orthonormal, such thatdot(t1,t2) = dot(vec(t1),vec(t2))
, i.e. the inner product corresponds to the normal scalar product of the coefficient vectors.vecnorm(t)
computes the norm of tensort
; it is essentially equivalent tosqrt(dot(t,t))
and is therefore subject to the same restrictions (S<:EuclideanSpace
) and satisfiesvecnorm(t)=norm(vec(t))
.
Currently, conj
, transpose
and ctranspose
allocate new tensors for storing the result. This might change in the future such that they return a simple view over the same data, although this is not entirely trivial for tensors which do not live in a simple ProductSpace{S,N}
.
TODO: Indexing
Tensor operations
scalar(t)
can be applied to a rankN=0
tensor to construct the single scalar component, since in that casespace(t)
is an empty tensor product space and thus equivalent to the corresponding number field.
Tensor factorizations
Tensor Maps
Linear maps between tensor spaces with possible efficient implementation.
Tensor Networks
Bibliography
Footnotes
-
Wu-Ki Tung. Group Theory in Physics: Introduction to Symmetry Principles, Group Representations, and Special Functions in Classical and Quantum Physics. World Scientific Publishing Company, 1985.
↩