Source code for spynnaker.pyNN.extra_algorithms.splitter_components.splitter_abstract_pop_vertex_slice

# Copyright (c) 2020-2021 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
from spinn_utilities.overrides import overrides
from pacman.exceptions import PacmanConfigurationException
from pacman.model.constraints.partitioner_constraints import (
    MaxVertexAtomsConstraint, FixedVertexAtomsConstraint,
    AbstractPartitionerConstraint)
from pacman.model.graphs.machine import MachineEdge
from pacman.model.resources import (
    ResourceContainer, DTCMResource, CPUCyclesPerTickResource,
    MultiRegionSDRAM)
from pacman.model.partitioner_splitters.abstract_splitters import (
    AbstractSplitterSlice)
from pacman.utilities import utility_calls
from spynnaker.pyNN.models.neuron import (
    AbstractPopulationVertex, PopulationMachineVertex)
from spynnaker.pyNN.models.neuron.population_machine_vertex import (
    NeuronProvenance, SynapseProvenance, MainProvenance,
    SpikeProcessingProvenance)
from spynnaker.pyNN.models.neuron.master_pop_table import (
    MasterPopTableAsBinarySearch)
from .abstract_spynnaker_splitter_delay import AbstractSpynnakerSplitterDelay
from spynnaker.pyNN.utilities.bit_field_utilities import (
    get_estimated_sdram_for_bit_field_region,
    get_estimated_sdram_for_key_region,
    exact_sdram_for_bit_field_builder_region)
from spynnaker.pyNN.models.neuron.synapse_dynamics import (
    AbstractSynapseDynamicsStructural)


