Source code for supervillain.configurations

#!/usr/bin/env python

from supervillain.h5 import ReadWriteable, Extendable
import supervillain.h5.extendable as extendable

import logging
logger = logging.getLogger(__name__)

[docs]class Configurations(Extendable, ReadWriteable): r''' A group of configurations has fields (which you can access by doing ``cfgs.field``) and other auxiliary information (one per configuration). However, you can also use ``cfgs[step]`` to get a dictionary with keys that correspond to the names of the fields (and the auxiliary information) and associated values. If you like you can think of a set of Configurations as a very lightweight barely-featured `pandas DataFrame`_. Parameters ---------- dictionary: dict A dictionary with ``{key: value}`` pairs, where each value is an array whose first dimension is one per configuration. .. _pandas DataFrame: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.html ''' def __init__(self, dictionary): self.fields = dictionary def __str__(self): return str(self.fields) def __contains__(self, name): return (name in self.fields)
[docs] def __getitem__(self, index): r''' Parameters ---------- index: fancy indexing A subset of numpy `fancy indexing`_ is supported; this selection is used for selecting configurations based on their location in the dataset, rather than their ``index``. Some valid choices are ``7``, ``[1,2,3]``, ``slice(1,4)``, ``slice(1,10,2)``. Returns ------- one or many configurations: If index is an integer, returns a dictionary with key/value pairs for the requested configuration. If the index is fancier, return another set of :class:`Configurations`. .. _fancy indexing: https://docs.h5py.org/en/stable/high/dataset.html#fancy-indexing ''' t = type(index) if t is int: return {key: value[index] for key, value in self.fields.items()} if t is slice or list: return Configurations({key: value[index] for key, value in self.fields.items()}) raise ValueError(f'Not sure how to select configurations given a {type(index)}.')
[docs] def __setitem__(self, index, new): r''' Parameters ---------- index: fancy indexing Index or indices to overwrite. new: dictionary or Configurations Data to write. ''' for key, value in new.items(): self.fields[key][index] = value
def __len__(self): L = None for k, v in self.items(): try: l = len(v) except TypeError as e: continue if L is None: L = l elif L == l: pass else: raise ValueError("Configurations have no consistent length") return L
[docs] def items(self): r''' Like a dictionary's ``.items()``, iterates over the fields and auxiliary information. ''' return self.fields.items()
def __getattr__(self, name): try: return self.fields[name] except: raise AttributeError(name) def __setattr__(self, name, value): if name == 'fields': self.__dict__['fields'] = value elif name in self.fields: self.fields[name] = value else: self.__dict__[name] = value
[docs] def extend_h5(self, group, _top=True): logger.info(f'Extending h5 {group.name}.') for attr, value in self.items(): if isinstance(value, Extendable): value.extend_h5(group['fields'][attr]) elif isinstance(value, extendable.array): extendable.strategy.extend(group['fields'], attr, value)
def __ior__(self, value): self.fields |= value return self
[docs] def copy(self): return Configurations(self.fields.copy())