Beispiel #1
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)
def test_constraint():
    """Test creating a constraint."""
    # Constraints consist of: a limit, and an optional target.
    constraint = pac.Constraint(100)
    assert constraint.maximum == 100
    assert constraint.target == 1.0
    assert constraint.max_usage == 100.0

    constraint = pac.Constraint(100, 0.9)
    assert constraint.maximum == 100
    assert constraint.target == 0.9
    assert constraint.max_usage == 90.0
    def test_single_partition_step(self):
        # Create the constraint
        constraint_a = pac.Constraint(100, .7)
        constraint_b = pac.Constraint(50)

        # Create the constraint -> usage mapping
        constraints = {
            constraint_a: lambda sl: sl.stop - sl.start + 10,
            constraint_b: lambda sl: sl.stop - sl.start
        }

        # Perform the partitioning
        assert list(pac.partition(
            slice(100), constraints)) == [slice(0, 50),
                                          slice(50, 100)]
    def test_unpartitionable(self):
        # Create the constraint
        constraint_a = pac.Constraint(50)

        # Create the constraint -> usage mapping
        constraints = {constraint_a: lambda sl: sl.stop - sl.start + 100}

        # Perform the partitioning
        with pytest.raises(pac.UnpartitionableError):
            list(pac.partition(slice(100), constraints))
    def test_no_partitioning(self):
        # Create the constraint
        constraint = pac.Constraint(100, 0.9)

        # Create the constraint -> usage mapping
        constraints = {constraint: lambda sl: sl.stop - sl.start + 10}

        # Perform the partitioning
        assert list(pac.partition(slice(0, 80), constraints)) == [slice(0, 80)]
        assert list(pac.partition(slice(80), constraints)) == [slice(0, 80)]
    def test_just_partitionable(self):
        # Create the constraint
        constraint_a = pac.Constraint(50)

        # Create the constraint -> usage mapping
        constraints = {constraint_a: lambda sl: sl.stop - sl.start + 49}

        # Perform the partitioning
        assert (list(pac.partition(slice(100), constraints)) == [
            slice(n, n + 1) for n in range(100)
        ])  # pragma : no cover
    def test_unpartitionable(self):
        # Create the constraint
        constraint = pac.Constraint(50)

        # Create the constraint -> usage mapping
        def cons(*slices):
            return sum(sl.stop - sl.start for sl in slices) + 50

        constraints = {constraint: cons}

        # Perform the partitioning
        with pytest.raises(pac.UnpartitionableError):
            list(pac.partition_multiple((slice(10), slice(2)), constraints))
    def test_single_partition_step(self):
        # Create the constraint
        constraint = pac.Constraint(50)

        # Create the constraint -> usage mapping
        def cons(*slices):
            return sum(sl.stop - sl.start for sl in slices)

        constraints = {constraint: cons}

        # Perform the partitioning
        assert (list(
            pac.partition_multiple(
                (slice(80), slice(20)),
                constraints)) == [(slice(0, 40), slice(0, 10)),
                                  (slice(40, 80), slice(10, 20))])
    def test_no_partitioning(self):
        # Create the constraint
        constraint = pac.Constraint(100, 0.9)

        # Create the constraint -> usage mapping
        def cons(*slices):
            return sum(sl.stop - sl.start for sl in slices) + 10

        constraints = {constraint: cons}

        # Perform the partitioning
        assert (list(
            pac.partition_multiple(
                (slice(0, 40), slice(0, 30)), constraints)) == list(
                    pac.partition_multiple(
                        (slice(40), slice(30)), constraints)) == [(slice(
                            0, 40), slice(0, 30))])
