Tensors

Constructors

LinearCombinations.TensorType
Tensor{T<:Tuple}

Tensor{T}(xs...) where T
Tensor(xs...)

The type Tensor represents pure tensors.

A general tensor is a linear combination of pure tensors and can conveniently be created using tensor. LinearCombinations takes pure tensors as basis elements.

A Tensor can be created out of a Tuple or out of the individual components. The second form is not available if the tensor has a tuple as its only component.

Tensor implements the iteration and indexing interfaces. This makes for example splatting available for tensors, and the i-th component of t::Tensor can be accessed as t[i].

Tensors can be nested. Different bracketings lead to different tensors. The functions cat, flatten, swap and the type Regroup are provided to make rearranging tensors more easily.

Note that the type parameter of Tensor is always a Tuple. For instance, the type of a Tensor with two components of types T1 and T2 is Tensor{Tuple{T1,T2}}, not Tensor{T1,T2}.

See also tensor, cat, flatten, swap, Regroup.

Examples

julia> t = Tensor('x', 'y', "z")
'x'⊗'y'⊗"z"

julia> typeof(t)
Tensor{Tuple{Char, Char, String}}

julia> Tuple(t)
('x', 'y', "z")

julia> length(t), t[2], t[end]
(3, 'y', "z")

julia> a = Linear('x' => 1, 'y' => 2)
Linear{Char, Int64} with 2 terms:
'x'+2*'y'

julia> b = Linear(Tensor('x', 'z') => 1, Tensor('y', 'z') => 2)
Linear{Tensor{Tuple{Char, Char}}, Int64} with 2 terms:
'x'⊗'z'+2*'y'⊗'z'

julia> b == tensor(a, 'z')
true

julia> [uppercase(x) for x in t]
3-element Vector{Any}:
 'X': ASCII/Unicode U+0058 (category Lu: Letter, uppercase)
 'Y': ASCII/Unicode U+0059 (category Lu: Letter, uppercase)
 "Z"

julia> f((x1, xs...)::Tensor) = x1
f (generic function with 1 method)

julia> f(t)
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)

julia> t == Tensor(Tensor('x', 'y'), "z")
false

julia> Tensor() |> typeof
Tensor{Tuple{}}
source
LinearCombinations.tensorFunction
x ⊗ y -> AbstractLinear{<:Tensor}
⊗(xs...) -> AbstractLinear{<:Tensor}
tensor(xs...) -> AbstractLinear{<:Tensor}

tensor is the multilinear extension of Tensor. The operator is a synomym for tensor. The return value is a Tensor if no argument is of type AbstractLinear. It is DenseLinear if all arguments are DenseLinear; the corresponding basis is the TensorBasis of the bases of the arguments. In all other cases the return type is Linear. This can be overriden via the addto keyword argument.

Remember that is not associative in Julia, unlike *. Writing tensors with more than two factors in infix notation therefore produces nested tensors.

See also Tensor, @multilinear, TensorBasis.

Examples

julia> tensor('x', "w")
'x'⊗"w"

julia> a = Linear('x' => 1, 'y' => 2)
Linear{Char, Int64} with 2 terms:
'x'+2*'y'

julia> b = Linear("w" => 3, "z" => -1)
Linear{String, Int64} with 2 terms:
3*"w"-"z"

julia> tensor(a, "w")
Linear{Tensor{Tuple{Char, String}}, Int64} with 2 terms:
'x'⊗"w"+2*'y'⊗"w"

julia> a ⊗ b
Linear{Tensor{Tuple{Char, String}}, Int64} with 4 terms:
-2*'y'⊗"z"+3*'x'⊗"w"+6*'y'⊗"w"-'x'⊗"z"

julia> tensor('x', b, a; coefftype = Float64)
Linear{Tensor{Tuple{Char, String, Char}}, Float64} with 4 terms:
3.0*'x'⊗"w"⊗'x'-'x'⊗"z"⊗'x'+6.0*'x'⊗"w"⊗'y'-2.0*'x'⊗"z"⊗'y'

julia> 'x' ⊗ b ⊗ a
Linear{Tensor{Tuple{Tensor{Tuple{Char, String}}, Char}}, Int64} with 4 terms:
-2*('x'⊗"z")⊗'y'-('x'⊗"z")⊗'x'+3*('x'⊗"w")⊗'x'+6*('x'⊗"w")⊗'y'

