Implementing block Sparse MPOs and relation with Quantum Numbers

Hi I am trying to work with iTensor to support different types of block sparse MPOs. If I understand correctly, the only form of block sparse MPOs one can construct right now with iTensor is using Quantum Numbers.

However I have two opSum operators of the form say:

I_1 I_2 I_3 I_4 + X_1 X_2 X_3 X_4
I_3 I_4 I_5 I_6 + X_3 X_4 X_5 X_6

When I create MPOs out of these operators and multiply them together, I will get a MPO with bond dimension 2/4 and would look like the following (on contraction). Where MPO final = MPO1 * MPO2

MPO
[1] ((dim=2|id=561|"S=1/2,Site,n=1"), (dim=2|id=561|"S=1/2,Site,n=1")', (dim=2|id=18|"Link,l=1"))
[2] ((dim=2|id=18|"Link,l=1"), (dim=2|id=330|"S=1/2,Site,n=2"), (dim=2|id=330|"S=1/2,Site,n=2")', (dim=2|id=943|"Link,l=2"))
[3] ((dim=2|id=943|"Link,l=2"), (dim=2|id=685|"S=1/2,Site,n=3"), (dim=2|id=685|"S=1/2,Site,n=3")', (dim=4|id=989|"Link,l=3"))
[4] ((dim=4|id=989|"Link,l=3"), (dim=2|id=623|"S=1/2,Site,n=4"), (dim=2|id=623|"S=1/2,Site,n=4")', (dim=2|id=551|"Link,l=4"))
[5] ((dim=2|id=551|"Link,l=4"), (dim=2|id=779|"S=1/2,Site,n=5"), (dim=2|id=779|"S=1/2,Site,n=5")', (dim=2|id=712|"Link,l=5"))
[6] ((dim=2|id=712|"Link,l=5"), (dim=2|id=934|"S=1/2,Site,n=6"), (dim=2|id=934|"S=1/2,Site,n=6")')

However for the 2nd element in this MPO, we have a tensor that is equal to Identity if link l=1 and link=2 are both at index 1 and is equal to X operator if link l=1 and link l=2 are at index 2. For any other cases (1, 2) or (2, 1) this tensor is just 0.

Later on I will be calling MPO final * psi. But I want to take advantage of the block sparsity of the MPO final (it is 0 for the two possible link indexes). Is there a easy way to create such block sparse MPOs? I get that Quantum Numbers take advantage of block sparsity so I wonder if I can reuse any code there to exploit this?

It’s a good question. The QN ITensors in ITensors.jl are in fact built on top of a more general block sparse tensor type in NDTensors.jl, however right now that is more an internal detail that is not easily exposed to users. Having said that, you can use a slight hack to construct block sparse tensors that don’t have specified quantum numbers:

using ITensors: ITensor, Index, QN, Block

i = Index([QN() => 2, QN() => 2])
j = Index([QN() => 2, QN() => 2])
a = ITensor(Float64, i, j)
a[Block(1, 1)] = randn(2, 2)
a[Block(2, 2)] = randn(2, 2)

which constructs a block sparse tensor with two nonzero blocks:

julia> show(a)
ITensor ord=2
Dim 1: (dim=4|id=613) <Out>
 1: QN() => 2
 2: QN() => 2
Dim 2: (dim=4|id=939) <Out>
 1: QN() => 2
 2: QN() => 2
NDTensors.BlockSparse{Float64, Vector{Float64}, 2}
 4×4
Block(2, 2)
 [3:4, 3:4]
  0.8563781437612569   -0.3262202190043517
 -0.17888014351124132  -0.5242686688221203

Block(1, 1)
 [1:2, 1:2]
  0.18128185576868744  -1.5151649967961565
 -0.7430343642573164   -0.3957748521149144

In our ongoing rewrite of ITensors.jl, non-QN block sparse tensors will be directly supported through the BlockSparseArrays.jl package: GitHub - ITensor/BlockSparseArrays.jl , which will provide the basis for our new symmetric tensor types but will be usable as a standalone array backend for ITensor.

However, one thing to keep in mind is that in some operations like SVD, it isn’t necessarily straightforward to preserve the block sparsity, so the usefulness of a general block sparse tensor may be limited depending on the algorithm.