Source code for spynnaker.pyNN.models.common.population_application_vertex

# Copyright (c) 2020 The University of Manchester
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from typing import Collection, Container, Iterable, List, Optional, Tuple
from spinn_utilities.overrides import overrides
from spinn_utilities.ranged.abstract_sized import Selector
from pacman.model.graphs.application import ApplicationVertex
from pacman.model.graphs.machine import MachineVertex
from pacman.model.graphs.common import Slice
from pacman.model.routing_info import RoutingInfo
from pacman.utilities.utility_calls import get_keys
from spinn_front_end_common.utilities.exceptions import ConfigurationException
from spinn_front_end_common.abstract_models import HasCustomAtomKeyMap
from spinn_front_end_common.interface.ds import DataType
from spynnaker.pyNN.models.common.parameter_holder import ParameterHolder
from spynnaker.pyNN.utilities.buffer_data_type import BufferDataType
from spynnaker.pyNN.models.current_sources import AbstractCurrentSource
from .types import Names, Values


# pylint: disable=abstract-method
class PopulationApplicationVertex(ApplicationVertex, HasCustomAtomKeyMap):
    """
    A vertex that can be used in a Population.

    Provides some default functions that can be overridden if the vertex
    supports these.
    """

    # No data required here; makes it easy to mix in!
    __slots__ = ()

    @staticmethod
    def _as_list(names: Names) -> List[str]:
        """
        Normalise the input to a list.

        :param names: The item or items to normalise
        :type names: str or list
        :rtype: list(str)
        """
        if isinstance(names, str):
            return [names]
        return list(names)

    @staticmethod
    def _check_names(names: Names, allowed: Container[str],
                     type_of_thing: str):
        """
        Check the list of names are allowed or not.

        :param names: The names to check
        :type names: str or list
        :param set(str) allowed: The set of allowed names
        :param str type_of_thing: The "type of thing" to report in any error
        :raises KeyError: If one of the names is not allowed
        """
        for name in PopulationApplicationVertex._as_list(names):
            if name not in allowed:
                raise KeyError(f"{name} is not a recognized {type_of_thing}")

    @staticmethod
    def _check_parameters(names: Names, allowed: Container[str]):
        """
        Check that parameters are allowed.

        :param names: The names to check
        :type names: str or list
        :param set(str) allowed: The set of allowed names
        """
        PopulationApplicationVertex._check_names(names, allowed, "parameter")

    @staticmethod
    def _check_variables(names: Names, allowed: Container[str]):
        """
        Check that state variables are allowed.

        :param names: The names to check
        :type names: str or list
        :param set(str) allowed: The set of allowed names
        """
        PopulationApplicationVertex._check_names(names, allowed, "variable")

