Ejemplo n.º 1
0
    def __call__(self, router_tables):
        tables = MulticastRoutingTables()
        previous_masks = dict()

        progress = ProgressBar(
            len(router_tables.routing_tables) * 2,
            "Compressing Routing Tables")

        # Create all masks without holes
        allowed_masks = [_32_BITS - ((2**i) - 1) for i in range(33)]

        # Check that none of the masks have "holes" e.g. 0xFFFF0FFF has a hole
        for router_table in router_tables.routing_tables:
            for entry in router_table.multicast_routing_entries:
                if entry.mask not in allowed_masks:
                    raise PacmanRoutingException(
                        "Only masks without holes are allowed in tables for"
                        " MallocBasedRouteMerger (disallowed mask={})".format(
                            hex(entry.mask)))

        for router_table in progress.over(router_tables.routing_tables):
            new_table = self._merge_routes(router_table, previous_masks)
            tables.add_routing_table(new_table)
            n_entries = len([
                entry for entry in new_table.multicast_routing_entries
                if not entry.defaultable
            ])
            print("Reduced from {} to {}".format(
                len(router_table.multicast_routing_entries), n_entries))
            if n_entries > 1023:
                raise PacmanRoutingException(
                    "Cannot make table small enough: {} entries".format(
                        n_entries))
        return tables
Ejemplo n.º 2
0
    def _update_neighbour(tables, neighbour, current, source, weight):
        """ Update the lowest cost for each neighbour_xy of a node

        :param dict(tuple(int,int),_DijkstraInfo) tables:
        :param ~spinn_machine.Link neighbour:
        :param tuple(int,int) current:
        :param tuple(int,int) source:
        :param float weight:
        :raise PacmanRoutingException: when the algorithm goes to a node that
            doesn't exist in the machine or the node's cost was set too low.
        """
        neighbour_xy = (neighbour.destination_x, neighbour.destination_y)
        if neighbour_xy not in tables:
            raise PacmanRoutingException(
                "Tried to propagate to ({}, {}), which is not in the"
                " graph: remove non-existent neighbours".format(
                    neighbour.destination_x, neighbour.destination_y))

        chip_cost = tables[current].cost
        neighbour_cost = tables[neighbour_xy].cost

        # Only try to update if the neighbour_xy is within the graph and the
        # cost if the node hasn't already been activated and the lowest cost
        # if the new cost is less, or if there is no current cost.
        new_weight = float(chip_cost + weight)
        if (not tables[neighbour_xy].activated
                and (neighbour_cost is None or new_weight < neighbour_cost)):
            # update Dijkstra table
            tables[neighbour_xy].cost = new_weight

        if tables[neighbour_xy].cost == 0 and neighbour_xy != source:
            raise PacmanRoutingException(
                "!!!Cost of non-source node ({}, {}) was set to zero!!!".
                format(neighbour.destination_x, neighbour.destination_y))
Ejemplo n.º 3
0
def compare_route(f, o_route, compressed_dict, o_code=None, start=0):
    if o_code is None:
        o_code = codify(o_route)
    keys = list(compressed_dict.keys())
    for i in range(start, len(keys)):
        c_code = keys[i]
        if covers(o_code, c_code):
            c_route = compressed_dict[c_code]
            f.write("\t\t{}\n".format(reports.format_route(c_route)))
            if o_route.defaultable != c_route.defaultable:
                raise PacmanRoutingException(
                    "Compressed route {} covers original route {} but has "
                    "a different defaultable value.".format(c_route, o_route))
            if o_route.processor_ids != c_route.processor_ids:
                raise PacmanRoutingException(
                    "Compressed route {} covers original route {} but has "
                    "a different processor_ids.".format(c_route, o_route))
            if o_route.link_ids != c_route.link_ids:
                raise PacmanRoutingException(
                    "Compressed route {} covers original route {} but has "
                    "a different link_ids.".format(c_route, o_route))
            remainders = calc_remainders(o_code, c_code)
            for remainder in remainders:
                compare_route(f,
                              o_route,
                              compressed_dict,
                              o_code=remainder,
                              start=i + 1)
            return
        compare_route(f, o_route, compressed_dict, o_code=o_code, start=i + 1)
        return
