Source code for spinn_machine.spinnaker_triad_geometry

# Copyright (c) 2017 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 typing import Sequence, Optional, Tuple
from typing_extensions import TypeAlias
from spinn_utilities.typing.coords import XY

_Centre: TypeAlias = Tuple[float, float]


[docs] class SpiNNakerTriadGeometry(object): """ Geometry of a "triad" of SpiNNaker boards. The geometry is defined by the arguments to the constructor; the standard arrangement can be obtained from get_spinn5_geometry. .. note:: The geometry defines what a Triad is in terms of the dimensions of a triad and where the Ethernet chips occur in the triad. """ __slots__ = ( "_ethernet_offset", "_triad_height", "_triad_width", "_board_height", "_board_width", "_roots") # Stored singleton spinn5_triad_geometry: Optional['SpiNNakerTriadGeometry'] = None
[docs] @staticmethod def get_spinn5_geometry() -> 'SpiNNakerTriadGeometry': """ Get the geometry object for a SpiNN-5 arrangement of boards :return: a :py:class:`SpiNNakerTriadGeometry` object. """ # Note the centres are slightly offset so as to force which edges are # included where if SpiNNakerTriadGeometry.spinn5_triad_geometry is None: SpiNNakerTriadGeometry.spinn5_triad_geometry = \ SpiNNakerTriadGeometry( 12, 12, 8, 8, [(0, 0), (4, 8), (8, 4)], (3.6, 3.4)) return SpiNNakerTriadGeometry.spinn5_triad_geometry
def __init__( self, triad_width: int, triad_height: int, board_width: int, board_height: int, roots: Sequence[XY], centre: _Centre): """ :param int triad_width: width of a triad in chips :param int triad_height: height of a triad in chips :param int board_width: width of a board in chips :param int board_height: height of a board in chips :param roots: locations of the Ethernet connected chips :type roots: list(tuple(int, int)) :param centre: the distance from each Ethernet chip to the centre of the hexagon .. note:: This is the theoretical centre, it might not be an actual chip :type centre: tuple(float, float) """ self._triad_width = triad_width self._triad_height = triad_height self._board_width = board_width self._board_height = board_height self._roots = roots # Copy the Ethernet locations to surrounding triads to make the # mathematics easier extended_ethernet_chips = [ (x + x1, y + y1) for (x, y) in roots for x1 in (-triad_width, 0, triad_width) for y1 in (-triad_height, 0, triad_height) ] # Find the nearest Ethernet to each chip by hexagonal distance nearest_ethernets = ( (self.__locate_nearest_ethernet( (x, y), extended_ethernet_chips, centre) for x in range(triad_width)) for y in range(triad_height) ) # SpiNN-5 Ethernet connected chip lookup. # Used by :py:meth:`.local_eth_coord`. Given an x and y # chip position return the offset of the chip's position # from the board's bottom-left chip. # Note: the order of indexes: ``_ethernet_offset[y][x]``! self._ethernet_offset = [ [(x - vx, y - vy) for x, (vx, vy) in enumerate(row)] for y, row in enumerate(nearest_ethernets)] @staticmethod def __hexagonal_metric_distance(xy: XY, centre: _Centre) -> float: """ Get the hexagonal metric distance of a point from the centre of the hexagon. Computes the max of the magnitude of the dot products with the normal vectors for the hexagon sides. The normal vectors are (1,0), (0,1) and (1,-1); we don't need to be careful with the signs of the vectors because we're about to do abs() of them anyway. :param int x: The x-coordinate of the chip to get the distance for :param int y: The y-coordinate of the chip to get the distance for :param float x_centre: The x-coordinate of the centre of the hexagon. .. note:: This is the theoretical centre, it might not be an actual chip :param float y_centre: The y-coordinate of the centre of the hexagon. .. note:: This is the theoretical centre, it might not be an actual chip :return: how far the chip is away from the centre of the hexagon :rtype: float """ x, y = xy x_centre, y_centre = centre dx = x - x_centre dy = y - y_centre return max(abs(dx), abs(dy), abs(dx - dy)) def __locate_nearest_ethernet( self, xy: XY, ethernet_chips: Sequence[XY], centre: _Centre) -> XY: """ Get the coordinate of the nearest Ethernet chip down and left from a given chip. :param x: The x-coordinate of the chip to find the nearest Ethernet to :param y: The y-coordinate of the chip to find the nearest Ethernet to :param ethernet_chips: The locations of the Ethernet chips :param centre: The distance from the Ethernet chip of the centre of the hexagonal board :return: The nearest Ethernet coordinates as a tuple of x, y """ x_c, y_c = centre def measure(xy0: XY) -> float: x0, y0 = xy0 return self.__hexagonal_metric_distance(xy, (x0 + x_c, y0 + y_c)) # Find the coordinates of the closest Ethernet chip by measuring # the distance to the nominal centre of each board; the closest # Ethernet is the one that is on the same board as the one the chip # is closest to the centre of return min(ethernet_chips, key=measure) # pylint: disable=too-many-arguments
[docs] def get_ethernet_chip_coordinates( self, x: int, y: int, width: int, height: int, root_x: int = 0, root_y: int = 0) -> XY: """ Get the coordinates of a chip's local Ethernet connected chip according to this triad geometry object. .. warning:: :py:meth:`.local_eth_coord` will always produce the coordinates of the Ethernet-connected SpiNNaker chip on the same SpiNN-5 board as the supplied chip. This chip may not actually be working. :param int x: x-coordinate of the chip to find the nearest Ethernet of :param int y: y-coordinate of the chip to find the nearest Ethernet of :param int width: width of the SpiNNaker machine (must be a multiple of the triad width of this geometry) :param int height: height of the SpiNNaker machine (must be a multiple of the triad height of this geometry) :param int root_x: x-coordinate of the boot chip (default 0, 0) :param int root_y: y-coordinate of the boot chip (default 0, 0) :return: The coordinates of the closest Ethernet chip :rtype: (int, int) """ dx, dy = self.get_local_chip_coordinate(x, y, root_x, root_y) return ((x - dx) % width), ((y - dy) % height)
[docs] def get_local_chip_coordinate( self, x: int, y: int, root_x: int = 0, root_y: int = 0) -> XY: """ Get the coordinates of a chip on its board of a multi-board system relative to the Ethernet chip of the board. .. note:: This function assumes the system is constructed from SpiNN-5 boards :param int x: The x-coordinate of the chip to find the location of :param int y: The y-coordinate of the chip to find the location of :param int root_x: The x-coordinate of the boot chip (default 0, 0) :param int root_y: The y-coordinate of the boot chip (default 0, 0) :return: the coordinates of the chip relative to its board :rtype: (int, int) """ dx = (x - root_x) % self._triad_width dy = (y - root_y) % self._triad_height return self._ethernet_offset[dy][dx]
[docs] def get_potential_ethernet_chips( self, width: int, height: int) -> Sequence[XY]: """ Get the coordinates of chips that should be Ethernet chips :param int width: The width of the machine to find the chips in :param int height: The height of the machine to find the chips in :rtype: list(tuple(int, int)) """ if width % self._triad_width == 0: eth_width = width else: eth_width = width - self._board_width + 1 if height % self._triad_height == 0: eth_height = height else: eth_height = height - self._board_height + 1 # special case for single boards like the 2,2 if (eth_width <= 0 or eth_height <= 0): return [(0, 0)] return [ (x, y) for start_x, start_y in self._roots for y in range(start_y, eth_height, self._triad_height) for x in range(start_x, eth_width, self._triad_width)]