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 test_single_vertices(self):
        """Test that operators which produce single vertices work correctly and
        that all functions and signals are correctly collected and included in
        the final netlist.
        """
        # Create the first operator
        vertex_a = mock.Mock(name="vertex A")
        load_fn_a = mock.Mock(name="load function A")
        pre_fn_a = mock.Mock(name="pre function A")
        post_fn_a = mock.Mock(name="post function A")

        object_a = mock.Mock(name="object A")
        operator_a = mock.Mock(name="operator A")
        operator_a.make_vertices.return_value = \
            netlistspec(vertex_a, load_fn_a, pre_fn_a, post_fn_a)

        # Create the second operator
        vertex_b = mock.Mock(name="vertex B")
        load_fn_b = mock.Mock(name="load function B")

        object_b = mock.Mock(name="object B")
        operator_b = mock.Mock(name="operator B")
        operator_b.make_vertices.return_value = \
            netlistspec(vertex_b, load_fn_b)

        # Create a signal between the operators
        keyspace = mock.Mock(name="keyspace")
        keyspace.length = 32
        signal_ab = Signal(ObjectPort(operator_a, None),
                           ObjectPort(operator_b, None),
                           keyspace=keyspace, weight=43)

        # Create the model, add the items and then generate the netlist
        model = Model()
        model.object_operators[object_a] = operator_a
        model.object_operators[object_b] = operator_b
        model.connections_signals[None] = signal_ab
        netlist = model.make_netlist()

        # Check that the make_vertices functions were called
        operator_a.make_vertices.assert_called_once_with(model)
        operator_b.make_vertices.assert_called_once_with(model)

        # Check that the netlist is as expected
        assert len(netlist.nets) == 1
        for net in netlist.nets:
            assert net.source is vertex_a
            assert net.sinks == [vertex_b]
            assert net.keyspace is keyspace
            assert net.weight == signal_ab.weight

        assert set(netlist.vertices) == set([vertex_a, vertex_b])
        assert netlist.keyspaces is model.keyspaces
        assert netlist.groups == list()
        assert set(netlist.load_functions) == set([load_fn_a, load_fn_b])
        assert netlist.before_simulation_functions == [pre_fn_a]
        assert netlist.after_simulation_functions == [post_fn_a]
    def test_extra_operators_and_signals(self):
        """Test the operators and signals in the extra_operators and
        extra_signals lists are included when building netlists.
        """
        # Create the first operator
        vertex_a = mock.Mock(name="vertex A")
        load_fn_a = mock.Mock(name="load function A")
        pre_fn_a = mock.Mock(name="pre function A")
        post_fn_a = mock.Mock(name="post function A")

        operator_a = mock.Mock(name="operator A")
        operator_a.make_vertices.return_value = \
            netlistspec(vertex_a, load_fn_a, pre_fn_a, post_fn_a)

        # Create the second operator
        vertex_b = mock.Mock(name="vertex B")
        load_fn_b = mock.Mock(name="load function B")

        operator_b = mock.Mock(name="operator B")
        operator_b.make_vertices.return_value = \
            netlistspec(vertex_b, load_fn_b)

        # Create a signal between the operators
        keyspace = mock.Mock(name="keyspace")
        keyspace.length = 32
        signal_ab = Signal(ObjectPort(operator_a, None),
                           ObjectPort(operator_b, None),
                           keyspace=keyspace, weight=43)

        # Create the model, add the items and then generate the netlist
        model = Model()
        model.extra_operators = [operator_a, operator_b]
        model.extra_signals = [signal_ab]
        netlist = model.make_netlist()

        # Check that the make_vertices functions were called
        operator_a.make_vertices.assert_called_once_with(model)
        operator_b.make_vertices.assert_called_once_with(model)

        # Check that the netlist is as expected
        assert len(netlist.nets) == 1
        for net in netlist.nets:
            assert net.source is vertex_a
            assert net.sinks == [vertex_b]
            assert net.keyspace is keyspace
            assert net.weight == signal_ab.weight

        assert set(netlist.vertices) == set([vertex_a, vertex_b])
        assert netlist.keyspaces is model.keyspaces
        assert netlist.groups == list()
        assert set(netlist.load_functions) == set([load_fn_a, load_fn_b])
        assert netlist.before_simulation_functions == [pre_fn_a]
        assert netlist.after_simulation_functions == [post_fn_a]
    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 test_multiple_sink_vertices(self):
        # Create the first operator
        vertex_a = mock.Mock(name="vertex A")
        load_fn_a = mock.Mock(name="load function A")
        pre_fn_a = mock.Mock(name="pre function A")
        post_fn_a = mock.Mock(name="post function A")

        object_a = mock.Mock(name="object A")
        operator_a = mock.Mock(name="operator A")
        operator_a.make_vertices.return_value = \
            netlistspec(vertex_a, load_fn_a, pre_fn_a, post_fn_a)

        # Create the second operator
        vertex_b0 = mock.Mock(name="vertex B0")
        vertex_b1 = mock.Mock(name="vertex B1")
        load_fn_b = mock.Mock(name="load function B")

        object_b = mock.Mock(name="object B")
        operator_b = mock.Mock(name="operator B")
        operator_b.make_vertices.return_value = \
            netlistspec([vertex_b0, vertex_b1], load_fn_b)

        # Create a signal between the operators
        keyspace = mock.Mock(name="keyspace")
        keyspace.length = 32
        signal_ab = Signal(ObjectPort(operator_a, None),
                           ObjectPort(operator_b, None),
                           keyspace=keyspace, weight=3)

        # Create the model, add the items and then generate the netlist
        model = Model()
        model.object_operators[object_a] = operator_a
        model.object_operators[object_b] = operator_b
        model.connections_signals[None] = signal_ab
        netlist = model.make_netlist()

        # Check that the netlist is as expected
        assert set(netlist.vertices) == set([vertex_a, vertex_b0, vertex_b1])
        assert len(netlist.nets) == 1
        for net in netlist.nets:
            assert net.source is vertex_a
            assert net.sinks == [vertex_b0, vertex_b1]
            assert net.keyspace is keyspace
            assert net.weight == signal_ab.weight

        # Check that the groups are correct
        assert netlist.groups == [set([vertex_b0, vertex_b1])]
    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)
Exemple #7
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)