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.
These functions come in a mutating and non-mutating version. The mutating versions mimick the argument order of some of the BLAS functions, such as blascopy!
, axpy!
and gemm!
. Symbols A
and B
always refer to input arrays, whereas C
is used to denote the array where the result will be stored. They also return C
and are therefore type stable. The greek letters α
and β
denote scalar coefficients.
TensorOperations.tensorcopy!
— Function.tensorcopy!(A, IA, C, IC)
Copies A
into C
by permuting the dimensions according to the pattern specified by IA
and IC
. Both iterables should contain the same elements in a different order. The result of this method is equivalent to permutedims!(C, A, p)
where p
is the permutation such that IC=IA[p]
. The implementation of tensorcopy! is however more efficient on average, especially if Threads.nthreads() > 1
.
TensorOperations.tensoradd!
— Function.tensoradd!(α, A, IA, β, C, IC)
Updates C
to β*C + α * tensorcopy(A,IA,IC)
, but without creating the temporary permuted array.
See also: tensorcopy
TensorOperations.tensortrace!
— Function.tensortrace!(α, A, IA, β, C, IC)
Updates C
to β*C + α tensortrace(A,IA,IC)
, but without creating the temporary traced array.
See also: tensortrace
TensorOperations.tensorcontract!
— Function.tensorcontract!(α, A, labelsA, conjA, B, labelsB, conjB, β, C, labelsC)
Replaces C
with β C + α A * B
, where some indices of array A
are contracted with corresponding indices in array B
by assigning them identical labels in the iterables labelsA
and labelsB
. The arguments conjA
and conjB
should be of type Char
and indicate whether the data of arrays A
and B
, respectively, need to be conjugated (value 'C'
) or not (value 'N'
). Every label should appear exactly twice in the union of labelsA
, labelsB
and labelsC
, either in the intersection of labelsA
and labelsB
(for indices that need to be contracted) or in the interaction of either labelsA
or labelsB
with labelsC
, for indicating the order in which the open indices should be match to the indices of the output array C
.
TensorOperations.tensorproduct!
— Function.tensorproduct!(α, A, labelsA, B, labelsB, β, C, labelsC)
Replaces C with β C + α A * B
without any indices being contracted.
The non-mutating functions are simpler in not allowing scalar coefficients and conjugation. They also take a default value for the labels of the output array if these are not specified. However, the return type is only inferred if the labels are entered as tuples, and also IC
is specified. They are simply called as:
TensorOperations.tensorcopy
— Function.tensorcopy(A, IA, IC = IA)
Creates 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 in a different order.
The result of this method is equivalent to permutedims(A, p)
where p is the permutation such that IC = IA[p]
. The implementation of tensorcopy
is however more efficient on average, especially if Threads.nthreads() > 1
.
TensorOperations.tensoradd
— Function.tensoradd(A, IA, B, IB, IC = IA)
Returns the result of adding arrays A
and B
where the iterabels 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
tensorcopy(A, IA, IC) + tensorcopy(B, IB, IC)
but without creating the temporary permuted arrays.
TensorOperations.tensortrace
— Function.tensortrace(A, IA [, IC])
Trace or contract pairs of indices of array A
, by assigning them an 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).
TensorOperations.tensorcontract
— Function.tensorcontract(A, IA, B, IB[, IC])
Contract indices of array A
with corresponding indices in array B
by assigning them identical labels in the iterables IA
and IB
. The indices of the resulting array 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 array A
followed by all open indices of array 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 open index) or twice (for contracted index) in the union of IA
and IB
.
The contraction can be performed by a native Julia algorithm without creating any temporaries, or by first permuting the arrays such that the contraction becomes equivalent to a matrix product, which is then performed by BLAS. The latter is typically faster for large arrays. The choice of method is globally controlled by the methods enable_blas()
](@ref) and disable_blas()
](@ref).
TensorOperations.tensorproduct
— Function.tensorproduct(A, IA, B, IB, IC = (IA..., IB...))
Computes the tensor product of two arrays A
and B
, i.e. returns a new array 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.