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 are either inferred from shared Index objects (when constructing from a collection of ITensors) or taken from a graph passed explicitly alongside the tensors.

Construction

When you already have ITensors in hand, edges are inferred automatically from shared indices:

using Graphs: edges, ne, neighbors, nv, vertices
using ITensorNetworks: ITensorNetwork, add, linkinds, siteinds
using ITensors: Index, ITensor
using NamedGraphs.NamedGraphGenerators: named_grid

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(Dict("A" => A, "B" => B, "C" => C))  # named vertices via a Dict
ITensorNetworks.ITensorNetwork{String} with 3 vertices:
3-element NamedGraphs.OrderedDictionaries.OrderedIndices{String}:
 "B"
 "A"
 "C"

and 2 edge(s):
"B" => "A"
"B" => "C"

with vertex data:
3-element Dictionaries.Dictionary{String, Any}:
 "B" │ ((dim=2|id=239|"j"), (dim=2|id=667|"k"))
 "A" │ ((dim=2|id=391|"i"), (dim=2|id=239|"j"))
 "C" │ ((dim=2|id=667|"k"),)

If you want to control edges directly — for example to build a structured network on a prescribed lattice and fill in tensors later — pass a NamedGraph along with a collection of ITensors indexed by vertex. To create a tensor network with shared link indices on each edge, build the indices once per edge and reuse them at both endpoints:

using ITensors: random_itensor
using NamedGraphs: NamedGraph
using NamedGraphs.GraphsExtensions: edgetype, incident_edges

g = NamedGraph(named_grid((3, 3)))
s = siteinds("S=1/2", g)  # one spin-½ site Index per vertex

# One shared link Index per edge; bond dimension χ
χ = 2
links = Dict(e => Index(χ, "Link") for e in edges(g))

# Per-vertex tensor: the site Index plus the link Index of every incident edge
tensors = Dict(map(collect(vertices(g))) do v
    site_v = s[v]
    link_v = [haskey(links, e) ? links[e] : links[reverse(e)] for e in incident_edges(g, v)]
    return v => random_itensor(site_v..., link_v...)
end)

ψ = ITensorNetwork(tensors)
ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
 (3, 2)
 (1, 2)
 (3, 1)
 (1, 1)
 (3, 3)
 (1, 3)
 (2, 2)
 (2, 1)
 (2, 3)

and 12 edge(s):
(3, 2) => (3, 1)
(3, 2) => (3, 3)
(3, 2) => (2, 2)
(1, 2) => (1, 1)
(1, 2) => (1, 3)
(1, 2) => (2, 2)
(3, 1) => (2, 1)
(1, 1) => (2, 1)
(3, 3) => (2, 3)
(1, 3) => (2, 3)
(2, 2) => (2, 1)
(2, 2) => (2, 3)