julia> tensor('x', b, a) == 'x' ⊗ b ⊗ a
false

julia> tensor() |> typeof
Tensor{Tuple{}}

julia> d = DenseLinear(a; basis = Basis('w':'z'))
DenseLinear{Char, Int64} with 2 terms:
'x'+2*'y'

julia> d ⊗ d
DenseLinear{Tensor{Tuple{Char, Char}}, Int64} with 4 terms:
'x'⊗'x'+2*'y'⊗'x'+2*'x'⊗'y'+4*'y'⊗'y'

julia> d ⊗ d == a ⊗ a
true

julia> basis(d ⊗ d)
TensorBasis(Basis('w':1:'z'), Basis('w':1:'z'))
source

Manipulating tensors

Base.fieldtypesFunction
fieldtypes(::Type{T}) where T <:AbstractTensor -> Tuple

Return the types of the components of T as a tuple.

Example

julia> fieldtypes(Tensor{Tuple{Char, String}})
(Char, String)
source
Core.TupleMethod
Tuple(t::AbstractTensor{T}) -> T <: Tuple

Return the tuple of components of t.

Although any AbstractTensor has to supports the iteration interface, it is often more efficient to deal with the underlying Tuple of components. For instance, functions like map or reduce map return a Tuple in this case instead of a Vector.

Example

julia> t = Tensor('A','b','c')
'A'⊗'b'⊗'c'

julia> Tuple(t)
('A', 'b', 'c')

julia> map(isuppercase, t)
3-element Vector{Bool}:
 1
 0
 0

julia> map(isuppercase, Tuple(t))
(true, false, false)
source
LinearCombinations.catFunction
LinearCombinations.cat(t::AbstractTensor...) -> Tensor

Concatenate the tensors given as arguments. This function is multilinear.

See also flatten.

Example

julia> LinearCombinations.cat(Tensor('x'), Tensor('y', Tensor('z', 'w')))
'x'⊗'y'⊗('z'⊗'w')
source
LinearCombinations.flattenFunction
flatten(t::AbstractTensor) -> Tensor
flatten(a::AbstractLinear{<:AbstractTensor}) -> AbstractLinear{Tensor}

Recursively take all tensor components and concatenate the result. This function is linear.

See also cat.

Example

julia> t = Tensor('x', Tensor('y', Tensor('z', 'w')))
'x'⊗('y'⊗('z'⊗'w'))

julia> flatten(t)
'x'⊗'y'⊗'z'⊗'w'
source
LinearCombinations.swapConstant
swap(t::AbstractTensor{Tuple{T1,T2}}) where {T1,T2}

This linear function swaps the components of two-component tensors. If the two components of a tensor t have non-zero degrees, then the usual sign (-1)^(deg(t[1])*deg(t[2])) is introduced. In this case the returned value is of type Linear1 instead of Tensor. By default, all terms have zero degree.

Note that swap is a special case of regroup: it is simply defined as regroup(:((1, 2)), :((2, 1))).

See also Tensor, deg, regroup, LinearCombinations.DefaultCoefftype.

Examples

Examples without degrees

julia> t = Tensor("x", "z")
"x"⊗"z"

julia> swap(t)
"z"⊗"x"

julia> a = Linear("x" => 1, "yy" => 1) ⊗ Linear("z" => 1, "ww" => 1)
Linear{Tensor{Tuple{String, String}}, Int64} with 4 terms:
"x"⊗"z"+"x"⊗"ww"+"yy"⊗"z"+"yy"⊗"ww"

julia> swap(a)
Linear{Tensor{Tuple{String, String}}, Int64} with 4 terms:
"ww"⊗"yy"+"ww"⊗"x"+"z"⊗"x"+"z"⊗"yy"

julia> swap(a; coeff = 2)
Linear{Tensor{Tuple{String, String}}, Int64} with 4 terms:
2*"ww"⊗"yy"+2*"ww"⊗"x"+2*"z"⊗"x"+2*"z"⊗"yy"

Examples with degrees

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> t = Tensor(gr"x", gr"z")
gr"x"⊗gr"z"

