From 653b20a8d518743361427dd1d36667b5df704271 Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Fri, 22 Nov 2024 11:40:23 +0000 Subject: [PATCH] build based on 4247207 --- dev/.documenter-siteinfo.json | 2 +- dev/alternatives.html | 2 +- dev/ansatz/chain.html | 2 +- dev/ansatz/product.html | 2 +- dev/contraction.html | 2 +- dev/developer/type-hierarchy.html | 2 +- dev/index.html | 2 +- dev/quantum.html | 2 +- dev/references.html | 2 +- dev/tensor-network.html | 2 +- dev/tensors.html | 12 ++++++------ dev/transformations-a30dcedf.png | Bin 0 -> 18040 bytes dev/transformations-f136c61b.png | Bin 18255 -> 0 bytes dev/transformations.html | 2 +- dev/visualization.html | 2 +- 15 files changed, 18 insertions(+), 18 deletions(-) create mode 100644 dev/transformations-a30dcedf.png delete mode 100644 dev/transformations-f136c61b.png diff --git a/dev/.documenter-siteinfo.json b/dev/.documenter-siteinfo.json index da3afbd9..5a640a8c 100644 --- a/dev/.documenter-siteinfo.json +++ b/dev/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-22T11:39:53","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-22T11:40:18","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/dev/alternatives.html b/dev/alternatives.html index db35f154..6f212b25 100644 --- a/dev/alternatives.html +++ b/dev/alternatives.html @@ -1,2 +1,2 @@ -Alternatives · Tenet.jl

Alternatives

Tenet is strongly opinionated. We acknowledge that it may not suit all cases (although we try 🙂). If your case doesn't fit Tenet's design, you can try the following libraries:

+Alternatives · Tenet.jl

Alternatives

Tenet is strongly opinionated. We acknowledge that it may not suit all cases (although we try 🙂). If your case doesn't fit Tenet's design, you can try the following libraries:

diff --git a/dev/ansatz/chain.html b/dev/ansatz/chain.html index 8395b3c1..3279bacd 100644 --- a/dev/ansatz/chain.html +++ b/dev/ansatz/chain.html @@ -21,4 +21,4 @@ Label(fig[1,1, Bottom()], "Open") # hide Label(fig[1,2, Bottom()], "Periodic") # hide -fig # hide

In Tenet, the generic MatrixProduct ansatz implements this topology. Type variables are used to address their functionality (State or Operator) and their boundary conditions (Open or Periodic).

Missing docstring.

Missing docstring for MatrixProduct. Check Documenter's build log for details.

Missing docstring.

Missing docstring for MatrixProduct(::Any). Check Documenter's build log for details.

+fig # hide

In Tenet, the generic MatrixProduct ansatz implements this topology. Type variables are used to address their functionality (State or Operator) and their boundary conditions (Open or Periodic).

Missing docstring.

Missing docstring for MatrixProduct. Check Documenter's build log for details.

Missing docstring.

Missing docstring for MatrixProduct(::Any). Check Documenter's build log for details.

diff --git a/dev/ansatz/product.html b/dev/ansatz/product.html index 67afdfe6..906ede5e 100644 --- a/dev/ansatz/product.html +++ b/dev/ansatz/product.html @@ -1,2 +1,2 @@ -Product ansatz · Tenet.jl
+Product ansatz · Tenet.jl
diff --git a/dev/contraction.html b/dev/contraction.html index ed8fc9be..2198d6cc 100644 --- a/dev/contraction.html +++ b/dev/contraction.html @@ -1,2 +1,2 @@ -Contraction · Tenet.jl

Contraction

Contraction path optimization and execution is delegated to the EinExprs library. A EinExpr is a lower-level form of a Tensor Network, in which the contraction path has been laid out as a tree. It is similar to a symbolic expression (i.e. Expr) but in which every node represents an Einstein summation expression (aka einsum).

EinExprs.einexprMethod
einexpr(tn::AbstractTensorNetwork; optimizer = EinExprs.Greedy, output = inds(tn, :open), kwargs...)

Search a contraction path for the given AbstractTensorNetwork and return it as a EinExpr.