[docs]class SplitterAbstractPopulationVertexSlice( AbstractSplitterSlice, AbstractSpynnakerSplitterDelay): """ handles the splitting of the AbstractPopulationVertex via slice logic. """ __slots__ = [ # The pre-calculated ring buffer shifts "__ring_buffer_shifts", # The pre-calculated weight scales "__weight_scales", # The size of all the synapses on a core "__all_syn_block_sz", # The size of the structural plasticity data "__structural_sz", # The size of the synaptic expander data "__synapse_expander_sz", # The size of all the bitfield data "__bitfield_sz", # The next index to use for a synapse core "__next_index" ] """ The name of the splitter """ SPLITTER_NAME = "SplitterAbstractPopulationVertexSlice" """ The message to use when the Population is invalid """ INVALID_POP_ERROR_MESSAGE = ( "The vertex {} cannot be supported by the " "SplitterAbstractPopulationVertexSlice as" " the only vertex supported by this splitter is a " "AbstractPopulationVertex. Please use the correct splitter for " "your vertex and try again.") def __init__(self): super().__init__(self.SPLITTER_NAME) self.__ring_buffer_shifts = None self.__weight_scales = None self.__all_syn_block_sz = dict() self.__structural_sz = dict() self.__synapse_expander_sz = None self.__bitfield_sz = None self.__next_index = 0
[docs] @overrides(AbstractSplitterSlice.set_governed_app_vertex) def set_governed_app_vertex(self, app_vertex): super().set_governed_app_vertex(app_vertex) if not isinstance(app_vertex, AbstractPopulationVertex): raise PacmanConfigurationException( self.INVALID_POP_ERROR_MESSAGE.format(app_vertex))
[docs] @overrides(AbstractSplitterSlice.create_machine_vertices) def create_machine_vertices(self, resource_tracker, machine_graph): app_vertex = self._governed_app_vertex app_vertex.synapse_recorder.add_region_offset( len(app_vertex.neuron_recorder.get_recordable_variables())) return super(SplitterAbstractPopulationVertexSlice, self)\ .create_machine_vertices(resource_tracker, machine_graph)
[docs] @overrides(AbstractSplitterSlice.get_out_going_vertices) def get_out_going_vertices(self, edge, outgoing_edge_partition): return self._get_map([MachineEdge])
[docs] @overrides(AbstractSplitterSlice.get_in_coming_vertices) def get_in_coming_vertices( self, edge, outgoing_edge_partition, src_machine_vertex): return self._get_map([MachineEdge])
@property def n_synapse_vertices(self): """ Return the number of synapse vertices per neuron vertex :rtype: int """ return 1
[docs] @overrides(AbstractSplitterSlice.create_machine_vertex) def create_machine_vertex( self, vertex_slice, resources, label, remaining_constraints): if self.__ring_buffer_shifts is None: app_vertex = self._governed_app_vertex self.__ring_buffer_shifts = app_vertex.get_ring_buffer_shifts( app_vertex.incoming_projections) self.__weight_scales = app_vertex.get_weight_scales( self.__ring_buffer_shifts) index = self.__next_index self.__next_index += 1 return PopulationMachineVertex( resources, label, remaining_constraints, self._governed_app_vertex, vertex_slice, index, self.__ring_buffer_shifts, self.__weight_scales, self.__all_syn_block_size(vertex_slice), self.__structural_size(vertex_slice))
[docs] @overrides(AbstractSplitterSlice.get_resources_used_by_atoms) def get_resources_used_by_atoms(self, vertex_slice): """ Gets the resources of a slice of atoms :param ~pacman.model.graphs.common.Slice vertex_slice: the slice :rtype: ~pacman.model.resources.ResourceContainer """ # pylint: disable=arguments-differ variable_sdram = self.__get_variable_sdram(vertex_slice) constant_sdram = self.__get_constant_sdram(vertex_slice) sdram = MultiRegionSDRAM() sdram.nest(len(PopulationMachineVertex.REGIONS) + 1, variable_sdram) sdram.merge(constant_sdram) # set resources required from this object container = ResourceContainer( sdram=sdram, dtcm=self.__get_dtcm_cost(vertex_slice), cpu_cycles=self.__get_cpu_cost(vertex_slice)) # return the total resources. return container
def __get_variable_sdram(self, vertex_slice): """ returns the variable sdram from the recorders :param ~pacman.model.graphs.common.Slice vertex_slice: the atom slice for recording sdram :return: the variable sdram used by the neuron recorder :rtype: VariableSDRAM """ s_dynamics = self._governed_app_vertex.synapse_dynamics if isinstance(s_dynamics, AbstractSynapseDynamicsStructural): max_rewires_per_ts = s_dynamics.get_max_rewires_per_ts() self._governed_app_vertex.synapse_recorder.set_max_rewires_per_ts( max_rewires_per_ts) return ( self._governed_app_vertex.get_neuron_variable_sdram(vertex_slice) + self._governed_app_vertex.get_synapse_variable_sdram(vertex_slice)) def __get_constant_sdram(self, vertex_slice): """ returns the constant sdram used by the vertex slice. :param ~pacman.model.graphs.common.Slice vertex_slice: the atoms to get constant sdram of :rtype: ~pacman.model.resources.MultiRegionSDRAM """ n_record = ( len(self._governed_app_vertex.neuron_recordables) + len(self._governed_app_vertex.synapse_recordables)) n_provenance = ( NeuronProvenance.N_ITEMS + SynapseProvenance.N_ITEMS + MainProvenance.N_ITEMS + SpikeProcessingProvenance.N_ITEMS) sdram = MultiRegionSDRAM() sdram.merge(self._governed_app_vertex.get_common_constant_sdram( n_record, n_provenance, PopulationMachineVertex.COMMON_REGIONS)) sdram.merge(self._governed_app_vertex.get_neuron_constant_sdram( vertex_slice, PopulationMachineVertex.NEURON_REGIONS)) sdram.merge(self.__get_synapse_constant_sdram(vertex_slice)) return sdram def __get_synapse_constant_sdram(self, vertex_slice): """ Get the amount of fixed SDRAM used by synapse parts :param ~pacman.model.graphs.common.Slice vertex_slice: The slice of neurons to get the size of :rtype: ~pacman.model.resources.MultiRegionSDRAM """ sdram = MultiRegionSDRAM() app_vertex = self._governed_app_vertex sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.synapse_params, app_vertex.get_synapse_params_size()) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.synapse_dynamics, app_vertex.get_synapse_dynamics_size(vertex_slice)) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.structural_dynamics, self.__structural_size(vertex_slice)) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.synaptic_matrix, self.__all_syn_block_size(vertex_slice)) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.direct_matrix, app_vertex.all_single_syn_size) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.pop_table, MasterPopTableAsBinarySearch.get_master_population_table_size( app_vertex.incoming_projections)) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.connection_builder, self.__synapse_expander_size()) sdram.merge(self.__bitfield_size()) return sdram def __all_syn_block_size(self, vertex_slice): """ Work out how much SDRAM is needed for all the synapses :param ~pacman.model.graphs.common.Slice vertex_slice: The slice of neurons to get the size of :rtype: int """ if vertex_slice in self.__all_syn_block_sz: return self.__all_syn_block_sz[vertex_slice] all_syn_block_sz = self._governed_app_vertex.get_synapses_size( vertex_slice, self._governed_app_vertex.incoming_projections) self.__all_syn_block_sz[vertex_slice] = all_syn_block_sz return all_syn_block_sz def __structural_size(self, vertex_slice): """ Work out how much SDRAM is needed by the structural plasticity data :param ~pacman.model.graphs.common.Slice vertex_slice: The slice of neurons to get the size of :rtype: int """ if vertex_slice in self.__structural_sz: return self.__structural_sz[vertex_slice] structural_sz = self._governed_app_vertex.get_structural_dynamics_size( vertex_slice, self._governed_app_vertex.incoming_projections) self.__structural_sz[vertex_slice] = structural_sz return structural_sz def __synapse_expander_size(self): """ Work out how much SDRAM is needed for the synapse expander :rtype: int """ if self.__synapse_expander_sz is None: self.__synapse_expander_sz = \ self._governed_app_vertex.get_synapse_expander_size( self._governed_app_vertex.incoming_projections) return self.__synapse_expander_sz def __bitfield_size(self): """ Work out how much SDRAM is needed by the bit fields :rtype: ~pacman.model.resources.MultiRegionSDRAM """ if self.__bitfield_sz is None: sdram = MultiRegionSDRAM() projections = self._governed_app_vertex.incoming_projections sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.bitfield_filter, get_estimated_sdram_for_bit_field_region(projections)) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.bitfield_key_map, get_estimated_sdram_for_key_region(projections)) sdram.add_cost( PopulationMachineVertex.SYNAPSE_REGIONS.bitfield_builder, exact_sdram_for_bit_field_builder_region()) self.__bitfield_sz = sdram return self.__bitfield_sz def __get_dtcm_cost(self, vertex_slice): """ get the dtcm cost for the slice of atoms :param Slice vertex_slice: atom slice for dtcm calc. :rtype: DTCMResource """ return DTCMResource( self._governed_app_vertex.get_common_dtcm() + self._governed_app_vertex.get_neuron_dtcm(vertex_slice) + self._governed_app_vertex.get_synapse_dtcm(vertex_slice)) def __get_cpu_cost(self, vertex_slice): """ get cpu cost for a slice of atoms :param Slice vertex_slice: slice of atoms :rtype: CPUCyclesPerTickResourcer """ return CPUCyclesPerTickResource( self._governed_app_vertex.get_common_cpu() + self._governed_app_vertex.get_neuron_cpu(vertex_slice) + self._governed_app_vertex.get_synapse_cpu(vertex_slice))
[docs] @overrides(AbstractSplitterSlice.check_supported_constraints) def check_supported_constraints(self): utility_calls.check_algorithm_can_support_constraints( constrained_vertices=[self._governed_app_vertex], supported_constraints=[ MaxVertexAtomsConstraint, FixedVertexAtomsConstraint], abstract_constraint_type=AbstractPartitionerConstraint)
[docs] @overrides(AbstractSplitterSlice.reset_called) def reset_called(self): super(SplitterAbstractPopulationVertexSlice, self).reset_called() self.__ring_buffer_shifts = None self.__weight_scales = None self.__all_syn_block_sz = dict() self.__structural_sz = dict() self.__next_index = 0