# Artificially increase bond dimension of MPS with conserved QNs

**URL:** <https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133>\
**Category:** ITensor Julia Questions\
**Tags:** julia, mps, qn\
**Created:** [November 23, 2024, 12:15pm UTC](https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133 "2024-11-23T12:15:24Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![phx](https://yyz2.discourse-cdn.com/free1/user_avatar/itensor.discourse.group/phx/32/673_2.png) [@phx](https://itensor.discourse.group/u/phx)\
**Post date:** [November 23, 2024, 12:15pm UTC](https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133/1 "2024-11-23T12:15:24Z")

</div>

Hi everyone,  
I’m trying to artificially increase the bond dimensions of a product-state MPS to a given bond dimension N (since I want to use this state in a TDVP evolution algorithm with fixed bond dimensions). I start from

```julia
sites = siteinds("Qubit", 10; conserve_parity=true)
initstate = MPS(sites, "0")

```

Normally, without QNs, I would use the method described [here](https://itensor.discourse.group/t/unreasonable-results-occurred-when-using-tdvp-to-calculate-the-long-range-interaction-hamiltonian/1653/6) to “artificially” enlarge the bond dimension to the desired size.  
Taking quantum numbers into account, I defined the following function:

```julia
function enlarge!(v::MPS, new_d)
    N = length(v)
    if hasqns(v[1])
        lflux = sum(flux, v[1:end-1])
        enlarged_links = Vector{ITensors.QNIndex}(undef, N - 1)
        for bond in (N - 1):-1:1
            enlarged_links[bond] = dag(Index(lflux => new_d; tags=tags(commonind(v[bond], v[bond + 1]))))
            lflux -= flux(v[bond])
        end
    else
        enlarged_links = [
            Index(new_d; tags=tags(commonind(v[bond], v[bond + 1]))) for bond in 1:(N - 1)
        ]
    end
    @show first(linkinds(v), 5)
    @show first(enlarged_links, 5)

    for bond in 1:(N - 1)
        bond_index = commonind(v[bond], v[bond + 1])
        v[bond] = v[bond] * delta(dag(bond_index),enlarged_links[bond])
        v[bond + 1] = v[bond + 1] * delta(bond_index, dag(enlarged_links[bond]))
    end
    return v
end

```

I took inspiration from the MPS constructor method

```julia
MPS(eltype::Type{<:Number}, sites::Vector{<:Index}, states_)

```

in ITensors/…/src/lib/ITensorMPS/src/mps.jl to define the new link indices, since this constructor is what gets called when I create the product state above; I figured I would get the same index structure.  
Unfortunately this does not happen: the @show statements print

```julia-repl
julia> enlarge!(initstate, 10)

first(linkinds(v), 5) = Index{Vector{Pair{QN, Int64}}}[(dim=1|id=315|"Link,l=1") <In>
 1: QN("Parity",1,2) => 1, (dim=1|id=773|"Link,l=2") <In>
 1: QN("Parity",0,2) => 1, (dim=1|id=639|"Link,l=3") <In>
 1: QN("Parity",1,2) => 1, (dim=1|id=593|"Link,l=4") <In>
 1: QN("Parity",0,2) => 1, (dim=1|id=474|"Link,l=5") <In>
 1: QN("Parity",1,2) => 1]
first(enlarged_links, 5) = Index{Vector{Pair{QN, Int64}}}[(dim=10|id=583|"Link,l=1") <In>
 1: QN("Parity",0,2) => 10, (dim=10|id=654|"Link,l=2") <In>
 1: QN("Parity",0,2) => 10, (dim=10|id=118|"Link,l=3") <In>
 1: QN("Parity",0,2) => 10, (dim=10|id=72|"Link,l=4") <In>
 1: QN("Parity",0,2) => 10, (dim=10|id=973|"Link,l=5") <In>
 1: QN("Parity",0,2) => 10]

```

and as you can see the alternating parity in the link indices is lost in the enlarged MPS.  
So I ask you: what isn’t working in my method? Or is there another way I can enlarge the bond dimensions of an MPS?

---

<div class="post-metadata">

**Author:** ![mtfishman](https://yyz2.discourse-cdn.com/free1/user_avatar/itensor.discourse.group/mtfishman/32/11_2.png) [@mtfishman](https://itensor.discourse.group/u/mtfishman)\
**Post date:** [November 23, 2024, 12:22pm UTC](https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133/2 "2024-11-23T12:22:25Z")

</div>

There are many ways of expanding the bond dimension of an MPS with tradeoffs in terms of what properties you want the new basis to have, computational cost of performing the expansion, etc. and it is even an active area of research to find new ways.

You could try the `ITensorMPS.expand` function mentioned here: [GitHub - ITensor/ITensorMPS.jl: MPS and MPO methods based on ITensor (ITensors.jl)](https://github.com/ITensor/ITensorMPS.jl?tab=readme-ov-file#new-features), execute `julia> ?expand` at the REPL to see instructions on how to use it.

---

<div class="post-metadata">

**Author:** ![phx](https://yyz2.discourse-cdn.com/free1/user_avatar/itensor.discourse.group/phx/32/673_2.png) [@phx](https://itensor.discourse.group/u/phx)\
**Post date:** [November 23, 2024, 5:12pm UTC](https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133/3 "2024-11-23T17:12:40Z")

</div>

I’ve already tried `expand` in the following way:

```julia
N=10
s = siteinds("Qubit", N; conserve_parity=true)
v = MPS(s, "Dn")
h = OpSum()
for n in 1:N
       h += "S+ * S-", n
end
for n in 1:N-1
       h += "S+", n, "S-", n+1
       h -= "S-", n, "S+", n+1
end
H = MPO(h, s)
state_t = expand(v, H; alg="global_krylov")

```

but I always get this error:

```julia
ERROR: BoundsError: attempt to access 0-element Vector{Pair{ITensors.QuantumNumbers.QN, Int64}} at index [1]
Stacktrace:
  [1] throw_boundserror(A::Vector{Pair{ITensors.QuantumNumbers.QN, Int64}}, I::Tuple{Int64})
    @ Base ./essentials.jl:14
  [2] getindex
    @ ./essentials.jl:915 [inlined]
  [3] combineblocks(qns::Vector{Pair{ITensors.QuantumNumbers.QN, Int64}})
    @ ITensors /opt/julia/packages/ITensors/AbUYR/src/qn/qnindex.jl:417
  [4] combineblocks
    @ /opt/julia/packages/ITensors/AbUYR/src/qn/qnindex.jl:479 [inlined]
  [5] combiner(inds::Tuple{ITensors.Index{…}, ITensors.Index{…}}; dir::ITensors.QuantumNumbers.Arrow, tags::String)
    @ ITensors /opt/julia/packages/ITensors/AbUYR/src/qn/qnitensor.jl:449
  [6] combiner
    @ /opt/julia/packages/ITensors/AbUYR/src/qn/qnitensor.jl:445 [inlined]
  [7] #combiner#322
    @ /opt/julia/packages/ITensors/AbUYR/src/tensor_operations/itensor_combiner.jl:7 [inlined]
  [8] eigen(A::ITensors.ITensor, Linds::Vector{…}, Rinds::Vector{…}; mindim::Int64, maxdim::Int64, cutoff::Float64, use_absolute_cutoff::Nothing, use_relative_cutoff::Nothing, ishermitian::Bool, tags::ITensors.TagSets.GenericTagSet{…}, lefttags::Nothing, righttags::Nothing, plev::Nothing, leftplev::Nothing, rightplev::Nothing)
    @ ITensors /opt/julia/packages/ITensors/AbUYR/src/tensor_operations/matrix_decomposition.jl:365
  [9] contract(::NDTensors.BackendSelection.Algorithm{…}, A::MPO, ψ::MPS; cutoff::Float64, maxdim::Int64, mindim::Int64, normalize::Bool, kwargs::@Kwargs{})
    @ ITensors.ITensorMPS /opt/julia/packages/ITensors/AbUYR/src/lib/ITensorMPS/src/mpo.jl:762
 [10] contract
    @ /opt/julia/packages/ITensors/AbUYR/src/lib/ITensorMPS/src/mpo.jl:691 [inlined]
 [11] product(alg::NDTensors.BackendSelection.Algorithm{…}, A::MPO, ψ::MPS; kwargs::@Kwargs{…})
    @ ITensors.ITensorMPS /opt/julia/packages/ITensors/AbUYR/src/lib/ITensorMPS/src/mpo.jl:604
 [12] product
    @ /opt/julia/packages/ITensors/AbUYR/src/lib/ITensorMPS/src/mpo.jl:603 [inlined]
 [13] #apply#453
    @ /opt/julia/packages/ITensors/AbUYR/src/lib/ITensorMPS/src/mpo.jl:600 [inlined]
 [14] product
    @ /opt/julia/packages/ITensors/AbUYR/src/lib/ITensorMPS/src/mpo.jl:599 [inlined]
 [15] expand(::NDTensors.BackendSelection.Algorithm{…}, state::MPS, operator::MPO; krylovdim::Int64, cutoff::Float64, apply_kwargs::@NamedTuple{…})
    @ ITensorTDVP /opt/julia/packages/ITensorTDVP/O4qn7/src/expand.jl:159
 [16] expand(::NDTensors.BackendSelection.Algorithm{:global_krylov, @NamedTuple{}}, state::MPS, operator::MPO)
    @ ITensorTDVP /opt/julia/packages/ITensorTDVP/O4qn7/src/expand.jl:147
 [17] expand(state::MPS, reference::MPO; alg::String, kwargs::@Kwargs{})
    @ ITensorTDVP /opt/julia/packages/ITensorTDVP/O4qn7/src/expand.jl:31
 [18] top-level scope
    @ REPL[24]:1
Some type information was truncated. Use `show(err)` to see complete types.

```

and now that I took more time to investigate this error I realize that it comes from just doing `H * v`. I didn’t expect that… I’ll try to understand what’s happening here.

---

<div class="post-metadata">

**Author:** ![mtfishman](https://yyz2.discourse-cdn.com/free1/user_avatar/itensor.discourse.group/mtfishman/32/11_2.png) [@mtfishman](https://itensor.discourse.group/u/mtfishman)\
**Post date:** [November 23, 2024, 5:31pm UTC](https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133/4 "2024-11-23T17:31:27Z")

</div>

Starting with an all down state:

```julia
v = MPS(s, "Dn")

```

likely isn’t what you want, the state will always remain in that state because of the QN conservation. You should start in a state in the symmetry sector you are interested in, i.e. start in a Néel state if you want to study the 0 spin sector.

Probably the error you are getting is because that state is being projected to the norm zero state by the Hamiltonian, ideally we would have a better error message (or output a norm zero state).

---

<div class="post-metadata">

**Author:** ![phx](https://yyz2.discourse-cdn.com/free1/user_avatar/itensor.discourse.group/phx/32/673_2.png) [@phx](https://itensor.discourse.group/u/phx)\
**Post date:** [November 24, 2024, 7:58am UTC](https://itensor.discourse.group/t/artificially-increase-bond-dimension-of-mps-with-conserved-qns/2133/5 "2024-11-24T07:58:42Z")

</div>

You are absolutely right. I chose the first `v` and `H` that came to mind without thinking about their action. I can get something from `expand` if I use a proper `v` and `H`, and the bond dimension does increase a bit on some sites.  
However I don’t think this method is for me since I can’t control the bond dimension of the output. I’d like to be able to get an MPS of a predetermined bond dimension.

In the end I realized the mistake in my original method: I was computing the `flux` of the MPS elements `v[n]`, but the MPS constructor I took the code from computes it from the state ITensors the MPS is built from. The states do not yet have the link indices attached to them, while the `v[n]` elements do, and I’m guessing this is the cause of the different QNs I’m getting.  
I can recover the original states (without the links) by doing

```julia
states = Vector{ITensor}(undef, N)                                                   
states[1] = v[1] * dag(onehot(linkind(v,1) => 1))                                    
for n in 2:N-1                                                                       
    states[n] = v[n] * onehot(linkind(v,n-1) => 1) * onehot(dag(linkind(v, n)) => 1) 
end                                                                                  
states[N] = v[N] * onehot(linkind(v, N-1) => 1)

```

and compute `flux` on `states` instead of `v`. This seems to get me what I want in the end, even if it’s probably a bit convoluted.
