ITensor Networks
The ITensorNetwork Type
An ITensorNetwork is the central data structure of this package. It represents a collection of ITensors arranged on a graph, where each edge encodes a shared (contracted) index between the neighboring tensors.
Key facts:
- The underlying graph is a
NamedGraph, so vertices can be any hashable Julia value: integers, tuples, strings, etc. - Each vertex holds exactly one
ITensor. - Edges and link indices are either inferred from shared
Indexobjects (when constructing from a collection ofITensors) or inserted automatically (when constructing from anIndsNetwork).
Construction
The most common entry point is an IndsNetwork — a graph whose vertices and edges carry Index objects. Generate site indices with the siteinds function which takes a site type string (such as "S=1/2" or "Electron") and a NamedGraph. The NamedGraph can be generated from functions such as named_grid, named_comb_tree, etc. from the NamedGraphs.jl NamedGraphGenerators module:
using Graphs: edges, ne, neighbors, nv, vertices
using ITensorNetworks: ITensorNetwork, add, linkinds, siteinds
using ITensors: Index, ITensor
using NamedGraphs.NamedGraphGenerators: named_grid
# 3×3 square-lattice tensor network
g = named_grid((3, 3))
s = siteinds("S=1/2", g) # one spin-½ Index per vertex
# Zero-initialized, bond dimension 2
ψ = ITensorNetwork(s; link_space = 2)
# Product state — every site in the |↑⟩ state
ψ = ITensorNetwork("Up", s)
# Staggered initialization with a vertex-dependent function
ψ = ITensorNetwork(v -> isodd(sum(v)) ? "Up" : "Dn", s)ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
(1, 1)
(2, 1)
(3, 1)
(1, 2)
(2, 2)
(3, 2)
(1, 3)
(2, 3)
(3, 3)
and 12 edge(s):
(1, 1) => (2, 1)
(1, 1) => (1, 2)
(2, 1) => (3, 1)
(2, 1) => (2, 2)
(3, 1) => (3, 2)
(1, 2) => (2, 2)
(1, 2) => (1, 3)
(2, 2) => (3, 2)
(2, 2) => (2, 3)
(3, 2) => (3, 3)
(1, 3) => (2, 3)
(2, 3) => (3, 3)
with vertex data:
9-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any}:
(1, 1) │ ((dim=2|id=386|"S=1/2,Site,n=1×1"), (dim=1|id=109|"1×1,2×1"), (dim=1|…
(2, 1) │ ((dim=2|id=627|"S=1/2,Site,n=2×1"), (dim=1|id=109|"1×1,2×1"), (dim=1|…
(3, 1) │ ((dim=2|id=140|"S=1/2,Site,n=3×1"), (dim=1|id=949|"2×1,3×1"), (dim=1|…
(1, 2) │ ((dim=2|id=171|"S=1/2,Site,n=1×2"), (dim=1|id=95|"1×1,1×2"), (dim=1|i…
(2, 2) │ ((dim=2|id=948|"S=1/2,Site,n=2×2"), (dim=1|id=539|"2×1,2×2"), (dim=1|…
(3, 2) │ ((dim=2|id=218|"S=1/2,Site,n=3×2"), (dim=1|id=157|"3×1,3×2"), (dim=1|…
(1, 3) │ ((dim=2|id=446|"S=1/2,Site,n=1×3"), (dim=1|id=979|"1×2,1×3"), (dim=1|…
(2, 3) │ ((dim=2|id=91|"S=1/2,Site,n=2×3"), (dim=1|id=292|"2×2,2×3"), (dim=1|i…
(3, 3) │ ((dim=2|id=415|"S=1/2,Site,n=3×3"), (dim=1|id=839|"3×2,3×3"), (dim=1|…When you already have ITensors in hand, edges are inferred automatically from shared indices:
i, j, k = Index(2, "i"), Index(2, "j"), Index(2, "k")
A, B, C = ITensor(i, j), ITensor(j, k), ITensor(k)
tn = ITensorNetwork([A, B, C]) # integer vertices 1, 2, 3
tn = ITensorNetwork(["A", "B", "C"], [A, B, C]) # named vertices
tn = ITensorNetwork(["A" => A, "B" => B, "C" => C]) # from pairsITensorNetworks.ITensorNetwork{String} with 3 vertices:
3-element NamedGraphs.OrderedDictionaries.OrderedIndices{String}:
"A"
"B"
"C"
and 2 edge(s):
"A" => "B"
"B" => "C"
with vertex data:
3-element Dictionaries.Dictionary{String, Any}:
"A" │ ((dim=2|id=865|"i"), (dim=2|id=454|"j"))
"B" │ ((dim=2|id=454|"j"), (dim=2|id=689|"k"))
"C" │ ((dim=2|id=689|"k"),)ITensorNetworks.ITensorNetwork — Type
ITensorNetwork{V}A tensor network where each vertex holds an ITensor. The network graph is a NamedGraph{V} and edges represent shared indices between neighboring tensors.
Constructors
From an IndsNetwork (most common):
ITensorNetwork(is::IndsNetwork; link_space = 1)
ITensorNetwork(f, is::IndsNetwork; link_space = 1)
ITensorNetwork(eltype, undef, is::IndsNetwork; link_space = 1)- With no function argument
f, tensors are initialized to zero. - With a function
f(v)that returns a state label (e.g."Up","Dn") or anITensorconstructor, tensors are initialized accordingly. link_spacecontrols the bond-index dimension (default 1).
From a graph (site indices inferred as trivial):
ITensorNetwork(graph::AbstractNamedGraph; link_space = 1)
ITensorNetwork(f, graph::AbstractNamedGraph; link_space = 1)From a collection of ITensors:
ITensorNetwork(ts::AbstractVector{ITensor})
ITensorNetwork(vs, ts::AbstractVector{ITensor})
ITensorNetwork(ts::AbstractVector{<:Pair{<:Any, ITensor}})
ITensorNetwork(ts::AbstractDict{<:Any, ITensor})Edges are inferred from shared indices between tensors.
From a single ITensor:
ITensorNetwork(t::ITensor)Wraps the tensor in a single-vertex network.
Example
julia> using NamedGraphs.NamedGraphGenerators: named_grid
julia> g = named_grid((4,));
julia> s = siteinds("S=1/2", g);
julia> tn = ITensorNetwork(s; link_space = 2);
julia> tn = ITensorNetwork("Up", s);
See also: IndsNetwork, ttn, TreeTensorNetwork.
Accessing Data
v = (1, 2)
T = ψ[v] # ITensor at vertex (1,2)
ψ[v] = T # replace tensor at a vertex
vertices(ψ) # all vertex labels
edges(ψ) # all edges
neighbors(ψ, v) # neighbouring vertices of v
nv(ψ), ne(ψ) # vertex / edge counts
siteinds(ψ) # IndsNetwork of site (physical) indices
linkinds(ψ) # IndsNetwork of bond (virtual) indicesITensorNetworks.IndsNetwork{Tuple{Int64, Int64}, ITensors.Index} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
(1, 1)
(2, 1)
(3, 1)
(1, 2)
(2, 2)
(3, 2)
(1, 3)
(2, 3)
(3, 3)
and 12 edge(s):
(1, 1) => (2, 1)
(1, 1) => (1, 2)
(2, 1) => (3, 1)
(2, 1) => (2, 2)
(3, 1) => (3, 2)
(1, 2) => (2, 2)
(1, 2) => (1, 3)
(2, 2) => (3, 2)
(2, 2) => (2, 3)
(3, 2) => (3, 3)
(1, 3) => (2, 3)
(2, 3) => (3, 3)
with vertex data:
0-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Vector{ITensors.Index}}
and edge data:
12-element Dictionaries.Dictionary{NamedGraphs.NamedEdge{Tuple{Int64, Int64}}, Vector{ITensors.Index}}:
(1, 1) => (2, 1) │ ITensors.Index[(dim=1|id=109|"1×1,2×1")]
(1, 1) => (1, 2) │ ITensors.Index[(dim=1|id=95|"1×1,1×2")]
(2, 1) => (3, 1) │ ITensors.Index[(dim=1|id=949|"2×1,3×1")]
(2, 1) => (2, 2) │ ITensors.Index[(dim=1|id=539|"2×1,2×2")]
(3, 1) => (3, 2) │ ITensors.Index[(dim=1|id=157|"3×1,3×2")]
(1, 2) => (2, 2) │ ITensors.Index[(dim=1|id=429|"1×2,2×2")]
(1, 2) => (1, 3) │ ITensors.Index[(dim=1|id=979|"1×2,1×3")]
(2, 2) => (3, 2) │ ITensors.Index[(dim=1|id=11|"2×2,3×2")]
(2, 2) => (2, 3) │ ITensors.Index[(dim=1|id=292|"2×2,2×3")]
(3, 2) => (3, 3) │ ITensors.Index[(dim=1|id=839|"3×2,3×3")]
(1, 3) => (2, 3) │ ITensors.Index[(dim=1|id=824|"1×3,2×3")]
(2, 3) => (3, 3) │ ITensors.Index[(dim=1|id=777|"2×3,3×3")]Adding Two ITensorNetworks
Two networks with the same graph and site indices can be added. The result represents the tensor network ψ₁ + ψ₂ and has bond dimension equal to the sum of the two input bond dimensions. Individual bonds of the result can be recompressed with truncate(tn, edge). For TreeTensorNetwork, the no-argument form truncate(ttn; kwargs...) sweeps and recompresses all bonds at once.
ψ1, ψ2 = ψ, ψ
ψ12 = add(ψ1, ψ2)
ψ12 = ψ1 + ψ2ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
(1, 1)
(2, 1)
(3, 1)
(1, 2)
(2, 2)
(3, 2)
(1, 3)
(2, 3)
(3, 3)
and 12 edge(s):
(1, 1) => (2, 1)
(1, 1) => (1, 2)
(2, 1) => (3, 1)
(2, 1) => (2, 2)
(3, 1) => (3, 2)
(1, 2) => (2, 2)
(1, 2) => (1, 3)
(2, 2) => (3, 2)
(2, 2) => (2, 3)
(3, 2) => (3, 3)
(1, 3) => (2, 3)
(2, 3) => (3, 3)
with vertex data:
9-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any}:
(1, 1) │ ((dim=2|id=386|"S=1/2,Site,n=1×1"), (dim=2|id=734|"1×1,2×1"), (dim=2|…
(2, 1) │ ((dim=2|id=627|"S=1/2,Site,n=2×1"), (dim=2|id=734|"1×1,2×1"), (dim=2|…
(3, 1) │ ((dim=2|id=140|"S=1/2,Site,n=3×1"), (dim=2|id=236|"2×1,3×1"), (dim=2|…
(1, 2) │ ((dim=2|id=171|"S=1/2,Site,n=1×2"), (dim=2|id=76|"1×1,1×2"), (dim=2|i…
(2, 2) │ ((dim=2|id=948|"S=1/2,Site,n=2×2"), (dim=2|id=151|"2×1,2×2"), (dim=2|…
(3, 2) │ ((dim=2|id=218|"S=1/2,Site,n=3×2"), (dim=2|id=246|"3×1,3×2"), (dim=2|…
(1, 3) │ ((dim=2|id=446|"S=1/2,Site,n=1×3"), (dim=2|id=156|"1×2,1×3"), (dim=2|…
(2, 3) │ ((dim=2|id=91|"S=1/2,Site,n=2×3"), (dim=2|id=819|"2×2,2×3"), (dim=2|i…
(3, 3) │ ((dim=2|id=415|"S=1/2,Site,n=3×3"), (dim=2|id=555|"3×2,3×3"), (dim=2|…ITensorNetworks.add — Method
add(tn1::AbstractITensorNetwork, tn2::AbstractITensorNetwork) -> ITensorNetworkAdd two ITensorNetworks together by taking their direct sum (growing the bond dimension). The result represents the state tn1 + tn2, with bond dimension on each edge equal to the sum of the bond dimensions of tn1 and tn2.
Both networks must have the same vertex set and matching site indices at each vertex.
Use truncate on the result to compress back to a lower bond dimension.
See also: Base.:+ for TreeTensorNetwork, truncate.
Bond Truncation
A single bond (edge) of any ITensorNetwork can be truncated by SVD:
edge = (1, 2) => (1, 3)
ψ12 = truncate(ψ12, (1, 2) => (1, 3)) # truncate the bond between vertices (1,2) and (1,3)
ψ12 = truncate(ψ12, edge) # or pass an AbstractEdge directlyITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
(1, 1)
(2, 1)
(3, 1)
(1, 2)
(2, 2)
(3, 2)
(1, 3)
(2, 3)
(3, 3)
and 12 edge(s):
(1, 1) => (2, 1)
(1, 1) => (1, 2)
(2, 1) => (3, 1)
(2, 1) => (2, 2)
(3, 1) => (3, 2)
(1, 2) => (2, 2)
(1, 2) => (1, 3)
(2, 2) => (3, 2)
(2, 2) => (2, 3)
(3, 2) => (3, 3)
(1, 3) => (2, 3)
(2, 3) => (3, 3)
with vertex data:
9-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any}:
(1, 1) │ ((dim=2|id=386|"S=1/2,Site,n=1×1"), (dim=2|id=734|"1×1,2×1"), (dim=2|…
(2, 1) │ ((dim=2|id=627|"S=1/2,Site,n=2×1"), (dim=2|id=734|"1×1,2×1"), (dim=2|…
(3, 1) │ ((dim=2|id=140|"S=1/2,Site,n=3×1"), (dim=2|id=236|"2×1,3×1"), (dim=2|…
(1, 2) │ ((dim=2|id=171|"S=1/2,Site,n=1×2"), (dim=2|id=76|"1×1,1×2"), (dim=2|i…
(2, 2) │ ((dim=2|id=948|"S=1/2,Site,n=2×2"), (dim=2|id=151|"2×1,2×2"), (dim=2|…
(3, 2) │ ((dim=2|id=218|"S=1/2,Site,n=3×2"), (dim=2|id=246|"3×1,3×2"), (dim=2|…
(1, 3) │ ((dim=2|id=446|"S=1/2,Site,n=1×3"), (dim=2|id=167|"1×3,2×3"), (dim=2|…
(2, 3) │ ((dim=2|id=91|"S=1/2,Site,n=2×3"), (dim=2|id=819|"2×2,2×3"), (dim=2|i…
(3, 3) │ ((dim=2|id=415|"S=1/2,Site,n=3×3"), (dim=2|id=555|"3×2,3×3"), (dim=2|…Truncation parameters (cutoff, maxdim, mindim, …) are forwarded to ITensors.svd. For a TreeTensorNetwork, the sweep-based truncate(ttn; kwargs...) is usually more convenient because it recompresses the entire network at once with controlled errors; see the Tree Tensor Networks page.
Base.truncate — Method
truncate(tn::AbstractITensorNetwork, edge; kwargs...) -> ITensorNetworkTruncate the bond across edge in tn by performing an SVD and discarding small singular values. edge may be an AbstractEdge or a Pair of vertices.
Truncation parameters are passed as keyword arguments and forwarded to ITensors.svd:
cutoff: Drop singular values smaller than this threshold.maxdim: Maximum number of singular values to keep.mindim: Minimum number of singular values to keep.
This operates on a single bond. For TreeTensorNetwork, the no-argument form truncate(ttn; kwargs...) sweeps all bonds and is generally preferred for full recompression after addition or subspace expansion.
See also: Base.truncate(::AbstractTreeTensorNetwork).