julia> swap(t)
Linear1{Tensor{Tuple{GradedString, GradedString}}, Int64} with 1 term:
-gr"z"⊗gr"x"

julia> a = Linear(gr"x" => 1, gr"yy" => 1) ⊗ Linear(gr"z" => 1, gr"ww" => 1)
Linear{Tensor{Tuple{GradedString, GradedString}}, Int64} with 4 terms:
gr"x"⊗gr"z"+gr"yy"⊗gr"ww"+gr"x"⊗gr"ww"+gr"yy"⊗gr"z"

julia> swap(a)
Linear{Tensor{Tuple{GradedString, GradedString}}, Int64} with 4 terms:
-gr"z"⊗gr"x"+gr"ww"⊗gr"x"+gr"z"⊗gr"yy"+gr"ww"⊗gr"yy"
source
LinearCombinations.RegroupType
LinearCombinations.Regroup{A, B}

Applying a Regroup object to a Tensor or a linear combinations of tensors rearranges the components of the tensor. Use the regroup"" string macro to create a Regroup object. It is possible to define additional methods to apply Regroup objects to other arguments besides tensors.

See also @regroup_str.

source
LinearCombinations.@regroup_strMacro
regroup"a -> b" -> Regroup

Create a Regroup object that can be used to rearrange the components of tensors and possibly other structures.

The actual rearrangement is specified by the two parameters a and b, which are (possibly nested) tuples of integers. These tuples encode the structure of nested tensors, and the integers specify a mapping from the components of the nested source tensor to the nested target tensor. The labels for a and b can in fact be of any isbits type or Symbol instead of Int, but they must be the same for a and b.

The created object rg = regroup"a -> b" is callable. An argument t for rg must be a nested tensor of the same shape as the a tree, and the return value is a Tensor of the same shape as b. The components of the nested tensor t are permuted according to the labels.

If the components of t have non-zero degrees, then rg(t) additionally has a sign according to the usual sign rule: whenever two ojects x and y are swapped, then this incurs the sign (-1)^(deg(x)*(deg(y))). In this case the returned value is of type Linear1 instead of Tensor.

Moreover, rg is linear and can be called with linear combinations of tensors.

Note that for each Regroup element rg, Julia generates separate, efficient code for computing rg(t).

See also swap, @regroup_inv_str, Regroup, LinearCombinations.DefaultCoefftype.

Examples

Example without degrees

julia> rg = regroup"(1, (2, 3), 4) -> ((3, 1), (4, 2))"
Regroup{(1, (2, 3), 4), ((3, 1), (4, 2))}

julia> rg == regroup"(a, (b, c), d) -> ((c, a), (d, b))"
true

julia> t = Tensor("x", Tensor("y", "z"), "w")
"x"⊗("y"⊗"z")⊗"w"

julia> rg(t)
("z"⊗"x")⊗("w"⊗"y")

Example with degrees

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> t = Tensor(gr"x", Tensor(gr"y", gr"z"), gr"w")
gr"x"⊗(gr"y"⊗gr"z")⊗gr"w"

julia> rg(t)   # same rg as before
Linear1{Tensor{Tuple{Tensor{Tuple{GradedString, GradedString}}, Tensor{Tuple{GradedString, GradedString}}}}, Int64} with 1 term:
-(gr"z"⊗gr"x")⊗(gr"w"⊗gr"y")
source
LinearCombinations.regroupFunction
regroup(a, b) -> Regroup

Return a Regroup object that can be used to rearrange the components of tensors and possibly other structures.

Warning

regroup is deprecated. Use the regroup"" string macro instead.

See @regroup_str.

source
Base.transposeFunction
transpose(t::AbstractTensor{T}) where T <: Tuple{Vararg{AbstractTensor}}

Return the transpose of a tensor t whose components are tensors of the same length. In other words, the component transpose(t)[i][j] is t[j][i]. If the components t[i][j] may have non-zero degrees, a sign is added according to the usual sign rule. In this case the return type is Linear1 instead of Tensor. The tensor t must have at least one component. If all component tensors are empty, then the empty tensor Tensor() is returned.

This function is linear.

Examples

Example without signs

