def test_routing(self): graph = MachineGraph("Test") machine = VirtualMachine(2, 2) placements = Placements() vertices = list() for x in range(machine.max_chip_x + 1): for y in range(machine.max_chip_y + 1): chip = machine.get_chip_at(x, y) if chip is not None: for processor in chip.processors: if not processor.is_monitor: vertex = SimpleMachineVertex( resources=ResourceContainer()) graph.add_vertex(vertex) placements.add_placement( Placement(vertex, x, y, processor.processor_id)) vertices.append(vertex) for vertex in vertices: for vertex_to in vertices: if vertex != vertex_to: graph.add_edge(MachineEdge(vertex, vertex_to), "Test") router = BasicDijkstraRouting() routing_paths = router.__call__(placements, machine, graph) for vertex in vertices: vertices_reached = set() queue = deque() seen_entries = set() placement = placements.get_placement_of_vertex(vertex) partition = graph.get_outgoing_edge_partition_starting_at_vertex( vertex, "Test") entry = routing_paths.get_entry_on_coords_for_edge( partition, placement.x, placement.y) self.assertEqual(entry.incoming_processor, placement.p) queue.append((placement.x, placement.y)) while len(queue) > 0: x, y = queue.pop() entry = routing_paths.get_entry_on_coords_for_edge( partition, x, y) self.assertIsNotNone(entry) chip = machine.get_chip_at(x, y) for p in entry.out_going_processors: self.assertIsNotNone(chip.get_processor_with_id(p)) vertex_found = placements.get_vertex_on_processor(x, y, p) vertices_reached.add(vertex_found) seen_entries.add((x, y)) for link_id in entry.out_going_links: link = chip.router.get_link(link_id) self.assertIsNotNone(link) dest_x, dest_y = link.destination_x, link.destination_y if (dest_x, dest_y) not in seen_entries: queue.append((dest_x, dest_y)) for vertex_to in vertices: if vertex != vertex_to: self.assertIn(vertex_to, vertices_reached)
def test_routing(self): graph = MachineGraph("Test") machine = VirtualMachine(2, 2) placements = Placements() vertices = list() for x in range(machine.max_chip_x + 1): for y in range(machine.max_chip_y + 1): chip = machine.get_chip_at(x, y) if chip is not None: for processor in chip.processors: if not processor.is_monitor: vertex = SimpleMachineVertex( resources=ResourceContainer()) graph.add_vertex(vertex) placements.add_placement(Placement( vertex, x, y, processor.processor_id)) vertices.append(vertex) for vertex in vertices: for vertex_to in vertices: if vertex != vertex_to: graph.add_edge(MachineEdge(vertex, vertex_to), "Test") router = BasicDijkstraRouting() routing_paths = router.__call__(placements, machine, graph) for vertex in vertices: vertices_reached = set() queue = deque() seen_entries = set() placement = placements.get_placement_of_vertex(vertex) partition = graph.get_outgoing_edge_partition_starting_at_vertex( vertex, "Test") entry = routing_paths.get_entry_on_coords_for_edge( partition, placement.x, placement.y) self.assertEqual(entry.incoming_processor, placement.p) queue.append((placement.x, placement.y)) while len(queue) > 0: x, y = queue.pop() entry = routing_paths.get_entry_on_coords_for_edge( partition, x, y) self.assertIsNotNone(entry) chip = machine.get_chip_at(x, y) for p in entry.processor_ids: self.assertIsNotNone(chip.get_processor_with_id(p)) vertex_found = placements.get_vertex_on_processor(x, y, p) vertices_reached.add(vertex_found) seen_entries.add((x, y)) for link_id in entry.link_ids: link = chip.router.get_link(link_id) self.assertIsNotNone(link) dest_x, dest_y = link.destination_x, link.destination_y if (dest_x, dest_y) not in seen_entries: queue.append((dest_x, dest_y)) for vertex_to in vertices: if vertex != vertex_to: self.assertIn(vertex_to, vertices_reached)
def test_virtual_vertices_one_to_one(): """ Test that the placer works with a virtual vertex """ # Create a graph with a virtual vertex machine_graph = MachineGraph("Test") virtual_vertex = MachineSpiNNakerLinkVertex( spinnaker_link_id=0, label="Virtual") machine_graph.add_vertex(virtual_vertex) # These vertices are fixed on 0, 0 misc_vertices = list() for i in range(3): misc_vertex = SimpleMachineVertex( resources=ResourceContainer(), constraints=[ ChipAndCoreConstraint(0, 0)], label="Fixed_0_0_{}".format(i)) machine_graph.add_vertex(misc_vertex) misc_vertices.append(misc_vertex) # These vertices are 1-1 connected to the virtual vertex one_to_one_vertices = list() for i in range(16): one_to_one_vertex = SimpleMachineVertex( resources=ResourceContainer(), label="Vertex_{}".format(i)) machine_graph.add_vertex(one_to_one_vertex) edge = MachineEdge(virtual_vertex, one_to_one_vertex) machine_graph.add_edge(edge, "SPIKES") one_to_one_vertices.append(one_to_one_vertex) # Get and extend the machine for the virtual chip machine = VirtualMachine(version=5) extended_machine = MallocBasedChipIdAllocator()(machine, machine_graph) # Do placements placements = OneToOnePlacer()( machine_graph, extended_machine, plan_n_timesteps=1000) # The virtual vertex should be on a virtual chip placement = placements.get_placement_of_vertex(virtual_vertex) assert machine.get_chip_at(placement.x, placement.y).virtual # The 0, 0 vertices should be on 0, 0 for vertex in misc_vertices: placement = placements.get_placement_of_vertex(vertex) assert placement.x == placement.y == 0 # The other vertices should *not* be on a virtual chip for vertex in one_to_one_vertices: placement = placements.get_placement_of_vertex(vertex) assert not machine.get_chip_at(placement.x, placement.y).virtual
def __call__(self, width=None, height=None, virtual_has_wrap_arounds=False, version=None, n_cpus_per_chip=18, with_monitors=True): """ :param width: The width of the machine in chips :param height: The height of the machine in chips :param virtual_has_wrap_arounds: True if the machine is virtual and\ should be created with wrap_arounds :param version: The version of board to create :param n_cpus_per_chip: The number of cores to put on each chip :param with_monitors: If true, CPU 0 will be marked as a monitor :return: None """ machine = VirtualMachine(width=width, height=height, with_wrap_arounds=virtual_has_wrap_arounds, version=version, n_cpus_per_chip=n_cpus_per_chip, with_monitors=with_monitors) return {"machine": machine}
def test_partition_with_fixed_atom_constraints_at_limit(self): """ test a partitioning with a graph with fixed atom constraint which\ should fit but is close to the limit """ # Create a 2x2 machine with 1 core per chip (so 4 cores), # and 8MB SDRAM per chip n_cores_per_chip = 1 sdram_per_chip = 8 machine = VirtualMachine(width=2, height=2, with_monitors=False, n_cpus_per_chip=n_cores_per_chip, sdram_per_chip=sdram_per_chip) # Create a vertex which will need to be split perfectly into 4 cores # to work and which max atoms per core must be ignored vertex = SimpleTestVertex( sdram_per_chip * 2, max_atoms_per_core=sdram_per_chip, constraints=[FixedVertexAtomsConstraint(sdram_per_chip // 2)]) app_graph = ApplicationGraph("Test") app_graph.add_vertex(vertex) # Do the partitioning - this should just work partitioner = BasicPartitioner() machine_graph, _, _ = partitioner(app_graph, machine) self.assert_(len(machine_graph.vertices) == 4)
def test_partition_with_fixed_atom_constraints(self): """ test a partitioning with a graph with fixed atom constraint """ # Create a 2x2 machine with 10 cores per chip (so 40 cores), # but 1MB off 2MB per chip (so 19MB per chip) n_cores_per_chip = 10 sdram_per_chip = (n_cores_per_chip * 2) - 1 machine = VirtualMachine(width=2, height=2, with_monitors=False, n_cpus_per_chip=n_cores_per_chip, sdram_per_chip=sdram_per_chip) # Create a vertex where each atom requires 1MB (default) of SDRAM # but which can't be subdivided lower than 2 atoms per core. # The vertex has 1 atom per MB of SDRAM, and so would fit but will # be disallowed by the fixed atoms per core constraint vertex = SimpleTestVertex(sdram_per_chip * machine.n_chips, max_atoms_per_core=2, constraints=[FixedVertexAtomsConstraint(2)]) app_graph = ApplicationGraph("Test") app_graph.add_vertex(vertex) # Do the partitioning - this should result in an error with self.assertRaises(PacmanValueError): partitioner = BasicPartitioner() partitioner(app_graph, machine)
def test_create_constraints_to_file(tmpdir): # Construct the sample machine and graph machine = VirtualMachine(version=3, with_wrap_arounds=None) # TODO: define some extra monitor cores (how?) graph = MachineGraph("foo") tag1 = IPtagResource("1.2.3.4", 5, False, tag="footag") tag2 = ReverseIPtagResource(tag="bartag") v0 = SimpleMachineVertex(ResourceContainer(iptags=[tag1], reverse_iptags=[tag2]), constraints=[ChipAndCoreConstraint(1, 1, 3)]) graph.add_vertex(v0) v0_id = ident(v0) v1 = MachineSpiNNakerLinkVertex(2, constraints=[ChipAndCoreConstraint(1, 1)]) v1.set_virtual_chip_coordinates(0, 2) graph.add_vertex(v1) v1_id = ident(v1) algo = CreateConstraintsToFile() fn = tmpdir.join("foo.json") filename, mapping = algo(graph, machine, str(fn)) assert filename == str(fn) for vid in mapping: assert vid in [v0_id, v1_id] assert vid == ident(mapping[vid]) obj = json.loads(fn.read()) baseline = [{ "type": "reserve_resource", "location": None, "reservation": [0, 1], "resource": "cores" }, { "type": "location", "location": [1, 1], "vertex": v0_id }, { "type": "resource", "resource": "cores", "range": [3, 4], "vertex": v0_id }, { "type": "resource", "resource": "iptag", "range": [0, 1], "vertex": v0_id }, { "type": "resource", "resource": "reverse_iptag", "range": [0, 1], "vertex": v0_id }, { "type": "route_endpoint", "direction": "south", "vertex": v1_id }, { "type": "location", "location": [1, 0], "vertex": v1_id }] assert obj == baseline
def __call__(self, spalloc_server, spalloc_port=22244, spalloc_machine=None): with ProtocolClient(spalloc_server, spalloc_port) as client: machines = client.list_machines() # Close the context immediately; don't want to keep this particular # connection around as there's not a great chance of this code # being rerun in this process any time soon. max_width = None max_height = None max_area = -1 for machine in self._filter(machines, spalloc_machine): # Get the width and height in chips, and logical area in chips**2 width, height, area = self._get_size(machine) # The "biggest" board is the one with the most chips if area > max_area: max_area = area max_width = width max_height = height if max_width is None: raise Exception( "The spalloc server appears to have no compatible machines") # Return the width and height, and make no assumption about wrap- # arounds or version. return VirtualMachine(width=max_width, height=max_height, with_wrap_arounds=None, version=None)
def test_call(self): executor = HostExecuteDataSpecification() transceiver = _MockTransceiver(user_0_addresses={0: 1000}) machine = VirtualMachine(2, 2) # Write a data spec to execute temp_spec = mktemp() spec_writer = FileDataWriter(temp_spec) spec = DataSpecificationGenerator(spec_writer) spec.reserve_memory_region(0, 100) spec.reserve_memory_region(1, 100, empty=True) spec.reserve_memory_region(2, 100) spec.switch_write_focus(0) spec.write_value(0) spec.write_value(1) spec.write_value(2) spec.switch_write_focus(2) spec.write_value(3) spec.end_specification() # Execute the spec dsg_targets = {(0, 0, 0): temp_spec} executor.__call__(transceiver, machine, 30, dsg_targets) # Test regions - although 3 are created, only 2 should be uploaded # (0 and 2), and only the data written should be uploaded # The space between regions should be as allocated regardless of # how much data is written header_and_table_size = (constants.MAX_MEM_REGIONS + 2) * 4 regions = transceiver.regions_written self.assertEqual(len(regions), 4) # Base address for header and table self.assertEqual(regions[0][0], 0) # Base address for region 0 (after header and table) self.assertEqual(regions[1][0], header_and_table_size) # Base address for region 2 self.assertEqual(regions[2][0], header_and_table_size + 200) # User 0 write address self.assertEqual(regions[3][0], 1000) # Size of header and table self.assertEqual(len(regions[0][1]), header_and_table_size) # Size of region 0 self.assertEqual(len(regions[1][1]), 12) # Size of region 2 self.assertEqual(len(regions[2][1]), 4) # Size of user 0 self.assertEqual(len(regions[3][1]), 4)
def __call__(self, hbp_server_url, total_run_time): """ :param hbp_server_url: \ The URL of the HBP server from which to get the machine :param total_run_time: The total run time to request """ max_machine = self._max_machine_request(hbp_server_url, total_run_time) # Return the width and height and assume that it has wrap arounds return VirtualMachine( width=max_machine["width"], height=max_machine["height"], with_wrap_arounds=None, version=None)
def test_convert_to_file_machine(tmpdir): # Construct the sample machine machine = VirtualMachine(version=3, with_wrap_arounds=None) # Convert it to JSON algo = ConvertToFileMachine() fn = tmpdir.join("foo.json") filename = algo(machine, str(fn)) assert filename == str(fn) def fix_cre(obj): obj = dict(obj) if "chip_resource_exceptions" in obj: cre = list(obj["chip_resource_exceptions"]) cre.sort(key=lambda e: (e[0], e[1])) obj["chip_resource_exceptions"] = cre return obj # Rebuild and compare; simplest way of checking given that order is not # preserved in the underlying string and altering that is hard obj = json.loads(fn.read()) baseline = { "chip_resource_exceptions": [[0, 1, { "cores": 17 }], [1, 0, { "cores": 17 }], [0, 0, { "cores": 17, "tags": 7 }], [1, 1, { "cores": 17 }]], "chip_resources": { "cores": 18, "router_entries": 1024, "sdram": 119275520, "sram": 24320, "tags": 0 }, "dead_chips": [], "dead_links": [[0, 0, "west"], [0, 0, "south_west"], [0, 1, "west"], [0, 1, "south_west"], [1, 0, "east"], [1, 0, "north_east"], [1, 1, "east"], [1, 1, "north_east"]], "height": 2, "width": 2 } assert fix_cre(obj) == fix_cre(baseline)
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