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.