Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Measurement function fixes #182

Merged
merged 12 commits into from Nov 4, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 7 additions & 1 deletion doc/changelog/latest.rst
Expand Up @@ -5,13 +5,19 @@ Release Notes
-------------
Backwards-incompatible rewrite of TDVP!

Note that measurement functions for simulations need to be updated to accept a `model` keyword argument.


Changelog
---------

Backwards incompatible changes
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- Replace the :class:`~tenpy.algorithms.tdvp.TDVPEngine` with a new version.
The previous one is for now still available as :class:`~tenpy.algorithms.tdvp.OldTDVPEngine`.
- Measurement functions now have to take another argument `model` as well, which matches the indexing/sites of `psi`.
This helps to avoid special cases for grouped sites and `OrthogonalExciations`.
Moreover, we renamed all measurement functions to start with ``m_`` to clarify their usage, and renamed the (optional) argument `key` to `results_key`.
- Add more fine grained sweep convergence checks for the :class:`~tenpy.algorithms.mps_common.VariationalCompression` (used when applying an MPO to an MPS!).
In this context, we renamed the parameter `N_sweeps` to :cfg:option:`VariationalCompression.max_sweeps`.
Further, we added the parameter :cfg:option:`VariationalCompression.min_sweeps` and :cfg:option:`VariationalCompression.tol_theta_diff`
Expand All @@ -36,7 +42,7 @@ Added
- Allow non-trivial :attr:`~tenpy.models.lattice.Lattice.position_disorder` for lattices.
- Option `fix_u` for :func:`~tenpy.simulations.measurement.onsite_expectation_value`.
- Lattice :attr:`~tenpy.models.lattice.Lattice.cylinder_axis`.
- Random number generator :attr:`~tenpy.models.model.Model.rng` for models.
- Random number generator :attr:`~tenpy.models.model.Model.rng` for models. Any randomness of model (parameters) should use this!
- :meth:`~tenpy.models.aklt.AKLTChain.psi_AKLT` for the exact MPS ground state of (spin-1/2) AKLT chain.
- :func:`~tenpy.simulations.simulation.init_simulation` and :func:`~tenpy.simulations.simulation.init_simulation_from_checkpoint` for debugging or post-simulation measurement.
- :func:`~tenpy.linalg.np_conserved.orthogonal_columns` constructing orthogonal columns to a given (rectangular) matrix.
Expand Down
154 changes: 154 additions & 0 deletions doc/intro/measurements.rst
@@ -0,0 +1,154 @@
Measurements for Simulations
============================

Rationale
---------

When we run a simulation performing a time evolution, we are interested in measurements
after every (n-th) time step, but it would be too costly (in terms of disk space) to save the
full psi at each time step; we only have the ``|psi(t)>`` *during* the simulation, not afterwards.
Hence, we need to define what measurements we want to perform for a given simulation **before**
running it.

.. note ::
For variational ground state searches, e.g. DMRG, the situation is better: we're not
interested in how we got to the ground state, but only properties of the ground state itselft.
In this case, we can first run DMRG, save the state, and then perform additional
measurements and analysis *after* finishing the simulation, so it is not crucial to
define all the measurements before the simulation.

The setup for simulations in TeNPy is as follows.

1) For each measurement that is to be done, we need a measurement function that evaluates
whatever we want to measure, e.g., the expectation value or correlation function of some operators.
If needed, you can define your own, custom functions.
2) For a given simulation, we specify the list of measurement functions in the simulation parameter
:cfg:option:`Simulation.connect_measurements`.
3) When the simulation runs, it calls the :meth:`~tenpy.simulations.Simulation.make_measurements` method
each time a set of measurements should be performed, e.g. on the initial state, during the time
evolution, and on the final state.
This causes a call to each of the measurement functions specified in
the :cfg:option:`Simulation.connect_measurements` parameter, passing the current state
``psi, model, simulation`` as arguments (possibly amongst other keyword arguments
also specified in :cfg:option:`Simulation.connect_measurements`).
Moreover, it passes a dictionary ``results``, in which measurement results should be saved.
At the end of `make_measurements`, the simulation class merges the obtained results
into the collection :attr:`~tenpy.simulations.Simulation.results` of all previous measurements
4) At the end of simulation, the `results` are saved and returned for further analysis (e.g. plotting).


Measurement functions
---------------------

