Skip to content

Basic iterator interface for optimization drivers

Created by: femtobit

This PR contains a first version of the iterator interface for the VMC (and exact imaginary time) driver, addressing #107 (closed). (The implementation is partially modeled after the description here.)

Basically, it allows to write code such as

VariationalMonteCarlo vmc(...);
for (const auto& step : vmc.Iterate()) {
    outfile << 'step=' << step.index << '\n';
    outfile << json(step.observables) << '\n';
}

In principle, it is possible to modify the state, Hamiltonian, or other objects in between iteration steps (though this is not done anywhere right now). The Python bindings expose this function as well, allowing, e.g.,

vmc = nk.gs.vmc.Vmc(...)
for i, step in enumerate(vmc.iter()):
    print(step.observables.Energy)

In order to expose step.observables, Python bindings for the ObsManager class are provided, with an interface similar to a python dict (similar enough to make d = dict(step.observables) work).

Some open issues (or potentially points to discuss):

  1. The (VMC) iterators dereference to a struct containing basic information on the state: https://github.com/netket/netket/blob/a5c6d62cf46e3cbee63073190e53d23ede0c68bd/NetKet/GroundState/variational_montecarlo.hpp#L98-L103 This contains a copy of the data (as opposed to referencing the corresponding internal state of the VariationalMonteCarlo class) in order to make it possible to write code such as steps = list(vmc.iter(100)). This has the downside of copying this data every step, regardless of whether it is needed. For the machine parameters, an option is provided, so that step.parameters can be left empty (== nonstd::nullopt) if storage of the machine parameters is not needed.
  2. Extracting the data from ObsManager still performs an MPI reduction which has to be performed on all MPI processes, leading to code such as https://github.com/netket/netket/blob/00a7c60382f9fc1005100a9613379d9b5c8456dc/Tutorials/PyNetKet/ground_state_iter.py#L40-L45 The code should probably be changed so that step.observables does not perform MPI calls. The question is whether it should contain sensible values only on rank 0 or on all MPI processes (the first option should be sufficient but might also lead to annoying-to-debug errors.)
  3. The VariationalMonteCarlo::Run function is provided which is now implemented in terms of the iterator interface. This can be used to get essentially the behaviour of NetKet v1: https://github.com/netket/netket/blob/00a7c60382f9fc1005100a9613379d9b5c8456dc/Tutorials/PyNetKet/ground_state.py#L42-L50 We can discuss whether we want to provide this function or maybe a different kind of simplified interface.
  4. This PR also contains a commit introducing a header for common type aliases (related to #86 (closed)). For now, it contains Complex = std::complex<double> (because I think it is cumbersome to always spell-out that type) and Index. The Index type is added because right now there are several types used as indices (usually int or std::size_t) in different places in the netket codebase. I suggest standardizing on Index = std::ptrdiff_t (which is a 64-bit integer on my system). (Whether signed or unsigned types should be used as array indices is a somewhat controversial question in C++, but note that newer additions to the standard such as std::span uses ptrdiff_t. This is also in line with what the Google Style Guide has to say about unsigned integers.)

Let me know what you think.

Merge request reports