julia> t = Tensor(Tensor("a", "b", "c"), Tensor("x", "y", "z"))
("a"⊗"b"⊗"c")⊗("x"⊗"y"⊗"z")

julia> transpose(t)
("a"⊗"x")⊗("b"⊗"y")⊗("c"⊗"z")

Example with degrees

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> t = Tensor(Tensor(gr"a", gr"b", gr"c"), Tensor(gr"x", gr"y", gr"z"))
(gr"a"⊗gr"b"⊗gr"c")⊗(gr"x"⊗gr"y"⊗gr"z")

julia> transpose(t)
Linear1{Tensor{Tuple{Tensor{Tuple{GradedString, GradedString}}, Tensor{Tuple{GradedString, GradedString}}, Tensor{Tuple{GradedString, GradedString}}}}, Int64} with 1 term:
-(gr"a"⊗gr"x")⊗(gr"b"⊗gr"y")⊗(gr"c"⊗gr"z")
source

Calling tensors

LinearCombinations.AbstractTensorMethod
(tf::AbstractTensor)(tx::AbstractTensor...)

Evaluating an AbstractTensor on other AbstractTensors (with the same number of components) is done componentwise. If the degrees of the components or the maps may be non-zero, then the usual sign is introduced: whenever a map f is moved past a component x, then this changes the sign by (-1)^(deg(f)*deg(x)). In this case the return type is Linear1 instead of Tensor if no map returns a linear combination.

Examples

Examples without degrees

julia> @linear f; f(x) = uppercase(x)
f (generic function with 2 methods)

julia> @linear g; g(x) = lowercase(x)
g (generic function with 2 methods)

julia> const h = Tensor(f, g)
f⊗g

julia> a = Linear('x' => 1, 'y' => 2)
Linear{Char, Int64} with 2 terms:
'x'+2*'y'

julia> b = Linear('Z' => -1, 'W' => 3)
Linear{Char, Int64} with 2 terms:
-'Z'+3*'W'

julia> h(Tensor('x', 'Z'))
'X'⊗'z'

julia> h(tensor(a, b))
Linear{Tensor{Tuple{Char, Char}}, Int64} with 4 terms:
-2*'Y'⊗'z'+6*'Y'⊗'w'+3*'X'⊗'w'-'X'⊗'z'

julia> Tensor()(Tensor())
()

Examples with degrees

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> using Base: Fix2

julia> j = Tensor(Fix2(*, gr"pp"), Fix2(*, gr"qqq"))
Fix2{typeof(*), GradedString}(*, gr"pp")⊗Fix2{typeof(*), GradedString}(*, gr"qqq")

julia> j(Tensor(gr"x", gr"yy"))
Linear1{Tensor{Tuple{GradedString, GradedString}}, Int64} with 1 term:
-gr"xpp"⊗gr"yyqqq"

julia> a = Linear(gr"x" => 1, gr"yy" => 2)
Linear{GradedString, Int64} with 2 terms:
2*gr"yy"+gr"x"

julia> b = tensor(a, a)
Linear{Tensor{Tuple{GradedString, GradedString}}, Int64} with 4 terms:
gr"x"⊗gr"x"+2*gr"yy"⊗gr"x"+4*gr"yy"⊗gr"yy"+2*gr"x"⊗gr"yy"

julia> j(b)
Linear{Tensor{Tuple{GradedString, GradedString}}, Int64} with 4 terms:
-gr"xpp"⊗gr"xqqq"+2*gr"yypp"⊗gr"xqqq"-2*gr"xpp"⊗gr"yyqqq"+4*gr"yypp"⊗gr"yyqqq"

A multilinear example

julia> Tensor(*, *)('a'⊗'b', 'p'⊗'q', 'x'⊗'y')
"apx"⊗"bqy"
source

Other functions accepting tensors

LinearCombinations.degMethod
deg(t::AbstractTensor)

Return the degree of a tensor, which is the sum of the degrees of its components.

See also deg.

source
Base.:*Method
*(t1::AbstractTensor , t2::AbstractTensor, ...)

Return the product of the tensors, computed from the products of its components. Signs are introduced according to the usual sign rule. If all degrees are integers, then the coefficient type is DefaultCoefftype.

This function is linear.

