Source code for spynnaker.pyNN.models.neuron.population_machine_synapses

# Copyright (c) 2017 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 __future__ import annotations
from typing import List, Sequence, Tuple, TYPE_CHECKING

from numpy import floating
from numpy.typing import NDArray

from spinn_utilities.overrides import overrides
from spinn_utilities.abstract_base import abstractmethod

from pacman.model.graphs.common import Slice
from pacman.model.graphs.machine import MachineVertex
from pacman.model.placements import Placement

from spinn_front_end_common.utilities.helpful_functions import (
    locate_memory_region_for_placement)
from spinn_front_end_common.abstract_models import (
    AbstractSupportsBitFieldRoutingCompression)
from spinn_front_end_common.interface.ds import DataSpecificationBase

from spynnaker.pyNN.models.neuron.synapse_dynamics import (
    AbstractSynapseDynamicsStructural, AbstractSDRAMSynapseDynamics)
from spynnaker.pyNN.utilities.utility_calls import get_n_bits
from spynnaker.pyNN.models.abstract_models import (
    AbstractSynapseExpandable, HasSynapses)

from .abstract_population_vertex import AbstractPopulationVertex
from .population_machine_synapses_provenance import (
    PopulationMachineSynapsesProvenance)
from .synaptic_matrices import SynapseRegions, SynapseRegionReferences

if TYPE_CHECKING:
    from spynnaker.pyNN.models.neuron.synaptic_matrices import SynapticMatrices
    from spynnaker.pyNN.models.neural_projections import (
        ProjectionApplicationEdge, SynapseInformation)


