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):
- 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 theVariationalMonteCarlo
class) in order to make it possible to write code such assteps = 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 thatstep.parameters
can be left empty (== nonstd::nullopt
) if storage of the machine parameters is not needed. - 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 thatstep.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.) - 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. - 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) andIndex
. TheIndex
type is added because right now there are several types used as indices (usuallyint
orstd::size_t
) in different places in the netket codebase. I suggest standardizing onIndex = 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 asstd::span
usesptrdiff_t
. This is also in line with what the Google Style Guide has to say about unsigned integers.)
Let me know what you think.