TensorNetworkTensors.jl
Tensors for Tensor Network Methods. TensorNetworkTensors.jl
overloads methods from TensorOperations
and KrylovKit
to work with Tensors that might be symmetric under some abelina symmetry.
Usage
Tensors
You can define tensors without symmetry using the DTensor
type as
julia> a = DTensor{Complex{Float64}}((2,2));
which will return a 2x2
-tensor with elements of type Complex{Float64}
. DTensor
is just a thin wrapper around Array
.
A tensor with a symmetry can be defined by specifying the:
- symmetry
- possible charge
- sizes of the degeneracy spaces of those charge
- action of the group acts on an index
The currently implemented Discrete Abelian Symmetries are U1
and ZN
symmetries. To define a U1
symmetric rank-3 tensor which, in the particle conservation picture, has two incoming, one outgoing leg and supports carrying between 3 and -3 particles per leg where each charge has a degeneracy-size of 2, we write:
julia> sym = U1()
julia> chs = (U1Charges(-3:3), U1Charges(-3:3), U1Charges(-3:3))
julia> dims = ([2 for ch in U1Charges(-3:3)],
[2 for ch in U1Charges(-3:3)],
[2 for ch in U1Charges(-3:3)])
julia> io = InOut(1,1,-1)
julia> a = DASTensor{Float64,3}(sym, chs, dims, io)
where a
is a Discrete Abelian Symmetric rank-3 tensor with degeneracy tensors of type Float64
. It's first two indices can be read as incoming while the last is outgoing. All indices support charges in U1Charges(-3:3)
which can be looked at using
julia> foreach(println, U1Charges(-3:3))
U1Charge(-3)
U1Charge(-2)
U1Charge(-1)
U1Charge(0)
U1Charge(1)
U1Charge(2)
U1Charge(3)
The same for a Z2-symmetric tensor looks like
julia> sym = ZN{2}()
julia> chs = (ZNCharges{2}(), ZNCharges{2}(), ZNCharges{2}())
julia> dims = ([2 for ch in ZNCharges{2}()],
[2 for ch in ZNCharges{2}()],
[2 for ch in ZNCharges{2}()])
julia> io = InOut(1,1,-1)
julia> a = DASTensor{Float64,3}(sym, chs, dims, io)
Above we just defined tensors but they are either filled with garbage (DTensor
) or empty (DASTensor
). To initialize a tensor, use initwithzero!
or initwithrand!
, e.g.
julia> a = DTensor{Complex{Float64}}((2,2))
DTensor{Complex{Float64},2}Complex{Float64}[6.93789e-310+6.93789e-310im 6.93789e-310+6.93786e-310im; 6.93789e-310+6.93786e-310im 6.93786e-310+6.93789e-310im]
julia> initwithzero!(a)
DTensor{Complex{Float64},2}Complex{Float64}[0.0+0.0im 0.0+0.0im; 0.0+0.0im 0.0+0.0im]
For tensors with symmetry - DASTensor
- the same works but the underlying structure is different:
julia> sym = ZN{2}()
julia> chs = (ZNCharges{2}(), ZNCharges{2}(), ZNCharges{2}())
julia> dims = ([2 for ch in ZNCharges{2}()],
[2 for ch in ZNCharges{2}()],
[2 for ch in ZNCharges{2}()])
julia> io = InOut(1,1,-1)
julia> a = DASTensor{Float64,3}(sym, chs, dims, io);
julia> initwithzero!(a)
julia> tensor(a)
To look into a DASTensor
, you can use tensor
to see the underlying dictionary which maps DASSectors
, discrete abelian symmetry sectors, to degeneracy tensors.
Dict{DASSector{3,ZNCharge{2}},Array{Float64,3}} with 4 entries:
DASSector(Z2Charge(0), Z2Charge(0), Z2Charge(0)) => [0.0 0.0; 0.0 0.0]…
DASSector(Z2Charge(0), Z2Charge(1), Z2Charge(1)) => [0.0 0.0; 0.0 0.0]…
DASSector(Z2Charge(1), Z2Charge(0), Z2Charge(1)) => [0.0 0.0; 0.0 0.0]…
DASSector(Z2Charge(1), Z2Charge(1), Z2Charge(0)) => [0.0 0.0; 0.0 0.0]…
You can also directly access a degeneracy tensor like this:
julia> a[DASSector(Z2Charge(0), Z2Charge(0), Z2Charge(0))]
2×2×2 Array{Float64,3}:
[:, :, 1] =
0.0 0.0
0.0 0.0
[:, :, 2] =
0.0 0.0
0.0 0.0
Specific functions for DASTensors
include:
charges
,setcharges!
sizes
,setsizes!
in_out
,setin_out!
tensor
,settensor!
isinvariant
charge
to learn more, use ?
as in e.g.
julia>?charge
search: charge charges chargedim chargetype chargesize chargestype chargeindex ZNCharge Z2Charge U1Charge ZNCharges Z2Charges U1Charges DASCharge setcharges! DASCharges NDASCharge NDASCharges splitchargeit SplitChargeIt connectingcharge
charge(a::DASSector)
returns the charge which is calculated as the the sum of all charges it contains.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
charge(a::DASTensor)
returns the charge which is calculated as the charge of its non-zero sectors which needs to be unique.
Reshaping
Reshaping can be done by either fusing or splitting legs. Since splitting with symmetries is highly nontrivial, it might only be done after a fusion which provides the necessary information of how to recombine indices and charges.
To fuse indices, we use the function fuselegs
:
help?>fuselegs
fuselegs(A, indexes)
Fuse the indices in A as given by indexes where indexes is a tuple containing indices either alone or grouped in tuples - the latter will be fused. Returns a tuple of a tensor and the object necessary to undo the fusion.
Examples
≡≡≡≡≡≡≡≡≡≡
julia> a = DTensor(collect(reshape(1:8,2,2,2))
DTensor{Int64,3}[1 3; 2 4]
[5 7; 6 8]
julia> fuselegs(a, ((1,2),3))
(DTensor{Int64,2}[1 5; 2 6; 3 7; 4 8], ((2, 2),))
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
fuselegs(A, indexes[, io])
For DASTensors, the directions of the resulting legs might be specified. If io is ommitted, the default is InOut(1,1,1...).
Fusion might also include permutation. Note that fuselegs
returns two objects:
- A new tensor that corresponds to the input with the fusion applie
- An object to undo that fusion.
The latter is achieved using splitlegs
:
help?> splitlegs
search: splitlegs splitlegs!
splitlegs(A, indexes, rs...)
Split the indices in A as given by indexes and rs. indexes is a tuple of integers and 3-tuple where each 3-tuple (i,j,k) specifies that index i in A is split according to rs[j], index k therein. Returns tensor with fused legs.
Examples
≡≡≡≡≡≡≡≡≡≡
julia> a = DTensor(collect(reshape(1:8,2,2,2))
DTensor{Int64,3}[1 3; 2 4]
[5 7; 6 8]
julia> af, rs = fuselegs(a, ((1,2),3))
(DTensor{Int64,2}[1 5; 2 6; 3 7; 4 8], ((2, 2),))
julia> splitlegs(af, ((1,1,1),(1,1,2),2), rs...)
DTensor{Int64,3}[1 3; 2 4]
[5 7; 6 8]
The same is true for the case of DASTensor
s, although fuselegs
returns a more complicated object of the type Reshaper
. If there is already a tensor of the correct shape and charges available, both fuselegs
and splitlegs
can be used as their in-place
version fuselegs!
and splitlegs!
(see help
).
Note that fusion is specified with permutation of the indices of a tensor and indices grouped in a tuple are fused, e.g. ((1,2),3)
means the first two indices of a rank-3 tensor are grouped whereas (1,(4,3),2)
means that the third and fourth index of a rank-4 tensor are grouped and switched with the second index.
For splitting, indices are a list of integers and 3-tuples (i,j,k)
where the latter specifies that index i
in given tensor is split according to index j
in the reshapeing-information and part k
of that split is to be at the position of that tuple.
Let's look at an example:
julia> sym = U1()
julia> chs = (U1Charges(-3:3), U1Charges(-3:3), U1Charges(-3:3))
julia> dims = ([2 for ch in U1Charges(-3:3)],
[2 for ch in U1Charges(-3:3)],
[2 for ch in U1Charges(-3:3)])
julia> io = InOut(1,1,1)
julia> a = DASTensor{Float64,3}(sym, chs, dims, io)
julia> initwithrand!(a)
First we fuse indices 1 and 3 into (3,1)
putting them in the first place and specifying the directions of the legs as both outgoing:
julia> af, rs = fuselegs(a, ((3,1),2),InOut(-1,-1))
To undo that fusion, we need to split the first index of af
according to the first reshaper in rs
(there's a reshaper for each index in the fused tensor in order). A valid splitting would be:
julia> splitlegs(af,((1,1,1),(1,1,2),2),rs...)
but that would correspond to a permutation (3,1,2)
of a
. Additionally, the unfused index was also changed by switching its InOut
! We thus want to specify both the correct permutation and that index 2 needs to be changed accordingly, arriving at
julia> splitlegs(af, ((1,1,2),(2,2,1),(1,1,1)), rs...) ≈ a
true
Factorizations
So far the factorizations available are tensorsvd
which returns the SVD
of a tensor, see
help?> tensorsvd
search: tensorsvd tensorsvd! _tensorsvd TensorOperations
tensorsvd(A::AbstractTensor, indexes; svdtrunc)
works like tensorsvd except that A can have arbitrary rank.
indexes specifies which indices the fuse for A to be a rank-2 tensor as in fuselegs.
The tensor is then fused, tensorsvd applied and split again.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
tensorsvd(A::AbstractTensor{T,2}; svdtrunc)
returns the svd of an AbstractTensor.
svdtrunc is a function that has the singular values as input and returns a number specifying how many of them to keep.
The default svdtrunc is svdtrunc_default and keeps all of them.
Other options are: svdtruncdiscardzero svdtruncmaxχ svdtruncmaxcumerror svdtruncmaxerror
and tensorqr
which returns the QR
-decomposition of a tensor, see
help?> tensorqr
search: tensorqr tensor tensorsvd tensoradd tensorsvd! tensortrace tensorcopy tensoradd! tensortrace! tensorcopy! tensorproduct tensorproduct! tensorcontract
tensorqr(A::AbstractTensor)
returns tensor Q,R such that A = QR and Q is an orthogonal/unitary matrix and R is an upper triangular matrix.
───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
tensorqr(A::AbstractTensor, inds)
returns the tensorqr of A fused according to inds.
KrylovKit
All AbstractTensor
s work with KrylovKit
, a package that combines a number of Krylov-based algorithms including exponentiate.
To use KrylovKit, follow the instruction for installation at the github repository and do
using KrylovKit
As an example, consider eigsolve
for a tensor with two independent symmetries:
julia> a = DASTensor{Complex{Float64},2}(
NDAS(Z2(),U1()),
(NDASCharges(Z2Charges(), U1Charges(-1:1)), NDASCharges(Z2Charges(), U1Charges(-1:1))),
(fill(2,6), fill(2,6)),
InOut(1,-1))
julia> initwithrand!(a)
julia> using TensorOperations
julia> @tensor a[1,2] := a[1,2] + a'[2,1];
Since we are not working with a regular array, we need to provide an initial guess for an eigenvector, e.g.
julia> v0 = DASTensor{Complex{Float64},1}(
NDAS(Z2(),U1()),
(NDASCharges(Z2Charges(), U1Charges(-1:1)),),
(fill(2,6),),
InOut(1))
julia> initwithrand!(v0)
Then we can define a function for applying a
to vectors like v0
:
julia> f(v) = @tensor v2[1] := a[1,-1] * v[-1]
Then we can use eigsolve
with f
and v0
to get an eigenvector of a
or the map that it describes:
julia> eigsolve(f,v0)
[...]
which returns two eigenvalues, two eigenvectors and an object containing information about convergence.
Similarly we can apply exp(0.1*a)
to v0
with exponentiate
:
julia> exponentiate(f,0.1,v0, ishermitian=true)
TensorNetworkTensors.AbstractTensor
TensorNetworkTensors.DAS
TensorNetworkTensors.DASCharge
TensorNetworkTensors.DASCharges
TensorNetworkTensors.DASSector
TensorNetworkTensors.DTensor
TensorNetworkTensors.InOut
TensorNetworkTensors.NDAS
TensorNetworkTensors.NDASCharge
TensorNetworkTensors.NDASCharges
TensorNetworkTensors.U1
TensorNetworkTensors.U1Charge
TensorNetworkTensors.U1Charges
TensorNetworkTensors.ZN
TensorNetworkTensors.ZNCharge
TensorNetworkTensors.ZNCharges
TensorNetworkTensors.:⊕
TensorNetworkTensors.allsectors
TensorNetworkTensors.charge
TensorNetworkTensors.charge
TensorNetworkTensors.chargeindex
TensorNetworkTensors.charges
TensorNetworkTensors.connectingcharge
TensorNetworkTensors.covariantsectors
TensorNetworkTensors.fuselegs
TensorNetworkTensors.fuselegs
TensorNetworkTensors.fuselegs!
TensorNetworkTensors.gatherby
TensorNetworkTensors.groupby
TensorNetworkTensors.in_out
TensorNetworkTensors.initwith!
TensorNetworkTensors.initwithrand!
TensorNetworkTensors.initwithzero!
TensorNetworkTensors.invariantsectors
TensorNetworkTensors.isinvariant
TensorNetworkTensors.setcharges!
TensorNetworkTensors.setin_out!
TensorNetworkTensors.setsizes!
TensorNetworkTensors.sizes
TensorNetworkTensors.splitlegs
TensorNetworkTensors.splitlegs!
TensorNetworkTensors.svdtrunc_discardzero
TensorNetworkTensors.svdtrunc_maxcumerror
TensorNetworkTensors.svdtrunc_maxerror
TensorNetworkTensors.svdtrunc_maxχ
TensorNetworkTensors.symmetry
TensorNetworkTensors.tensor
TensorNetworkTensors.tensorqr
TensorNetworkTensors.tensorqr
TensorNetworkTensors.tensorrq
TensorNetworkTensors.tensorrq
TensorNetworkTensors.tensorsvd
TensorNetworkTensors.tensorsvd
AbstractTensor{T,N}
Abstract supertype for all tensornetwork-tensors.
TensorNetworkTensors.DAS
— Type.DAS
Abstract supertype of all Discrete Abelian Symmetries
TensorNetworkTensors.DASCharge
— Type.DASCharge
Abstract supertype of all Charges of Discrete Abelian Symmetries
TensorNetworkTensors.DASCharges
— Type.DASCharges
Abstract supertype of all collections of Charges of Discrete Abelian Symmetries
TensorNetworkTensors.DASSector
— Type.DASSector{N,T}
DASSectors are a configuration of charges that are allowed under a given symmetry and index degeneracy spaces in DASTensors{T,N}.
Example
julia> DASSector(U1Charge(1), U1Charge(2))
DASSector(U1Charge(1), U1Charge(2))
TensorNetworkTensors.DTensor
— Type.DTensor{T,N} <: AbstractTensor{T,N}
wrapper for generic Array{T,N} to represent dense tensors.
TensorNetworkTensors.InOut
— Type.InOut{N}
InOut describes whether representations of the DAS act on an index of a DASTensor{T,N} directly or via their dual. InOut(1,1,1,-1) can be read as the first three indices corresponding to incoming, the last as an outgoing index w.r.t the group action.
TensorNetworkTensors.NDAS
— Type.NDAS{N,S} <: DAS
wraps multiple independent symmetries to work together.
Example
julia>NDAS(U1(), Z2())
NDAS{2,(U1, Z2)}()
TensorNetworkTensors.NDASCharge
— Type.NDASCharge{N} <: DASCharge
holds the charge of multiple independent symmetries grouped together.
Example
julia> a = NDASCharge(U1Charge(1), Z2Charge(1));
julia> a ⊕ a
NDASCharge(U1Charge(2), Z2Charge(0))
TensorNetworkTensors.NDASCharges
— Type.NDASCharges{N,T} <: DASCharges
holds combinations of multiple independent DASCharges
that are grouped together. Yields NDASCharge{N,T}
s upon iteration.
Example
julia> a = NDASCharges(U1Charges(-1:1), ZNCharges{3}());
julia> foreach(println, a)
NDASCharge(U1Charge(-1), Z3Charge(0))
NDASCharge(U1Charge(0), Z3Charge(0))
NDASCharge(U1Charge(1), Z3Charge(0))
NDASCharge(U1Charge(-1), Z3Charge(1))
NDASCharge(U1Charge(0), Z3Charge(1))
NDASCharge(U1Charge(1), Z3Charge(1))
NDASCharge(U1Charge(-1), Z3Charge(2))
NDASCharge(U1Charge(0), Z3Charge(2))
NDASCharge(U1Charge(1), Z3Charge(2))
TensorNetworkTensors.U1
— Type.U1 <: DAS
U1 symmetry singleton-type
TensorNetworkTensors.U1Charge
— Type.U1Charge <: DASCharge
holds the charge of a U1 symmetry as an integer.
Example
julia>a = U1Charge(1);
julia>a ⊕ a
U1Charge(2)
TensorNetworkTensors.U1Charges
— Type.U1Charges <: DASCharges
type for collection of U1Charge
that holds StepRange{Int,Int}
which represents the valid values for U1Charge
.
Example
julia>a = U1Charges(-1:1);
julia>foreach(println, a)
U1Charge(-1)
U1Charge(0)
U1Charge(1)
TensorNetworkTensors.ZN
— Type.ZN{N} <: DAS
ZN symmetry singleton type where N is an Int.
TensorNetworkTensors.ZNCharge
— Type.ZNCharge{N} <: DASCharge
holds the charge of a ZN symmetry as an integer. The integer is taken mod N
s.t. the charge is always between 0
and N-1
Example
julia>a = ZNCharge{2}(1);
julia>a ⊕ a
ZNCharge{2}(0)
julia>a = ZNCharge{2}(3) == ZNCharge{2}(1)
true
TensorNetworkTensors.ZNCharges
— Type.ZNCharges{N} <: DASCharges
singleton-type for collections of ZNCharge{N}
objects. Only the type parameter is provided - ZNCharges{N}
doesn't have a field. Upon iteration yields ZNCharge{N}(i)
for i = 0,...,N-1
Example
julia>a = ZNCharges{2}()
julia>foreach(println, a)
ZNCharge{2}(0)
ZNCharge{2}(1)
TensorNetworkTensors.:⊕
— Method.⊕(a::T, bs::T...) where {T<:Union{DASCharge, DASCharges}}
returns the result of fusing one or more DASCharge
or DASCharges
together. Fusing DASCharges
yields a DASCharges
that holds all elements that result from fusing the DASCharges
.
TensorNetworkTensors.allsectors
— Method.allsectors(chs)
returns a generator that generates all possible combinations of charges in chs
wrapped in a DASSector
.
#Example
julia> allsectors((U1Charges(-1:1), U1Charges(4:5))) |> collect
3×2 Array{DASSector{2,U1Charge},2}:
DASSector(U1Charge(-1), U1Charge(4)) DASSector(U1Charge(-1), U1Charge(5))
DASSector(U1Charge(0), U1Charge(4)) DASSector(U1Charge(0), U1Charge(5))
DASSector(U1Charge(1), U1Charge(4)) DASSector(U1Charge(1), U1Charge(5))
TensorNetworkTensors.charge
— Method.charge(a::DASSector)
returns the charge which is calculated as minus the sum of all charges it contains.
TensorNetworkTensors.charge
— Method.charge(a::DASTensor)
returns the charge of a tensor.
TensorNetworkTensors.chargeindex
— Function.chargeindex(ch::DASCharge, chs::DASCharges)
returns the index i of ch in chs
Example
julia> ch = U1Charge(0); chs = U1Charges(-1:1);
julia> chargeindex(ch, chs)
2
julia> chs[2] == ch
true
TensorNetworkTensors.charges
— Method.charges(A::DASTensor[,i])
returns the charges of A
. If i
is specified as either Int
or Tuple
, returns only the charges of the indices in i
.
TensorNetworkTensors.covariantsectors
— Function.covariantsectors(chs, io[, ch = zero(chs)])
returns all sectors in allsectors(io ⊗ chs) that have total charge ch
TensorNetworkTensors.fuselegs
— Function.fuselegs(A, indexes)
Fuse the indices in A
as given by indexes
where indexes
is a tuple containing indices either alone or grouped in tuples - the latter will be fused. Returns a tuple of a tensor and the object necessary to undo the fusion.
Examples
julia> a = DTensor(collect(reshape(1:8,2,2,2))
DTensor{Int64,3}[1 3; 2 4]
[5 7; 6 8]
julia> fuselegs(a, ((1,2),3))
(DTensor{Int64,2}[1 5; 2 6; 3 7; 4 8], ((2, 2),))
TensorNetworkTensors.fuselegs!
— Function.fuselegs!(AF,A, indexes)
In-place version of fuselegs
where AF
is a tensor of the correct type and size to hold the fused version of A
. See fuselegs
TensorNetworkTensors.fuselegs
— Method.fuselegs(A, indexes[, io])
For DASTensor
s, the directions of the resulting legs might be specified. If io
is ommitted, the default is InOut(1,1,1...)
.
TensorNetworkTensors.gatherby
— Method.gatherby(f, xs)
Like groupby
but only returns values, i.e. elements of xs
in Vector
s such that f
applied to an element of a group is the same as for any other element of that Vector
.
Example
julia> gatherby(isodd, 1:10)
2-element Array{Array{Int64,1},1}:
[2, 4, 6, 8, 10]
[3, 5, 7, 9]
TensorNetworkTensors.groupby
— Method.groupby(f, xs)
group values x ∈ xs
by the result of applying f
. Returns a Dict
with keys yi
and values [xi1, xi2,...]
such that f(xij) = yi
.
Example
julia> groupby(isodd,1:10)
Dict{Bool,Array{Int64,1}} with 2 entries:
false => [2, 4, 6, 8, 10]
true => [3, 5, 7, 9]
TensorNetworkTensors.in_out
— Method.in_out(A::DASTensor[,i])
returns the InOut
of A
which specifies the action of the symmetry group on the corresponding leg. If i
is specified as either Int
or Tuple
, returns only the charges of the indices in i
.
TensorNetworkTensors.initwithrand!
— Method.initwithrand!(A::DASTensor)
construct all valid sectors in A
and initialize their degeneracy spaces with rand
.
TensorNetworkTensors.initwithzero!
— Method.initwithzero!(A::DASTensor)
construct all valid sectors in A
and initialize their degeneracy spaces with zeros.
TensorNetworkTensors.invariantsectors
— Method.covariantsectors(chs, io)
returns all sectors in allsectors(io ⊗ chs) that have total charge zero.
TensorNetworkTensors.isinvariant
— Method.isinvariant(a::DASTensor)
return true if charge(a)
is zero
.
TensorNetworkTensors.setcharges!
— Method.setcharges!(A::DASTensor, chs)
set the charges of A
to be chs
where the latter is a tuple of DASCharges
.
TensorNetworkTensors.setin_out!
— Method.setin_out!(A::DASTensor, io)
set the InOut
of A
to be io
where the latter is a InOut
.
TensorNetworkTensors.setsizes!
— Method.setsizes!(A::DASTensor, s)
set the sizes of A
to be s
where the latter is a tuple of Vector{Int}
.
TensorNetworkTensors.sizes
— Method.sizes(A::DASTensor[,i])
returns the sizes of A
as a tuple of vectors v
such that the degeneracy space associated with a charge ch
has size v[chargeindex(ch, chs)]
where chs
is the DASCharges
associated with the specified leg. If i
is specified as either Int
or Tuple
, returns only the charges of the indices in i
.
TensorNetworkTensors.splitlegs
— Function.splitlegs(A, indexes, rs...)
Split the indices in A
as given by indexes
and rs
. indexes
is a tuple of integers and 3-tuple where each 3-tuple (i,j,k) specifies that index i
in A
is split according to rs[j]
, index k
therein. Returns tensor with fused legs.
Examples
julia> a = DTensor(collect(reshape(1:8,2,2,2))
DTensor{Int64,3}[1 3; 2 4]
[5 7; 6 8]
julia> af, rs = fuselegs(a, ((1,2),3))
(DTensor{Int64,2}[1 5; 2 6; 3 7; 4 8], ((2, 2),))
julia> splitlegs(af, ((1,1,1),(1,1,2),2), rs...)
DTensor{Int64,3}[1 3; 2 4]
[5 7; 6 8]
TensorNetworkTensors.splitlegs!
— Function.splitlegs!(AS,A, indexes, rs...)
In-place version of splitlegs
where AS
is a tensor of the correct type and size to hold the split version of A
. See splitlegs
svdtrunc_discardzero(s)
return the number of non-zero values in s
svdtrunc_maxcumerror(ϵ::Real; χ::Int = typemax(Int))
return a function that returns then min of χ and l where l is the number of singular values that need to be kept to have the truncated sum up to not more than ϵ.
TensorNetworkTensors.svdtrunc_maxerror
— Method.svdtrunc_maxerror(ϵ::Real; χ::Int = typemax(Int))
return a function that returns then min of χ and l where l is the number of singular values that need to be kept such that the largest discarded singular value is below ϵ.
TensorNetworkTensors.svdtrunc_maxχ
— Method.svdtrunc_maxχ(χ)
return a function that given singular values s
returns the min of the length of s
and χ.
TensorNetworkTensors.symmetry
— Method.symmetry(A::DASTensor)
returns the symmetry of a tensor A
TensorNetworkTensors.tensor
— Method.tensor(A::DASTensor[,i])
returns a dictionary of DASSectors
and their associated degeneracy spaces.
TensorNetworkTensors.tensorqr
— Function.tensorqr(A::AbstractTensor)
returns tensor Q,R such that A = QR and Q is an orthogonal/unitary matrix and R is an upper triangular matrix.
TensorNetworkTensors.tensorqr
— Method.tensorqr(A::AbstractTensor, inds)
returns the tensorqr
of A
fused according to inds
.
TensorNetworkTensors.tensorrq
— Function.tensorrq(A::AbstractTensor)
returns tensor R,Q such that A = RQ and Q is obeys Q*Q' = 1 and R is triangular. This is simply a wrapper for tensorqr
, useful for e.g. MPS canonicalization.
TensorNetworkTensors.tensorrq
— Function.tensorrq(A::AbstractTensor, inds)
returns the tensorrq
of A
fused according to inds
.
TensorNetworkTensors.tensorsvd
— Function.tensorsvd(A::AbstractTensor{T,2}; svdtrunc)
returns the svd
of an AbstractTensor. svdtrunc
is a function that has the singular values as input and returns a number specifying how many of them to keep. The default svdtrunc
is svdtrunc_default
and keeps all of them.
Other options are: svdtruncdiscardzero svdtruncmaxχ svdtruncmaxcumerror svdtruncmaxerror
TensorNetworkTensors.tensorsvd
— Method.tensorsvd(A::AbstractTensor, indexes; svdtrunc)
works like tensorsvd
except that A
can have arbitrary rank. indexes
specifies which indices the fuse for A
to be a rank-2 tensor as in fuselegs
. The tensor is then fused, tensorsvd
applied and split again.
TensorNetworkTensors.connectingcharge
— Method.connectingcharge(A)
where A is a rank-2 DASTensor, returns the charges on the second index that can be realized given the charges on the first index and the charge of A.
TensorNetworkTensors.initwith!
— Method.initwith!(A::DASTensor{T}, fun [,ch])
modifies A
such that each sector with charge ch
(default=zero) is (independently) set to fun(T, dims...)
where dims
is the size of the degeneracy space for the sector.