[docs] class PopulationMachineSynapses( PopulationMachineSynapsesProvenance, AbstractSupportsBitFieldRoutingCompression, AbstractSynapseExpandable, HasSynapses, allow_derivation=True): """ Mix-in for machine vertices that contain synapses. """ # This MUST stay empty to allow mixing with other things with slots __slots__ = () @property @abstractmethod def _pop_vertex(self) -> AbstractPopulationVertex: """ The application vertex of the machine vertex. .. note:: This is likely to be available via the MachineVertex. :rtype: AbstractPopulationVertex """ raise NotImplementedError @property @abstractmethod @overrides(MachineVertex.vertex_slice) def vertex_slice(self) -> Slice: # classes that implement this API also implement MachineVertex # pylint: disable=missing-function-docstring raise NotImplementedError @property @abstractmethod def _synaptic_matrices(self) -> SynapticMatrices: """ The object holding synaptic matrices. :rtype: SynapticMatrices """ raise NotImplementedError @property @abstractmethod def _synapse_regions(self) -> SynapseRegions: """ The identifiers of synaptic regions. :rtype: .SynapseRegions """ raise NotImplementedError @property @abstractmethod def _max_atoms_per_core(self) -> int: """ The maximum number of atoms on any core targeted by these synapses. :rtype: int """ raise NotImplementedError
[docs] @abstractmethod def set_do_synapse_regeneration(self) -> None: """ Indicates that synaptic data regeneration is required. """ raise NotImplementedError
@property def _synapse_references(self) -> SynapseRegionReferences: """ The references to synapse regions. Override to provide these. :rtype: .SynapseRegionReferences """ return SynapseRegionReferences()
[docs] @overrides(AbstractSupportsBitFieldRoutingCompression. bit_field_base_address) def bit_field_base_address(self, placement: Placement) -> int: return locate_memory_region_for_placement( placement=placement, region=self._synapse_regions.bitfield_filter)
[docs] @overrides(AbstractSupportsBitFieldRoutingCompression. regeneratable_sdram_blocks_and_sizes) def regeneratable_sdram_blocks_and_sizes( self, placement: Placement) -> List[Tuple[int, int]]: synaptic_matrix_base_address = locate_memory_region_for_placement( placement=placement, region=self._synapse_regions.synaptic_matrix) return [( self._synaptic_matrices.host_generated_block_addr + synaptic_matrix_base_address, self._synaptic_matrices.on_chip_generated_matrix_size)]
def _write_synapse_data_spec( self, spec: DataSpecificationBase, ring_buffer_shifts: Sequence[int], weight_scales: NDArray[floating], structural_sz: int): """ Write the data specification for the synapse data. :param ~data_specification.DataSpecificationGenerator spec: The data specification to write to :param list(int) ring_buffer_shifts: The shifts to apply to convert ring buffer values to S1615 values :param list(int) weight_scales: The scaling to apply to weights to store them in the synapses :param int structural_sz: The size of the structural data """ # Write the synapse parameters self._write_synapse_parameters(spec, ring_buffer_shifts) # Write the synaptic matrices self._synaptic_matrices.generate_data() self._synaptic_matrices.write_synaptic_data( spec, self.vertex_slice, self._synapse_references) # Write any synapse dynamics synapse_dynamics = self._pop_vertex.synapse_dynamics synapse_dynamics_sz = self._pop_vertex.get_synapse_dynamics_size( self.vertex_slice.n_atoms) if synapse_dynamics_sz > 0: assert isinstance(synapse_dynamics, AbstractSDRAMSynapseDynamics) spec.reserve_memory_region( region=self._synapse_regions.synapse_dynamics, size=synapse_dynamics_sz, label='synapseDynamicsParams', reference=self._synapse_references.synapse_dynamics) synapse_dynamics.write_parameters( spec, self._synapse_regions.synapse_dynamics, self._pop_vertex.weight_scale, weight_scales) elif self._synapse_references.synapse_dynamics is not None: # If there is a reference for this region, we have to create it! spec.reserve_memory_region( region=self._synapse_regions.synapse_dynamics, size=4, label='synapseDynamicsParams', reference=self._synapse_references.synapse_dynamics) if isinstance(synapse_dynamics, AbstractSynapseDynamicsStructural): spec.reserve_memory_region( region=self._synapse_regions.structural_dynamics, size=structural_sz, label='synapseDynamicsStructuralParams', reference=self._synapse_references.structural_dynamics) synapse_dynamics.write_structural_parameters( spec, self._synapse_regions.structural_dynamics, weight_scales, self._pop_vertex, self.vertex_slice, self._synaptic_matrices) elif self._synapse_references.structural_dynamics is not None: # If there is a reference for this region, we have to create it! spec.reserve_memory_region( region=self._synapse_regions.structural_dynamics, size=4, label='synapseDynamicsStructuralParams', reference=self._synapse_references.structural_dynamics) def _write_synapse_parameters( self, spec: DataSpecificationBase, ring_buffer_shifts: Sequence[int]): """ Write the synapse parameters data region. :param ~data_specification.DataSpecificationGenerator spec: The data specification to write to :param list(int) ring_buffer_shifts: The shifts to apply to convert ring buffer values to S1615 values """ # Reserve space spec.reserve_memory_region( region=self._synapse_regions.synapse_params, size=self._pop_vertex.get_synapse_params_size(), label='SynapseParams', reference=self._synapse_references.synapse_params) # Get values n_neurons = self._max_atoms_per_core # We only count neuron synapse types here, as this is related to # the ring buffers n_synapse_types = self._pop_vertex.neuron_impl.get_n_synapse_types() max_delay = self._pop_vertex.splitter.max_support_delay() # Write synapse parameters spec.switch_write_focus(self._synapse_regions.synapse_params) spec.write_value(n_neurons) spec.write_value(n_synapse_types) spec.write_value(get_n_bits(n_neurons)) spec.write_value(get_n_bits(n_synapse_types)) spec.write_value(get_n_bits(max_delay)) spec.write_value(int(self._pop_vertex.drop_late_spikes)) spec.write_value(self._pop_vertex.incoming_spike_buffer_size) spec.write_array(ring_buffer_shifts)
[docs] @overrides(AbstractSynapseExpandable.gen_on_machine) def gen_on_machine(self) -> bool: return self._synaptic_matrices.gen_on_machine
[docs] @overrides(AbstractSynapseExpandable.read_generated_connection_holders) def read_generated_connection_holders(self, placement: Placement): self._synaptic_matrices.read_generated_connection_holders(placement)
@property @overrides(AbstractSynapseExpandable.connection_generator_region) def connection_generator_region(self) -> int: return self._synapse_regions.connection_builder
[docs] @overrides(HasSynapses.get_connections_from_machine) def get_connections_from_machine( self, placement: Placement, app_edge: ProjectionApplicationEdge, synapse_info: SynapseInformation) -> Sequence[NDArray]: return self._synaptic_matrices.get_connections_from_machine( placement, app_edge, synapse_info)
@property @overrides(AbstractSynapseExpandable.max_gen_data) def max_gen_data(self) -> int: return self._synaptic_matrices.max_gen_data @property @overrides(AbstractSynapseExpandable.bit_field_size) def bit_field_size(self) -> int: return self._synaptic_matrices.bit_field_size