Ejemplo n.º 4
0
def compare_route(o_route, compressed_dict, o_code=None, start=0):
    if o_code is None:
        o_code = codify(o_route)
    keys = list(compressed_dict.keys())
    for i in range(start, len(keys)):
        c_code = keys[i]
        print(o_code, c_code)  # TODO: Don't print this message!
        if covers(o_code, c_code):
            print("covers")  # TODO: Don't print this message!
            c_route = compressed_dict[c_code]
            if o_route.defaultable != c_route.defaultable:
                PacmanRoutingException(  # TODO: Raise this exception!
                    "Compressed route {} covers original route {} but has "
                    "a different defaultable value.".format(c_route, o_route))
            if o_route.processor_ids != c_route.processor_ids:
                PacmanRoutingException(  # TODO: Raise this exception!
                    "Compressed route {} covers original route {} but has "
                    "a different processor_ids.".format(c_route, o_route))
            if o_route.link_ids != c_route.link_ids:
                PacmanRoutingException(  # TODO: Raise this exception!
                    "Compressed route {} covers original route {} but has "
                    "a different link_ids.".format(c_route, o_route))
            remainders = calc_remainders(o_code, c_code)
            print(remainders)  # TODO: Don't print this message!
            for remainder in remainders:
                compare_route(o_route,
                              compressed_dict,
                              o_code=remainder,
                              start=i + 1)
            return
        compare_route(o_route, compressed_dict, o_code=o_code, start=i + 1)
        return
Ejemplo n.º 5
0
def compare_route(o_route, compressed_dict, o_code=None, start=0, f=None):
    """
    :param ~spinn_machine.MulticastRoutingEntry o_route: the original route
    :param dict compressed_dict:
    :param str o_code:
    :param int start:
    :param ~io.FileIO f:
    :rtype: None
    """
    if o_code is None:
        o_code = codify(o_route)
    keys = list(compressed_dict.keys())
    for i in range(start, len(keys)):
        c_code = keys[i]
        if covers(o_code, c_code):
            c_route = compressed_dict[c_code]
            if f is not None:
                f.write("\t\t{}\n".format(format_route(c_route)))
            if o_route.processor_ids != c_route.processor_ids:
                if set(o_route.processor_ids) != set(c_route.processor_ids):
                    raise PacmanRoutingException(
                        "Compressed route {} covers original route {} but has "
                        "a different processor_ids.".format(c_route, o_route))
            if o_route.link_ids != c_route.link_ids:
                if set(o_route.link_ids) != set(c_route.link_ids):
                    raise PacmanRoutingException(
                        "Compressed route {} covers original route {} but has "
                        "a different link_ids.".format(c_route, o_route))
            if not o_route.defaultable and c_route.defaultable:
                if o_route == c_route:
                    raise PacmanRoutingException(
                        "Compressed route {} while original route {} but has "
                        "a different defaultable value.".format(
                            c_route, o_route))
                else:
                    compare_route(o_route,
                                  compressed_dict,
                                  o_code=o_code,
                                  start=i + 1,
                                  f=f)
            else:
                remainders = calc_remainders(o_code, c_code)
                for remainder in remainders:
                    compare_route(o_route,
                                  compressed_dict,
                                  o_code=remainder,
                                  start=i + 1,
                                  f=f)
            return
    if not o_route.defaultable:
        # print("No route found {}".format(o_route))
        raise PacmanRoutingException("No route found {}".format(o_route))
Ejemplo n.º 6
0
    def get_multicast_routing_entry_by_routing_entry_key(
            self, routing_entry_key, mask):
        """ Get the routing entry associated with the specified key_combo-mask\
            combination or None if the routing table does not match the\
            key_combo

        :param routing_entry_key: the routing key to be searched
        :type routing_entry_key: int
        :param mask: the routing mask to be searched
        :type mask: int
        :return: the routing entry associated with the routing key_combo or\
            None if no such entry exists
        :rtype:\
            :py:class:`spinn_machine.MulticastRoutingEntry`
        """
        if (routing_entry_key & mask) != routing_entry_key:
            raise PacmanRoutingException(
                "The key {} is changed when masked with the mask {}."
                " This is determined to be an error in the tool chain. Please "
                "correct this and try again.".format(routing_entry_key, mask))

        tuple_key = (routing_entry_key, mask)
        if tuple_key in self._multicast_routing_entries_by_routing_entry_key:
            return self._multicast_routing_entries_by_routing_entry_key[
                tuple_key]
        return None