with vertex data:
9-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any}:
 (3, 2) │ ((dim=2|id=166|"S=1/2,Site,n=3×2"), (dim=2|id=248|"Link"), (dim=2|id=…
 (1, 2) │ ((dim=2|id=499|"S=1/2,Site,n=1×2"), (dim=2|id=972|"Link"), (dim=2|id=…
 (3, 1) │ ((dim=2|id=489|"S=1/2,Site,n=3×1"), (dim=2|id=215|"Link"), (dim=2|id=…
 (1, 1) │ ((dim=2|id=304|"S=1/2,Site,n=1×1"), (dim=2|id=904|"Link"), (dim=2|id=…
 (3, 3) │ ((dim=2|id=845|"S=1/2,Site,n=3×3"), (dim=2|id=978|"Link"), (dim=2|id=…
 (1, 3) │ ((dim=2|id=673|"S=1/2,Site,n=1×3"), (dim=2|id=135|"Link"), (dim=2|id=…
 (2, 2) │ ((dim=2|id=4|"S=1/2,Site,n=2×2"), (dim=2|id=405|"Link"), (dim=2|id=80…
 (2, 1) │ ((dim=2|id=502|"S=1/2,Site,n=2×1"), (dim=2|id=904|"Link"), (dim=2|id=…
 (2, 3) │ ((dim=2|id=99|"S=1/2,Site,n=2×3"), (dim=2|id=147|"Link"), (dim=2|id=1…

Higher-level construction routines (random networks, product states, OpSum-derived TTNs, etc.) are provided by sibling functions like TreeTensorNetwork(opsum, sites) and the test-only helpers in test/utils.jl.

ITensorNetworks.ITensorNetworkType
ITensorNetwork{V}

A tensor network where each vertex holds an ITensor. Storage is split across three fields:

  • graph::NamedGraph{V} — the network's graph (V is the vertex type),
  • vertex_data::Dictionary{V, ITensor} — the tensor at each vertex,
  • ind_to_vertices::Dict{Index, Set{V}} — reverse map from each Index to the vertices it appears in.

The reverse map keeps vertex lookup by shared Index O(1) and enforces the graph-edge ↔ shared-Index invariant: every Index appears at either one vertex (an external / site index) or two (a bond), and every graph edge corresponds to exactly the pair of vertices sharing at least one Index. Hyperedges (an Index shared by three or more vertices) are rejected.

Construction

ITensorNetwork(tensors)
ITensorNetwork{V}(tensors)

tensors is any collection where keys(tensors) are vertex labels and values(tensors) are the ITensors at those vertices (e.g. a Dict, a Dictionary, or a Vector{ITensor} with linear-index vertex labels). Edges are inferred from shared Indexes.

Example

julia> using ITensors: Index, ITensor

julia> i, j, k = Index(2, "i"), Index(2, "j"), Index(2, "k");

julia> tn = ITensorNetwork([ITensor(i, j), ITensor(j, k)]);

See also: IndsNetwork, TreeTensorNetwork.

source

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) indices
ITensorNetworks.IndsNetwork{Tuple{Int64, Int64}, ITensors.Index} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
 (3, 2)
 (1, 2)
 (3, 1)
 (1, 1)
 (3, 3)
 (1, 3)
 (2, 2)
 (2, 1)
 (2, 3)

and 12 edge(s):
(3, 2) => (3, 1)
(3, 2) => (3, 3)
(3, 2) => (2, 2)
(1, 2) => (1, 1)
(1, 2) => (1, 3)
(1, 2) => (2, 2)
(3, 1) => (2, 1)
(1, 1) => (2, 1)
(3, 3) => (2, 3)
(1, 3) => (2, 3)
(2, 2) => (2, 1)
(2, 2) => (2, 3)

with vertex data:
9-element DataGraphs.VertexDataView{Tuple{Int64, Int64}, Vector{ITensors.Index}, ITensorNetworks.IndsNetwork{Tuple{Int64, Int64}, ITensors.Index}}:
 (3, 2) │ #undef
 (1, 2) │ #undef
 (3, 1) │ #undef
 (1, 1) │ #undef
 (3, 3) │ #undef
 (1, 3) │ #undef
 (2, 2) │ #undef
 (2, 1) │ #undef
 (2, 3) │ #undef

and edge data:
12-element DataGraphs.EdgeDataView{NamedGraphs.NamedEdge{Tuple{Int64, Int64}}, Vector{ITensors.Index}, ITensorNetworks.IndsNetwork{Tuple{Int64, Int64}, ITensors.Index}}:
 (3, 2) => (3, 1) │ ITensors.Index{Int64}[(dim=2|id=248|"Link")]
 (3, 2) => (3, 3) │ ITensors.Index[(dim=2|id=978|"Link")]
 (3, 2) => (2, 2) │ ITensors.Index{Int64}[(dim=2|id=956|"Link")]
 (1, 2) => (1, 1) │ ITensors.Index{Int64}[(dim=2|id=972|"Link")]
 (1, 2) => (1, 3) │ ITensors.Index[(dim=2|id=135|"Link")]
 (1, 2) => (2, 2) │ ITensors.Index[(dim=2|id=801|"Link")]
 (3, 1) => (2, 1) │ ITensors.Index{Int64}[(dim=2|id=215|"Link")]
 (1, 1) => (2, 1) │ ITensors.Index[(dim=2|id=904|"Link")]
 (3, 3) => (2, 3) │ ITensors.Index{Int64}[(dim=2|id=533|"Link")]
 (1, 3) => (2, 3) │ ITensors.Index[(dim=2|id=182|"Link")]
 (2, 2) => (2, 1) │ ITensors.Index{Int64}[(dim=2|id=405|"Link")]
 (2, 2) => (2, 3) │ ITensors.Index[(dim=2|id=147|"Link")]

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(tn; kwargs...) sweeps and recompresses all bonds at once.

ψ1, ψ2 = ψ, ψ
ψ12 = add(ψ1, ψ2)
ψ12 = ψ1 + ψ2
ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
 (3, 2)
 (1, 2)
 (3, 1)
 (1, 1)
 (3, 3)
 (1, 3)
 (2, 2)
 (2, 1)
 (2, 3)

and 12 edge(s):
(3, 2) => (3, 1)
(3, 2) => (3, 3)
(3, 2) => (2, 2)
(1, 2) => (1, 1)
(1, 2) => (1, 3)
(1, 2) => (2, 2)
(3, 1) => (2, 1)
(1, 1) => (2, 1)
(3, 3) => (2, 3)
(1, 3) => (2, 3)
(2, 2) => (2, 1)
(2, 2) => (2, 3)

with vertex data:
9-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any}:
 (3, 2) │ ((dim=2|id=166|"S=1/2,Site,n=3×2"), (dim=4|id=788|"Link"), (dim=4|id=…
 (1, 2) │ ((dim=2|id=499|"S=1/2,Site,n=1×2"), (dim=4|id=318|"Link"), (dim=4|id=…
 (3, 1) │ ((dim=2|id=489|"S=1/2,Site,n=3×1"), (dim=4|id=788|"Link"), (dim=4|id=…
 (1, 1) │ ((dim=2|id=304|"S=1/2,Site,n=1×1"), (dim=4|id=318|"Link"), (dim=4|id=…
 (3, 3) │ ((dim=2|id=845|"S=1/2,Site,n=3×3"), (dim=4|id=565|"Link"), (dim=4|id=…
 (1, 3) │ ((dim=2|id=673|"S=1/2,Site,n=1×3"), (dim=4|id=984|"Link"), (dim=4|id=…
 (2, 2) │ ((dim=2|id=4|"S=1/2,Site,n=2×2"), (dim=4|id=567|"Link"), (dim=4|id=39…
 (2, 1) │ ((dim=2|id=502|"S=1/2,Site,n=2×1"), (dim=4|id=693|"Link"), (dim=4|id=…
 (2, 3) │ ((dim=2|id=99|"S=1/2,Site,n=2×3"), (dim=4|id=514|"Link"), (dim=4|id=1…
ITensorNetworks.addMethod
add(tn1::AbstractITensorNetwork, tn2::AbstractITensorNetwork) -> ITensorNetwork

Add 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.

source

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 directly
ITensorNetworks.ITensorNetwork{Tuple{Int64, Int64}} with 9 vertices:
9-element NamedGraphs.OrderedDictionaries.OrderedIndices{Tuple{Int64, Int64}}:
 (3, 2)
 (1, 2)
 (3, 1)
 (1, 1)
 (3, 3)
 (1, 3)
 (2, 2)
 (2, 1)
 (2, 3)

and 12 edge(s):
(3, 2) => (3, 1)
(3, 2) => (3, 3)
(3, 2) => (2, 2)
(1, 2) => (1, 1)
(1, 2) => (1, 3)
(1, 2) => (2, 2)
(3, 1) => (2, 1)
(1, 1) => (2, 1)
(3, 3) => (2, 3)
(1, 3) => (2, 3)
(2, 2) => (2, 1)
(2, 2) => (2, 3)

with vertex data:
9-element Dictionaries.Dictionary{Tuple{Int64, Int64}, Any}:
 (3, 2) │ ((dim=2|id=166|"S=1/2,Site,n=3×2"), (dim=4|id=788|"Link"), (dim=4|id=…
 (1, 2) │ ((dim=2|id=499|"S=1/2,Site,n=1×2"), (dim=4|id=318|"Link"), (dim=4|id=…
 (3, 1) │ ((dim=2|id=489|"S=1/2,Site,n=3×1"), (dim=4|id=788|"Link"), (dim=4|id=…
 (1, 1) │ ((dim=2|id=304|"S=1/2,Site,n=1×1"), (dim=4|id=318|"Link"), (dim=4|id=…
 (3, 3) │ ((dim=2|id=845|"S=1/2,Site,n=3×3"), (dim=4|id=565|"Link"), (dim=4|id=…
 (1, 3) │ ((dim=2|id=673|"S=1/2,Site,n=1×3"), (dim=4|id=191|"Link"), (dim=4|id=…
 (2, 2) │ ((dim=2|id=4|"S=1/2,Site,n=2×2"), (dim=4|id=567|"Link"), (dim=4|id=39…
 (2, 1) │ ((dim=2|id=502|"S=1/2,Site,n=2×1"), (dim=4|id=693|"Link"), (dim=4|id=…
 (2, 3) │ ((dim=2|id=99|"S=1/2,Site,n=2×3"), (dim=4|id=514|"Link"), (dim=4|id=1…

Truncation parameters (cutoff, maxdim, mindim, …) are forwarded to ITensors.svd. For a TreeTensorNetwork, the sweep-based truncate(tn; kwargs...) is usually more convenient because it recompresses the entire network at once with controlled errors; see the Tree Tensor Networks page.

Base.truncateMethod
truncate(tn::AbstractITensorNetwork, edge; kwargs...) -> AbstractITensorNetwork

Truncate 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.

This is the user-facing verb for bond compression and delegates to left_orth, which names the underlying left-orthogonal gauge transformation. 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(tn; kwargs...) sweeps all bonds and is generally preferred for full recompression after addition or subspace expansion.

See also: left_orth, Base.truncate(::AbstractTreeTensorNetwork).

source
ITensorNetworks.left_orthFunction
left_orth(tn::AbstractITensorNetwork, edge; kwargs...) -> AbstractITensorNetwork

Non-mutating version of left_orth!: return a copy of tn with the bond at edge made left-orthogonal.

source
ITensorNetworks.left_orth!Function
left_orth!(tn::AbstractITensorNetwork, edge; kwargs...) -> AbstractITensorNetwork

Mutate tn so the bond at edge is left-orthogonal: SVD-factorize tn[src(edge)] into U * S * V, replace tn[src(edge)] with the left-isometric U, and absorb S * V into tn[dst(edge)]. edge may be an AbstractEdge or a Pair of vertices.

kwargs... are forwarded to ITensors.svd, so passing cutoff / maxdim / mindim truncates the bond in addition to gauging it. For a non-truncating left-orthogonal gauge, prefer LinearAlgebra.qr!(tn, edge), which uses QR instead of SVD.

See also: left_orth, Base.truncate(::AbstractITensorNetwork, ::AbstractEdge).

source