Source code for spynnaker.pyNN.utilities.struct
# Copyright (c) 2017-2019 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import numpy
from pyNN.random import RandomDistribution
from spinn_utilities.helpful_functions import is_singleton
from spinn_utilities.ranged.ranged_list import RangedList
from spinn_front_end_common.utilities.constants import BYTES_PER_WORD
from spynnaker.pyNN.utilities.utility_calls import convert_to
[docs]class Struct(object):
""" Represents a C code structure.
"""
__slots__ = ["__field_types"]
def __init__(self, field_types):
"""
:param list(~data_specification.enums.DataType) field_types:
The types of the fields, ordered as they appear in the struct.
"""
self.__field_types = field_types
@property
def field_types(self):
""" The types of the fields, ordered as they appear in the struct.
:rtype: list(~data_specification.enums.DataType)
"""
return self.__field_types
@property
def numpy_dtype(self):
""" The numpy data type of the struct
:rtype: ~numpy.dtype
"""
return numpy.dtype(
[("f" + str(i), numpy.dtype(data_type.struct_encoding))
for i, data_type in enumerate(self.field_types)],
align=True)
[docs] def get_size_in_whole_words(self, array_size=1):
""" Get the size of the struct in whole words in an array of given\
size (default 1 item)
:param int array_size: The number of elements in an array of structs
:rtype: int
"""
datatype = self.numpy_dtype
size_in_bytes = array_size * datatype.itemsize
return (size_in_bytes + (BYTES_PER_WORD - 1)) // BYTES_PER_WORD
[docs] def get_data(self, values, offset=0, array_size=1):
""" Get a numpy array of uint32 of data for the given values
:param values:
A list of values with length the same size as the number of fields
returned by field_types
:type values:
list(int or float or list(int) or list(float) or
~spinn_utilities.ranged.RangedList)
:param int offset: The offset into each of the values where to start
:param int array_size: The number of structs to generate
:rtype: ~numpy.ndarray(dtype="uint32")
"""
# Create an array to store values in
data = numpy.zeros(array_size, dtype=self.numpy_dtype)
# Go through and get the values and put them in the array
for i, (values, data_type) in enumerate(zip(values, self.field_types)):
if is_singleton(values):
data_value = convert_to(values, data_type)
data["f" + str(i)] = data_value
elif not isinstance(values, RangedList):
data_value = [convert_to(v, data_type)
for v in values[offset:(offset + array_size)]]
data["f" + str(i)] = data_value
else:
for start, end, value in values.iter_ranges_by_slice(
offset, offset + array_size):
# Get the values and get them into the correct data type
if isinstance(value, RandomDistribution):
values = value.next(end - start)
data_value = [convert_to(v, data_type) for v in values]
else:
data_value = convert_to(value, data_type)
data["f" + str(i)][
start - offset:end - offset] = data_value
# Pad to whole number of uint32s
overflow = (array_size * self.numpy_dtype.itemsize) % BYTES_PER_WORD
if overflow != 0:
data = numpy.pad(
data.view("uint8"), (0, BYTES_PER_WORD - overflow), "constant")
return data.view("uint32")
[docs] def read_data(self, data, offset=0, array_size=1):
""" Read a bytearray of data and convert to struct values
:param data: The data to be read
:type data: bytes or bytearray
:param int offset: Index of the byte at the start of the valid data
:param int array_size: The number of struct elements to read
:return:
a list of lists of data values, one list for each struct element
:rtype: list(float)
"""
if self.numpy_dtype.itemsize == 0:
return numpy.zeros(0, dtype=self.numpy_dtype)
# Prepare items to return
items_to_return = list()
# It could be possible that a component has no parameters
# (for example, InputTypeCurrent): this needs to be dealt with,
# as numpy.frombuffer does not like an empty type
if len(self.numpy_dtype) == 0:
return items_to_return
# Read in the data values
numpy_data = numpy.frombuffer(
data, offset=offset, dtype=self.numpy_dtype, count=array_size)
# Go through the things to be set
items_to_return = list()
for i, data_type in enumerate(self.field_types):
# Get the data to set for this item
values = numpy_data["f" + str(i)]
# TODO: types that are integers should become integers
items_to_return.append(values / float(data_type.scale))
# Return values read
return items_to_return