In the simplest case, a measurement function is just a function, which can take the keyword arguments
``results, psi, model, simulation`` and saves the measurement results in the dictionary `results`.
The other arguments `psi` and `model` are the current MPS and model that can be used for measurements,
and `simulation` gives access to the full simulation class, in case other addiontal data is needed.

Within TeNPy, we use the convention that measurement functions (taking these arguments and saving to `results` instead
of simply returning values) start with an ``m_`` in their name.
A few generic measurement functions are defined in :mod:`~tenpy.simulations.measurement`.

As a first, somewhat trivial example, let us look at the source code of
:func:`tenpy.simulations.measurement.m_entropy`::

def m_entropy(results, psi, model, simulation, results_key='entropy'):
results[results_key] = psi.entanglement_entropy()

As you can see, it's a simple wrapper around the MPS method :meth:`~tenpy.networks.mps.MPS.entanglement_entropy`.
Note that usually the `psi` and `model` arguments are the same as the simulation attributes
``simulation.psi`` and ``simulation.model``, but they can be different in certain cases, e.g. when grouping sites.
In most cases, you should directly use the passed `psi` and `model`.

Of course, you can also do some actual calculations in the measurement functions.
A good example of this is the :func:`tenpy.simulations.measurement.m_onsite_expectation_value` - take a look at it's
source code. Another example could be the `m_pollmann_turner_inversion` measurement function defined in the
:doc:`/examples/model_custom` example from the :doc:`/intro/simulations` guide.


The connect_measurements parameter
----------------------------------

The :cfg:option:`Simulation.connect_measurements` parameter is a list with one entry for each measurment function to be
used. Each function is specified by a tuple ``module, func_name, extra_kwargs, priority``.
Here, `module` and `func` specfiy the module and name of the function, `extra_kwargs` are (optional) additional keyword
arguments to be given to the function, and `priority` allows to control the order in which the measurement functions get
called. The latter is usefull if you want to "post-process" results of another measurement function.

For example, say you want to measure local expectation values of both `Sz` and `Sx` with
:func:`tenpy.simulations.measurment.m_onsite_expectation_value`, then you could use

.. code :: yaml

connect_measurement
- - tenpy.simulations.measurement
- m_onsite_expectation_value
- opname: Sx
- - tenpy.simulations.measurement
- m_onsite_expectation_value
- opname: Sz

These measurement functions have default `results_key` under which they save values in the `results`, so you can then
read out ``results['<Sx>']`` and ``results['<Sz>']`` in the simulation results.
If you want other keys, you can explicitly specify them with the `results_key` argument of the function, e.g.,

.. code :: yaml

connect_measurement:
- - tenpy.simulations.measurement
- m_onsite_expectation_value
- opname: Sx
results_key: X_i # save as results['X_i']
- - tenpy.simulations.measurement
- m_onsite_expectation_value
- opname: Sz
results_key: Z_i # save as results['Z_i']


Some measurements are actually that common that they get added by default to the simulations (unless you explicitly
disable them with :cfg:option:`Simulation.use_default_measurements`); for example the :func:`tenpy.simulations.measurement.m_entropy`
is measured for any simulation, as it appears in :attr:`~tenpy.simulations.simulation.Simulation.default_measurements`.

Often, what you want to measure is just calling a method of the state `psi`, so there is a special syntax in the
`connect_measurement` parameter:
if you **specify the first entry for to be `psi_method`, `model_method` or `simulation_method`**, you can call a method of the
corresponding classes.
As for global measurement functions, we pass the corresponding ``results, psi, model, simulation`` keyword arguments,
e.g. `psi_method` measurement functions need to accept ``results, model, simulation`` as arguments, and
`simulation_method` measurement functions should accept ``results, psi, model``.

This is already very usefull to call measurement functions defined inside (custom) models or simulation classes,
yet methods of `psi` don't follow the measurement function call structure, but simply return values.
For those cases, you can use another special syntax, namely to **simply add `wrap` before the function name**.
In this case, we don't pass ``results, psi, model, simulation``, but simply save the return values of the function
in the results, under the `results_key` that gets passed as extra keyword argument,
see :func:`~tenpy.simulations.measurment.measurment_wrapper`.
The `results_key` defaults to the function name.

To make this clearer, let's extend the example above with more measurements:

.. code :: yaml

