Functions
The elementary tensor operations can also be accessed via functions, mainly for compatibility with older versions of this toolbox. The function-based syntax is also required when the contraction pattern is not known at compile time but is rather determined dynamically.
The basic exposed interface, as listed below, makes use of any iterable IA
, IB
or IC
to denote labels of indices, in a similar fashion as when used in the context of @tensor
. When making use of this functionality, in-place operations are no longer supported, as these are reserved for the expert mode. Note that the return type is only inferred when the labels are entered as tuples, and also IC
is specified. The type of labels that are allowed are encoded in the following type aliases
TensorOperations.LabelType
— Typeconst LabelType = Union{Int,Symbol,Char}
Alias for supported label types.
TensorOperations.Labels
— Typeconst Labels{I<:LabelType} = Union{Tuple{Vararg{I}},Vector{I}}
Alias for supported label containers.
The expert mode exposes both mutating and non-mutating versions of these functions. In this case, selected indices are determined through permutations, specified by pA
, pB
and pC
. In order to distinguish from the non-mutating version in simple mode, overlapping functionality is distinguished by specializing on these permutations, which are required to take a particular form of the type Index2Tuple
.
TensorOperations.Index2Tuple
— TypeIndex2Tuple{N₁,N₂} = Tuple{NTuple{N₁,Int},NTuple{N₂,Int}}
A specification of a permutation of N₁ + N₂
indices that are partitioned into N₁
left and N₂
right indices.
The motivation for this particular convention for specifying permutations comes from the fact that for many operations, it is useful to think of a tensor as a linear map or matrix, in which its different indices are partioned into two groups, the first of which correspond to the range of the linear map (the row index of the associated matrix), whereas the second group corresponds to the domain of the linear map (the column index of the associated matrix). This is most obvious for tensor contraction, which then becomes equivalent to matrix multiplication (which is also how it is implemented by the StridedBLAS
backend). While less relevant for tensor permutations, we use this convention throughout for uniformity and generality (e.g. for compatibility with libraries that always represent tensors as linear maps, such as TensorKit.jl).
Note, finally, that only the expert mode call style exposes the ability to select custom backends and allocators.
Non-mutating functions
TensorOperations.tensorcopy
— Functiontensorcopy([IC=IA], A, IA, [conjA=false, [α=1]]; [backend=..., allocator=...])
tensorcopy(A, pA::Index2Tuple, conjA, α, [backend, allocator]) # expert mode
Create a copy of A
, where the dimensions of A
are assigned indices from the iterable IA
and the indices of the copy are contained in IC
. Both iterables should contain the same elements, optionally in a different order.
The result of this method is equivalent to α * permutedims(A, pA)
where pA
is the permutation such that IC = IA[pA]
. The implementation of tensorcopy
is however more efficient on average, especially if Threads.nthreads() > 1
.
The optional argument conjA
can be used to specify whether the input tensor should be conjugated (true
) or not (false
), whereas α
can be used to scale the result. It is also optional to specify a backend implementation to use, and an allocator to be used if temporary tensor objects are needed.
See also tensorcopy!
.
TensorOperations.tensoradd
— Functiontensoradd([IC=IA], A, IA, [conjA], B, IB, [conjB], [α=1, [β=1]]; [backend=..., allocator=...])
tensoradd(A, pA::Index2Tuple, conjA, B, pB::Index2Tuple, conjB, α=1, β=1, [backend, allocator]) # expert mode
Return the result of adding arrays A
and B
where the iterables IA
and IB
denote how the array data should be permuted in order to be added. More specifically, the result of this method is equivalent to α * permutedims(A, pA) + β * permutedims(B, pB)
where pA
(pB
) is the permutation such that IC = IA[pA]
(IB[pB]
). The implementation of tensoradd
is however more efficient on average, as the temporary permuted arrays are not created.
Optionally, the symbols conjA
and conjB
can be used to specify whether the input tensors should be conjugated (true
) or not (false
).
See also tensoradd!
.
TensorOperations.tensortrace
— Functiontensortrace([IC], A, IA, [conjA], [α=1]; [backend=..., allocator=...])
tensortrace(A, p::Index2Tuple, q::Index2Tuple, conjA, α=1, [backend, allocator]) # expert mode
Trace or contract pairs of indices of tensor A
, by assigning them identical indices in the iterable IA
. The untraced indices, which are assigned a unique index, can be reordered according to the optional argument IC
. The default value corresponds to the order in which they appear. Note that only pairs of indices can be contracted, so that every index in IA
can appear only once (for an untraced index) or twice (for an index in a contracted pair).
Optionally, the symbol conjA
can be used to specify that the input tensor should be conjugated.
See also tensortrace!
.
TensorOperations.tensorcontract
— Functiontensorcontract([IC], A, IA, [conjA], B, IB, [conjB], [α=1]; [backend=..., allocator=...])
tensorcontract(A, pA::Index2Tuple, conjA, B, pB::Index2Tuple, conjB, pAB::Index2Tuple, α=1, [backend, allocator]) # expert mode
Contract indices of tensor A
with corresponding indices in tensor B
by assigning them identical labels in the iterables IA
and IB
. The indices of the resulting tensor correspond to the indices that only appear in either IA
or IB
and can be ordered by specifying the optional argument IC
. The default is to have all open indices of A
followed by all open indices of B
. Note that inner contractions of an array should be handled first with tensortrace
, so that every label can appear only once in IA
or IB
seperately, and once (for an open index) or twice (for a contracted index) in the union of IA
and IB
.
Optionally, the symbols conjA
and conjB
can be used to specify that the input tensors should be conjugated.
See also tensorcontract!
.
TensorOperations.tensorproduct
— Functiontensorproduct([IC], A, IA, [conjA], B, IB, [conjB], [α=1]; [backend=..., allocator=...])
tensorproduct(A, pA::Index2Tuple, conjA, B, pB::Index2Tuple, conjB, pAB::Index2Tuple, α=1, [backend, allocator]) # expert mode
Compute the tensor product (outer product) of two tensors A
and B
, i.e. returns a new tensor C
with ndims(C) = ndims(A) + ndims(B)
. The indices of the output tensor are related to those of the input tensors by the pattern specified by the indices. Essentially, this is a special case of tensorcontract
with no indices being contracted over. This method checks whether the indices indeed specify a tensor product instead of a genuine contraction.
Optionally, the symbols conjA
and conjB
can be used to specify that the input tensors should be conjugated.
See also tensorproduct!
and tensorcontract
.
Mutating functions
TensorOperations.tensorcopy!
— Functiontensorcopy!(C, A, pA::Index2Tuple, conjA=false, α=1, [backend, allocator])
Copy the contents of tensor A
into C
, where the dimensions A
are permuted according to the permutation and repartition pA
.
The result of this method is equivalent to α * permutedims!(C, A, pA)
.
Optionally, the flag conjA
can be used to specify whether the input tensor should be conjugated (true
) or not (false
).
The object C
must not be aliased with A
.
See also tensorcopy
and tensoradd!
TensorOperations.tensoradd!
— Functiontensoradd!(C, A, pA, conjA, α=1, β=1 [, backend, allocator])
Compute C = β * C + α * permutedims(opA(A), pA)
. The operation opA
acts as identity
if conjA
equals :N
and as conj
if conjA
equals :C
. Optionally specify a backend implementation to use, and an allocator to be used if temporary tensor objects are needed.
The permutation needs to be trivial or C
must not be aliased with A
.
See also tensoradd
.
TensorOperations.tensortrace!
— Functiontensortrace!(C, A, p, q, conjA, α=1, β=0 [, backend, allocator])
Compute C = β * C + α * permutedims(opA(A), (p, q))
, where A
is partially traced, such that indices in q[1]
are contracted with indices in q[2]
, and the other indices are permuted according to p
. The operation opA
acts as identity
if conjA
equals false
and as conj
if conjA
equals true
. Optionally specify a backend implementation to use, and an allocator to be used if temporary tensor objects are needed.
The object C
must not be aliased with A
.
See also tensortrace
.
TensorOperations.tensorcontract!
— Functiontensorcontract!(C, A, pA, conjA, B, pB, conjB, pAB, α=1, β=0 [, backend, allocator])
Compute C = β * C + α * permutedims(contract(opA(A), opB(B)), pAB)
without creating the intermediate temporary, where A
and B
are contracted such that the indices pA[2]
of A
are contracted with indices pB[1]
of B
. The remaining indices (pA[1]..., pB[2]...)
are then permuted according to pAB
. The operation opA
acts as identity
if conjA
equals false
and as conj
if conjA
equals true
; the operation opB
is determined by conjB
analogously. Optionally specify a backend implementation to use, and an allocator to be used if temporary tensor objects are needed.
The object C
must not be aliased with A
or B
.
See also tensorcontract
.
TensorOperations.tensorproduct!
— Functiontensorproduct!(C, A, pA::Index2Tuple, conjA, B, pB::Index2Tuple, conjB, pAB::Index2Tuple, α=1, β=0, [backend, allocator])
Compute the tensor product (outer product) of two tensors A
and B
, i.e. a wrapper of tensorcontract!
with no indices being contracted over. This method checks whether the indices indeed specify a tensor product instead of a genuine contraction. Optionally specify a backend implementation to use, and an allocator to be used if temporary tensor objects are needed.
The object C
must not be aliased with A
or B
.
See als tensorproduct
and tensorcontract!
.