+1 vote
asked by (170 points)
edited by


I want to time evolve an operator in the Heisenberg picture using MPO methods. Let us call the operator A. So I want to compute A(t) = U'AU (U' is the adjoint of U). Note that I am using the Julia version throughout.

I have an array containing Trotter gates for U' called gates_adjoint. These can be applied to the MPO representation of A from the left (or rather, from "above") by using apply(gates_adjoint, A). This is basically the same as what is done in https://itensor.github.io/ITensors.jl/stable/tutorials/MPSTimeEvolution.html for an MPS.

However, my issue is in applying U on the right (or rather, on the "bottom" indices of the MPO A). My first approach was to try the following:

A = dag(apply(gates_adjoint, dag(apply(gates_adjoint, A; cutoff = 1E-8)); cutoff = 1E-8));

where I am basically trying to carrying out the sequence A --->(apply) U'A --->(dag) A'U --->(apply) U'A'U --->(dag) U'AU = A(t)

But, dag doesn't appear to work the way I expected it to. For example, in the following code:

using ITensors

s = siteinds("S=1/2", 1);
A = op("S+", s[1]);
B = op("S+", s[1]);
@show C = apply(A,B)
@show D = apply(A,dag(B))

C comes out correctly as a zero ITensor but D also comes out as a zero ITensor even though I want it to be @@S_+S_-@@. I think I may not be fully understanding how dag works.

My question now is how do I correctly evolve my MPO A? Is there a nice direct way to apply the gates from both sides and/or if I have to use dag, how do I use it correctly? Am I perhaps making a mistake with how I deal with indices (for example, do I also have prime/unprime some indices at some point)?

This is my first time working with ITensor and any help would be much appreciated!

(Edit: Formatting)

commented by (70.1k points)
Hi thanks for the question. Does the example code below do what you need? At the end of the code, the apply function is used to apply a collection of gates to an MPO (it is written to do the appropriate algorithm for both the MPS and MPO cases):


If that doesn’t do what you need, or you have a reason you need to code it yourself, let’s discuss more.

commented by (170 points)
Hi Miles,

Thank you very much for your reply. Yes, the code you pointed out does indeed do the trick!

I guess I misunderstood how apply works. For clarification, does setting apply_dag = true mean that the gates are daggered when applied to the bottom of the MPO?

Also, could you tell me what dag does specifically (since it doesn't seem to turn S+ into S- for example even though S- is the adjoint of S+) and if it is not the same as the adjoint, then how would one find the adjoint of an MPO/ITensor?

commented by (14.1k points)
Hi Thivan,

The part that you are missing from the code you wrote is that you need to both `dag` the gates and also swap the prime levels to the perform the conjugate transpose. You can generally think of `dag` as simply a complex conjugation, where it also reverses the "Index direction" in the case where there is quantum number conservation, while swapping the prime levels is the ITensor way of performing a transpose.

To be more precise, in your simple example above you should get what are expecting with:

@show D = apply(A,swapprime(dag(B), 0 => 1))

The `dag` operation won't actually do anything if the tensor is real and you are not conserving QNs, but even in that case it is best to put it in so that your code handles the complex case and QN conservation case if you want to generalize your code in the future.
When you use `apply(U, A; apply_dag=true)`, it performs the operation `U * A * U^dag`, where `U^dag` both applies `dag` to the gates as well as swaps the prime levels of the gates correctly to perform the transpose. This code should be more efficient than the manual version you wrote above where you used `apply` twice (which you should be able to fix by performing `swapprime(..., 0 => 1)` each time you use `dag`), since at each gate application it contracts both the bra and ket gates in pairs in a single operation, so it should involve fewer truncation (SVD) operations and fewer swap operations in the case of nonlocal gates.

commented by (70.1k points)
To add one other thing to Matt’s answer, what happens if apply_dag=false? Then apply(U,A; apply_dag=false) performs the operation U * A.
commented by (170 points)
Hi Matt,

Thank you very much for this detailed reply! I think I now understand how dag works - it now makes sense why taking dag of a real Itensor just returned the Itensor itself.

Though I do have another question now: what does it mean to "reverse the index direction"? Back when I wrote my question, I was expecting that to be a swapping of prime levels, but I see that this is not correct.

commented by (170 points)
Hi Miles,

Thank you also for your clarification regarding what happens if apply_dag = false.

commented by (70.1k points)
Hi Thivan,
The short answer about 'reverse index direction' means changing the "dir" or arrow property of the index from ITensors.In to ITensors.Out or the other way around. For non-QN conserving tensors, these arrows can be ignored. For QN conserving ITensors, the arrows indicate whether to add or subtract the QN values when working out the "flux" of an ITensor, which is the amount of QN that it adds or subtracts overall when attached to a network.

Mathematically, an Out arrow means a "contravariant" or "ket" index and an In arrow is a "covariant" or "bra" index. Also mathematically, these concepts tell how such indices transform under representations of symmetry groups (the QNs being added or subtracted have to do with combining irreducible representations of the symmetry groups, but that machinery isn't necessary to fully understand to understand QN tensors, and it's more useful usually just to think about adding or subtracting quantum numbers and quantum numbers "flowing" along the index lines).

commented by (70.1k points)
For a partial background about all this, please see these pages:


(pages meaning clicking on the links at the bottom to go through the pages of the book).
commented by (170 points)
Hi Miles,

Sorry about my late reply. Thank you very much for this information - looks like I completely misunderstood what index directions meant. Upon reading the pages of the book you've recommended, I believe I now understand how QN tensors work and what the arrows represent. And indeed, my work doesn't involve QN tensors so it makes sense why trying to reverse index direction didn't actually do anything.

commented by (70.1k points)
Great to hear that

1 Answer

+1 vote
answered by (14.1k points)
selected by
Best answer

See the comments above.

Welcome to ITensor Support Q&A, where you can ask questions and receive answers from other members of the community.

Formatting Tips:
  • To format code, indent by four spaces
  • To format inline LaTeX, surround it by @@ on both sides
  • To format LaTeX on its own line, surround it by $$ above and below
  • For LaTeX, it may be necessary to backslash-escape underscore characters to obtain proper formatting. So for example writing \sum\_i to represent a sum over i.
If you cannot register due to firewall issues (e.g. you cannot see the capcha box) please email Miles Stoudenmire to ask for an account.

To report ITensor bugs, please use the issue tracker.