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. Attributes ---------- chip : (x, y) The chip the route is currently passing through. children : list A :py:class:`list` 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. """ # 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): self.chip = chip self._children = [] @property def chip(self): return (self._chip_x, self._chip_y) @chip.setter def chip(self, chip): self._chip_x, self._chip_y = chip @property def children(self): 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 co-ordinates 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) co-ordinate 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