Out of memory when applying gates

Hi,

Hope this finds you well. I am currently trying to calculate correlations on my ground state MPS. Unfortunately, because of how my lattice is arranged, some of the two-site operators I am trying to apply act on sites that are spaced 6 sites apart. The local site dimension is also very large. In all, this results in huge memory requirements. Here is some code describing my operator and how I am applying it to the MPS:

"""
This is my operator 
"""
function Δ̂(sites, s1::Int, s2::Int)
    # Δ_ij = 1/√2 * (c↑i c↓j - c↓i c↑j)

    if s1 > s2 # ordering matters! 
        @warn "s1 > s2"
        s1, s2 = s2, s1
    end

    # First part
    Δ1 = op("Cup",sites[s1])
    for s in (s1+1):(s2-1)
        Δ1 *= op("F",sites[s])
    end
    Δ1 *= op("Cdn",sites[s2])

    # Second part 
    Δ2 = op("Cdn",sites[s1])
    for s in (s1+1):(s2-1)
        Δ2 *= op("F",sites[s])
    end
    Δ2 *= op("Cup",sites[s2])

    # Take difference and normalize
    Δ = 1/sqrt(2)*(Δ1 - Δ2)
end

"""
This is my code for applying the operator to an MPS at sites s1 and s2 
"""
function apply_twosite_operator(ϕ::MPS, opname::String, sites, s1::Int, s2::Int)
    ϕ = copy(ϕ)
    if opname == "pSC"
        Π = Π̂(sites, s1, s2)
        return apply(Π, ϕ)     
    elseif opname == "dSC"
        Δ = Δ̂(sites, s1, s2)
        return apply(Δ, ϕ)
    end
    @error "No recognized two-site operator"
end

Given that my Hamiltonian involves terms like c^\dagger_i c_j, where i and j are 6 sites apart, and the DMRG code runs perfectly fine without throwing an out-of-memory error, I am led to believe that my naive implementation and/or application of the \Delta_{ij} \propto (c_{i\uparrow}c_{j\downarrow} - c_{\downarrow i}c_{\uparrow j}) operator is just extremely memory inefficient. Do you have any suggestions? How is this step implemented in the DMRG code?

Thank you very much! Have a great week, and thanks again for all the wonderful work you do with ITensor.

Thanks for the nice feedback about ITensor!

Yes the way you are making the operator could work ok for short distances but not scale well for longer ones. If I understand your code correctly, then the Δ̂ function is making a single ITensor for the operator, and when s1 and s2 are far apart, this ITensor could have a large number of indices. As you know, the number of elements (memory requirement) of a tensor grows exponentially with the number of indices it has.

So I would recommend a different approach here. The simplest one that might work well for you is to make a copy of the wavefunction, then apply the individual “ops” making up your operator to it. Then finally use inner to compute the overlap of this “operated on” wavefunction with the original wavefunction. If that’s efficient enough then it could work well and definitely won’t use up a ton of memory.

If that isn’t efficient enough (i.e. becomes slow) then there are more sophisticated ways where you can either mix the application of the operators along with the inner computation manually. Or you can make an MPO for your operator and compute things that way (which could actually be a little slower but could be convenient).

3 Likes

Thank you very much! I will try this and let you know how it works

Just wanted to close this thread with some stats on how Miles’s solution compared to what I had previously:

Old solution: 135.729476 seconds (224.40 M allocations: 39.072 GiB, 6.75% gc time, 36.24% compilation time)

Miles’s solution (apply the operators sequentially to the MPS): 4.715385 seconds (15.05 M allocations: 2.021 GiB, 9.22% gc time, 21.09% compilation time)

This is exactly what I was looking for! Thank you very much for the help. Hope you have a great week.

Glad the new approach solved your issue! Thanks for sharing the timings -