Keyword Arguments

  • optimizer Contraction path optimizer. Check EinExprs documentation for more info.
  • outputs Indices that won't be contracted. Defaults to open indices.
  • kwargs Options to be passed to the optimizer.

See also: contract.

source
Missing docstring.

Missing docstring for contract(::Tenet.TensorNetwork). Check Documenter's build log for details.

+Contraction · Tenet.jl

Contraction

Contraction path optimization and execution is delegated to the EinExprs library. A EinExpr is a lower-level form of a Tensor Network, in which the contraction path has been laid out as a tree. It is similar to a symbolic expression (i.e. Expr) but in which every node represents an Einstein summation expression (aka einsum).

EinExprs.einexprMethod
einexpr(tn::AbstractTensorNetwork; optimizer = EinExprs.Greedy, output = inds(tn, :open), kwargs...)

Search a contraction path for the given AbstractTensorNetwork and return it as a EinExpr.

Keyword Arguments

  • optimizer Contraction path optimizer. Check EinExprs documentation for more info.
  • outputs Indices that won't be contracted. Defaults to open indices.
  • kwargs Options to be passed to the optimizer.

See also: contract.

source
Missing docstring.

Missing docstring for contract(::Tenet.TensorNetwork). Check Documenter's build log for details.

diff --git a/dev/developer/type-hierarchy.html b/dev/developer/type-hierarchy.html index 8d92ee7e..e02e4d48 100644 --- a/dev/developer/type-hierarchy.html +++ b/dev/developer/type-hierarchy.html @@ -9,4 +9,4 @@ id3(Ansatz) --> Chain style id1 stroke-dasharray: 5 5 style id2 stroke-dasharray: 5 5 - style id3 stroke-dasharray: 5 5 + style id3 stroke-dasharray: 5 5 diff --git a/dev/index.html b/dev/index.html index 142da2c9..2db8ebaa 100644 --- a/dev/index.html +++ b/dev/index.html @@ -2,4 +2,4 @@ Home · Tenet.jl

Tenet.jl

BSC-Quantic's Registry

Tenet and some of its dependencies are located in our own Julia registry. In order to download Tenet, add our registry to your Julia installation by using the Pkg mode in a REPL session,

using Pkg
 pkg"registry add https://github.com/bsc-quantic/Registry"

A Julia library for Tensor Networks. Tenet can be executed both at local environments and on large supercomputers. Its goals are,

  • Expressiveness Simple to use 👶
  • Flexibility Extend it to your needs 🔧
  • Performance Goes brr... fast 🏎️

A video of its presentation at JuliaCon 2023 can be seen here:

-

Features

  • Optimized Tensor Network contraction, powered by EinExprs
  • Tensor Network slicing/cuttings
  • Automatic Differentiation of TN contraction, powered by EinExprs and ChainRules
  • 3D visualization of large networks, powered by Makie
+

Features

diff --git a/dev/quantum.html b/dev/quantum.html index 477d34a1..7caa3bb2 100644 --- a/dev/quantum.html +++ b/dev/quantum.html @@ -1,2 +1,2 @@ -Introduction · Tenet.jl

Quantum Tensor Networks

Tenet.QuantumType
Quantum

Tensor Network with a notion of "causality". This leads to the notion of sites and directionality (input/output).

Notes

  • Indices are referenced by Sites.
source
Base.adjointMethod
adjoint(q::Quantum)

Returns the adjoint of a Quantum Tensor Network; i.e. the conjugate Tensor Network with the inputs and outputs swapped.

source

Queries

Missing docstring.

Missing docstring for Tenet.inds(::Quantum; kwargs...). Check Documenter's build log for details.

Missing docstring.

Missing docstring for Tenet.tensors(::Quantum; kwargs...). Check Documenter's build log for details.

Connecting Quantum Tensor Networks

Missing docstring.

Missing docstring for inputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for outputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for lanes. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ninputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for noutputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for nlanes. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Socket. Check Documenter's build log for details.

Tenet.ScalarType
Scalar <: Socket

Socket representing a scalar; i.e. a Tensor Network with no open sites.

source
Tenet.StateType
State <: Socket

Socket representing a state; i.e. a Tensor Network with only input sites (or only output sites if dual = true).

source
Tenet.OperatorType
Operator <: Socket

Socket representing an operator; i.e. a Tensor Network with both input and output sites.

