def _do_allocation( self, one_to_one_groups, same_chip_vertex_groups, machine, plan_n_timesteps, machine_graph, progress): """ :param list(set(MachineVertex)) one_to_one_groups: Groups of vertexes that would be nice on same chip :param same_chip_vertex_groups: Mapping of Vertex to the Vertex that must be on the same Chip :type same_chip_vertex_groups: dict(MachineVertex, collection(MachineVertex)) :param ~spinn_machine.Machine machine: The machine with respect to which to partition the application graph :param int plan_n_timesteps: number of timesteps to plan for :param MachineGraph machine_graph: The machine_graph to place :param ~spinn_utilities.progress_bar.ProgressBar progress: :rtype: Placements """ placements = Placements() resource_tracker = ResourceTracker( machine, plan_n_timesteps, self._generate_radial_chips(machine)) all_vertices_placed = set() # RadialPlacementFromChipConstraint won't work here for vertex in machine_graph.vertices: for constraint in vertex.constraints: if isinstance(constraint, RadialPlacementFromChipConstraint): raise PacmanPlaceException( "A RadialPlacementFromChipConstraint will not work " "with the OneToOnePlacer algorithm; use the " "RadialPlacer algorithm instead") # Find and place vertices with hard constraints for vertex in machine_graph.vertices: if isinstance(vertex, AbstractVirtual): virtual_p = 0 while placements.is_processor_occupied( vertex.virtual_chip_x, vertex.virtual_chip_y, virtual_p): virtual_p += 1 placements.add_placement(Placement( vertex, vertex.virtual_chip_x, vertex.virtual_chip_y, virtual_p)) all_vertices_placed.add(vertex) elif locate_constraints_of_type( vertex.constraints, ChipAndCoreConstraint): self._allocate_same_chip_as_group( vertex, placements, resource_tracker, same_chip_vertex_groups, all_vertices_placed, progress, machine_graph) for grouped_vertices in one_to_one_groups: # Get unallocated vertices and placements of allocated vertices unallocated = list() chips = list() for vert in grouped_vertices: if vert in all_vertices_placed: placement = placements.get_placement_of_vertex(vert) chips.append((placement.x, placement.y)) else: unallocated.append(vert) if not chips: chips = None if 0 < len(unallocated) <=\ resource_tracker.get_maximum_cores_available_on_a_chip(): # Try to allocate all vertices to the same chip self._allocate_one_to_one_group( resource_tracker, unallocated, progress, placements, chips, all_vertices_placed, machine_graph) # if too big or failed go on to other groups first # check all have been allocated if not do so now. for vertex in machine_graph.vertices: if vertex not in all_vertices_placed: self._allocate_same_chip_as_group( vertex, placements, resource_tracker, same_chip_vertex_groups, all_vertices_placed, progress, machine_graph) progress.end() return placements
def _do_allocation( self, one_to_one_groups, same_chip_vertex_groups, machine, plan_n_timesteps, machine_graph, progress): """ :param one_to_one_groups: Groups of vertexes that would be nice on same chip :type one_to_one_groups: list(set(vertex)) :param same_chip_vertex_groups: Mapping of Vertex to the Vertex that must be on the same Chip :type same_chip_vertex_groups: dict(vertex, collection(vertex)) :param machine:\ The machine with respect to which to partition the application\ graph :type machine: :py:class:`spinn_machine.Machine` :param plan_n_timesteps: number of timesteps to plan for :type plan_n_timesteps: int :param machine_graph: The machine_graph to place :type machine_graph:\ :py:class:`pacman.model.graphs.machine.MachineGraph` :param progress: :return: """ placements = Placements() resource_tracker = ResourceTracker( machine, plan_n_timesteps, self._generate_radial_chips(machine)) all_vertices_placed = set() # RadialPlacementFromChipConstraint won't work here for vertex in machine_graph.vertices: for constraint in vertex.constraints: if isinstance(constraint, RadialPlacementFromChipConstraint): raise PacmanPlaceException( "A RadialPlacementFromChipConstraint will not work " "with the OneToOnePlacer algorithm; use the " "RadialPlacer algorithm instead") unconstrained = list() # Find and place vertices with hard constraints for vertex in machine_graph.vertices: if isinstance(vertex, AbstractVirtualVertex): virtual_p = 0 while placements.is_processor_occupied( vertex.virtual_chip_x, vertex.virtual_chip_y, virtual_p): virtual_p += 1 placements.add_placement(Placement( vertex, vertex.virtual_chip_x, vertex.virtual_chip_y, virtual_p)) all_vertices_placed.add(vertex) elif locate_constraints_of_type( vertex.constraints, ChipAndCoreConstraint): self._allocate_same_chip_as_group( vertex, placements, resource_tracker, same_chip_vertex_groups, all_vertices_placed, progress) else: unconstrained.append(vertex) for grouped_vertices in one_to_one_groups: # Get unallocated vertices and placements of allocated vertices unallocated = list() chips = list() for vert in grouped_vertices: if vert in all_vertices_placed: placement = placements.get_placement_of_vertex(vert) chips.append((placement.x, placement.y)) else: unallocated.append(vert) if 0 < len(unallocated) <=\ resource_tracker.get_maximum_cores_available_on_a_chip(): # Try to allocate all vertices to the same chip self._allocate_one_to_one_group( resource_tracker, unallocated, progress, placements, chips, all_vertices_placed) # if too big or failed go on to other groups first # check all have been allocated if not do so now. for vertex in machine_graph.vertices: if vertex not in all_vertices_placed: self._allocate_same_chip_as_group( vertex, placements, resource_tracker, same_chip_vertex_groups, all_vertices_placed, progress) progress.end() return placements
def _create_fake_network(self, ethernet_connected_chip): """ Generate the fake network for each board :param ethernet_connected_chip: the ethernet chip to fire from :return: fake graph, fake placements, fake machine. """ fake_graph = MachineGraph(label="routing fake_graph") fake_placements = Placements() destination_to_partition_id_map = dict() # build fake setup for the routing eth_x = ethernet_connected_chip.x eth_y = ethernet_connected_chip.y fake_machine = virtual_submachine(self._real_machine, ethernet_connected_chip) # Build a fake graph with vertices for all the monitors for chip in self._real_machine.get_chips_by_ethernet(eth_x, eth_y): # locate correct chips extra monitor placement placement = self._real_placements.get_placement_of_vertex( self._monitors[chip.x, chip.y]) # adjust for wrap around's fake_x, fake_y = self._real_machine.get_local_xy(chip) # add destination vertex vertex = RoutingMachineVertex() fake_graph.add_vertex(vertex) # build fake placement fake_placements.add_placement( Placement(x=fake_x, y=fake_y, p=placement.p, vertex=vertex)) # build source vertex, which is for the Gatherer vertex_source = RoutingMachineVertex() fake_graph.add_vertex(vertex_source) for free_processor in range(Machine.MAX_CORES_PER_CHIP): if not fake_placements.is_processor_occupied( x=FAKE_ETHERNET_CHIP_X, y=FAKE_ETHERNET_CHIP_Y, p=free_processor): fake_placements.add_placement( Placement(x=FAKE_ETHERNET_CHIP_X, y=FAKE_ETHERNET_CHIP_Y, p=free_processor, vertex=vertex_source)) break # deal with edges, each one being in a unique partition id, to # allow unique routing to each chip. counter = KEY_START_VALUE for vertex in fake_graph.vertices: if vertex == vertex_source: continue fake_graph.add_edge( MachineEdge(pre_vertex=vertex_source, post_vertex=vertex), counter) placement = fake_placements.get_placement_of_vertex(vertex) # adjust to real chip ids real_chip_xy = self._real_machine.get_global_xy( placement.x, placement.y, eth_x, eth_y) destination_to_partition_id_map[real_chip_xy] = counter counter += N_KEYS_PER_PARTITION_ID return (fake_graph, fake_placements, fake_machine, destination_to_partition_id_map)
def _do_dynamic_routing( self, fixed_route_tables, placements, ethernet_connected_chip, destination_class, machine, board_version): """ Uses a router to route fixed routes :param fixed_route_tables: fixed route tables entry holder :param placements: placements :param ethernet_connected_chip: the chip to consider for this routing :param destination_class: the class at the Ethernet connected chip\ for receiving all these routes. :param machine: SpiNNMachine instance :param board_version: The version of the machine :rtype: None """ graph = MachineGraph(label="routing graph") fake_placements = Placements() # build fake setup for the routing eth_x = ethernet_connected_chip.x eth_y = ethernet_connected_chip.y down_links = set() for (chip_x, chip_y) in machine.get_chips_on_board( ethernet_connected_chip): vertex = RoutingMachineVertex() graph.add_vertex(vertex) rel_x = chip_x - eth_x if rel_x < 0: rel_x += machine.max_chip_x + 1 rel_y = chip_y - eth_y if rel_y < 0: rel_y += machine.max_chip_y + 1 free_processor = 0 while ((free_processor < machine.MAX_CORES_PER_CHIP) and fake_placements.is_processor_occupied( self.FAKE_ETHERNET_CHIP_X, y=self.FAKE_ETHERNET_CHIP_Y, p=free_processor)): free_processor += 1 fake_placements.add_placement(Placement( x=rel_x, y=rel_y, p=free_processor, vertex=vertex)) down_links.update({ (rel_x, rel_y, link) for link in range( Router.MAX_LINKS_PER_ROUTER) if not machine.is_link_at(chip_x, chip_y, link)}) # Create a fake machine consisting of only the one board that # the routes should go over fake_machine = machine if (board_version in machine.BOARD_VERSION_FOR_48_CHIPS and (machine.max_chip_x > machine.MAX_CHIP_X_ID_ON_ONE_BOARD or machine.max_chip_y > machine.MAX_CHIP_Y_ID_ON_ONE_BOARD)): down_chips = { (x, y) for x, y in zip( range(machine.SIZE_X_OF_ONE_BOARD), range(machine.SIZE_Y_OF_ONE_BOARD)) if not machine.is_chip_at( (x + eth_x) % (machine.max_chip_x + 1), (y + eth_y) % (machine.max_chip_y + 1))} # build a fake machine which is just one board but with the missing # bits of the real board fake_machine = VirtualMachine( machine.SIZE_X_OF_ONE_BOARD, machine.SIZE_Y_OF_ONE_BOARD, False, down_chips=down_chips, down_links=down_links) # build destination verts = graph.vertices vertex_dest = RoutingMachineVertex() graph.add_vertex(vertex_dest) destination_processor = self._locate_destination( ethernet_chip_x=ethernet_connected_chip.x, ethernet_chip_y=ethernet_connected_chip.y, destination_class=destination_class, placements=placements) fake_placements.add_placement(Placement( x=self.FAKE_ETHERNET_CHIP_X, y=self.FAKE_ETHERNET_CHIP_Y, p=destination_processor, vertex=vertex_dest)) # deal with edges for vertex in verts: graph.add_edge( MachineEdge(pre_vertex=vertex, post_vertex=vertex_dest), self.FAKE_ROUTING_PARTITION) # route as if using multicast router = BasicDijkstraRouting() routing_tables_by_partition = router( placements=fake_placements, machine=fake_machine, machine_graph=graph, use_progress_bar=False) # convert to fixed route entries for (chip_x, chip_y) in routing_tables_by_partition.get_routers(): mc_entries = routing_tables_by_partition.get_entries_for_router( chip_x, chip_y) # only want the first entry, as that will all be the same. mc_entry = next(itervalues(mc_entries)) fixed_route_entry = FixedRouteEntry( link_ids=mc_entry.out_going_links, processor_ids=mc_entry.out_going_processors) x = (chip_x + eth_x) % (machine.max_chip_x + 1) y = (chip_y + eth_y) % (machine.max_chip_y + 1) key = (x, y) if key in fixed_route_tables: raise PacmanAlreadyExistsException( "fixed route entry", str(key)) fixed_route_tables[key] = fixed_route_entry
def _do_dynamic_routing( self, fixed_route_tables, placements, ethernet_connected_chip, destination_class, machine, board_version): """ Uses a router to route fixed routes :param fixed_route_tables: fixed route tables entry holder :param placements: placements :param ethernet_connected_chip: the chip to consider for this routing :param destination_class: the class at the Ethernet connected chip\ for receiving all these routes. :param machine: SpiNNMachine instance :param board_version: The version of the machine :rtype: None """ graph = MachineGraph(label="routing graph") fake_placements = Placements() # build fake setup for the routing eth_x = ethernet_connected_chip.x eth_y = ethernet_connected_chip.y down_links = set() for (chip_x, chip_y) in machine.get_chips_on_board( ethernet_connected_chip): vertex = RoutingMachineVertex() graph.add_vertex(vertex) rel_x = chip_x - eth_x if rel_x < 0: rel_x += machine.max_chip_x + 1 rel_y = chip_y - eth_y if rel_y < 0: rel_y += machine.max_chip_y + 1 free_processor = 0 while ((free_processor < machine.MAX_CORES_PER_CHIP) and fake_placements.is_processor_occupied( self.FAKE_ETHERNET_CHIP_X, y=self.FAKE_ETHERNET_CHIP_Y, p=free_processor)): free_processor += 1 fake_placements.add_placement(Placement( x=rel_x, y=rel_y, p=free_processor, vertex=vertex)) down_links.update({ (rel_x, rel_y, link) for link in range( Router.MAX_LINKS_PER_ROUTER) if not machine.is_link_at(chip_x, chip_y, link)}) # Create a fake machine consisting of only the one board that # the routes should go over fake_machine = machine if (board_version in machine.BOARD_VERSION_FOR_48_CHIPS and (machine.max_chip_x > machine.MAX_CHIP_X_ID_ON_ONE_BOARD or machine.max_chip_y > machine.MAX_CHIP_Y_ID_ON_ONE_BOARD)): down_chips = { (x, y) for x, y in zip( range(machine.SIZE_X_OF_ONE_BOARD), range(machine.SIZE_Y_OF_ONE_BOARD)) if not machine.is_chip_at( (x + eth_x) % (machine.max_chip_x + 1), (y + eth_y) % (machine.max_chip_y + 1))} # build a fake machine which is just one board but with the missing # bits of the real board fake_machine = VirtualMachine( machine.SIZE_X_OF_ONE_BOARD, machine.SIZE_Y_OF_ONE_BOARD, False, down_chips=down_chips, down_links=down_links) # build destination verts = graph.vertices vertex_dest = RoutingMachineVertex() graph.add_vertex(vertex_dest) destination_processor = self._locate_destination( ethernet_chip_x=ethernet_connected_chip.x, ethernet_chip_y=ethernet_connected_chip.y, destination_class=destination_class, placements=placements) fake_placements.add_placement(Placement( x=self.FAKE_ETHERNET_CHIP_X, y=self.FAKE_ETHERNET_CHIP_Y, p=destination_processor, vertex=vertex_dest)) # deal with edges for vertex in verts: graph.add_edge( MachineEdge(pre_vertex=vertex, post_vertex=vertex_dest), self.FAKE_ROUTING_PARTITION) # route as if using multicast router = BasicDijkstraRouting() routing_tables_by_partition = router( placements=fake_placements, machine=fake_machine, machine_graph=graph, use_progress_bar=False) # convert to fixed route entries for (chip_x, chip_y) in routing_tables_by_partition.get_routers(): mc_entries = routing_tables_by_partition.get_entries_for_router( chip_x, chip_y) # only want the first entry, as that will all be the same. mc_entry = next(itervalues(mc_entries)) fixed_route_entry = FixedRouteEntry( link_ids=mc_entry.link_ids, processor_ids=mc_entry.processor_ids) x = (chip_x + eth_x) % (machine.max_chip_x + 1) y = (chip_y + eth_y) % (machine.max_chip_y + 1) key = (x, y) if key in fixed_route_tables: raise PacmanAlreadyExistsException( "fixed route entry", str(key)) fixed_route_tables[key] = fixed_route_entry