Source code for pathsim.utils.register
#########################################################################################
##
## Register Class
## (pathsim/utils/register.py)
##
#########################################################################################
# IMPORTS ===============================================================================
import numpy as np
# CLASSES ===============================================================================
[docs]
class Register:
"""This class is a intended to be used for the inputs and outputs of blocks.
Its basic functionality is similar to a `dict` but with some additional methods
and implemented as a numpy array for fast data transfer.
The core functionality is that values can be added dynamically and the size of the
register doesnt have to be specified. It also implements some methods to interact
with numpy arrays and to streamline convergence checks.
Parameters
----------
size : int, optional
initial size of the register
mapping : dict[str: int]
string aliases for integer ports
Attributes
----------
_data : np.ndarray
internal numpy array that holds the values
_mapping : dict[str: int]
internal mapping for port aliases from string to int (index)
"""
__slots__ = ["_data", "_mapping"]
def __init__(self, size=None, mapping=None, dtype=np.float64):
self._data = np.zeros(1 if size is None else size, dtype=dtype)
self._mapping = {} if mapping is None else mapping
def _map(self, key):
"""Map string keys to integers defined in '_mapping'
Parameters
----------
key : int, str
port key, to map to index
Returns
-------
_key : int
port index
"""
return self._mapping.get(key, key)
def _get_max_index(self, key):
"""Identify max index from different key types."""
if isinstance(key, int):
return key
elif isinstance(key, slice):
return key.stop - 1 if key.stop is not None else -1
elif isinstance(key, (list, tuple, np.ndarray)):
return max(key) if key else -1
return -1
def __len__(self):
return len(self._data)
def __iter__(self):
"""Iteration and unpacking into tuples or lists"""
return iter(self._data)
def __getitem__(self, key):
"""Get the value for direct access to the
register values.
Parameters
----------
key : int, str
port key, where to get value from
Returns
-------
out : float, obj
value from port at `key` position
"""
if isinstance(key, str):
key = self._map(key)
if not isinstance(key, int):
return 0.0
if isinstance(key, int):
if key < 0 or key >= len(self._data):
return 0.0
return self._data[key]
return self._data[key]
def __setitem__(self, key, value):
"""Set the value at key index for direct access
to the register values.
Parameters
----------
key : int, str
port key, where to set value
val : float, obj
value to set at port
"""
if key in self._mapping:
key = self._mapping[key]
max_idx = self._get_max_index(key)
self.resize(max_idx + 1)
#convert to scalar if needed to avoid numpy deprecation warning
if isinstance(value, np.ndarray) and value.ndim == 0:
value = value.item()
self._data[key] = value
[docs]
def resize(self, size):
if size > len(self._data):
self._data.resize(size)
[docs]
def reset(self):
"""Set all stored values to zero."""
self._data[:] = 0.0
[docs]
def to_array(self):
"""Returns a copy of the internal array.
Returns
-------
arr : np.ndarray
converted register as array
"""
return self._data.copy()
[docs]
def update_from_array(self, arr):
"""Update the register values from an array in place.
Parameters
----------
arr : np.ndarray, float
array or scalar that is used to update internal register values
"""
if np.isscalar(arr):
self._data[0] = arr
return
if not isinstance(arr, np.ndarray):
arr = np.asarray(arr)
n_arr = len(arr)
self.resize(n_arr)
np.copyto(self._data[:n_arr], arr)
def __contains__(self, key):
"""Check if a key is in mapping or is valid integer index."""
return key in self._mapping or isinstance(key, int)