How to measure observables after every time-step when time evolving an MPS with Trotter Gates?

Hi!
If I want to know how observables change with time evolving MPS, how to get the MPS in every
time step?

c++
gateTEvol(gates,ttotal,tstep,psi,{"Cutoff=",cutoff,"Verbose=",true,"Maxm=",3000});

Here I can only get psi at the end of time-evoving.

It’s a good question. The way to do this is to make a custom Observer object. That is, a custom type which is a subtype of Observer.

You can see an example of a time-evolution observer in this file (this is actually the one that is used by default by the gateTEvol function):
https://github.com/ITensor/ITensor/blob/v3/itensor/mps/TEvolObserver.h

If you copy the code from that file, then change the name of the class from TEvolObserver to something else (say CustomObserver) then you can make your custom observer:

auto obs = CustomObserver(...); // pass any constructor arguments you define

and then pass it to gateTEvol like this:

gateTEvol(gates,ttotal,tstep,psi,obs,{"Cutoff=",cutoff, ... });

If you want to have access to the wavefunction inside the observer while the time evolution is happening, just pass a reference to your MPS to the constructor of your observer and save that reference as a data field in the class. Then the measure method of your observer will have access to the wavefunction that way and you can perform any measurements you want.

The named arguments (“args”) passed to the measure method by the gateTEvol function are:

  • “TimeStepNum” (int) - which step of the time evolution was just performed
  • “Time” (Real) - the time evolved so far
  • “TotalTime” (Real) - the final, total time requested for the evolution (always equals the value ttotal passed to gateTEvol)

You can see the code inside of gateTEvol where obs.measure(args); gets called here if you want to see how it is implemented:
https://github.com/ITensor/ITensor/blob/45bf86103b232f6b0d9992137253945f44e2a2f3/itensor/mps/tevol.h#L125

Thank you very much!
Unfortunately, I don’t think I really understand it.
Do you mean I should fill observable operators in “CustomObserver(…)” like “op(sites,“N”,i)” or something else?

Or should I modify TEvolObser.h like " Making a Custom DMRG Observer ITensor"
and do measurements in the head file?

And if it is the second one, how could I get the MPS after every time-step
Is that by auto& psi = gateTEvol::psi
like auto& psi = DMRGObserver::psi()in" Making a Custom DMRG Observer"

Yes, I would say more of your second guess: if you follow the “Making a Custom DMRG Observer” code formula then you can use that CustomObserver type also in the gateTEvol function. The only difference is that the named arguments passed to the measure function are different for time evolution, and have the values I described above.

Also to get the wavefunction, just also do

auto& psi = DMRGObserver::psi()

if you make your CustomObserver to be a sub-class of the DMRGObserver type. If you don’t make it inherit from that type, then you’d have to pass the wavefunction a different way.

Thanks to your kind help.
Well, I have made a CustomeObserver h file posted below.

c++
#ifndef __ITENSOR_TEVOLOBSERVER_H
#define __ITENSOR_TEVOLOBSERVER_H
#include "itensor/mps/mps.h"
#include "itensor/util/readwrite.h"
#include "itensor/mps/observer.h"

namespace itensor {

class CustomObserver : public DMRGObserver
    {
    public:
    
    CustomObserver(MPS const& psi,
                  Args const& args = Args::global());
                :DMRGObserver(psi)
                {}
    
    virtual ~CustomObserver() { }

    void virtual
    measure(Args const& args = Args::global());
    
    bool virtual
    checkDone(Args const& args = Args::global());

    MPS const& 
    psi() const { return psi_; }

    private:

    /////////////
    //
    // Data Members

    bool done_,
         show_percent_;

    //
    /////////////

    };

inline CustomObserver::
CustomObserver(MPS const& psi,
               Args const& args = Args::global())
    :
    done_(false),
    show_percent_(args.getBool("ShowPercent",true))
    psi_(psi),
    { 
    }
    
void inline CustomObserver::
measure(Args const& args = Args::global() )
    {
    auto N = length(psi_);
    const Real t = args.getReal("Time");
    if(show_percent_)
        {
        const Real ttotal = args.getReal("TotalTime");
        Real percentdone = (100.*t)/ttotal;
        if(percentdone < 99.5 || (std::fabs(t-ttotal) < 1E-10))
            {
            printf("\b\b\b%2.f%%",percentdone);
            std::cout.flush();
            }
        }
    for(auto b=1;b < N; b++)
        {
//measure the on-site density at every time-step
        auto wfb = psi_(b)*psi_(b+1);
        auto sb = IndexT(psi_.sites()(b));
        auto opname="N";
        auto z = (dag(prime(wfb,sb))*psi_.sites().op(opname,b)*wfb).eltC(); 
        if(std::fabs(z.imag()) < 1E-14)
            printfln("<%s>(%d) = %.10E",opname,b,z.real());
        else
            printfln("<%s>(%d) = (%.10E,%.10E)",opname,b,z.real(),z.imag());
        }
    
    }


bool inline TEvolObserver::
checkDone(const Args& args)
    {
    const Real t = args.getReal("Time");
    if(fileExists("STOP_TEVOL"))
        {
        println("File STOP_TEVOL found: stopping this time evolution run at time ",t);
        std::remove("STOP_TEVOL");
        return true;
        }

    if(fileExists("STOP_TEVOL_ALL"))
        {
        println("File STOP_TEVOL_ALL found: stopping this time evolution at time ",t);
        std::remove("STOP_TEVOL_ALL");
        done_ = true;
        return done_;
        }
    
    return done_;
    }

} //namespace itensor

#endif // __ITENSOR_TEVOLOBSERVER_H

So is that correct?

And when compiling my codes, it comes the error " CustomObserver was not declared in this scope. "

Are there other files I need to add #include/mps/CustomObserver.h in except tevol.h?

So it should be sufficient to include the header (.h) file where CustomObserver is defined into just the same code file where your main function is defined. You should not need to modify tevol.h or any ITensor library code.

If you are getting the error that CustomObserver was not declared, it most likely means that the compiler cannot see the code for CustomObserver or that you have not done using namespace itensor; at the beginning of your code. Is the location of the file correct? It looks like you’re saying #include/mps/CustomObserver.h but that’s not standard C++ notation for an include… Do you mean something like #include "path/to/CustomObserver.h" where path/to is the path on your system where the CustomObserver.h file is located?

Well, it is excatly my carelessness.
So, if I want to do measurements in the header file, how can I pass sites information to CustomObserver or TEvolObserver so that I can call op(sites,"N",i) in the file ?