connect_measurement:
- - tenpy.simulations.measurement
- m_onsite_expectation_value
- opname: Sx
- - tenpy.simulations.measurement
- m_onsite_expectation_value
- opname: Sz
- - psi_method
- wrap correlation_function # call psi.correlation_function()
- results_key: '<Sz Sz>' # save returned value as results["<Sz Sz>"]
ops1: Sz # other (necessary) arguments to psi.correlation_function
ops2: Sz
- - simulation_method
- wrap walltime # "measure" wall clock time it took to run so far
- - tenpy.tools.process
- wrap memory_usage # "measure" the current RAM usage in MB


.. note ::

The `*_method` and `wrap` syntax are (currently) special to the :cfg:option:`Simulation.connect_measurements`
parameter, and do not apply to e.g. :cfg:option:`Simulation.connect_algorithm_checkpoint`, which uses an analogous
setup to allow calling functions at each algorithm checkpoint.
27 changes: 14 additions & 13 deletions doc/intro/simulations.rst
Expand Up @@ -171,31 +171,32 @@ such that we can read out the final half-chain entanglement entropy like this::
(2, 31)
>>> print(results['measurements']['entropy'][-1, (L-1)//2])

Here, the shape of the entropy array is ``(2, 31)`` since 2 is the number of measurements, and 31=L-1 the number of bonds.
Note that you can easily read out the simulation parameters, even default ones.
Here, the shape of the entropy array is ``(2, 31)`` since 2 is the number of measurements
(one on the initial state, one on the final ground state), and 31=L-1 the number of bonds.
Note that you can easily read out the simulation parameters, even default ones that are only implicitly defined
somewhere in the code!


Adding more measurements
------------------------
Most simulation classes have only a few :attr:`~tenpy.simulations.Simulation.default_measurements`, but you can easily
add more under :cfg:option:`Simulation.connect_measurements`. Each measurement is simply a function that is
add more with the :cfg:option:`Simulation.connect_measurements` parameters. Each measurement is simply a function that is
called whenever the simulation wants to measure, e.g. with the initial state, at the end of the simulation, and for time
evolutions also during the evolution. The default measurement functions are defined in
the module :mod:`tenpy.simulations.measurement`; :func:`~tenpy.simulations.measurement.measurement_index` documents what
arguments a measurement function should have.
In the simplest case, you just specify the module and function name, but you can also add more arguments, as the
following example shows:
following example shows.

.. code-block :: yaml

connect_measurements:
connect_measurements:
- - tenpy.simulations.measurement
- onsite_expectation_value
- opname: Sz
- - tenpy.simulations.measurement
- psi_method
- method: correlation_function
key: '<Sp_i Sm_j>'
- - psi_method
- wrap correlation_function
- key: '<Sp_i Sm_j>'
ops1: Sp
ops2: Sm

Expand All @@ -206,16 +207,16 @@ Note the indentation and minus signs here: this yaml syntax is equivalent to the
{'connect_measurements': [['tenpy.simulations.measurement',
'onsite_expectation_value',
{'opname': 'Sz'}],
['tenpy.simulations.measurement',
'psi_method',
['psi_method',
'wrap correlation_function',
{'key': '<Sp_i Sm_j>',
'method': 'correlation_function',
'ops1': 'Sp',
'ops2': 'Sm'}]]}

The measurement functions add the values under the specified `key` to the `results` returned and saved by the
simulation, e.g. for the above measurements you can now read out ``results['measurements']['<Sz>']`` (default key) and ``results['measurements']['<Sp_i Sm_j>']``.

For more details, see the extra guide :doc:`/intro/measurements`.

A full example with custom python code
--------------------------------------
Expand Down Expand Up @@ -266,7 +267,7 @@ e.g., in DMRG at the end of a sweep.
The checkpoints are saved to the same filename as the desired final output file, and get overwritten by each following save at a checkpoint.
You can check ``results['finished']`` in the output file to see whether it finished.

You can then resume the simulation using the function :func:`~tenpy.resume_from_checkpoint`.
You can then resume the simulation using the function :func:`tenpy.resume_from_checkpoint`.

Note that you can also adjust parameters for the resume.
For example, if you find that a DMRG result (even a finished one) is not yet fully converged in bond dimension, you can "resume" the simulation
Expand Down
1 change: 1 addition & 0 deletions doc/introductions.rst
Expand Up @@ -15,6 +15,7 @@ If you are new to TeNPy, read the :doc:`intro/overview`.
intro/input_output
intro/logging
intro/options
intro/measurements
intro/npc
intro/JordanWigner
intro/dmrg-protocol
2 changes: 1 addition & 1 deletion examples/model_custom.py
Expand Up @@ -48,7 +48,7 @@ def init_terms(self, model_params):
self.add_onsite(D, u, 'Sz Sz')


def pollmann_turner_inversion(results, psi, simulation, tol=0.01):
def m_pollmann_turner_inversion(results, psi, model, simulation, tol=0.01):
"""Measurement function for equation 15 of :arxiv:`1204.0704`.

See :func:`~tenpy.simulations.measurement.measurement_index` for the call structure.
Expand Down
14 changes: 7 additions & 7 deletions examples/simulation_custom.yml
Expand Up @@ -41,13 +41,13 @@ algorithm_params:

connect_measurements:
- - tenpy.simulations.measurement
- onsite_expectation_value
- m_onsite_expectation_value
- opname: Sz
- - tenpy.simulations.measurement
- psi_method
- method: correlation_function
key: '<Sp_i Sm_j>'
- - psi_method
- wrap correlation_function
- results_key: '<Sp_i Sm_j>'
ops1: Sp
ops2: Sm
- - model_custom # module where function is defined
- pollmann_turner_inversion
- - model_custom # module where function is defined
- m_pollmann_turner_inversion # function defined in the module to be measured
# - extra: argument # any additional keyword argument(s) to be given to the function.
11 changes: 6 additions & 5 deletions examples/yaml/details_simulation.yml
Expand Up @@ -37,11 +37,12 @@ algorithm_params:

connect_measurements:
- - tenpy.simulations.measurement
- onsite_expectation_value
- m_onsite_expectation_value
- opname: Sz
- - tenpy.simulations.measurement
- psi_method
- method: correlation_function
key: '<Sp_i Sm_j>'
- - psi_method
- wrap correlation_function
- results_key: '<Sp_i Sm_j>'
ops1: Sp
ops2: Sm
- - simulation_method
- wrap walltime
8 changes: 3 additions & 5 deletions examples/yaml/minimal_ExpMPOEvolution.yml
Expand Up @@ -32,10 +32,8 @@ connect_measurements:
- - tenpy.simulations.measurement
- onsite_expectation_value
- opname: Sz
- - tenpy.simulations.measurement
- psi_method
- method: correlation_function
key: '<Sp_i Sm_j>'
- - psi_method
- wrap correlation_function
- results_key: '<Sp_i Sm_j>'
ops1: Sp
ops2: Sm

8 changes: 3 additions & 5 deletions examples/yaml/minimal_TDVP.yml
Expand Up @@ -29,10 +29,8 @@ connect_measurements:
- - tenpy.simulations.measurement
- onsite_expectation_value
- opname: Sz
- - tenpy.simulations.measurement
- psi_method
- method: correlation_function
key: '<Sp_i Sm_j>'
- - psi_method
- wrap correlation_function
- results_key: '<Sp_i Sm_j>'
ops1: Sp
ops2: Sm

8 changes: 3 additions & 5 deletions examples/yaml/minimal_TEBD.yml
Expand Up @@ -29,10 +29,8 @@ connect_measurements:
- - tenpy.simulations.measurement
- onsite_expectation_value
- opname: Sz
- - tenpy.simulations.measurement
- psi_method
- method: correlation_function
key: '<Sp_i Sm_j>'
- - psi_method
- wrap correlation_function
- results_key: '<Sp_i Sm_j>'
ops1: Sp
ops2: Sm

2 changes: 1 addition & 1 deletion tenpy/networks/site.py
Expand Up @@ -1005,7 +1005,7 @@ def multi_sites_combine_charges(sites, same_charges=[]):
This function adjusts the charges of these sites such that they can be used together.

.. deprecated :: 0.7.3
Deprecated in favore of the new, more powerful
Deprecated in favor of the new, more powerful
:func:`~tenpy.networks.site.set_common_charges`.
Be aware of the slightly different argument structure though, namely that
this function keeps charges not included in `same_charges`, whereas you need
Expand Down
2 changes: 1 addition & 1 deletion tenpy/simulations/__init__.py
Expand Up @@ -17,8 +17,8 @@
from . import measurement, simulation, ground_state_search, time_evolution

__all__ = [
"simulation",
"measurement",
"simulation",
"ground_state_search",
"time_evolution",
]