Beispiel #10
0
    def make_vertices(self, model, 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]
        ens_regions = dict()

        # Convert the encoders combined with the gain to S1615 before creating
        # the region.
        encoders_with_gain = params.scaled_encoders
        ens_regions[EnsembleRegions.encoders] = 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
        ens_regions[EnsembleRegions.bias] = regions.MatrixRegion(
            tp.np_to_fix(bias_with_di),
            sliced_dimension=regions.MatrixPartitioning.rows)

        # Convert the gains to S1615 before creating the region
        ens_regions[EnsembleRegions.gain] = 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_to_object(self)

        (ens_regions[EnsembleRegions.input_filters],
         ens_regions[EnsembleRegions.input_routing]) = make_filter_regions(
             incoming[InputPort.standard],
             model.dt,
             True,
             model.keyspaces.filter_routing_tag,
             width=self.ensemble.size_in)
        (ens_regions[EnsembleRegions.inhibition_filters],
         ens_regions[EnsembleRegions.inhibition_routing]) = \
            make_filter_regions(
                incoming[EnsembleInputPort.global_inhibition], model.dt, True,
                model.keyspaces.filter_routing_tag, width=1
            )

        # 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_from_object(self)
        if OutputPort.standard in outgoing:
            decoders, output_keys = \
                get_decoders_and_keys(outgoing[OutputPort.standard], True)
        else:
            decoders = np.array([])
            output_keys = list()
        size_out = decoders.shape[0]

        ens_regions[EnsembleRegions.decoders] = regions.MatrixRegion(
            tp.np_to_fix(decoders / model.dt),
            sliced_dimension=regions.MatrixPartitioning.rows)
        ens_regions[EnsembleRegions.keys] = regions.KeyspacesRegion(
            output_keys,
            fields=[regions.KeyField({'cluster': 'cluster'})],
            partitioned_by_atom=True)

        # The population length region stores information about groups of
        # co-operating cores.
        ens_regions[EnsembleRegions.population_length] = \
            regions.ListRegion("I")

        # The ensemble region contains basic information about the ensemble
        ens_regions[EnsembleRegions.ensemble] = EnsembleRegion(
            model.machine_timestep, self.ensemble.size_in)

        # The neuron region contains information specific to the neuron type
        ens_regions[EnsembleRegions.neuron] = LIFRegion(
            model.dt, self.ensemble.neuron_type.tau_rc,
            self.ensemble.neuron_type.tau_ref)

        # Manage profiling
        n_profiler_samples = 0
        self.profiled = getconfig(model.config, self.ensemble, "profile",
                                  False)
        if self.profiled:
            # Try and get number of samples from config
            n_profiler_samples = getconfig(model.config, self.ensemble,
                                           "profile_num_samples")

            # If it's not specified, calculate sensible default
            if n_profiler_samples is None:
                n_profiler_samples = (len(EnsembleSlice.profiler_tag_names) *
                                      n_steps * 2)

        # Create profiler region
        ens_regions[EnsembleRegions.profiler] = regions.Profiler(
            n_profiler_samples)
        ens_regions[EnsembleRegions.ensemble].n_profiler_samples = \
            n_profiler_samples

        # Manage probes
        for probe in self.local_probes:
            if probe.attr in ("output", "spikes"):
                self.record_spikes = True
            elif probe.attr == "voltage":
                self.record_voltages = True
            else:
                raise NotImplementedError(
                    "Cannot probe {} on Ensembles".format(probe.attr))

        # Set the flags
        ens_regions[EnsembleRegions.ensemble].record_spikes = \
            self.record_spikes
        ens_regions[EnsembleRegions.ensemble].record_voltages = \
            self.record_voltages

        # Create the probe recording regions
        ens_regions[EnsembleRegions.spikes] = regions.SpikeRecordingRegion(
            n_steps if self.record_spikes else 0)
        ens_regions[EnsembleRegions.voltages] = regions.VoltageRecordingRegion(
            n_steps if self.record_voltages else 0)

        # Create constraints against which to partition, initially assume that
        # we can devote 16 cores to every problem.
        sdram_constraint = partition.Constraint(128 * 2**20,
                                                0.9)  # 90% of 128MiB
        dtcm_constraint = partition.Constraint(16 * 64 * 2**10,
                                               0.9)  # 90% of 16 cores DTCM

        # The number of cycles available is 200MHz * the machine timestep; or
        # 200 * the machine timestep in microseconds.
        cycles = 200 * model.machine_timestep
        cpu_constraint = partition.Constraint(cycles * 16,
                                              0.8)  # 80% of 16 cores compute

        # Form the constraints dictionary
        def _make_constraint(f, size_in, size_out, **kwargs):
            """Wrap a usage computation method to work with the partitioner."""
            def f_(vertex_slice):
                # Calculate the number of neurons
                n_neurons = vertex_slice.stop - vertex_slice.start

                # Call the original method
                return f(size_in, size_out, n_neurons, **kwargs)

            return f_

        partition_constraints = {
            sdram_constraint:
            _make_constraint(_lif_sdram_usage, self.ensemble.size_in,
                             size_out),
            dtcm_constraint:
            _make_constraint(_lif_dtcm_usage, self.ensemble.size_in, size_out),
            cpu_constraint:
            _make_constraint(_lif_cpu_usage, self.ensemble.size_in, size_out),
        }

        # Partition the ensemble to create clusters of co-operating cores
        self.clusters = list()
        vertices = list()
        constraints = list()
        for sl in partition.partition(slice(0, self.ensemble.n_neurons),
                                      partition_constraints):
            # For each slice we create a cluster of co-operating cores.  We
            # instantiate the cluster and then ask it to produce vertices which
            # will be added to the netlist.
            cluster = EnsembleCluster(sl, self.ensemble.size_in, size_out,
                                      ens_regions)
            self.clusters.append(cluster)

            # Get the vertices for the cluster
            cluster_vertices = cluster.make_vertices(cycles)
            vertices.extend(cluster_vertices)

            # Create a constraint which forces these vertices to be present on
            # the same chip
            constraints.append(SameChipConstraint(cluster_vertices))

        # Return the vertices and callback methods
        return netlistspec(vertices,
                           self.load_to_machine,
                           after_simulation_function=self.after_simulation,
                           constraints=constraints)