Ejemplo n.º 7
0
def _locate_routing_entry(current_router, key, n_atoms):
    """ Locate the entry from the router based off the edge

    :param MulticastRoutingTable current_router:
        the current router being used in the trace
    :param int key: the key being used by the source placement
    :param int n_atoms: the number of atoms
    :rtype: ~spinn_machine.MulticastRoutingEntry
    :raise PacmanRoutingException:
        when there is no entry located on this router
    """
    found_entry = None
    for entry in current_router.multicast_routing_entries:
        key_combo = entry.mask & key
        e_key = entry.routing_entry_key
        if key_combo == e_key:
            if found_entry is None:
                found_entry = entry
            else:
                logger.warning(
                    "Found more than one entry for key {}. This could be "
                    "an error, as currently no router supports overloading"
                    " of entries.", hex(key))
            if entry.mask in range_masks:
                last_atom = key + n_atoms - 1
                last_key = e_key + (~entry.mask & FULL_MASK)
                if last_key < last_atom:
                    raise PacmanRoutingException(
                        "Full key range not covered: key:{} key_combo:{} "
                        "mask:{}, last_key:{}, e_key:{}".format(
                            hex(key), hex(key_combo), hex(entry.mask),
                            hex(last_key), hex(e_key)))
        elif entry.mask in range_masks:
            last_atom = key + n_atoms
            last_key = e_key + (~entry.mask & FULL_MASK)
            if min(last_key, last_atom) - max(e_key, key) + 1 > 0:
                raise Exception(
                    "Key range partially covered:  key:{} key_combo:{} "
                    "mask:{}, last_key:{}, e_key:{}".format(
                        hex(key), hex(key_combo), hex(entry.mask),
                        hex(last_key), hex(e_key)))
    if found_entry is None:
        raise PacmanRoutingException("no entry located")
    return found_entry
Ejemplo n.º 8
0
def _check_visited_routers(chip_x, chip_y, visited_routers):
    """ Check if the trace has visited this router already

    :param int chip_x: the x coordinate of the chip being checked
    :param int chip_y: the y coordinate of the chip being checked
    :param set(tuple(int,int)) visited_routers: routers already visited
    :rtype: None
    :raise PacmanRoutingException: when a router has been visited twice.
    """
    visited_routers_router = (chip_x, chip_y)
    if visited_routers_router in visited_routers:
        raise PacmanRoutingException(
            "visited this router before, there is a cycle here. "
            "The routers I've currently visited are {} and the router i'm "
            "visiting is {}".format(visited_routers, visited_routers_router))
    visited_routers.add(visited_routers_router)
    def _do_fixed_routing(self, ethernet_connected_chip):
        """ Handles this board through the quick routing process, based on a\
            predefined routing table.

        :param ~spinn_machine.Chip ethernet_connected_chip:
            the Ethernet connected chip
        :raises PacmanRoutingException:
        :raises PacmanAlreadyExistsException:
        """
        eth_x = ethernet_connected_chip.x
        eth_y = ethernet_connected_chip.y

        to_route = set(self._machine.get_existing_xys_by_ethernet(
            eth_x, eth_y))
        routed = set()
        routed.add((eth_x, eth_y))
        to_route.remove((eth_x, eth_y))

        while len(to_route) > 0:
            found = []
            for x, y in to_route:
                # Check links starting with the most direct to 0,0
                for link_id in [4, 3, 5, 2, 0, 1]:
                    # Get protential destination
                    destination = self._machine.xy_over_link(x, y, link_id)
                    # If it is useful
                    if destination in routed:
                        # check it actually exits
                        if self._machine.is_link_at(x, y, link_id):
                            # build entry and add to table and add to tables
                            key = (x, y)
                            self.__add_fixed_route_entry(key, [link_id], [])
                            found.append(key)
                            break
            if len(found) == 0:
                raise PacmanRoutingException(
                    "Unable to do fixed point routing on {}.".format(
                        ethernet_connected_chip.ip_address))
            for key in found:
                to_route.remove(key)
                routed.add(key)

        # create final fixed route entry
        # locate where to put data on ethernet chip
        processor_id = self.__locate_destination(ethernet_connected_chip)
        # build entry and add to table and add to tables
        self.__add_fixed_route_entry((eth_x, eth_y), [], [processor_id])