source
Missing docstring.

Missing docstring for Base.merge(::Quantum, ::Quantum...). Check Documenter's build log for details.

+Introduction · Tenet.jl

Quantum Tensor Networks

Tenet.QuantumType
Quantum

Tensor Network with a notion of "causality". This leads to the notion of sites and directionality (input/output).

Notes

  • Indices are referenced by Sites.
source
Base.adjointMethod
adjoint(q::Quantum)

Returns the adjoint of a Quantum Tensor Network; i.e. the conjugate Tensor Network with the inputs and outputs swapped.

source

Queries

Missing docstring.

Missing docstring for Tenet.inds(::Quantum; kwargs...). Check Documenter's build log for details.

Missing docstring.

Missing docstring for Tenet.tensors(::Quantum; kwargs...). Check Documenter's build log for details.

Connecting Quantum Tensor Networks

Missing docstring.

Missing docstring for inputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for outputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for lanes. Check Documenter's build log for details.

Missing docstring.

Missing docstring for ninputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for noutputs. Check Documenter's build log for details.

Missing docstring.

Missing docstring for nlanes. Check Documenter's build log for details.

Missing docstring.

Missing docstring for Socket. Check Documenter's build log for details.

Tenet.ScalarType
Scalar <: Socket

Socket representing a scalar; i.e. a Tensor Network with no open sites.

source
Tenet.StateType
State <: Socket

Socket representing a state; i.e. a Tensor Network with only input sites (or only output sites if dual = true).

source
Tenet.OperatorType
Operator <: Socket

Socket representing an operator; i.e. a Tensor Network with both input and output sites.

source
Missing docstring.

Missing docstring for Base.merge(::Quantum, ::Quantum...). Check Documenter's build log for details.

diff --git a/dev/references.html b/dev/references.html index e89dd7bd..1f670794 100644 --- a/dev/references.html +++ b/dev/references.html @@ -1,2 +1,2 @@ -References · Tenet.jl

References

  • Fishman, M.; White, S. R. and Stoudenmire, E. M. (2022). The ITensor Software Library for Tensor Network Calculations. SciPost Phys. Codebases, 4.
  • Gray, J. (2018), quimb: A python package for quantum information and many-body calculations. Journal of Open Source Software 3, 819.
  • Gray, J. and Kourtis, S. (2021). Hyper-optimized tensor network contraction. Quantum 5, 410.
  • Hauschild, J.; Pollmann, F. and Zaletel, M. (2021). The Tensor Network Python (TeNPy) Library. In: APS March Meeting Abstracts, Vol. 2021; p. R21–006.
  • Ramón Pareja Monturiol, J.; Pérez-García, D. and Pozas-Kerstjens, A. (2023). TensorKrowch: Smooth integration of tensor networks in machine learning, arXiv e-prints, arXiv–2306.
+References · Tenet.jl

References

  • Fishman, M.; White, S. R. and Stoudenmire, E. M. (2022). The ITensor Software Library for Tensor Network Calculations. SciPost Phys. Codebases, 4.
  • Gray, J. (2018), quimb: A python package for quantum information and many-body calculations. Journal of Open Source Software 3, 819.
  • Gray, J. and Kourtis, S. (2021). Hyper-optimized tensor network contraction. Quantum 5, 410.
  • Hauschild, J.; Pollmann, F. and Zaletel, M. (2021). The Tensor Network Python (TeNPy) Library. In: APS March Meeting Abstracts, Vol. 2021; p. R21–006.
  • Ramón Pareja Monturiol, J.; Pérez-García, D. and Pozas-Kerstjens, A. (2023). TensorKrowch: Smooth integration of tensor networks in machine learning, arXiv e-prints, arXiv–2306.
diff --git a/dev/tensor-network.html b/dev/tensor-network.html index 532ea51e..5225d1b0 100644 --- a/dev/tensor-network.html +++ b/dev/tensor-network.html @@ -6,4 +6,4 @@ size(tn::AbstractTensorNetwork, index)

Return a mapping from indices to their dimensionalities.

If index is set, return the dimensionality of index. This is equivalent to size(tn)[index].

source
Missing docstring.

Missing docstring for tensors(::Tenet.TensorNetwork). Check Documenter's build log for details.

