Tracking entanglement entropy convergence

Hi,

I am new to using ITensor (and Julia) and I noticed that the C++ version was outputting the vN entropy at the center bond as well as the energy, etc… after each sweep. On the other hand, the Julia version does not, and I’d like to do that. I’ve followed the advice I found in the documentation to define the vN entropy (see below)

using ITensors
function entropy_von_neumann(psi::MPS, b::Int)
    s = siteinds(psi)  
    orthogonalize!(psi, b)
    _,S = svd(psi[b], (linkind(psi, b-1), s[b]))
    SvN = 0.0
    for n in 1:dim(S, 1)
      p = S[n,n]^2
      SvN -= p * log(p)
    end
    return SvN
end

This works well after I finish the calculation, but I would like to output this after every sweep. From what I understood an observer is needed for that.

If I understood correctly a new ITensor.op operator that calls the entropy_von_neumann function needs to be defined and then be called under DMRGObserver, followed by calling such observer in the dmg arguments with outputlevel =1.

Does anyone know how to do this?

    function op!(Op::ITensor,::OpName"vN_entrop",::SiteType"MySiteType",s::Index)
        Op[s'=>1,s=>3] = entropy_von_neumann(psi,b)
    end
    
    vN_observer = DMRGObserver(["vN_entrop"],sites,energy_tol=1E-7)
    energy, psi = dmrg(H, psi0; nsweeps, maxdim, cutoff, noise, observer=vN_observer,outputlevel=1)

But I get the following error:

Overload of "op" or "op!" functions not found for operator name "vN_entrop" and Index tags: ("Electron,Site,n=9",)

And is also not very clear to me what should be the values going here Op[s'=>1,s=>3].

For context, I am trying to solve a small 3x3 OBC plain-vanilla Fermi Hubbard model in the square lattice.

Thanks!

Thanks for the question. It’s great that you already tried a way of doing it, but there’s no need to involve the ITensors.op function. (The ITensors.op family of functions is for defining custom operators, such as S^z or S^+ for a spin degree of freedom, so it’s not the thing to use to measure entanglement of a state.)

Here’s a working solution you can use, where I’ve made a custom observer type and overloaded its measure! function to print out the entanglement across the current bond of DMRG:

mutable struct EntanglementObserver <: AbstractObserver
end

function ITensors.measure!(o::EntanglementObserver; bond, sweep, half_sweep, psi, projected_operator, kwargs...)
  U,S,V = svd(psi[bond], uniqueinds(psi[bond],psi[bond+1]))
  SvN = 0.0
  for n=1:dim(S, 1)
    p = S[n,n]^2
    SvN -= p * log(p)
  end
  println("  Entanglement across bond $bond = $SvN")
end

You can pass this observer to a dmrg calculation like this:

observer = EntanglementObserver()
energy, psi = dmrg(H,psi0; nsweeps, maxdim, cutoff, observer, outputlevel=2)

Hope that helps – please let me know if you have a follow-up question about using this.

1 Like

I’ve also now posted this example in the ITensor documentation:
https://itensor.github.io/ITensors.jl/dev/examples/DMRG.html#Printing-the-Entanglement-Entropy-at-Each-Step

Thanks for the additional documentation!
Just a small follow up question on this, is it possible to have more than one kind of observer in the same dmrg calculation? For example, an energy observer as well as one for the entanglement entropy or any other operator averages? I understand how the energy observer can help terminate the dmrg calculation quicker, but if I wanted an additional observer (along with the energy observer) that checked the average of some local operator and then terminates the dmrg if some condition isn’t satisfied, is such a set up possible?

Hi Miles,

Thanks for the response. I tried running the example as is now in the documentation and I get the following error:

Sweep 1, half 1, bond (1,2) energy=-1.9510957542541163
  Truncated using cutoff=1.0E-08 maxdim=10 mindim=1
  Trunc. err=0.00E+00, bond dimension 2
ERROR: UndefKeywordError: keyword argument projected_operator not assigned

Do you have any suggestion for this?

Thanks!

For that issue, you can just remove the keyword argument projected_operator from your measure! function definition. Or you can upgrade ITensors.jl to the latest version (that keyword argument was only added more recently).

It’s a good question about multiple observers. Unfortunately we don’t support that yet. We will support that (actually an even better system) in an upcoming redesign of our DMRG code, but it’s not available yet in the ITensors.jl package.

For now, a good alternative is just to continue to customize your observer. So in the example above, you could make the measure! function of the observer do more things, and you can put data fields if needed into the definition of the observer type itself to store information and so on. Also you can define the checkdone! method to stop a DMRG calculation early. There are more details and examples on this documentation page:
https://itensor.github.io/ITensors.jl/dev/Observer.html

Thanks Miles! :slight_smile:

Cheers,
Eduardo

Hi Miles,
I’m not sure this code works for both sweeps. For half_sweep==1 this produces 0 each time (because the orthogonality center is in bond+1 already) and for half_sweep==2 you get a reasonable number

I suggest the following change (if I understand the bond correctly)

center,second = (half_sweep == 1) ? (bond+1,bond) : (bond,bond+1) 
U,S,V = svd(psi[center], uniqueinds(psi[center],psi[second]))