def make_vertices(self, model, *args, **kwargs): """Create vertices that will simulate the SDPTransmitter.""" # Build the system region self._sys_region = SystemRegion(model.machine_timestep, self.size_in, 1) # Build the filter regions in_sigs = model.get_signals_connections_to_object(self) self._filter_region, self._routing_region = make_filter_regions( in_sigs[InputPort.standard], model.dt, True, model.keyspaces.filter_routing_tag ) # Get the resources resources = { Cores: 1, SDRAM: region_utils.sizeof_regions( [self._sys_region, self._filter_region, self._routing_region], None ) } # Create the vertex self._vertex = Vertex(get_application("tx"), resources) # Return the netlist specification return netlistspec(self._vertex, load_function=self.load_to_machine)
def make_vertices(self, model, *args, **kwargs): """Create vertices that will simulate the SDPTransmitter.""" # Build the system region self._sys_region = SystemRegion(model.machine_timestep, self.size_in, 1) # Build the filter regions in_sigs = model.get_signals_to_object(self)[InputPort.standard] self._filter_region, self._routing_region = make_filter_regions( in_sigs, model.dt, True, model.keyspaces.filter_routing_tag) # Get the resources resources = { Cores: 1, SDRAM: region_utils.sizeof_regions( [self._sys_region, self._filter_region, self._routing_region], None ) } # Create the vertex self._vertex = Vertex(get_application("tx"), resources) # Return the netlist specification return netlistspec((self._vertex, ), # Tuple is required load_function=self.load_to_machine)
def make_vertices(self, model, n_steps): # TODO remove n_steps """Construct the data which can be loaded into the memory of a SpiNNaker machine. """ # Extract all the filters from the incoming connections to build the # filter regions. signals_conns = model.get_signals_to_object(self)[InputPort.standard] self.filter_region, self.filter_routing_region = make_filter_regions( signals_conns, model.dt, True, model.keyspaces.filter_routing_tag) # Use a matrix region to record into (slightly unpleasant) self.recording_region = regions.MatrixRegion( np.zeros((self.size_in, n_steps), dtype=np.uint32) ) # This isn't partitioned, so we just compute the SDRAM requirement and # return a new vertex. self.system_region = SystemRegion(model.machine_timestep, self.size_in) self.regions = [None] * 15 self.regions[0] = self.system_region self.regions[1] = self.filter_region self.regions[2] = self.filter_routing_region self.regions[14] = self.recording_region # **YUCK** resources = { Cores: 1, SDRAM: regions.utils.sizeof_regions(self.regions, None) } self.vertex = Vertex(get_application("value_sink"), resources) # Return the spec return netlistspec(self.vertex, self.load_to_machine, after_simulation_function=self.after_simulation)
def __init__(self, timestep, n_steps, input_slice, filter_region, filter_routing_region): """Create a new vertex for a portion of a value sink.""" self.input_slice = input_slice # Store the pre-existing regions and create new regions self.regions = { Regions.system: SystemRegion(timestep, input_slice), Regions.filters: filter_region, Regions.filter_routing: filter_routing_region, Regions.recording: regions.WordRecordingRegion(n_steps), } # Store region arguments w = input_slice.stop - input_slice.start self.region_arguments = { Regions.system: Args(), Regions.filters: Args(filter_width=w), Regions.filter_routing: Args(), Regions.recording: Args(input_slice), } # Determine resources usage resources = { Cores: 1, SDRAM: sizeof_regions_named(self.regions, self.region_arguments) } super(ValueSinkVertex, self).__init__(get_application("value_sink"), resources)
def make_vertices(self, model, n_steps): """Create the vertices to be simulated on the machine.""" # Create the system region self.system_region = SystemRegion(model.machine_timestep, self.period is not None, n_steps) # Get all the outgoing signals to determine how big the size out is and # to build a list of keys. sigs_conns = model.get_signals_connections_from_object(self) if len(sigs_conns) == 0: return netlistspec([]) keys = list() self.conns_transforms = list() for sig, conns in iteritems(sigs_conns[OutputPort.standard]): assert len(conns) == 1, "Expected a 1:1 mapping" # Add the keys for this connection conn = conns[0] transform, sig_keys = get_transform_keys(model, sig, conn) keys.extend(sig_keys) self.conns_transforms.append((conn, transform)) size_out = len(keys) # Build the keys region self.keys_region = regions.KeyspacesRegion( keys, [regions.KeyField({"cluster": "cluster"})], partitioned_by_atom=True ) # Create the output region self.output_region = regions.MatrixRegion( np.zeros((n_steps, size_out)), sliced_dimension=regions.MatrixPartitioning.columns ) self.regions = [self.system_region, self.keys_region, self.output_region] # Partition by output dimension to create vertices transmit_constraint = partition.Constraint(10) sdram_constraint = partition.Constraint(8*2**20) # Max 8MiB constraints = { transmit_constraint: lambda s: s.stop - s.start, sdram_constraint: lambda s: regions.utils.sizeof_regions(self.regions, s), } for sl in partition.partition(slice(0, size_out), constraints): # Determine the resources resources = { Cores: 1, SDRAM: regions.utils.sizeof_regions(self.regions, sl), } vsl = VertexSlice(sl, get_application("value_source"), resources) self.vertices.append(vsl) # Return the vertices and callback methods return netlistspec(self.vertices, self.load_to_machine, self.before_simulation)
def __init__(self, vertex_index, cluster_slices, input_slice, output_slice, ens_regions): """Create a new slice of an Ensemble. Parameters ---------- vertex_index : int Index of this vertex within the cluster. cluster_slices : [slice, ...] List of slices input_slice : slice Slice of the input space to be managed by this instance. output_slice : slice Slice of the output space to be managed by this instance. """ # Store the parameters self.input_slice = input_slice self.output_slice = output_slice self.regions = ens_regions # Get the specific neural slice we care about and information regarding # the rest of the vertices in this cluster. self.vertex_index = vertex_index self.neuron_slice = cluster_slices[vertex_index] self.n_vertices_in_cluster = len(cluster_slices) self.n_neurons_in_cluster = (cluster_slices[-1].stop - cluster_slices[0].start) # Get the basic arguments for the regions that we'll be storing self.region_arguments = _get_basic_region_arguments( self.neuron_slice, self.output_slice, cluster_slices) # Add some other arguments for the ensemble region self.region_arguments[EnsembleRegions.ensemble].kwargs.update({ "population_id": vertex_index, "input_slice": input_slice, "neuron_slice": self.neuron_slice, "output_slice": output_slice, }) # Compute the SDRAM usage sdram_usage = regions.utils.sizeof_regions_named( self.regions, self.region_arguments) # Prepare the vertex application = "ensemble" if ens_regions[EnsembleRegions.profiler].n_samples > 0: # If profiling then use the profiled version of the application application += "_profiled" super(EnsembleSlice, self).__init__(get_application(application), { Cores: 1, SDRAM: sdram_usage })
def make_vertices(self, model, n_steps): """Create the vertices to be simulated on the machine.""" # Create the system region self.system_region = SystemRegion(model.machine_timestep, self.period is not None, n_steps) # Get all the outgoing signals to determine how big the size out is and # to build a list of keys. sigs_conns = model.get_signals_from_object(self) if len(sigs_conns) == 0: return netlistspec([]) keys = list() self.transmission_parameters = list() for sig, transmission_params in sigs_conns[OutputPort.standard]: # Add the keys for this connection transform, sig_keys = get_transform_keys(sig, transmission_params) keys.extend(sig_keys) self.transmission_parameters.append((transmission_params, transform)) size_out = len(keys) # Build the keys region self.keys_region = regions.KeyspacesRegion( keys, [regions.KeyField({"cluster": "cluster"})], partitioned_by_atom=True ) # Create the output region self.output_region = regions.MatrixRegion( np.zeros((n_steps, size_out)), sliced_dimension=regions.MatrixPartitioning.columns ) self.regions = [self.system_region, self.keys_region, self.output_region] # Partition by output dimension to create vertices transmit_constraint = partition.Constraint(10) sdram_constraint = partition.Constraint(8*2**20) # Max 8MiB constraints = { transmit_constraint: lambda s: s.stop - s.start, sdram_constraint: ( lambda s: regions.utils.sizeof_regions(self.regions, s)), } for sl in partition.partition(slice(0, size_out), constraints): # Determine the resources resources = { Cores: 1, SDRAM: regions.utils.sizeof_regions(self.regions, sl), } vsl = VertexSlice(sl, self._label, get_application("value_source"), resources) self.vertices.append(vsl) # Return the vertices and callback methods return netlistspec(self.vertices, self.load_to_machine, self.before_simulation)
def test_get_application(app_name): with mock.patch.object(application, "pkg_resources") as pkg_resources: pkg_resources.resource_filename.return_value = "Camelot" # Get the application filename assert application.get_application(app_name) == "Camelot" pkg_resources.resource_filename.assert_called_once_with( "nengo_spinnaker", "binaries/nengo_{}.aplx".format(app_name))
def test_get_application(app_name): with mock.patch.object(application, "pkg_resources") as pkg_resources: pkg_resources.resource_filename.return_value = "Camelot" # Get the application filename assert application.get_application(app_name) == "Camelot" pkg_resources.resource_filename.assert_called_once_with( "nengo_spinnaker", "binaries/nengo_{}.aplx".format(app_name) )
def __init__(self, vertex_index, cluster_slices, input_slice, output_slice, ens_regions): """Create a new slice of an Ensemble. Parameters ---------- vertex_index : int Index of this vertex within the cluster. cluster_slices : [slice, ...] List of slices input_slice : slice Slice of the input space to be managed by this instance. output_slice : slice Slice of the output space to be managed by this instance. """ # Store the parameters self.input_slice = input_slice self.output_slice = output_slice self.regions = ens_regions # Get the specific neural slice we care about and information regarding # the rest of the vertices in this cluster. self.vertex_index = vertex_index self.neuron_slice = cluster_slices[vertex_index] self.n_vertices_in_cluster = len(cluster_slices) self.n_neurons_in_cluster = (cluster_slices[-1].stop - cluster_slices[0].start) # Get the basic arguments for the regions that we'll be storing self.region_arguments = _get_basic_region_arguments( self.neuron_slice, self.output_slice, cluster_slices ) # Add some other arguments for the ensemble region self.region_arguments[EnsembleRegions.ensemble].kwargs.update({ "population_id": vertex_index, "input_slice": input_slice, "neuron_slice": self.neuron_slice, "output_slice": output_slice, }) # Compute the SDRAM usage sdram_usage = regions.utils.sizeof_regions_named( self.regions, self.region_arguments) # Prepare the vertex application = "ensemble" if ens_regions[EnsembleRegions.profiler].n_samples > 0: # If profiling then use the profiled version of the application application += "_profiled" super(EnsembleSlice, self).__init__(get_application(application), {Cores: 1, SDRAM: sdram_usage})
def make_vertices(self, model, *args, **kwargs): """Create vertices that will simulate the SDPReceiver.""" # NOTE This approach will result in more routes being created than are # actually necessary; the way to avoid this is to modify how the # builder deals with signals when creating netlists. # Get all outgoing signals and their associated connections (this # SHOULD be a 1:1 mapping) out = model.get_signals_connections_from_object(self) for signal, connections in six.iteritems(out[OutputPort.standard]): assert len(connections) == 1, "Expecting a 1:1 mapping" conn = connections[0] # Get the transform, and from this the keys transform = model.params[conn].transform keys = [signal.keyspace(index=i) for i in range(transform.shape[0])] # Create a vertex for this connection (assuming its size out <= 64) if len(keys) > 64: raise NotImplementedError( "Connection {!s} is too wide to transmit to SpiNNaker. " "Consider breaking the connection up or making the " "originating node a function of time Node.".format(conn) ) # Create the regions for the system sys_region = SystemRegion(model.machine_timestep, len(keys)) keys_region = KeyspacesRegion(keys, [KeyField({"cluster": "cluster"})]) # Get the resources resources = { Cores: 1, SDRAM: region_utils.sizeof_regions([sys_region, keys_region], None) } # Create the vertex v = self.connection_vertices[conn] = Vertex(get_application("rx"), resources) self._sys_regions[v] = sys_region self._key_regions[v] = keys_region # Return the netlist specification return netlistspec(list(self.connection_vertices.values()), load_function=self.load_to_machine)
def __init__(self, in_slice, out_slice, resources=dict(), transmission_parameter_slices=list()): super(ParallelFilterSlice, self).__init__(get_application("filter"), resources) # Store the slices self.in_slice = in_slice self.out_slice = out_slice # Store which signal parameter slices we contain self.transmission_params = list() out_set = set(range(out_slice.start or 0, out_slice.stop or 0, out_slice.step or 1)) for transmission_params, outs in transmission_parameter_slices: # If there is an intersection between the outs and the set of outs # we're responsible for then store transmission parameters. if out_set & outs: self.transmission_params.append(transmission_params)
def make_vertices(self, model, *args, **kwargs): """Create vertices that will simulate the SDPReceiver.""" # NOTE This approach will result in more routes being created than are # actually necessary; the way to avoid this is to modify how the # builder deals with signals when creating netlists. # Get all outgoing signals and their associated transmission parameters for signal, transmission_params in \ model.get_signals_from_object(self)[OutputPort.standard]: # Get the transform, and from this the keys transform = transmission_params.full_transform(slice_out=False) keys = [(signal, {"index": i}) for i in range(transform.shape[0])] # Create a vertex for this connection (assuming its size out <= 64) if len(keys) > 64: raise NotImplementedError( "Connection is too wide to transmit to SpiNNaker. " "Consider breaking the connection up or making the " "originating node a function of time Node." ) # Create the regions for the system sys_region = SystemRegion(model.machine_timestep, len(keys)) keys_region = KeyspacesRegion(keys, [KeyField({"cluster": "cluster"})]) # Get the resources resources = { Cores: 1, SDRAM: region_utils.sizeof_regions([sys_region, keys_region], None) } # Create the vertex v = self.connection_vertices[transmission_params] = \ Vertex(self._label, get_application("rx"), resources) self._sys_regions[v] = sys_region self._key_regions[v] = keys_region # Return the netlist specification return netlistspec(list(self.connection_vertices.values()), load_function=self.load_to_machine)
def __init__(self, in_slice, out_slice, resources=dict(), transmission_parameter_slices=list()): super(ParallelFilterSlice, self).__init__(get_application("filter"), resources) # Store the slices self.in_slice = in_slice self.out_slice = out_slice # Store which signal parameter slices we contain self.transmission_params = list() out_set = set( range(out_slice.start or 0, out_slice.stop or 0, out_slice.step or 1)) for transmission_params, outs in transmission_parameter_slices: # If there is an intersection between the outs and the set of outs # we're responsible for then store transmission parameters. if out_set & outs: self.transmission_params.append(transmission_params)
def __init__(self, label, column_slice, output_slice, transform_region, output_keys, output_slices, machine_timestep, filter_region, filter_routing_region): """Allocate a portion of the overall matrix to a single processing core. Parameters ---------- column_slice : :py:class:`slice` Columns of the transform matrix managed by the group of vertices of which we are a member. output_slice : :py:class:`slice` Slice of the rows of the transform matrix that will be applied by this processing core. transform_region : MatrixRegion output_keys : [BitField, ...] Keys transmitted by filter. output_slices : [(TransmissionParameters, set), ...] Pairs of transmission parameters and sets containing the row indices of the transform matrix corresponding to the transmission parameters. """ # Check that the output slice is safe assert (output_slice.start is not None and output_slice.stop is not None and (output_slice.step is None or output_slice.step == 1)) # Store information about the slices of the for which matrix we're # responsible. self.output_slice = output_slice self.column_slice = column_slice # Store which signal parameter slices we contain self.transmission_params = set() out_set = set(range(output_slice.start, output_slice.stop)) for transmission_params, outs in output_slices: # If there is an intersection between the outs and the set of outs # we're responsible for then store transmission parameters. if out_set & outs: self.transmission_params.add(transmission_params) # Construct the regions self.regions = { Regions.system: SystemRegion(column_slice, output_slice, machine_timestep), Regions.transform: transform_region, Regions.keys: regions.KeyspacesRegion( output_keys, fields=[regions.KeyField(dict(cluster="cluster"))], partitioned_by_atom=True), Regions.input_filters: filter_region, Regions.input_routing: filter_routing_region, } # Construct the region arguments w = self.column_slice.stop - self.column_slice.start self.region_arguments = { Regions.transform: Args(vertex_slice=self.output_slice), Regions.keys: Args(vertex_slice=self.output_slice), Regions.system: Args(), # No arguments Regions.input_filters: Args(filter_width=w), # No arguments Regions.input_routing: Args(), # No arguments } # Determine the resource requirements and find the correct application sdram_usage = regions.utils.sizeof_regions_named( self.regions, self.region_arguments) super(FilterCore, self).__init__(label=self._label, application=get_application("filter"), resources={ Cores: 1, SDRAM: sdram_usage })
def __init__(self, column_slice, output_slice, transform_region, output_keys, output_slices, machine_timestep, filter_region, filter_routing_region): """Allocate a portion of the overall matrix to a single processing core. Parameters ---------- column_slice : :py:class:`slice` Columns of the transform matrix managed by the group of vertices of which we are a member. output_slice : :py:class:`slice` Slice of the rows of the transform matrix that will be applied by this processing core. transform_region : MatrixRegion output_keys : [BitField, ...] Keys transmitted by filter. output_slices : [(TransmissionParameters, set), ...] Pairs of transmission parameters and sets containing the row indices of the transform matrix corresponding to the transmission parameters. """ # Check that the output slice is safe assert (output_slice.start is not None and output_slice.stop is not None and (output_slice.step is None or output_slice.step == 1) ) # Store information about the slices of the for which matrix we're # responsible. self.output_slice = output_slice self.column_slice = column_slice # Store which signal parameter slices we contain self.transmission_params = set() out_set = set(range(output_slice.start, output_slice.stop)) for transmission_params, outs in output_slices: # If there is an intersection between the outs and the set of outs # we're responsible for then store transmission parameters. if out_set & outs: self.transmission_params.add(transmission_params) # Construct the regions self.regions = { Regions.system: SystemRegion(column_slice, output_slice, machine_timestep), Regions.transform: transform_region, Regions.keys: regions.KeyspacesRegion( output_keys, fields=[regions.KeyField(dict(cluster="cluster"))], partitioned_by_atom=True ), Regions.input_filters: filter_region, Regions.input_routing: filter_routing_region, } # Construct the region arguments w = self.column_slice.stop - self.column_slice.start self.region_arguments = { Regions.transform: Args(vertex_slice=self.output_slice), Regions.keys: Args(vertex_slice=self.output_slice), Regions.system: Args(), # No arguments Regions.input_filters: Args(filter_width=w), # No arguments Regions.input_routing: Args(), # No arguments } # Determine the resource requirements and find the correct application sdram_usage = regions.utils.sizeof_regions_named( self.regions, self.region_arguments ) super(FilterCore, self).__init__( application=get_application("filter"), resources={Cores: 1, SDRAM: sdram_usage} )
def make_vertices(self, model, n_steps): # TODO remove n_steps """Construct the data which can be loaded into the memory of a SpiNNaker machine. """ # Build encoders, gain and bias regions params = model.params[self.ensemble] # Convert the encoders combined with the gain to S1615 before creating # the region. encoders_with_gain = params.scaled_encoders self.encoders_region = regions.MatrixRegion( tp.np_to_fix(encoders_with_gain), sliced_dimension=regions.MatrixPartitioning.rows ) # Combine the direct input with the bias before converting to S1615 and # creating the region. bias_with_di = params.bias + np.dot(encoders_with_gain, self.direct_input) assert bias_with_di.ndim == 1 self.bias_region = regions.MatrixRegion( tp.np_to_fix(bias_with_di), sliced_dimension=regions.MatrixPartitioning.rows ) # Convert the gains to S1615 before creating the region self.gain_region = regions.MatrixRegion( tp.np_to_fix(params.gain), sliced_dimension=regions.MatrixPartitioning.rows ) # Extract all the filters from the incoming connections incoming = model.get_signals_connections_to_object(self) self.input_filters, self.input_filter_routing = make_filter_regions( incoming[InputPort.standard], model.dt, True, model.keyspaces.filter_routing_tag, width=self.ensemble.size_in ) self.inhib_filters, self.inhib_filter_routing = make_filter_regions( incoming[EnsembleInputPort.global_inhibition], model.dt, True, model.keyspaces.filter_routing_tag, width=1 ) self.mod_filters, self.mod_filter_routing = make_filter_regions( {}, model.dt, True, model.keyspaces.filter_routing_tag ) # Extract all the decoders for the outgoing connections and build the # regions for the decoders and the regions for the output keys. outgoing = model.get_signals_connections_from_object(self) decoders, output_keys = \ get_decoders_and_keys(model, outgoing[OutputPort.standard], True) size_out = decoders.shape[1] # TODO: Include learnt decoders self.pes_region = PESRegion() self.decoders_region = regions.MatrixRegion( tp.np_to_fix(decoders / model.dt), sliced_dimension=regions.MatrixPartitioning.rows ) self.output_keys_region = regions.KeyspacesRegion( output_keys, fields=[regions.KeyField({'cluster': 'cluster'})] ) # Create the recording regions for locally situated probes self.spike_region = None self.probe_spikes = False self.voltage_region = None self.probe_voltages = False for probe in self.local_probes: # For each probe determine which regions and flags should be set if probe.attr in ("output", "spikes"): # If spikes are being probed then ensure that the flag is set # and a region exists. if not self.probe_spikes: self.spike_region = SpikeRegion(n_steps) self.probe_spikes = True elif probe.attr in ("voltage"): # If voltages are being probed then ensure that the flag is set # and a region exists. if not self.probe_voltages: self.voltage_region = VoltageRegion(n_steps) self.probe_voltages = True # If profiling is enabled num_profiler_samples = 0 if getconfig(model.config, self.ensemble, "profile", False): # Try and get number of samples from config num_profiler_samples = getconfig(model.config, self.ensemble, "profile_num_samples") # If it's not specified, calculate sensible default if num_profiler_samples is None: num_profiler_samples =\ len(EnsembleLIF.profiler_tag_names) * n_steps * 2 # Create profiler region self.profiler_region = regions.Profiler(num_profiler_samples) # Create the regions list self.regions = [ SystemRegion(self.ensemble.size_in, size_out, model.machine_timestep, self.ensemble.neuron_type.tau_ref, self.ensemble.neuron_type.tau_rc, model.dt, self.probe_spikes, self.probe_voltages, num_profiler_samples ), self.bias_region, self.encoders_region, self.decoders_region, self.output_keys_region, self.input_filters, self.input_filter_routing, self.inhib_filters, self.inhib_filter_routing, self.gain_region, self.mod_filters, self.mod_filter_routing, self.pes_region, self.profiler_region, self.spike_region, self.voltage_region, ] # Partition the ensemble and get a list of vertices to load to the # machine. We can expect to be DTCM or CPU bound, so the SDRAM bound # can be quite lax to allow for lots of data probing. # TODO: Include other DTCM usage def cpu_usage(sl): """Calculate the CPU usage (in cycles) based on the number of neurons and the size_in and size_out of the ensemble. The equation and coefficients are taken from: "An Efficient SpiNNaker Implementation of the NEF", Mundy, Knight, Stewart and Furber [IJCNN 2015] """ n_neurons = (sl.stop - sl.start) return (245 + 43*self.ensemble.size_in + 100 + 702*size_out + 188 + 69*n_neurons + 13*n_neurons*self.ensemble.size_in) self.vertices = list() sdram_constraint = partition.Constraint(8*2**20) # Max 8MiB dtcm_constraint = partition.Constraint(64*2**10, .75) # 75% of 64KiB cpu_constraint = partition.Constraint(200000, .8) # 80% of 200k cycles constraints = { sdram_constraint: lambda s: regions.utils.sizeof_regions( self.regions, s), # **HACK** don't include last three regions in DTCM estimate # (profiler and spike recording) dtcm_constraint: lambda s: regions.utils.sizeof_regions( self.regions[:-3], s) + 5*(s.stop - s.start), cpu_constraint: cpu_usage, } app_name = ( "ensemble_profiled" if num_profiler_samples > 0 else "ensemble" ) for sl in partition.partition(slice(0, self.ensemble.n_neurons), constraints): resources = { Cores: 1, SDRAM: regions.utils.sizeof_regions(self.regions, sl), } vsl = VertexSlice(sl, get_application(app_name), resources) self.vertices.append(vsl) # Return the vertices and callback methods return netlistspec(self.vertices, self.load_to_machine, after_simulation_function=self.after_simulation)