Modification

Add/Remove tensors

Base.push!Method
push!(tn::AbstractTensorNetwork, tensor::Tensor)

Add a new tensor to the Tensor Network.

See also: append!, pop!.

source
Base.append!Method
append!(tn::TensorNetwork, tensors::AbstractVecOrTuple{<:Tensor})

Add a list of tensors to a TensorNetwork.

See also: push!, merge!.

source
Base.merge!Method
merge!(self::TensorNetwork, others::TensorNetwork...)
 merge(self::TensorNetwork, others::TensorNetwork...)

Fuse various TensorNetworks into one.

See also: append!.

source
Base.pop!Method
pop!(tn::TensorNetwork, tensor::Tensor)
 pop!(tn::TensorNetwork, i::Union{Symbol,AbstractVecOrTuple{Symbol}})

Remove a tensor from the Tensor Network and returns it. If a Tensor is passed, then the first tensor satisfies egality (i.e. or ===) will be removed. If a Symbol or a list of Symbols is passed, then remove and return the tensors that contain all the indices.

See also: push!, delete!.

source
Base.delete!Method
delete!(tn::TensorNetwork, x)

Like pop! but return the TensorNetwork instead.

source

Replace existing elements

Base.replace!Function
replace!(tn::AbstractTensorNetwork, old => new...)
-replace(tn::AbstractTensorNetwork, old => new...)

Replace the element in old with the one in new. Depending on the types of old and new, the following behaviour is expected:

  • If Symbols, it will correspond to a index renaming.
  • If Tensors, first element that satisfies egality ( or ===) will be replaced.
source

Slicing

Base.selectdimFunction
selectdim(tn::AbstractTensorNetwork, index::Symbol, i)

Return a copy of the AbstractTensorNetwork where index has been projected to dimension i.

See also: view, slice!.

source
Tenet.slice!Function
slice!(tn::AbstractTensorNetwork, index::Symbol, i)

In-place projection of index on dimension i.

See also: selectdim, view.

source
Base.viewMethod
view(tn::AbstractTensorNetwork, index => i...)

Return a copy of the AbstractTensorNetwork where each index has been projected to dimension i. It is equivalent to a recursive call of selectdim.

See also: selectdim, slice!.

source

Miscelaneous

Base.copyMethod
copy(tn::TensorNetwork)

Return a shallow copy of a TensorNetwork.

source
Missing docstring.

Missing docstring for Base.rand(::Type{TensorNetwork}, n::Integer, regularity::Integer). Check Documenter's build log for details.

+replace(tn::AbstractTensorNetwork, old => new...)

Replace the element in old with the one in new. Depending on the types of old and new, the following behaviour is expected:

source

Slicing

Base.selectdimFunction
selectdim(tn::AbstractTensorNetwork, index::Symbol, i)

Return a copy of the AbstractTensorNetwork where index has been projected to dimension i.

See also: view, slice!.

source
Tenet.slice!Function
slice!(tn::AbstractTensorNetwork, index::Symbol, i)

In-place projection of index on dimension i.

See also: selectdim, view.

source
Base.viewMethod
view(tn::AbstractTensorNetwork, index => i...)

Return a copy of the AbstractTensorNetwork where each index has been projected to dimension i. It is equivalent to a recursive call of selectdim.

See also: selectdim, slice!.

source

Miscelaneous

Base.copyMethod
copy(tn::TensorNetwork)

Return a shallow copy of a TensorNetwork.

source
Missing docstring.

Missing docstring for Base.rand(::Type{TensorNetwork}, n::Integer, regularity::Integer). Check Documenter's build log for details.

diff --git a/dev/tensors.html b/dev/tensors.html index 6f7c48c3..16879a82 100644 --- a/dev/tensors.html +++ b/dev/tensors.html @@ -1,11 +1,11 @@ Tensors · Tenet.jl

Tensors

