#!/usr/bin/env python
import numpy as np
import supervillain.action
from supervillain.generator import Generator
from supervillain.h5 import ReadWriteable
[docs]class Sequentially(ReadWriteable, Generator):
r'''
Sequentially applies the list of generators as a single step.
For example we can get an ergodic :class:`~.Worldline` update scheme by combining the :class:`~.PlaquetteUpdate` and :class:`~.WrappingUpdate`.
>>> p = supervillain.generator.worldline.PlaquetteUpdate(S)
>>> h = supervillain.generator.worldline.WrappingUpdate(S)
>>> g = supervillain.generator.combining.Sequentially((p, h))
Parameters
----------
generators: iterable of generators
The ordered iterable of generators to apply one after the next.
'''
def __init__(self, generators):
self.generators = generators
def __str__(self):
return f'Sequentially((' + ', '.join(f'{str(g)}' for g in self.generators) + '))'
[docs] def step(self, cfg):
r'''
Apply each generator's ``step`` one after the next and return the final configuration.
'''
result = cfg
for g in self.generators:
result = g.step(result)
return result
[docs] def inline_observables(self, steps):
combined = dict()
for g in self.generators:
combined |= g.inline_observables(steps)
return combined
[docs] def report(self):
r'''
Returns a string with some summarizing statistics.
'''
return '\n\n'.join(g.report() for g in self.generators)
[docs]class KeepEvery(ReadWriteable, Generator):
r'''
To decorrelate and get honest error estimates it can be helpful to do MCMC but then only analyze evenly-spaced configurations.
Rather than keep every single generated configuration and then throw a bunch away, we can keep only those we might analyze.
.. note::
The number of updates per second will decrease by a factor of n, but the autocorrelation time should be n less.
Generating a fixed number of configurations will take n times longer.
>>> p = supervillain.generator.worldline.PlaquetteUpdate(S)
>>> g = supervillain.generator.combining.KeepEvery(10, p)
Parameters
----------
n: int
How many generator updates to make before emitting a configuration.
generator:
The generator to use for updates.
blocked_inline: bool
Rather than just keeping the :py:meth:`~.Generator.inline_observables` from the last update, all of the inline measurements are averaged across all n updates.
This helps capture rare-but-important measurements that would otherwise be missed.
'''
def __init__(self, n, generator, blocked_inline=True):
self.stride = n
self.generator = generator
self.blocked_inline = blocked_inline
def __str__(self):
return f'KeepEvery({self.stride}, {str(self.generator)})'
[docs] def step(self, cfg):
r'''
Applies the generator n times and returns the resulting configuration.
'''
result = cfg
blocked = (self.inline_observables(1) if self.blocked_inline else dict())
for o in blocked:
blocked[o] = blocked[o][0]
for i in range(self.stride):
result = self.generator.step(result)
for o in blocked:
blocked[o] += result[o]/self.stride
return (result | blocked)
[docs] def inline_observables(self, steps):
return self.generator.inline_observables(steps)
[docs] def report(self):
r'''
Returns a string with some summarizing statistics.
'''
return self.generator.report()