See also: LinearCombinations.DefaultCoefftype.

Example without degrees

julia> (s, t) = Tensor("ab", "c"), Tensor("x", "yz");

julia> s*t
"abx"⊗"cyz"

Example with degrees

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> (s, t) = Tensor(gr"ab", gr"c"), Tensor(gr"x", gr"yz");

julia> s*t
Linear1{Tensor{Tuple{GradedString, GradedString}}, Int64} with 1 term:
-gr"abx"⊗gr"cyz"
source
LinearCombinations.coprodMethod
coprod(t::T) where T <: AbstractTensor -> Linear{Tensor{Tuple{T,T}}}

Return the coproduct of a tensor, computed from the coproducts of its components. Signs are introduced according to the usual sign rule. If all degrees are integers, then the coefficient type is DefaultCoefftype.

This function is linear.

See also: coprod, LinearCombinations.DefaultCoefftype.

Example

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> import LinearCombinations: coprod

julia> coprod(x::GradedString) = Linear(Tensor(x[1:k], x[k+1:end]) => 1 for k in 0:length(x));

julia> coprod(gr"xy")
Linear{Tensor{Tuple{GradedString, GradedString}}, Int64} with 3 terms:
gr"x"⊗gr"y"+gr""⊗gr"xy"+gr"xy"⊗gr""

julia> Tensor(gr"x", gr"y") |> coprod
Linear{Tensor{Tuple{Tensor{Tuple{GradedString, GradedString}}, Tensor{Tuple{GradedString, GradedString}}}}, Int64} with 4 terms:
(gr"x"⊗gr"y")⊗(gr""⊗gr"")+(gr"x"⊗gr"")⊗(gr""⊗gr"y")-(gr""⊗gr"y")⊗(gr"x"⊗gr"")+(gr""⊗gr"")⊗(gr"x"⊗gr"y")
source
LinearCombinations.diffMethod
diff(t::T) where T <: AbstractTensor -> Linear{T}

Return the differential of the tensor t by differentiating each tensor factor at a time and adding signs according to the degrees of the components. The coefficient type is usually DefaultCoefftype. However, if the degrees of the tensor components are not integers, then the coefficient type is chosen such that it can accommodate the signs.

See also diff, LinearCombinations.DefaultCoefftype.

Example

The degree of a GradedString (created with the gr"" string macro) is its length.

julia> using LinearCombinations.TestHelpers: GradedString, @gr_str

julia> import LinearCombinations: diff

julia> diff(x::GradedString) = Linear1(gr"δ"*x => Int(x[1] != 'δ'));

julia> gr"x" |> diff
Linear1{GradedString, Int64} with 1 term:
gr"δx"

julia> gr"x" |> diff |> diff
Linear1{GradedString, Int64} with 0 terms:
0

julia> Tensor(gr"x", gr"yy", gr"zzz") |> diff
Linear{Tensor{Tuple{GradedString, GradedString, GradedString}}, Int64} with 3 terms:
gr"δx"⊗gr"yy"⊗gr"zzz"-gr"x"⊗gr"δyy"⊗gr"zzz"-gr"x"⊗gr"yy"⊗gr"δzzz"
source

Twisted tensors

LinearCombinations.lefttwistedtensorFunction
lefttwistedtensor(twc)

Return a callable object that converts an AbstractTensor to a LeftTwistedTensor with twisting cochain twc. The callable object is linear.

See also LeftTwistedTensor, righttwistedtensor.

Example

julia> @linear twc;  # some twisting cochain

julia> t = Tensor("x", "y") |> lefttwistedtensor(twc)
"x"⊗˱"y"

julia> typeof(t)
LeftTwistedTensor{String, String, typeof(twc)}
source
LinearCombinations.righttwistedtensorFunction
righttwistedtensor(twc)

Return a callable object that converts an AbstractTensor to a RightTwistedTensor with twisting cochain twc. The callable object is linear.

See also RightTwistedTensor, lefttwistedtensor.

Example

julia> @linear twc;  # some twisting cochain

julia> t = Tensor("x", "y") |> righttwistedtensor(twc)
"x"⊗˲"y"

julia> typeof(t)
RightTwistedTensor{String, String, typeof(twc)}
source