Making a Custom DMRG Observer

The Observer system lets you define custom measurements to be performed within algorithms such as DMRG and time evolution. For the DMRG algorithm, you can make a subclass of the DMRGObserver type to implement custom measurements and printing of information from the ITensor dmrg function.

Let's see how to make a custom DMRG Observer by way of example.

Making our CustomObserver type

First make a type that is a subclass of DMRGObserver. We will call ours CustomObserver:

class CustomObserver : public DMRGObserver
    {
    public:

    CustomObserver(MPS const& psi,
                   Args const& args = Args::global())
        : DMRGObserver(psi)
        {}

    }; // class CustomObserver

Note that the parent type, DMRGObserver, requires an MPS to be passed to it, so we also require our CustomObserver to take an MPS in its constructor. This MPS is a reference to the one being optimized by the dmrg function.

Overloading the measure method

The DMRGObserver::measure method is what gets called by the dmrg function after each step. We can overload this method to change the information that gets printed by the dmrg function or to perform other custom measurements.

To overload measure, first we need to declare it as one of CustomObserver's methods:

class CustomObserver : public DMRGObserver
    {
    public:

    CustomObserver(MPS const& psi,
                   Args const& args = Args::global())
        : DMRGObserver(psi)
        {}

    void virtual
    measure(Args const& args);

    }; // class CustomObserver

Next we need to actually define this method (outside of the class):

void CustomObserver::
measure(Args const& args = Args::global())
    {
    // your custom measurement code goes here
    }

Now we can put any code we want into measure to measure the state being optimized by dmrg or report on the status of the DMRG optimization process.

Customizing the measure method

What kind of properties are available to measure? First of all, we can access the MPS being optimized by DMRG by calling DMRGObserver::psi():

void CustomObserver::
measure(Args const& args = Args::global())
    {
    // Obtain a reference to psi, the MPS being optimized:
    auto& psi = DMRGObserver::psi();
    }

Just as usefully, we can get a lot of information from the dmrg function itself that gets passed as named arguments through the args object. Here are the different named arguments available:

void CustomObserver::
measure(Args const& args = Args::global())
    {
    // Obtain a reference to psi, the MPS being optimized:
    auto& psi = DMRGObserver::psi();

    // How many sweeps of DMRG are to be performed:
    auto nsweep = args.getInt("NSweep",0);
    // Which sweep of DMRG we are on:
    auto sw = args.getInt("Sweep",0);
    // Which half sweep of DMRG we are on:
    auto ha = args.getInt("HalfSweep",0);
    // Which bond DMRG is on:
    auto b = args.getInt("AtBond",1);

    // What is the current energy?
    auto energy = args.getReal("Energy",0);
    // What was the last truncation error computed?
    auto truncerr = args.getReal("Truncerr",0);
    // What is the current truncation error cutoff?
    auto cutoff = args.getReal("Cutoff",0);
    // What is the current maximum bond dimension?
    auto maxdim = args.getInt("MaxDim",0);

    // Should output be silenced?
    auto silent = args.getBool("Silent",false);


    }

You do not have to define all of these variables. The code above is just to illustrate which named arguments are available to you to use.

Calling the default DMRGObserver::measure method

When you overload measure, by default it "shadows" the one defined by DMRGObserver which will no longer get called. However, the implementation of measure inside of DMRGObserver performs a number of useful measurements, such as reporting the entanglement entropy at the center of the MPS during each sweep.

To still run the DMRGObserver::measure method while also having your own custom implementation, just put the line DMRGObserver::measure(args); somewhere inside your own measure function.

Overloading the checkDone method

Subclasses of DMRGObserver can also overload a method named checkDone which returns a bool. If checkDone returns true, then the dmrg function will exit, returning the current energy and MPS wavefunction.

To overload checkDone, first declare it in your CustomObserver class:

class CustomObserver : public DMRGObserver
    {
    public:

    CustomObserver(MPS const& psi,
                   Args const& args = Args::global())
        : DMRGObserver(psi)
        {}

    void virtual
    measure(Args const& args);

    bool virtual
    checkDone(Args const& args);

    }; // class CustomObserver

Next define the checkDone method itself (outside of the class):

bool virtual
checkDone(Args const& args = Args::global())
    {
    return false;
    }

To customize this method, you can read in the same set of named arguments as in the measure function above. Let's use just one of them, the energy, to determine when we are going to stop our dmrg calculation:

bool virtual
checkDone(Args const& args = Args::global())
    {
    auto energy = args.getReal("Energy");
    if(energy < -100.0) return true;
    return false;
    }

This is just a contrived example, with -100 being a made-up value, and in your own application you might want to store the last reported energy inside your CustomObserver and check whether the latest energy differs from the last one by some relative amount. Or you could set a checkDone criterion based on the truncation error, entanglement entropy, or any other quantity you wish.


Back to Formulas
Back to Main