There are many jokes[1] about how to define a tensor. The definition we are giving here might not be the most correct one, but it is good enough for our use case (don't kill me please, mathematicians). A tensor $T$ of order[2] $n$ is a multilinear[3] application between $n$ vector spaces over a field $\mathcal{F}$.

\[T : \mathcal{F}^{\dim(1)} \times \dots \times \mathcal{F}^{\dim(n)} \mapsto \mathcal{F}\]

In layman's terms, it is a linear function whose inputs are vectors and the output is a scalar number.

\[T(\mathbf{v}^{(1)}, \dots, \mathbf{v}^{(n)}) = c \in \mathcal{F} \qquad\qquad \forall i, \mathbf{v}^{(i)} \in \mathcal{F}^{\dim(i)}\]

Tensor algebra is a higher-order generalization of linear algebra, where scalar numbers can be viewed as order-0 tensors, vectors as order-1 tensors, matrices as order-2 tensors, ...

Letters are used to identify each of the vector spaces the tensor relates to. In computer science, you would intuitively think of tensors as "n-dimensional arrays with named dimensions".

\[T_{ijk} \iff \mathtt{T[i,j,k]}\]

The Tensor type

In Tenet, a tensor is represented by the Tensor type, which wraps an array and a list of symbols. As it subtypes AbstractArray, many array operations can be dispatched to it.

You can create a Tensor by passing an array and a list of Symbols that name indices.

julia> Tᵢⱼₖ = Tensor(rand(3,5,2), (:i,:j,:k))3×5×2 Tensor{Float64, 3, Array{Float64, 3}}:
 [:, :, 1] =
- 0.778267  0.686699  0.689728  0.0125738  0.246916
- 0.453462  0.540498  0.145831  0.165915   0.204773
- 0.732301  0.264758  0.361329  0.506882   0.819037
+ 0.716966  0.816131  0.292421  0.389926  0.993739
+ 0.747671  0.88229   0.811297  0.905784  0.0419479
+ 0.911713  0.455111  0.388715  0.623101  0.794524
 
 [:, :, 2] =
- 0.709213   0.468461  0.958876  0.284568  0.194078
- 0.775376   0.157592  0.22332   0.400485  0.109313
- 0.0150892  0.946944  0.925321  0.722181  0.259605

The dimensionality or size of each index can be consulted using the size function.

Base.sizeMethod
Base.size(::Tensor[, i])

Return the size of the underlying array or the dimension i (specified by Symbol or Integer).

source
julia> size(Tᵢⱼₖ)(3, 5, 2)
julia> size(Tᵢⱼₖ, :j)5
julia> length(Tᵢⱼₖ)30

Operations

Contraction

Graphs.LinAlg.contractMethod
contract(a::Tensor[, b::Tensor]; dims=nonunique([inds(a)..., inds(b)...]))

Perform tensor contraction operation.

source

Factorizations

LinearAlgebra.svdMethod
LinearAlgebra.svd(tensor::Tensor; left_inds, right_inds, virtualind, kwargs...)

Perform SVD factorization on a tensor.

Keyword arguments

  • left_inds: left indices to be used in the SVD factorization. Defaults to all indices of t except right_inds.
  • right_inds: right indices to be used in the SVD factorization. Defaults to all indices of t except left_inds.
  • virtualind: name of the virtual bond. Defaults to a random Symbol.
source
LinearAlgebra.qrMethod
LinearAlgebra.qr(tensor::Tensor; left_inds, right_inds, virtualind, kwargs...)

Perform QR factorization on a tensor.

Keyword arguments

  • left_inds: left indices to be used in the QR factorization. Defaults to all indices of t except right_inds.
  • right_inds: right indices to be used in the QR factorization. Defaults to all indices of t except left_inds.
  • virtualind: name of the virtual bond. Defaults to a random Symbol.
source
LinearAlgebra.luMethod
LinearAlgebra.lu(tensor::Tensor; left_inds, right_inds, virtualind, kwargs...)

Perform LU factorization on a tensor.

Keyword arguments

  • left_inds: left indices to be used in the LU factorization. Defaults to all indices of t except right_inds.
  • right_inds: right indices to be used in the LU factorization. Defaults to all indices of t except left_inds.
  • virtualind: name of the virtual bond. Defaults to a random Symbol.
source
  • 1For example, recursive definitions like a tensor is whatever that transforms as a tensor.
  • 2The order of a tensor may also be known as rank or dimensionality in other fields. However, these can be missleading, since it has nothing to do with the rank of linear algebra nor with the dimensionality of a vector space. We prefer to use word order.
  • 3Meaning that the relationships between the output and the inputs, and the inputs between them, are linear.
+ 0.136287 0.341294 0.295501 0.387303 0.374442 + 0.659483 0.830502 0.488166 0.762 0.175478 + 0.625201 0.452687 0.128882 0.29749 0.623336

The dimensionality or size of each index can be consulted using the size function.

Base.sizeMethod
Base.size(::Tensor[, i])

Return the size of the underlying array or the dimension i (specified by Symbol or Integer).

source
julia> size(Tᵢⱼₖ)(3, 5, 2)
julia> size(Tᵢⱼₖ, :j)5
julia> length(Tᵢⱼₖ)30

Operations

Contraction

Graphs.LinAlg.contractMethod
contract(a::Tensor[, b::Tensor]; dims=nonunique([inds(a)..., inds(b)...]))

Perform tensor contraction operation.

source

Factorizations

LinearAlgebra.svdMethod
LinearAlgebra.svd(tensor::Tensor; left_inds, right_inds, virtualind, kwargs...)

Perform SVD factorization on a tensor.

Keyword arguments

  • left_inds: left indices to be used in the SVD factorization. Defaults to all indices of t except right_inds.
  • right_inds: right indices to be used in the SVD factorization. Defaults to all indices of t except left_inds.
  • virtualind: name of the virtual bond. Defaults to a random Symbol.
source
LinearAlgebra.qrMethod
LinearAlgebra.qr(tensor::Tensor; left_inds, right_inds, virtualind, kwargs...)

Perform QR factorization on a tensor.

Keyword arguments

  • left_inds: left indices to be used in the QR factorization. Defaults to all indices of t except right_inds.
  • right_inds: right indices to be used in the QR factorization. Defaults to all indices of t except left_inds.
  • virtualind: name of the virtual bond. Defaults to a random Symbol.
source
LinearAlgebra.luMethod
LinearAlgebra.lu(tensor::Tensor; left_inds, right_inds, virtualind, kwargs...)

Perform LU factorization on a tensor.

Keyword arguments

  • left_inds: left indices to be used in the LU factorization. Defaults to all indices of t except right_inds.
  • right_inds: right indices to be used in the LU factorization. Defaults to all indices of t except left_inds.
  • virtualind: name of the virtual bond. Defaults to a random Symbol.
source
diff --git a/dev/transformations-a30dcedf.png b/dev/transformations-a30dcedf.png new file mode 100644 index 0000000000000000000000000000000000000000..8462f0f6078dea5070d9e7e4c295940e5d958652 GIT binary patch literal 18040 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 literal 0 HcmV?d00001 diff --git a/dev/transformations-f136c61b.png b/dev/transformations-f136c61b.png deleted file mode 100644 index a9b449e26c18b49a7177192b37a3363dafbb318c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 18255 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 oPx-_Dhn4^L51;(s` Transformations · Tenet.jl

Transformations

In tensor network computations, it is good practice to apply various transformations to simplify the network structure, reduce computational cost, or prepare the network for further operations. These transformations modify the network's structure locally by permuting, contracting, factoring or truncating tensors.

A crucial reason why these methods are indispensable lies in their ability to drastically reduce the problem size of the contraction path search and also the contraction. This doesn't necessarily involve reducing the maximum rank of the Tensor Network itself, but more importantly, it reduces the size (or rank) of the involved tensors.

Our approach is based in (Gray and Kourtis, 2021), which can also be found in quimb.

In Tenet, we provide a set of predefined transformations which you can apply to your TensorNetwork using both the transform/transform! functions.

Tenet.transformFunction
transform(tn::TensorNetwork, config::Transformation)
 transform(tn::TensorNetwork, configs)

Return a new TensorNetwork where some Transformation has been performed into it.

See also: transform!.

source
Tenet.transform!Function
transform!(tn::TensorNetwork, config::Transformation)
-transform!(tn::TensorNetwork, configs)

In-place version of transform.

source

Available transformations

Hyperindex converter

Tenet.HyperFlattenType
HyperFlatten <: Transformation

Convert hyperindices to COPY-tensors, represented by DeltaArrays. This transformation is always used by default when visualizing a TensorNetwork with plot.

See also: HyperGroup.

source

Contraction simplification

Example block output

Diagonal reduction

Tenet.DiagonalReductionType
DiagonalReduction <: Transformation

Reduce the dimension of a Tensor in a TensorNetwork when it has a pair of indices that fulfil a diagonal structure.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-12.
source
Example block output

Anti-diagonal reduction

Tenet.AntiDiagonalGaugingType
AntiDiagonalGauging <: Transformation

Reverse the order of tensor indices that fulfill the anti-diagonal condition. While this transformation doesn't directly enhance computational efficiency, it sets up the TensorNetwork for other operations that do.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-12.
  • skip List of indices to skip. Defaults to [].
source

Dimension truncation

Tenet.TruncateType
Truncate <: Transformation

Truncate the dimension of a Tensor in a TensorNetwork when it contains columns with all elements smaller than atol.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-12.
  • skip List of indices to skip. Defaults to [].
source
Example block output

Split simplification

Tenet.SplitSimplificationType
SplitSimplification <: Transformation

Reduce the rank of tensors in the TensorNetwork by decomposing them using the Singular Value Decomposition (SVD). Tensors whose factorization do not increase the maximum rank of the network are left decomposed.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-10.
source
Example block output
+transform!(tn::TensorNetwork, configs)

In-place version of transform.

source

Available transformations

Hyperindex converter

Tenet.HyperFlattenType
HyperFlatten <: Transformation

Convert hyperindices to COPY-tensors, represented by DeltaArrays. This transformation is always used by default when visualizing a TensorNetwork with plot.

See also: HyperGroup.

source

Contraction simplification

Example block output

Diagonal reduction

Tenet.DiagonalReductionType
DiagonalReduction <: Transformation

Reduce the dimension of a Tensor in a TensorNetwork when it has a pair of indices that fulfil a diagonal structure.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-12.
source
Example block output

Anti-diagonal reduction

Tenet.AntiDiagonalGaugingType
AntiDiagonalGauging <: Transformation

Reverse the order of tensor indices that fulfill the anti-diagonal condition. While this transformation doesn't directly enhance computational efficiency, it sets up the TensorNetwork for other operations that do.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-12.
  • skip List of indices to skip. Defaults to [].
source

Dimension truncation

Tenet.TruncateType
Truncate <: Transformation

Truncate the dimension of a Tensor in a TensorNetwork when it contains columns with all elements smaller than atol.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-12.
  • skip List of indices to skip. Defaults to [].
source
Example block output

Split simplification

Tenet.SplitSimplificationType
SplitSimplification <: Transformation

Reduce the rank of tensors in the TensorNetwork by decomposing them using the Singular Value Decomposition (SVD). Tensors whose factorization do not increase the maximum rank of the network are left decomposed.

Keyword Arguments

  • atol Absolute tolerance. Defaults to 1e-10.
source
Example block output diff --git a/dev/visualization.html b/dev/visualization.html index 114ad14f..56571c4c 100644 --- a/dev/visualization.html +++ b/dev/visualization.html @@ -1,4 +1,4 @@ Visualization · Tenet.jl

Visualization

Tenet provides a Package Extension for Makie support. You can just import a Makie backend and call GraphMakie.graphplot on a TensorNetwork.

GraphMakie.graphplotMethod
graphplot(tn::TensorNetwork; kwargs...)
 graphplot!(f::Union{Figure,GridPosition}, tn::TensorNetwork; kwargs...)
-graphplot!(ax::Union{Axis,Axis3}, tn::TensorNetwork; kwargs...)

Plot a TensorNetwork as a graph.

Keyword Arguments

  • labels If true, show the labels of the tensor indices. Defaults to false.
  • The rest of kwargs are passed to GraphMakie.graphplot.
source
graphplot(tn, layout=Stress(), labels=true)
Example block output
+graphplot!(ax::Union{Axis,Axis3}, tn::TensorNetwork; kwargs...)

Plot a TensorNetwork as a graph.

Keyword Arguments

  • labels If true, show the labels of the tensor indices. Defaults to false.
  • The rest of kwargs are passed to GraphMakie.graphplot.
source
graphplot(tn, layout=Stress(), labels=true)
Example block output