def test_get_net_keyspaces(): """Test the correct specification of keyspaces for nets.""" # Create the vertices vertex_A = [Vertex() for _ in range(4)] vertex_B = Vertex() # Create placements such that vertex A and B fall on two chips and A[0] and # A[1] are on the same chip. placements = { vertex_A[0]: (0, 0), vertex_A[1]: (0, 0), vertex_A[2]: (0, 1), vertex_A[3]: (0, 1), vertex_B: (0, 0) } resources = {v: {} for v in placements} allocations = {v: {} for v in placements} # Create a container for the keyspaces ksc = KeyspaceContainer() # Create the nets nets = [ NMNet(vertex_A, vertex_B, 1.0, ksc["nengo"](connection_id=0)), NMNet(vertex_B, vertex_A, 2.0, ksc["nengo"](connection_id=1)), NMNet(vertex_A, vertex_A, 3.0, ksc["spam"]), ] # Identify groups groups = [set(vertex_A)] # Identify clusters utils.identify_clusters(groups, placements) assert vertex_B.cluster is None # Not clustered # Get the routing nets _, _, _, _, derived_nets = utils.get_nets_for_routing( resources, nets, placements, allocations) # Get the net keyspaces net_keyspaces = utils.get_net_keyspaces(placements, derived_nets) # Check the net keyspaces are correct # A -> B for xy, vertex in [((0, 0), vertex_A[0]), ((0, 1), vertex_A[2])]: net = derived_nets[nets[0]][xy] cluster = vertex.cluster assert net_keyspaces[net] == nets[0].keyspace(cluster=cluster) # B -> A net = derived_nets[nets[1]][(0, 0)] assert net_keyspaces[net] == nets[1].keyspace(cluster=0) # A -> A for xy in [(0, 0), (0, 1)]: net = derived_nets[nets[2]][xy] assert net_keyspaces[net] == nets[2].keyspace # No change
def test_get_net_keyspaces_fails_for_inconsistent_cluster(): """Test specification of keyspaces for nets fails in the case that inconsistent cluster IDs are assigned (this is unlikely to happen unless a Net somehow ends up having two different Nengo objects in its source list).""" # Create the vertices vertex_A = [Vertex() for _ in range(4)] vertex_B = Vertex() # Create placements such that vertex A and B fall on two chips and A[0] and # A[1] are on the same chip. placements = { vertex_A[0]: (0, 0), vertex_A[1]: (0, 0), vertex_A[2]: (0, 1), vertex_A[3]: (0, 1), vertex_B: (0, 0) } resources = {v: {} for v in placements} allocations = {v: {} for v in placements} # Create a container for the keyspaces ksc = KeyspaceContainer() # Create the signals and nets signal_a = Signal( object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=0)) ) signal_b = Signal( object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=1)) ) signal_c = Signal(object(), [], SignalParameters(keyspace=ksc["spam"])) nets = { signal_a: NMNet(vertex_A, vertex_B, 1.0), signal_b: NMNet(vertex_B, vertex_A, 2.0), signal_c: NMNet(vertex_A, vertex_A, 3.0), } # Identify groups groups = [set(vertex_A)] # Manually identify clusters (and do it such that it is inconsistent) vertex_A[0].cluster = 0 vertex_A[1].cluster = 1 vertex_A[2].cluster = 2 vertex_A[3].cluster = 3 # Get the routing nets _, _, _, _, derived_nets = utils.get_nets_for_routing( resources, nets, placements, allocations) # Get the net keyspaces with pytest.raises(AssertionError): utils.get_net_keyspaces(placements, nets, derived_nets)
def test_get_net_keyspaces_fails_for_inconsistent_cluster(): """Test specification of keyspaces for nets fails in the case that inconsistent cluster IDs are assigned (this is unlikely to happen unless a Net somehow ends up having two different Nengo objects in its source list).""" # Create the vertices vertex_A = [Vertex() for _ in range(4)] vertex_B = Vertex() # Create placements such that vertex A and B fall on two chips and A[0] and # A[1] are on the same chip. placements = { vertex_A[0]: (0, 0), vertex_A[1]: (0, 0), vertex_A[2]: (0, 1), vertex_A[3]: (0, 1), vertex_B: (0, 0) } resources = {v: {} for v in placements} allocations = {v: {} for v in placements} # Create a container for the keyspaces ksc = KeyspaceContainer() # Create the signals and nets signal_a = Signal(object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=0))) signal_b = Signal(object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=1))) signal_c = Signal(object(), [], SignalParameters(keyspace=ksc["spam"])) nets = { signal_a: NMNet(vertex_A, vertex_B, 1.0), signal_b: NMNet(vertex_B, vertex_A, 2.0), signal_c: NMNet(vertex_A, vertex_A, 3.0), } # Identify groups groups = [set(vertex_A)] # Manually identify clusters (and do it such that it is inconsistent) vertex_A[0].cluster = 0 vertex_A[1].cluster = 1 vertex_A[2].cluster = 2 vertex_A[3].cluster = 3 # Get the routing nets _, _, _, _, derived_nets = utils.get_nets_for_routing( resources, nets, placements, allocations) # Get the net keyspaces with pytest.raises(AssertionError): utils.get_net_keyspaces(placements, nets, derived_nets)
def place_and_route(self, system_info, place=place_and_route.place, place_kwargs={}, allocate=place_and_route.allocate, allocate_kwargs={}, route=place_and_route.route, route_kwargs={}): """Place and route the netlist onto the given SpiNNaker machine. Parameters ---------- system_info : \ :py:class:`~rig.machine_control.MachineController.SystemInfo` Describes the system onto which the netlist should be placed and routed. Other Parameters ---------------- place : function Placement function. Must support the interface defined by Rig. place_kwargs : dict Keyword arguments for the placement method. allocate : function Resource allocation function. Must support the interface defined by Rig. allocate_kwargs : dict Keyword arguments for the allocation function. route : function Router function. Must support the interface defined by Rig. route_kwargs : dict Keyword arguments for the router function. """ # Generate a Machine and set of core-reserving constraints to prevent # the use of non-idle cores. machine = build_machine(system_info) core_constraints = build_core_constraints(system_info) constraints = self.constraints + core_constraints # Build a map of vertices to the resources they require, get a list of # constraints. vertices_resources = {v: v.resources for v in self.vertices} # Perform placement and allocation place_nets = list(utils.get_nets_for_placement(self.nets)) self.placements = place(vertices_resources, place_nets, machine, constraints, **place_kwargs) self.allocations = allocate(vertices_resources, place_nets, machine, constraints, self.placements, **allocate_kwargs) # Identify clusters and modify vertices appropriately utils.identify_clusters(self.groups, self.placements) # Get the nets for routing (route_nets, vertices_resources, # Can safely overwrite the resource dictionary extended_placements, extended_allocations, derived_nets) = utils.get_nets_for_routing( vertices_resources, self.nets, self.placements, self.allocations) # Get a map from the nets we will route with to keyspaces self.net_keyspaces = utils.get_net_keyspaces(self.placements, derived_nets) # Fix all keyspaces self.keyspaces.assign_fields() # Finally, route all nets using the extended resource dictionary, # placements and allocations. self.routes = route(vertices_resources, route_nets, machine, constraints, extended_placements, extended_allocations, **route_kwargs)
def test_get_nets_for_routing(): """Test that Rig nets can be generated to be used during the routing.""" # Create the vertices a = object() b = object() c = object() d = object() e = object() # Create the nets ab_cd = NMNet([a, b], [c, d], 1.0) cd_e = NMNet([c, d], e, 2.0) # Create some signals signal_a = object() signal_b = object() nets = {signal_a: ab_cd, signal_b: cd_e} # Create some placements: # - a and b placed on the same chip # - c and d placed on different chips placements = {a: (0, 0), b: (0, 0), c: (1, 0), d: (0, 1), e: (1, 1)} # Create some resource requirements vertices_resources = {a: {Cores: 1}, b: {Cores: 2}, c: {Cores: 3}, d: {Cores: 4}, e: {Cores: 5}} # And create some sample allocations allocations = {a: {Cores: slice(1, 2)}, b: {Cores: slice(2, 4)}, c: {Cores: slice(1, 4)}, d: {Cores: slice(1, 5)}, e: {Cores: slice(1, 6)}} # Get the routing nets (routing_nets, extended_resources, extended_placements, extended_allocations, derived_nets) = utils.get_nets_for_routing( vertices_resources, nets, placements, allocations) # Check that the routing nets are sane assert len(routing_nets) == 3 seen_cd_placements = set() expected_cd_placements = {placements[c], placements[d]} for net in routing_nets: # Check the sources is in the extended allocations and resources. assert extended_resources[net.source] == dict() assert extended_allocations[net.source] == dict() if net.sinks == [c, d]: assert net.weight == ab_cd.weight # Source should have been a and b, check the extended placement is # correct assert net.source not in placements assert extended_placements[net.source] == placements[a] assert extended_placements[net.source] == placements[b] # Check that the net is correctly identified in the derived nets # mapping. assert derived_nets[ab_cd][placements[a]] is net else: assert net.sinks == [e] assert net.weight == cd_e.weight # Source should have been one of c or d, check that the placement # is appropriate. assert net.source not in placements placement = extended_placements[net.source] assert placement in expected_cd_placements assert placement not in seen_cd_placements seen_cd_placements.add(placement) # Check that the net is correctly identified in the derived nets # mapping. assert derived_nets[cd_e][placement] is net assert seen_cd_placements == expected_cd_placements # Check that the original vertices are still present in the extended # placements. for v in [a, b, c, d, e]: assert extended_placements[v] == placements[v] assert extended_resources[v] == vertices_resources[v] assert extended_allocations[v] == allocations[v]
def test_get_net_keyspaces(): """Test the correct specification of keyspaces for nets.""" # Create the vertices vertex_A = [Vertex() for _ in range(4)] vertex_B = Vertex() # Create placements such that vertex A and B fall on two chips and A[0] and # A[1] are on the same chip. placements = { vertex_A[0]: (0, 0), vertex_A[1]: (0, 0), vertex_A[2]: (0, 1), vertex_A[3]: (0, 1), vertex_B: (0, 0) } resources = {v: {} for v in placements} allocations = {v: {} for v in placements} # Manually assign cluster IDs vertex_A[0].cluster = 0 vertex_A[1].cluster = 0 vertex_A[2].cluster = 1 vertex_A[3].cluster = 1 # Create a container for the keyspaces ksc = KeyspaceContainer() # Create the signals and nets signal_a = Signal( object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=0)) ) signal_b = Signal( object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=1)) ) signal_c = Signal(object(), [], SignalParameters(keyspace=ksc["spam"])) nets = { signal_a: NMNet(vertex_A, vertex_B, 1.0), signal_b: NMNet(vertex_B, vertex_A, 2.0), signal_c: NMNet(vertex_A, vertex_A, 3.0), } # Get the routing nets _, _, _, _, derived_nets = utils.get_nets_for_routing( resources, nets, placements, allocations) # Get the net keyspaces net_keyspaces = utils.get_net_keyspaces(placements, nets, derived_nets) # Check the net keyspaces are correct # A -> B for xy, vertex in [((0, 0), vertex_A[0]), ((0, 1), vertex_A[2])]: net = derived_nets[nets[signal_a]][xy] cluster = vertex.cluster assert net_keyspaces[net] == signal_a.keyspace(cluster=cluster) # B -> A net = derived_nets[nets[signal_b]][(0, 0)] assert net_keyspaces[net] == signal_b.keyspace(cluster=0) # A -> A for xy in [(0, 0), (0, 1)]: net = derived_nets[nets[signal_c]][xy] assert net_keyspaces[net] == signal_c.keyspace # No change
def place_and_route(self, system_info, place=place_and_route.place, place_kwargs={}, allocate=place_and_route.allocate, allocate_kwargs={}, route=place_and_route.route, route_kwargs={}): """Place and route the netlist onto the given SpiNNaker machine. Parameters ---------- system_info : \ :py:class:`~rig.machine_control.MachineController.SystemInfo` Describes the system onto which the netlist should be placed and routed. Other Parameters ---------------- place : function Placement function. Must support the interface defined by Rig. place_kwargs : dict Keyword arguments for the placement method. allocate : function Resource allocation function. Must support the interface defined by Rig. allocate_kwargs : dict Keyword arguments for the allocation function. route : function Router function. Must support the interface defined by Rig. route_kwargs : dict Keyword arguments for the router function. """ # Generate a Machine and set of core-reserving constraints to prevent # the use of non-idle cores. machine = build_machine(system_info) core_constraints = build_core_constraints(system_info) constraints = self.constraints + core_constraints # Build a map of vertices to the resources they require, get a list of # constraints. vertices_resources = {v: v.resources for v in self.vertices} # Perform placement and allocation place_nets = list(utils.get_nets_for_placement(itervalues(self.nets))) self.placements = place(vertices_resources, place_nets, machine, constraints, **place_kwargs) self.allocations = allocate(vertices_resources, place_nets, machine, constraints, self.placements, **allocate_kwargs) # Get the nets for routing ( route_nets, vertices_resources, # Can safely overwrite the resource dictionary extended_placements, extended_allocations, derived_nets) = utils.get_nets_for_routing(vertices_resources, self.nets, self.placements, self.allocations) # Finally, route all nets using the extended resource dictionary, # placements and allocations. self.routes = route(vertices_resources, route_nets, machine, constraints, extended_placements, extended_allocations, **route_kwargs) # Assign keyspaces based on the placement signal_routes = collections.defaultdict(collections.deque) for signal, nmnet in iteritems(self.nets): for net in itervalues(derived_nets[nmnet]): signal_routes[signal].append(self.routes[net]) key_allocation.allocate_signal_keyspaces(signal_routes, self.signal_id_constraints, self.keyspaces) # Assign cluster IDs based on the placement and the routing key_allocation.assign_cluster_ids(self.operator_vertices, signal_routes, self.placements) # Get a map from the nets we will route with to keyspaces self.net_keyspaces = utils.get_net_keyspaces(self.placements, self.nets, derived_nets) # Fix all keyspaces self.keyspaces.assign_fields()
def test_get_nets_for_routing(): """Test that Rig nets can be generated to be used during the routing.""" # Create the vertices a = object() b = object() c = object() d = object() e = object() # Create the nets ab_cd = NMNet([a, b], [c, d], 1.0) cd_e = NMNet([c, d], e, 2.0) # Create some signals signal_a = object() signal_b = object() nets = {signal_a: ab_cd, signal_b: cd_e} # Create some placements: # - a and b placed on the same chip # - c and d placed on different chips placements = {a: (0, 0), b: (0, 0), c: (1, 0), d: (0, 1), e: (1, 1)} # Create some resource requirements vertices_resources = { a: { Cores: 1 }, b: { Cores: 2 }, c: { Cores: 3 }, d: { Cores: 4 }, e: { Cores: 5 } } # And create some sample allocations allocations = { a: { Cores: slice(1, 2) }, b: { Cores: slice(2, 4) }, c: { Cores: slice(1, 4) }, d: { Cores: slice(1, 5) }, e: { Cores: slice(1, 6) } } # Get the routing nets (routing_nets, extended_resources, extended_placements, extended_allocations, derived_nets) = utils.get_nets_for_routing(vertices_resources, nets, placements, allocations) # Check that the routing nets are sane assert len(routing_nets) == 3 seen_cd_placements = set() expected_cd_placements = {placements[c], placements[d]} for net in routing_nets: # Check the sources is in the extended allocations and resources. assert extended_resources[net.source] == dict() assert extended_allocations[net.source] == dict() if net.sinks == [c, d]: assert net.weight == ab_cd.weight # Source should have been a and b, check the extended placement is # correct assert net.source not in placements assert extended_placements[net.source] == placements[a] assert extended_placements[net.source] == placements[b] # Check that the net is correctly identified in the derived nets # mapping. assert derived_nets[ab_cd][placements[a]] is net else: assert net.sinks == [e] assert net.weight == cd_e.weight # Source should have been one of c or d, check that the placement # is appropriate. assert net.source not in placements placement = extended_placements[net.source] assert placement in expected_cd_placements assert placement not in seen_cd_placements seen_cd_placements.add(placement) # Check that the net is correctly identified in the derived nets # mapping. assert derived_nets[cd_e][placement] is net assert seen_cd_placements == expected_cd_placements # Check that the original vertices are still present in the extended # placements. for v in [a, b, c, d, e]: assert extended_placements[v] == placements[v] assert extended_resources[v] == vertices_resources[v] assert extended_allocations[v] == allocations[v]
def test_get_net_keyspaces(): """Test the correct specification of keyspaces for nets.""" # Create the vertices vertex_A = [Vertex() for _ in range(4)] vertex_B = Vertex() # Create placements such that vertex A and B fall on two chips and A[0] and # A[1] are on the same chip. placements = { vertex_A[0]: (0, 0), vertex_A[1]: (0, 0), vertex_A[2]: (0, 1), vertex_A[3]: (0, 1), vertex_B: (0, 0) } resources = {v: {} for v in placements} allocations = {v: {} for v in placements} # Manually assign cluster IDs vertex_A[0].cluster = 0 vertex_A[1].cluster = 0 vertex_A[2].cluster = 1 vertex_A[3].cluster = 1 # Create a container for the keyspaces ksc = KeyspaceContainer() # Create the signals and nets signal_a = Signal(object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=0))) signal_b = Signal(object(), [], SignalParameters(keyspace=ksc["nengo"](connection_id=1))) signal_c = Signal(object(), [], SignalParameters(keyspace=ksc["spam"])) nets = { signal_a: NMNet(vertex_A, vertex_B, 1.0), signal_b: NMNet(vertex_B, vertex_A, 2.0), signal_c: NMNet(vertex_A, vertex_A, 3.0), } # Get the routing nets _, _, _, _, derived_nets = utils.get_nets_for_routing( resources, nets, placements, allocations) # Get the net keyspaces net_keyspaces = utils.get_net_keyspaces(placements, nets, derived_nets) # Check the net keyspaces are correct # A -> B for xy, vertex in [((0, 0), vertex_A[0]), ((0, 1), vertex_A[2])]: net = derived_nets[nets[signal_a]][xy] cluster = vertex.cluster assert net_keyspaces[net] == signal_a.keyspace(cluster=cluster) # B -> A net = derived_nets[nets[signal_b]][(0, 0)] assert net_keyspaces[net] == signal_b.keyspace(cluster=0) # A -> A for xy in [(0, 0), (0, 1)]: net = derived_nets[nets[signal_c]][xy] assert net_keyspaces[net] == signal_c.keyspace # No change