Ejemplo n.º 10
0
    def _minimum(tables):
        # This is the lowest cost across ALL deactivated nodes in the graph.
        lowest_cost = sys.maxsize
        lowest = None

        # Find the next node to be activated
        for key in tables:
            # Don't continue if the node hasn't even been touched yet
            if (tables[key].cost is not None and not tables[key].activated
                    and tables[key].cost < lowest_cost):
                lowest_cost = tables[key].cost
                lowest = key

        # If there were no deactivated nodes with costs, but the destination
        # was not reached this iteration, raise an exception
        if lowest is None:
            raise PacmanRoutingException(
                "Destination could not be activated, ending run")

        return int(lowest[0]), int(lowest[1])
Ejemplo n.º 11
0
def _check_visited_routers(chip_x, chip_y, visited_routers):
    """ Check if the trace has visited this router already

    :param chip_x: the x coordinate of the chip being checked
    :param chip_y: the y coordinate of the chip being checked
    :param visited_routers: routers already visited
    :type chip_x: int
    :type chip_y: int
    :type visited_routers: iterable of\
        :py:class:`pacman.model.routing_tables.MulticastRoutingTable`
    :rtype: None
    :raise PacmanRoutingException: when a router has been visited twice.
    """
    visited_routers_router = (chip_x, chip_y)
    if visited_routers_router in visited_routers:
        raise PacmanRoutingException(
            "visited this router before, there is a cycle here. "
            "The routers I've currently visited are {} and the router i'm "
            "visiting is {}"
            .format(visited_routers, visited_routers_router))
    visited_routers.add(visited_routers_router)
    def _generate_routing_tree(self, ethernet_chip):
        """ Generates a map for each chip to over which link it gets its data.

        :param ~spinn_machine.Chip ethernet_chip:
        :return: Map of chip.x, chip.y tp (source.x, source.y, source.link)
        :rtype: dict(tuple(int, int), tuple(int, int, int))
        """
        eth_x = ethernet_chip.x
        eth_y = ethernet_chip.y
        tree = dict()

        to_reach = set(
            self._machine.get_existing_xys_by_ethernet(eth_x, eth_y))
        reached = set()
        reached.add((eth_x, eth_y))
        to_reach.remove((eth_x, eth_y))
        found = set()
        found.add((eth_x, eth_y))
        while len(to_reach) > 0:
            just_reached = found
            found = set()
            for x, y in just_reached:
                # Check links starting with the most direct from 0,0
                for link_id in [1, 0, 2, 5, 3, 4]:
                    # Get protential destination
                    destination = self._machine.xy_over_link(x, y, link_id)
                    # If it is useful
                    if destination in to_reach:
                        # check it actually exits
                        if self._machine.is_link_at(x, y, link_id):
                            # Add to tree and record chip reachable
                            tree[destination] = (x, y, link_id)
                            to_reach.remove(destination)
                            found.add(destination)
            if len(found) == 0:
                raise PacmanRoutingException(
                    "Unable to do data in routing on {}.".format(
                        ethernet_chip.ip_address))
        return tree
