Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 4
0
    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)
Esempio n. 5
0
    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)
Esempio n. 6
0
    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
        })
Esempio n. 7
0
    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)
Esempio n. 8
0
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))
Esempio n. 9
0
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)
    )
Esempio n. 10
0
    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})
Esempio n. 11
0
    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)
Esempio n. 12
0
    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)
Esempio n. 13
0
    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)
Esempio n. 14
0
    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)
Esempio n. 15
0
    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
                                         })
Esempio n. 16
0
    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}
        )
Esempio n. 17
0
    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)