def _update_neighbour(dijkstra_tables, x_neighbour, y_neighbour, x_current, y_current, x_source, y_source, weight): """ Update the lowest cost for each neighbour of a node :param dijkstra_tables: :param x_current: :param y_current: :param x_source: :param y_source: :param x_neighbour: :param y_neighbour: :param weight: :type dijkstra_tables: :type x_current: :type y_current: :type x_source: :type y_source: :type x_neighbour: :type y_neighbour: :type weight: :return: :rtype: :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_exists = (x_neighbour, y_neighbour) in dijkstra_tables if not neighbour_exists: raise exceptions.PacmanRoutingException( "Tried to propagate to ({}, {}), which is not in the" " partitionable_graph: remove non-existent neighbours".format( x_neighbour, y_neighbour)) neighbour_activated =\ dijkstra_tables[(x_neighbour, y_neighbour)]["activated?"] chip_lowest_cost =\ dijkstra_tables[(x_current, y_current)]["lowest cost"] neighbour_lowest_cost =\ dijkstra_tables[(x_neighbour, y_neighbour)]["lowest cost"] # Only try to update if the neighbour is within the partitionable_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_lowest_cost + weight) if (not neighbour_activated and (neighbour_lowest_cost is None or new_weight < neighbour_lowest_cost)): # update Dijkstra table dijkstra_tables[(x_neighbour, y_neighbour)]["lowest cost"] =\ new_weight if (dijkstra_tables[(x_neighbour, y_neighbour)]["lowest cost"] == 0) \ and (x_neighbour != x_source or y_neighbour != y_source): raise exceptions.PacmanRoutingException( "!!!Cost of non-source node ({}, {}) was set to zero!!!". format(x_neighbour, y_neighbour))
def _locate_routing_entry(current_router, key): """ Locate the entry from the router based off the edge :param current_router: the current router being used in the trace :param key: the key being used by the source placement :return: the routing table entry :raise PacmanRoutingException: \ when there is no entry located on this router. """ for entry in current_router.multicast_routing_entries: if entry.mask & key == entry.routing_entry_key: return entry raise exceptions.PacmanRoutingException("no entry located")
def _locate_routing_entry(current_router, key, n_atoms): """ locate the entry from the router based off the subedge :param current_router: the current router being used in the trace :param key: the key being used by the source placement :return None: :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.warn( "Found more than one entry for key {}. This could be " "an error, as currently no router supports overloading" " of entries.".format(hex(key))) if entry.mask in range_masks: last_atom = key + n_atoms - 1 last_key = e_key + (~entry.mask & 0xFFFFFFFFL) if last_key < last_atom: raise Exception( "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 & 0xFFFFFFFFL) 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 not None: return found_entry else: raise exceptions.PacmanRoutingException("no entry located")
def _locate_routing_entry(current_router, key): """ locate the entry from the router based off the subedge :param current_router: the current router being used in the trace :param key: the key being used by the source placement :return None: :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 if found_entry is not None: return found_entry else: raise exceptions.PacmanRoutingException("no entry located")
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\ pacman.model.routing_tables.multicast_routing_table.MulticastRoutingTable :return: 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 exceptions.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)) else: visited_routers.add(visited_routers_router)
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\ subedges :param n_atoms: the number of atoms going through this path :param is_continuous: bool stating if the keys and atoms mapping is continuous :type source_placement: instance of\ pacman.model.placements.placement.Placement :type dest_placements: iterable of PlacementTuple :type key_and_mask: instance of\ pacman.model.routing_info.key_and_mask.BaseKeyAndMask :return: None :raise PacmanRoutingException: when the trace completes and there are\ still destinations not visited """ for dest in dest_placements: logger.debug("[{}:{}:{}]".format(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 len(failed_to_reach_destinations) > 0: 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 subvertex" " {} on processor {} with keys {} as it did not " "reach destinations {}".format( source_placement.subvertex.label, source_processor, key_and_mask, output_string)) # check for error if the trace went to a destination it shouldn't have if len(located_destinations) > 0: 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 subvertex {} on processor {} " "with keys {} as it didn't reach destinations {}".format( source_placement.subvertex.label, source_processor, key_and_mask, output_string)) if len(failed_to_cover_all_keys_routers) > 0: 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 subvertex {} on processor {} and the" " failed routers are {}".format(source_placement.subvertex.label, source_processor, output_string)) # raise error if required if error_message != "": raise exceptions.PacmanRoutingException(error_message) else: logger.debug("successful test between {} and {}".format( source_placement.subvertex.label, dest_placements))
def _retrace_back_to_source(self, x_destination, y_destination, dijkstra_tables, processor_dest, subedge, nodes_info, source_processor, partitioned_graph): """ :param x_destination: :param y_destination: :param dijkstra_tables: :param processor_dest: :param subedge: :param nodes_info: :type nodes_info: :type subedge: :type x_destination: :type y_destination: :type dijkstra_tables: :type processor_dest: :param partitioned_graph: :type partitioned_graph: :return: the next coordinates to look into :rtype: 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_current, y_current = x_destination, y_destination routing_entry_route_processors = [] # if the processor is None, don't add to router path entry if processor_dest is not None: routing_entry_route_processors.append(processor_dest) routing_entry_route_links = None # build the multicast entry partitions = partitioned_graph.outgoing_edges_partitions_from_vertex( subedge.pre_subvertex) previous_routing_entry = None for partition_key in partitions: partition = partitions[partition_key] if subedge in partition: entry = MulticastRoutingTableByPartitionEntry( out_going_links=routing_entry_route_links, outgoing_processors=routing_entry_route_processors) self._routing_paths.add_path_entry(entry, x_destination, y_destination, partition) previous_routing_entry = entry while dijkstra_tables[(x_current, y_current)]["lowest cost"] != 0: x_check, y_check = x_current, y_current neighbours = nodes_info[(x_current, y_current)]["neighbours"] neighbour_index = 0 added_an_entry = False while not added_an_entry and neighbour_index < len(neighbours): neighbour = neighbours[neighbour_index] if neighbour is not None: x_neighbour, y_neighbour = (neighbour.destination_x, neighbour.destination_y) # Only check if it can be a preceding node if it actually # exists if (x_neighbour, y_neighbour) in dijkstra_tables: dijkstra_table_key = (x_neighbour, y_neighbour) lowest_cost = \ dijkstra_tables[dijkstra_table_key]["lowest cost"] if lowest_cost is not None: (x_current, y_current, previous_routing_entry, added_an_entry) = self._create_routing_entry( x_neighbour, y_neighbour, dijkstra_tables, neighbour_index, nodes_info, x_current, y_current, previous_routing_entry, subedge, partitioned_graph) else: raise exceptions.PacmanRoutingException( "Tried to trace back to node not in " "partitionable_graph: remove non-existent" " neighbours") neighbour_index += 1 if x_current == x_check and y_current == y_check: raise exceptions.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...") previous_routing_entry.incoming_processor = source_processor return x_current, y_current
def _propagate_costs_until_reached_destinations(self, dijkstra_tables, nodes_info, dest_chips, x_source, y_source): """ Propagate the weights till the destination nodes of the source\ nodes are retraced :param dijkstra_tables: the dictionary object for the Dijkstra-tables :param nodes_info: the dictionary object for the nodes inside a route\ scope :param dest_chips: :param x_source: :param y_source: :type dijkstra_tables: dict :type nodes_info: dict :type dest_chips: :type x_source: int :type y_source: int :return: None :rtype: None :raise PacmanRoutingException: when the destination node could not be\ reached from this source node. """ dest_chips_to_find = set(dest_chips) try: dest_chips_to_find.remove((x_source, y_source)) except KeyError: # Ignore this - it just isn't in the set of destinations pass x_current = x_source y_current = y_source # Iterate only if the destination node hasn't been activated while len(dest_chips_to_find) > 0: # PROPAGATE! for i in range( len(nodes_info[(x_current, y_current)]["neighbours"])): neighbour = nodes_info[(x_current, y_current)]["neighbours"][i] weight = nodes_info[(x_current, y_current)]["weights"][i] # "neighbours" is a list of 6 links or None objects. # There is a None object where there is no connection to # that neighbour. if ((neighbour is not None) and not (neighbour.destination_x == x_source and neighbour.destination_y == y_source)): # These variables change with every look at a new neighbour self._update_neighbour(dijkstra_tables, neighbour.destination_x, neighbour.destination_y, x_current, y_current, x_source, y_source, weight) # This is the lowest cost across ALL # deactivated nodes in the partitionable_graph. graph_lowest_cost = None # Find the next node to be activated for key in dijkstra_tables: # Don't continue if the node hasn't even been touched yet if (dijkstra_tables[key]["lowest cost"] is not None and not dijkstra_tables[key]["activated?"] and (graph_lowest_cost is not None and (dijkstra_tables[key]["lowest cost"] < graph_lowest_cost) or graph_lowest_cost is None)): graph_lowest_cost = dijkstra_tables[key]["lowest cost"] x_current, y_current = int(key[0]), int(key[1]) # If there were no deactivated nodes with costs, # but the destination was not reached this iteration, # raise an exception if graph_lowest_cost is None: raise exceptions.PacmanRoutingException( "Destination could not be activated, ending run") # Set the next activated node as the deactivated node with the # lowest current cost dijkstra_tables[(x_current, y_current)]["activated?"] = True try: dest_chips_to_find.remove((x_current, y_current)) except KeyError: # Ignore the error - it just isn't a destination chip pass