Ejemplo n.º 13
0
def _search_route(source_placement, dest_placements, key_and_mask,
                  routing_tables, machine, n_atoms, is_continuous):
    """ Locate if the routing tables work for the source to desks as\
        defined

    :param source_placement: the placement from which the search started
    :param dest_placements: \
        the placements to which this trace should visit only once
    :param key_and_mask: the key and mask associated with this set of edges
    :param n_atoms: the number of atoms going through this path
    :param is_continuous: \
        whether the keys and atoms mapping is continuous
    :type source_placement: \
        :py:class:`pacman.model.placements.Placement`
    :type dest_placements: iterable(PlacementTuple)
    :type key_and_mask: \
        :py:class:`pacman.model.routing_info.BaseKeyAndMask`
    :rtype: None
    :raise PacmanRoutingException: when the trace completes and there are\
        still destinations not visited
    """
    if logger.isEnabledFor(logging.DEBUG):
        for dest in dest_placements:
            logger.debug("[{}:{}:{}]", dest.x, dest.y, dest.p)

    located_destinations = set()

    failed_to_cover_all_keys_routers = list()

    _start_trace_via_routing_tables(
        source_placement, key_and_mask, located_destinations,
        routing_tables, machine, n_atoms, is_continuous,
        failed_to_cover_all_keys_routers)

    # start removing from located_destinations and check if destinations not
    #  reached
    failed_to_reach_destinations = list()
    for dest in dest_placements:
        if dest in located_destinations:
            located_destinations.remove(dest)
        else:
            failed_to_reach_destinations.append(dest)

    # check for error if trace didn't reach a destination it was meant to
    error_message = ""
    if failed_to_reach_destinations:
        output_string = ""
        for dest in failed_to_reach_destinations:
            output_string += "[{}:{}:{}]".format(dest.x, dest.y, dest.p)
        source_processor = "[{}:{}:{}]".format(
            source_placement.x, source_placement.y, source_placement.p)
        error_message += ("failed to locate all destinations with vertex"
                          " {} on processor {} with keys {} as it did not "
                          "reach destinations {}".format(
                              source_placement.vertex.label, source_processor,
                              key_and_mask, output_string))

    # check for error if the trace went to a destination it shouldn't have
    if located_destinations:
        output_string = ""
        for dest in located_destinations:
            output_string += "[{}:{}:{}]".format(dest.x, dest.y, dest.p)
        source_processor = "[{}:{}:{}]".format(
            source_placement.x, source_placement.y, source_placement.p)
        error_message += ("trace went to more failed to locate all "
                          "destinations with vertex {} on processor {} "
                          "with keys {} as it didn't reach destinations {}"
                          .format(
                              source_placement.vertex.label, source_processor,
                              key_and_mask, output_string))

    if failed_to_cover_all_keys_routers:
        output_string = ""
        for data_entry in failed_to_cover_all_keys_routers:
            output_string += "[{}, {}, {}, {}]".format(
                data_entry['router_x'], data_entry['router_y'],
                data_entry['keys'], data_entry['source_mask'])
        source_processor = "[{}:{}:{}]".format(
            source_placement.x, source_placement.y, source_placement.p)
        error_message += (
            "trace detected that there were atoms which the routing entry's"
            " wont cover and therefore packets will fly off to unknown places."
            " These keys came from the vertex {} on processor {} and the"
            " failed routers are {}".format(
                source_placement.vertex.label, source_processor,
                output_string))

    # raise error if required
    if error_message != "":
        raise PacmanRoutingException(error_message)
    logger.debug("successful test between {} and {}",
                 source_placement.vertex.label, dest_placements)
Ejemplo n.º 14
0
    def _retrace_back_to_source(self, dest, tables, edge, nodes_info,
                                source_processor, graph):
        """
        :param Placement dest: Destination placement
        :param dict(tuple(int,int),_DijkstraInfo) tables:
        :param MachineEdge edge:
        :param dict(tuple(int,int),_NodeInfo) nodes_info:
        :param int source_processor:
        :param MachineGraph graph:
        :return: the next coordinates to look into
        :rtype: tuple(int, int)
        :raise PacmanRoutingException:
            when the algorithm doesn't find a next point to search from. AKA,
            the neighbours of a chip do not have a cheaper cost than the node
            itself, but the node is not the destination or when the algorithm
            goes to a node that's not considered in the weighted search.
        """
        # Set the tracking node to the destination to begin with
        x, y = dest.x, dest.y
        routing_entry_route_processors = []

        # if the processor is None, don't add to router path entry
        if dest.p is not None:
            routing_entry_route_processors.append(dest.p)
        routing_entry_route_links = None

        # build the multicast entry
        partitions = graph.get_multicast_edge_partitions_starting_at_vertex(
            edge.pre_vertex)

        prev_entry = None
        for partition in partitions:
            if edge in partition:
                entry = MulticastRoutingTableByPartitionEntry(
                    out_going_links=routing_entry_route_links,
                    outgoing_processors=routing_entry_route_processors)
                self._routing_paths.add_path_entry(entry, dest.x, dest.y,
                                                   partition)
                prev_entry = entry

        while tables[x, y].cost != 0:
            for idx, neighbour in enumerate(nodes_info[x, y].neighbours):
                if neighbour is not None:
                    n_xy = (neighbour.destination_x, neighbour.destination_y)

                    # Only check if it can be a preceding node if it actually
                    # exists
                    if n_xy not in tables:
                        raise PacmanRoutingException(
                            "Tried to trace back to node not in "
                            "graph: remove non-existent neighbours")

                    if tables[n_xy].cost is not None:
                        x, y, prev_entry, added = self._create_routing_entry(
                            n_xy, tables, idx, nodes_info, x, y, prev_entry,
                            edge, graph)
                        if added:
                            break
            else:
                raise PacmanRoutingException(
                    "Iterated through all neighbours of tracking node but"
                    " did not find a preceding node! Consider increasing "
                    "acceptable discrepancy between sought traceback cost"
                    " and actual cost at node. Terminating...")
        prev_entry.incoming_processor = source_processor
        return x, y