Source code for pacman.model.graphs.application.abstract.abstract_2d_device_vertex
# Copyright (c) 2021 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from spinn_utilities.abstract_base import AbstractBase, abstractproperty
import math
from pacman.exceptions import PacmanConfigurationException
from pacman.utilities.utility_calls import get_n_bits
from pacman.utilities.constants import BITS_IN_KEY
from pacman.model.routing_info.base_key_and_mask import BaseKeyAndMask
from pacman.model.graphs.common.slice import Slice
[docs]class Abstract2DDeviceVertex(object, metaclass=AbstractBase):
"""
A helper for 2D input devices.
.. note::
This assumes that
the input keys will contain a field for each of the X and Y dimensions
with X field in the LSBs and the Y field in the next adjacent bits. If
the fields are in different places, override the methods:
`_source_x_shift`, `_source_y_shift`, `_source_x_mask` and
`_source_y_mask`.
If the key has bits in addition to the X and Y values, you can also
override `_key_shift`.
"""
@abstractproperty
def _width(self):
"""
The width of the device.
:rtype: int
"""
@abstractproperty
def _height(self):
"""
The height of the device.
:rtype: int
"""
@abstractproperty
def _sub_width(self):
"""
The width of the sub-rectangles to divide the input into.
:rtype: int
"""
@abstractproperty
def _sub_height(self):
"""
The height of the sub-rectangles to divide the input into.
:rtype: int
"""
def __is_power_of_2(self, v):
"""
Determine if a value is a power of 2.
:param int v: The value to test
:rtype: bool
"""
return (v & (v - 1) == 0) and (v != 0)
def _verify_sub_size(self):
"""
Ensure the sub width and height are within restrictions.
"""
if not self.__is_power_of_2(self._sub_width):
raise PacmanConfigurationException(
f"sub_width ({self._sub_width}) must be a power of 2")
if not self.__is_power_of_2(self._sub_height):
raise PacmanConfigurationException(
f"sub_height ({self._sub_height}) must be a power of 2")
if self._sub_width > self._width:
raise PacmanConfigurationException(
f"sub_width ({self._sub_width}) must not be greater than "
f"width ({self._width})")
if self._sub_height > self._height:
raise PacmanConfigurationException(
f"sub_height ({self._sub_height}) must not be greater than "
f"height ({self._height})")
@property
def _n_sub_rectangles(self):
"""
The number of sub-rectangles the device is made up of.
:rtype: int
"""
return (int(math.ceil(self._width / self._sub_width)) *
int(math.ceil(self._height / self._sub_height)))
def _sub_square_from_index(self, index):
"""
Work out the x and y components of the index.
:param int index: The index of the sub square
:rtype: tuple(int, int)
"""
n_squares_per_row = int(math.ceil(
self._width / self._sub_width))
x_index = index % n_squares_per_row
y_index = index // n_squares_per_row
# Return the information
return x_index, y_index
def _get_slice(self, index):
"""
Get the slice for the given machine vertex index.
:param int index: The machine vertex index
:rtype: Slice
"""
x_index, y_index = self._sub_square_from_index(index)
lo_atom_x = x_index * self._sub_width
lo_atom_y = y_index * self._sub_height
n_atoms_per_subsquare = self._sub_width * self._sub_height
lo_atom = index * n_atoms_per_subsquare
hi_atom = (lo_atom + n_atoms_per_subsquare) - 1
return Slice(
lo_atom, hi_atom, (self._sub_width, self._sub_height),
(lo_atom_x, lo_atom_y))
def _get_key_and_mask(self, base_key, index):
"""
Get the key and mask of the given machine vertex index.
:param int base_key: The unshifted key to use
:param int index: The machine vertex index
:rtype: BaseKeyAndMask
"""
x_index, y_index = self._sub_square_from_index(index)
key_bits = base_key << self._key_shift
key = (key_bits +
(y_index << self._y_index_shift) +
(x_index << self._x_index_shift))
return BaseKeyAndMask(key, self._mask)
@property
def _mask(self):
"""
The mask to be used for the key.
:rtype: int
"""
n_key_bits = BITS_IN_KEY - self._key_shift
key_mask = (1 << n_key_bits) - 1
sub_x_mask = (1 << self._sub_x_bits) - 1
sub_y_mask = (1 << self._sub_y_bits) - 1
return ((key_mask << self._key_shift) +
(sub_y_mask << self._y_index_shift) +
(sub_x_mask << self._x_index_shift))
@property
def _key_fields(self):
"""
The fields in the key for X and Y.
:return: (start, size, mask, shift) for each of X and Y
:rtype: tuple(tuple(int, int, int int), tuple(int, int, int, int))
"""
return ((0, self._width, self._source_x_mask, self._source_x_shift),
(0, self._height, self._source_y_mask, self._source_y_shift))
@property
def _x_bits(self):
"""
The number of bits to use for X.
:rtype: int
"""
return get_n_bits(self._width)
@property
def _y_bits(self):
"""
The number of bits to use for Y.
:rtype: int
"""
return get_n_bits(self._height)
@property
def _sub_x_bits(self):
"""
The number of bits to use for the X coordinate of a sub-rectangle.
:rtype: int
"""
n_per_row = int(math.ceil(self._width / self._sub_width))
return get_n_bits(n_per_row)
@property
def _sub_y_bits(self):
"""
The number of bits to use for the Y coordinate of a sub-rectangle.
:rtype: int
"""
n_per_col = int(math.ceil(self._height / self._sub_height))
return get_n_bits(n_per_col)
@property
def _x_index_shift(self):
"""
The shift to apply to the key to get the sub-X coordinate.
:rtype: int
"""
return self._source_x_shift + (self._x_bits - self._sub_x_bits)
@property
def _y_index_shift(self):
"""
The shift to apply to the key to get the sub-Y coordinate.
:rtype: int
"""
return self._source_y_shift + (self._y_bits - self._sub_y_bits)
@property
def _source_x_mask(self):
"""
The mask to apply to the key *before* shifting to get the
X coordinate.
:rtype: int
"""
return (1 << self._x_bits) - 1
@property
def _source_x_shift(self):
"""
The shift to apply to the key *after* masking to get the
X coordinate.
:rtype: int
"""
return 0
@property
def _source_y_mask(self):
"""
The mask to apply to the key *before* shifting to get the
Y coordinate.
:rtype: int
"""
return ((1 << self._y_bits) - 1) << self._x_bits
@property
def _source_y_shift(self):
"""
The shift to apply to the key *after* masking to get the
Y coordinate.
:rtype: int
"""
return self._x_bits
@property
def _key_shift(self):
"""
The shift to apply to the key to get the base key.
:rtype: int
"""
return self._y_bits + self._x_bits