Source code for pacman.operations.router_algorithms.routing_tree

# 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/>.

"""An explicit representation of a routing tree in a machine.

This representation of a route explicitly describes a tree-structure and the
complete path taken by a route. This is used during place and route in
preference to a set of RoutingTableEntry tuples since it is more easily
verified and more accurately represents the problem at hand.

Based on
https://github.com/project-rig/rig/blob/master/rig/place_and_route/routing_tree.py
"""
from collections import deque


[docs]class RoutingTree(object): """Explicitly defines a multicast route through a SpiNNaker machine. Each instance represents a single hop in a route and recursively refers to following steps. """ # noqa W605 # A *lot* of instances of this data structure are created and so its memory # consumption is a sensitive matter. The following optimisations have been # made: # * Using __slots__ hugely reduces the size of instances of this class # object # * Storing the chip coordinate as two values (_chip_x and _chip_y) rather # than a tuple saves 56 bytes per instance. __slots__ = ["_chip_x", "_chip_y", "_children"] def __init__(self, chip): """ :param chip: The chip the route is currently passing through. :type chip: tuple(int,int) """ self.chip = chip self._children = [] @property def chip(self): """The chip the route is currently passing through. :rtype: tuple(int,int) """ return (self._chip_x, self._chip_y) @chip.setter def chip(self, chip): self._chip_x, self._chip_y = chip @property def children(self): """ A :py:class:`iterable` of the next steps in the route represented by a\ (route, object) tuple. .. note:: Up until Rig 1.5.1, this structure used :py:class:`set`\\ s to \ store children. This was changed to :py:class:`list`\\ s since \ sets incur a large memory overhead and in practice the set-like \ behaviour of the list of children is not useful. The object indicates the intended destination of this step in the \ route. It may be one of: * :py:class:`RoutingTree` \ representing the continuation of the routing tree after following a \ given link. * A vertex (i.e. some other Python object) when the route terminates \ at the supplied vertex. Note that the direction may be None and so \ additional logic may be required to determine what core to target to\ reach the vertex. :rtype: iterable """ for child in self._children: yield child
[docs] def append_child(self, child): self._children.append(child)
[docs] def remove_child(self, child): self._children.remove(child)
def __iter__(self): """Iterate over this node and then all its children, recursively and in no specific order. This iterator iterates over the child *objects* (i.e. not the route part of the child tuple). """ yield self for _route, obj in self._children: if isinstance(obj, RoutingTree): for subchild in obj: yield subchild else: yield obj def __repr__(self): return "<RoutingTree at {} with {} {}>".format( self.chip, len(self._children), "child" if len(self._children) == 1 else "children")
[docs] def traverse(self): """ Traverse the tree yielding the direction taken to a node, the coordinates of that node and the directions leading from the Node. :return: (direction, (x, y), set(route)) \ Direction taken to reach a Node in the tree, the (x, y) coordinate\ of that Node and routes leading to children of the Node. """ # A queue of (direction, node) to visit. The direction is the Links # entry which describes the direction in which we last moved to reach # the node (or None for the root). to_visit = deque([(None, self)]) while to_visit: direction, node = to_visit.popleft() # Determine the set of directions we must travel to reach the # children out_directions = set() for child_direction, child in node._children: # Note that if the direction is unspecified, we simply # (silently) don't add a route for that child. if child_direction is not None: out_directions.add(child_direction) # Search the next steps of the route too if isinstance(child, RoutingTree): assert child_direction is not None to_visit.append((child_direction, child)) # Yield the information pertaining to this Node yield direction, node.chip, out_directions