Beispiel #11
0
    def make_vertices(self, cycles):
        """Partition the neurons onto multiple cores."""
        # Make reduced constraints to partition against, we don't partition
        # against SDRAM as we're already sure that there is sufficient SDRAM
        # (and if there isn't we can't possibly fit all the vertices on a
        # single chip).
        dtcm_constraint = partition.Constraint(64 * 2**10, 0.9)  # 90% of DTCM
        cpu_constraint = partition.Constraint(cycles, 0.8)  # 80% of compute

        # Get the number of neurons in this cluster
        n_neurons = self.neuron_slice.stop - self.neuron_slice.start

        # Form the constraints dictionary
        def _make_constraint(f, size_in, **kwargs):
            """Wrap a usage computation method to work with the partitioner."""
            def f_(neuron_slice, output_slice):
                # Calculate the number of neurons
                n_neurons = neuron_slice.stop - neuron_slice.start

                # Calculate the number of outgoing dimensions
                size_out = output_slice.stop - output_slice.start

                # Call the original method
                return f(size_in, size_out, n_neurons, **kwargs)

            return f_

        constraints = {
            dtcm_constraint:
            _make_constraint(_lif_dtcm_usage,
                             self.size_in,
                             n_neurons_in_cluster=n_neurons),
            cpu_constraint:
            _make_constraint(_lif_cpu_usage,
                             self.size_in,
                             n_neurons_in_cluster=n_neurons),
        }

        # Partition the slice of neurons that we have
        self.neuron_slices = list()
        output_slices = list()
        for neurons, outputs in partition.partition_multiple(
            (self.neuron_slice, slice(self.size_out)), constraints):
            self.neuron_slices.append(neurons)
            output_slices.append(outputs)

        n_slices = len(self.neuron_slices)
        assert n_slices <= 16  # Too many cores in the cluster

        # Also partition the input space
        input_slices = partition.divide_slice(slice(0, self.size_in), n_slices)

        # Zip these together to create the vertices
        all_slices = zip(input_slices, output_slices)
        for i, (in_slice, out_slice) in enumerate(all_slices):
            # Create the vertex
            vertex = EnsembleSlice(i, self.neuron_slices, in_slice, out_slice,
                                   self.regions)

            # Add to the list of vertices
            self.vertices.append(vertex)

        # Return all the vertices
        return self.vertices