[docs] def get_parameter_values( self, names: Names, selector: Selector = None) -> ParameterHolder: """ Get the values of a parameter or parameters for the whole Population or a subset if the selector is used. :param names: The name or names of the parameter to get :type names: str or list :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :rtype: ParameterHolder :raise KeyError: if the parameter is not something that can be read """ raise KeyError( "This Population does not support the reading of parameters")
[docs] def set_parameter_values( self, name: str, value: Values, selector: Selector = None): """ Set the values of a parameter for the whole Population or a subset if the selector is used. :param str name: The name of the parameter to set :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :raise KeyError: if the parameter is not something that can be changed """ raise KeyError( "This Population does not support the setting of parameters")
[docs] def get_parameters(self) -> List[str]: """ Get the names of all the parameters that can be obtained :rtype: list(str) """ return []
[docs] def get_initial_state_values( self, names: Names, selector: Selector = None) -> ParameterHolder: """ Get the initial values of a state variable for the whole Population or a subset if the selector is used. :param names: The name or names of the variable to get :type names: str or list(str) :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :rtype: ParameterHolder :raise KeyError: if the variable is not something that can be read """ raise KeyError( "This Population does not support the reading of state" " variables")
[docs] def set_initial_state_values( self, name: str, value: Values, selector: Selector = None): """ Set the initial values of a state variable for the whole Population or a subset if the selector is used. :param str name: The name of the variable to set :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :raise KeyError: if the variable is not something that can be changed """ raise KeyError( "This Population does not support the initialization of state" " variables")
[docs] def get_current_state_values( self, names: Names, selector: Selector = None) -> ParameterHolder: """ Get the current values of a state variable for the whole Population or a subset if the selector is used. :param names: The name or names of the variable to get :type names: str or list(str) :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :rtype: ParameterHolder :raise KeyError: if the variable is not something that can be read """ raise KeyError( "This Population does not support the reading of state" " variables")
[docs] def set_current_state_values( self, name: str, value: Values, selector: Selector = None): """ Set the current values of a state variable for the whole Population or a subset if the selector is used. :param str name: The name of the variable to set :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :raise KeyError: if the variable is not something that can be changed """ raise KeyError( "This Population does not support the setting of state" " variables")
[docs] def get_state_variables(self) -> List[str]: """ Get a list of supported state variables. :rtype: list(str) """ return []
[docs] def get_units(self, name: str) -> str: """ Get the units of the given parameter or state variable. :param str name: the name of the parameter to get the units of :rtype: str :raise KeyError: if the name isn't recognised or the units cannot be identified """ raise KeyError(f"The units for {name} cannot be found")
@property def conductance_based(self) -> bool: """ Whether the vertex models post-synaptic inputs as currents or conductance. By default this is False; override if the model accepts conductance based input. :rtype: bool """ return False # recording methods # If get_recordable_variables implemented other recording methods must # be too
[docs] def get_recordable_variables(self) -> List[str]: """ Get a list of the names and types of things that can be recorded. This methods list the variable recorded via the Population. :rtype: list(str) """ return []
[docs] def set_recording( self, name: str, sampling_interval: Optional[float] = None, indices: Optional[Collection[int]] = None): """ Set a variable recording. :param str name: The name of the variable to set the status of :param sampling_interval: How often the variable should be recorded or `None` for every time step, in milliseconds :type sampling_interval: float or None :param indices: The list of neuron indices to record or `None` for all :type indices: list(int) or None :raises KeyError: if the variable cannot be recorded """ # pylint: disable=unused-argument if not self.get_recordable_variables(): raise KeyError("This Population does not support recording") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"set_recording")
[docs] def set_not_recording( self, name: str, indices: Optional[Collection[int]] = None): """ Set a variable not recording. :param str name: The name of the variable to not record :param indices: The list of neuron indices to not record or `None` for all :type indices: list(int) or None :raises KeyError: if the variable cannot be stopped from recording """ # pylint: disable=unused-argument if not self.get_recordable_variables(): raise KeyError("This Population does not support recording") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"set_not_recording")
[docs] def get_recording_variables(self) -> List[str]: """ Get a list of variables that are currently being recorded. :rtype: list(str) """ if not self.get_recordable_variables(): return [] raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"get_recording_variables")
[docs] def get_buffer_data_type(self, name: str) -> BufferDataType: """ Get the type of data recorded by the buffer manager. The buffer data type controls how data returned by the cores is handled in NeoBufferDatabase. :param str name: The name of the variable recorded :rtype: ~spinn_front_end_common.interface.buffer_management.storage_objects.BufferDatabase :raises KeyError: if the variable isn't being recorded """ if name not in self.get_recordable_variables(): raise KeyError(f"{name} is not being recorded") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"get_recording_variables")
[docs] def get_sampling_interval_ms(self, name: str) -> float: """ Get the sampling interval of the recording for the given variable. The values is in ms and unless selective recording is used will be `SpynnakerDataView.get_simulation_time_step_us()` :rtype: float :raises KeyError: If the variable isn't being recorded """ if name not in self.get_recordable_variables(): raise KeyError(f"{name} is not being recorded") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"get_recording_variables")
[docs] def get_data_type(self, name: str) -> Optional[DataType]: """ Get the type data returned by a recording of the variable. This is the type of data the C code is returning. For instance data such as spikes this will be `None`. :param str name: The name of the variable to get the type of :rtype: ~data_specification.enums.DataType or None :raise KeyError: If the variable isn't recordable """ if name not in self.get_recordable_variables(): raise KeyError(f"{name} is not being recorded") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"get_data_type")
[docs] def get_recording_region(self, name: str) -> int: """ Gets the recording region for the named variable. :param str name: The name of the variable to get the region of :rtype: int :raises KeyError: If the variable isn't being recorded """ if name not in self.get_recordable_variables(): raise KeyError(f"{name} is not being recorded") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"get_recording_region")
[docs] def get_neurons_recording( self, name: str, vertex_slice: Slice) -> Optional[Collection[int]]: """ Gets the neurons being recorded on the core with this slice. Typically `vertex_slice.get_raster_ids(atoms_shape)` but may be a sublist if doing selective recording. :param str name: The name of the variable to get the region of :param ~pacman.model.graphs.common.Slice vertex_slice: :return: A list of the global raster IDs of the atoms in recording named variable within this slice :rtype: list(int) """ # pylint: disable=unused-argument if name not in self.get_recordable_variables(): raise KeyError(f"{name} is not being recorded") raise NotImplementedError( f"{type(self)} has recording variables so should implement " f"get_neurons_recording")
# end of recording methods
[docs] def inject(self, current_source: AbstractCurrentSource, selector: Selector = None): """ Inject a current source into this population. :param AbstractCurrentSource current_source: the current source to be injected :param selector: a description of the subrange to accept, or ``None`` for all. See: :py:meth:`~spinn_utilities.ranged.AbstractSized.selector_to_ids` :type selector: None or slice or int or list(bool) or list(int) :raises \ ~spinn_front_end_common.utilities.exceptions.ConfigurationException: if the population doesn't support injection """ raise ConfigurationException( "This Population doesn't support injection")
@property def n_colour_bits(self) -> int: """ The number of colour bits sent by this vertex. Assumed 0 unless overridden :rtype: int """ return 0
[docs] @overrides(HasCustomAtomKeyMap.get_atom_key_map) def get_atom_key_map( self, pre_vertex: MachineVertex, partition_id: str, routing_info: RoutingInfo) -> Iterable[Tuple[int, int]]: base_key = routing_info.get_key_from( pre_vertex, partition_id) vertex_slice = pre_vertex.vertex_slice keys = get_keys(base_key, vertex_slice, self.n_colour_bits) return zip(vertex_slice.get_raster_ids(), keys)