ITensors.jl Integration
IntU.jl provides a seamless bridge to ITensors.jl, allowing you to integrate entire tensor networks symbolically. This is particularly useful for studying random tensor networks, quantum circuits, and holographic models.
Basic Usage
The primary way to use the integration is to wrap the tensors you wish to integrate in the ITensorUnitary struct. For pure symbolic work, you don't even need a "dummy" ITensor object; you can just specify the indices.
using IntU, ITensors
# Define indices
i = Index(2, "Out")
j = Index(2, "In")
i2 = Index(2, "Out2")
j2 = Index(2, "In2")
# Mark a Haar-random unitary U and its adjoint U_dag
U = ITensorUnitary(out_indices=[i], in_indices=[j])
U_dag = ITensorUnitary(out_indices=[j2], in_indices=[i2], is_adj=true)
# Integrate E[Tr(U A U_dag B)] over U(2)
# Here we use A and B to connect the dangling indices
A = randomITensor(j, j2)
B = randomITensor(i2, i)
res = integrate([U, A, U_dag, B], dU(2))Defining Random Unitaries
The ITensorUnitary struct is used to mark tensors for integration. There are two primary ways to create it, depending on whether you are working purely symbolically or with existing data.
1. Symbolic Placeholders (Recommended)
For pure symbolic integration, you only need to specify the indices. No "dummy" ITensor object is required.
# Mark a Haar-random unitary by its indices
U = ITensorUnitary(out_indices=[i], in_indices=[j])[!TIP] This is usually the cleanest approach for deriving analytical formulas or performing benchmarks where the specific tensor values don't matter.
2. Wrapping Existing Tensors
If you already have an ITensor (e.g., from a numeric simulation) and want to integrate over its values as if it were random, you can wrap the existing object.
U_it = randomITensor(i, j)
U = ITensorUnitary(U_it; out_indices=[i], in_indices=[j])In this case, the integrate function will ignore the current numerical contents of U_it and treat it as a random placeholder. After integration, the result will correctly contract with any other tensors connected to those indices.
Supported Measures
The ITensors integration supports all measure types provided by IntU:
| Measure | Usage |
|---|---|
| Haar Unitary | dU(dim) |
| Orthogonal Group | dO(dim) |
| Symplectic Group | dSp(dim) |
| Unitary $t$-designs | dDesign(dim, t) |
Example: Orthogonal Integration
using IntU, ITensors
# Define indices for a 3x3 orthogonal matrix O
o1 = Index(3, "Out")
i1 = Index(3, "In")
# Mark O as an orthogonal random matrix
O = ITensorUnitary(out_indices=[o1], in_indices=[i1])
# Constant tensor A
A = randomITensor(o1, i1)
# Integrate Tr(O A) - should be zero as E[O_ij] = 0
res = integrate([O, A], dO(3))Symbolic Dimensions
You can use symbolic dimensions from Symbolics.jl even when working with ITensors. While ITensor objects require a concrete size for construction, IntU will interpret the integration dimension symbolically if a symbolic variable is passed to the measure.
using IntU, ITensors, Symbolics
@variables d_sym
i = Index(2, "Out"); j = Index(2, "In")
i2 = Index(2, "Out2"); j2 = Index(2, "In2")
U = ITensorUnitary(out_indices=[i], in_indices=[j])
U_dag = ITensorUnitary(out_indices=[j2], in_indices=[i2], is_adj=true)
# Integrate E[U ⊗ U†]
res = integrate([U, U_dag], dU(d_sym))
# Result is an ITensor with indices {i, j, i2, j2} involving d_symNested and Sequential Integration
For networks with multiple independent random unitaries, you can perform integration in steps.
using IntU, ITensors
i = Index(2); j = Index(2); k = Index(2)
U_it = randomITensor(i, j); V_it = randomITensor(j, k)
A = randomITensor(k, i) # Constant tensor to close the loop
# Wrap U and V
U_wrap = ITensorUnitary(U_it, out_indices=[i], in_indices=[j])
V_wrap = ITensorUnitary(V_it, out_indices=[j], in_indices=[k])
# Network: Tr(U * V * A)
# Step 1: Integrate over U
res_partial = integrate([U_wrap, V_it, A], dU(2))
# Step 2: Integrate the result over V
res_final = integrate([V_wrap, res_partial], dU(2))Performance & Algorithms
The ITensors integration uses a Graphical Weingarten Engine. Instead of expanding the Full matrix, it:
- Identifies the network topology (which indices are connected to which tensors).
- Generates the possible Weingarten contractions as deltas over original ITensor indices.
- Returns a sum of contracted ITensor networks.
This is much more efficient than traditional matrix methods for large sparse networks.
API Reference
IntU.ITensorUnitary — Type
ITensorUnitary(tensor; out_indices, in_indices, is_adj=false)Wrapper to mark an ITensor (or any other tensor object) as a random unitary for integration.
IntU.GraphicalUnitary — Type
GraphicalUnitaryRepresents a random unitary tensor in a graphical network. out_indices and in_indices are lists of index objects (e.g. ITensor Index).
IntU.integrate_graphical — Function
integrate_graphical(constants, unitaries, measure::AbstractMeasure)Integrates a tensor network. constants is a list of tensors (e.g. ITensors). unitaries is a list of GraphicalUnitary. measure is an AbstractMeasure (e.g. HaarMeasure, OrthogonalMeasure).
Returns a sum of terms, where each term is a product of constants and deltas.