Forcing hermitianity of an operator

Hi everyone!

I am trying to force an operator to be hermitian.

I would essentially like to sum an operator by its dagger operator.

B = A + A^\dagger

Where A is a complex MPO.

Should I do it with ITensors.dag() or with ITensors.adjoint()

Thank you in advance.

You can use swapprime(dag(A), 0 => 1). It is a bit confusing, but dag essentially just does the complex conjugation (as well as swapping arrow directions, when QN conservation is enabled), and swapprime swaps the prime levels of the MPO, which effectively does a transposition. So together they do a conjugate transpose/Hermitian transpose.

2 Likes

Thank you so much for the quick answer!

Hi Matt,

I think there is an issue here with your statement:

" and swapprime swaps the prime levels of the MPO, which effectively does a transposition "

because if this is done with conserved quantum numbers one would eventually run into the error that the primed indices are pointing in whereas the convention is that they are pointing out. E.g. an error can occur if you try to apply this MPO to an object like:

N = 1
sites = siteinds("S=1/2", N, conserve_qns = true)
state = [isodd(n) ? 1 : 2 for n=1:N]
mps = randomMPS(sites, state)

Example code:

sites = siteinds("S=1/2", N, conserve_qns = true)
mpo = randomMPO(sites)
println(inds(mpo))
println("------------------------------------------------------------")
mpoT = swapprime(mpo, 0 => 1)
println(inds(mpoT))

Output:

Base.Fix1{typeof(filter), MPO}(filter, MPO
[1] ((dim=2|id=380|"S=1/2,Site,n=1")' <Out>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1, (dim=2|id=380|"S=1/2,Site,n=1") <In>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1)
)
------------------------------------------------------------
Base.Fix1{typeof(filter), MPO}(filter, MPO
[1] ((dim=2|id=380|"S=1/2,Site,n=1") <Out>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1, (dim=2|id=380|"S=1/2,Site,n=1")' <In>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1)

Let me know if I am looking at this the wrong way or if indeed this is problematic, then I am curious what would be the correct way to transpose so that it works with conserved QN as well. One way I see is to reverse the direction of the site indices after the swapprime but I did not find a way except from dag which would (in this case unwantedly) also complex conjugate the elements. One possible ugly solution maybe can be this:

let

    sites = siteinds("S=1/2", 1, conserve_qns = true)
    mpo = randomMPO(sites)
    println(inds(mpo))
    println("------------------------------------------------------------")
    mpoT = swapprime(mpo, 0 => 1)
    for (idx, element) in enumerate(mpoT)
        s, sp = inds(element; :tags => "Site", :plev => 0)[1], inds(element; :tags => "Site", :plev => 1)[1]
        mpoT[idx] = setprime(setprime(element*delta(dag(s), dag(s''))*delta(dag(sp), dag(sp'')), 0; :plev => 2), 1; :plev => 3)
    end
    println(inds(mpoT))

end

Output:

Base.Fix1{typeof(filter), MPO}(filter, MPO
[1] ((dim=2|id=267|"S=1/2,Site,n=1")' <Out>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1, (dim=2|id=267|"S=1/2,Site,n=1") <In>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1)
)
------------------------------------------------------------
Base.Fix1{typeof(filter), MPO}(filter, MPO
[1] ((dim=2|id=267|"S=1/2,Site,n=1") <In>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1, (dim=2|id=267|"S=1/2,Site,n=1")' <Out>
 1: QN("Sz",1) => 1
 2: QN("Sz",-1) => 1)
)

Thanks in advance for the help!

You should use swapprime(dag(mpo), 0 => 1) or noprime(linkinds, swapprime(dag(mpo), 0 => 1)) since some functions like dmrg don’t work properly with primed link indices.

See also DMRG for non Hermitian MPO.

Also you should use siteinds or linkinds to print indices of MPOs.

But this will complex conjugate the elements of the mpo whereas I was referring to only the transpose action on an MPO.

You could use swapprime(dag(conj(mpo)), 0 => 1).

1 Like

This should do the job, did not know of the existence of the conj function, thanks!