From a32cb6a0c31879078be0f24d9085bb8dcf3b2e5d Mon Sep 17 00:00:00 2001 From: Andrew Rowley Date: Thu, 1 Aug 2024 10:29:00 +0100 Subject: [PATCH 1/9] Add changes to allow fast download of synapses, and test --- spynnaker/pyNN/__init__.py | 6 +- .../neural_projections/synapse_information.py | 28 +++++- .../neuron/population_machine_synapses.py | 13 ++- .../pyNN/models/neuron/synaptic_matrices.py | 20 +++++ .../pyNN/models/neuron/synaptic_matrix_app.py | 89 +++++++++++++------ spynnaker/pyNN/models/projection.py | 15 +++- .../test_split_struct_stdp.py | 6 +- ...test_IF_cond_exp_stdp_mad_pair_additive.py | 3 +- ..._IF_cond_exp_stdp_nearest_pair_additive.py | 3 +- .../test_stdp/test_IF_curr_delta_stdp.py | 4 +- .../test_STDP_nearest_pair_additive.py | 3 +- .../test_STDP_nearest_pair_multiplicative.py | 3 +- .../test_stdp/test_STDP_neuromodulation.py | 3 +- .../test_stdp/test_STDP_pair_additive.py | 3 +- .../test_structural_elimination_to_empty.py | 3 +- .../test_structural_formation_to_full.py | 3 +- .../test_struct_pl/test_structural_shared.py | 6 +- .../test_structural_with_stdp.py | 6 +- 18 files changed, 169 insertions(+), 48 deletions(-) diff --git a/spynnaker/pyNN/__init__.py b/spynnaker/pyNN/__init__.py index c0096541a58..f85342759f3 100644 --- a/spynnaker/pyNN/__init__.py +++ b/spynnaker/pyNN/__init__.py @@ -350,7 +350,8 @@ def Projection( connector: AbstractConnector, synapse_type: Optional[AbstractStaticSynapseDynamics] = None, source: None = None, receptor_type: str = "excitatory", - space: Optional[Space] = None, label: Optional[str] = None): + space: Optional[Space] = None, label: Optional[str] = None, + download_synapses: bool = False) -> SpiNNakerProjection: """ Used to support PEP 8 spelling correctly. @@ -368,6 +369,7 @@ def Projection( :type space: ~pyNN.space.Space or None :param label: the label :type label: str or None + :param bool download_synapses: whether to download synapses :return: a projection object for SpiNNaker :rtype: ~spynnaker.pyNN.models.projection.Projection """ @@ -376,7 +378,7 @@ def Projection( pre_synaptic_population=presynaptic_population, post_synaptic_population=postsynaptic_population, connector=connector, synapse_type=synapse_type, source=source, receptor_type=receptor_type, - space=space, label=label) + space=space, label=label, download_synapses=download_synapses) def _create_overloaded_functions(spinnaker_simulator: SpiNNaker): diff --git a/spynnaker/pyNN/models/neural_projections/synapse_information.py b/spynnaker/pyNN/models/neural_projections/synapse_information.py index dd19faec125..fc5f458a209 100644 --- a/spynnaker/pyNN/models/neural_projections/synapse_information.py +++ b/spynnaker/pyNN/models/neural_projections/synapse_information.py @@ -46,7 +46,8 @@ class SynapseInformation(object): "__weights", "__delays", "__pre_run_connection_holders", - "__synapse_type_from_dynamics") + "__synapse_type_from_dynamics", + "__download_on_pause") def __init__(self, connector: AbstractConnector, pre_population: Union[Population, PopulationView], @@ -56,7 +57,8 @@ def __init__(self, connector: AbstractConnector, synapse_type: int, receptor_type: str, synapse_type_from_dynamics: bool, weights: Weight_Types = None, - delays: Delay_Types = None): + delays: Delay_Types = None, + download_on_pause: bool = False): """ :param AbstractConnector connector: The connector connected to the synapse @@ -78,6 +80,8 @@ def __init__(self, connector: AbstractConnector, :type weights: float or list(float) or ~numpy.ndarray(float) or None :param delays: The total synaptic delays :type delays: float or list(float) or ~numpy.ndarray(float) or None + :param bool download_on_pause: + Whether to download the synapse matrix when the simulation pauses """ self.__connector = connector self.__pre_population = pre_population @@ -91,6 +95,7 @@ def __init__(self, connector: AbstractConnector, self.__weights = weights self.__delays = delays self.__synapse_type_from_dynamics = synapse_type_from_dynamics + self.__download_on_pause = download_on_pause # Make a list of holders to be updated self.__pre_run_connection_holders: List[ConnectionHolder] = list() @@ -285,3 +290,22 @@ def synapse_type_from_dynamics(self) -> bool: :rtype: bool """ return self.__synapse_type_from_dynamics + + @property + def download_on_pause(self) -> bool: + """ + Whether to download the synapse matrix when the simulation pauses. + + :rtype: bool + """ + return self.__download_on_pause + + @download_on_pause.setter + def download_on_pause(self, download_on_pause: bool): + """ + Set whether to download the synapse matrix when the simulation pauses. + + :param bool download_on_pause: + Whether to download the synapse matrix when the simulation pauses + """ + self.__download_on_pause = download_on_pause diff --git a/spynnaker/pyNN/models/neuron/population_machine_synapses.py b/spynnaker/pyNN/models/neuron/population_machine_synapses.py index fee6353fdf1..904a64aaf34 100644 --- a/spynnaker/pyNN/models/neuron/population_machine_synapses.py +++ b/spynnaker/pyNN/models/neuron/population_machine_synapses.py @@ -29,6 +29,8 @@ from spinn_front_end_common.abstract_models import ( AbstractSupportsBitFieldRoutingCompression) from spinn_front_end_common.interface.ds import DataSpecificationBase +from spinn_front_end_common.interface.buffer_management.buffer_models import ( + AbstractReceiveRegionsToHost) from spynnaker.pyNN.models.neuron.synapse_dynamics import ( AbstractSynapseDynamicsStructural, AbstractSDRAMSynapseDynamics) @@ -51,7 +53,7 @@ class PopulationMachineSynapses( PopulationMachineSynapsesProvenance, AbstractSupportsBitFieldRoutingCompression, AbstractSynapseExpandable, - HasSynapses, allow_derivation=True): + HasSynapses, AbstractReceiveRegionsToHost, allow_derivation=True): """ Mix-in for machine vertices that contain synapses. """ @@ -266,3 +268,12 @@ def max_gen_data(self) -> int: @overrides(AbstractSynapseExpandable.bit_field_size) def bit_field_size(self) -> int: return self._synaptic_matrices.bit_field_size + + @overrides(AbstractReceiveRegionsToHost.get_download_regions) + def get_download_regions( + self, placement: Placement) -> Sequence[Tuple[int, int]]: + + # Make sure not to overwrite the recording regions + start_index = len(self._pop_vertex.get_recording_variables()) + return self._synaptic_matrices.get_download_regions( + placement, start_index) diff --git a/spynnaker/pyNN/models/neuron/synaptic_matrices.py b/spynnaker/pyNN/models/neuron/synaptic_matrices.py index 1a82dbfb628..6e995c32c67 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_matrices.py +++ b/spynnaker/pyNN/models/neuron/synaptic_matrices.py @@ -566,3 +566,23 @@ def get_index(self, app_edge: ProjectionApplicationEdge, """ matrix = self.__matrices[app_edge, synapse_info] return matrix.get_index() + + def get_download_regions( + self, placement: Placement, + start_index: int) -> Tuple[int, int, int]: + """ + Get the regions that need to be downloaded. + + :param ~pacman.model.placements.Placement placement: + The placement of the vertex + :param int start_index: + The first index to use in the region identifiers + + :return: The index, the start address and the size of the regions + """ + regions = list() + for matrix in self.__matrices.values(): + mat_regions = matrix.get_download_regions(placement, start_index) + regions.extend(mat_regions) + start_index += len(mat_regions) + return regions diff --git a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py index de9a4a13793..0c4be666baa 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py +++ b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py @@ -24,6 +24,7 @@ from spinn_front_end_common.utilities.helpful_functions import ( locate_memory_region_for_placement) from spinn_front_end_common.utilities.constants import BYTES_PER_WORD +from spinn_front_end_common.interface.buffer_management import BufferManager from spynnaker.pyNN.data import SpynnakerDataView from spynnaker.pyNN.models.neuron.synapse_dynamics import ( @@ -82,7 +83,11 @@ class SynapticMatrixApp(object): # table "__delay_index", # The number of bits to use for neuron IDs - "__max_atoms_per_core") + "__max_atoms_per_core", + # The download index for the undelayed synaptic matrix + "__download_index", + # The download index for the delayed synaptic matrix + "__download_delay_index") def __init__( self, synapse_info: SynapseInformation, @@ -137,6 +142,9 @@ def __init__( self.__index: Optional[int] = None self.__delay_index: Optional[int] = None + self.__download_index = None + self.__download_delay_index = None + @property def gen_size(self) -> int: """ @@ -378,20 +386,6 @@ def get_generator_data(self) -> GeneratorData: self.__app_edge, self.__synapse_info, self.__max_row_info, max_pre_atoms_per_core, self.__max_atoms_per_core) - def get_connections(self, placement: Placement) -> List[NDArray]: - """ - Get the connections for this matrix from the machine. - - :param ~pacman.model.placements.Placement placement: - Where the matrix is on the machine - :return: A list of arrays of connections, each with dtype - :py:const:`~.NUMPY_CONNECTORS_DTYPE` - :rtype: list(~numpy.ndarray) - """ - synapses_address = locate_memory_region_for_placement( - placement, self.__synaptic_matrix_region) - return self.__read_connections(placement, synapses_address) - def read_generated_connection_holders(self, placement: Placement): """ Read any pre-run connection holders after data has been generated. @@ -406,40 +400,53 @@ def read_generated_connection_holders(self, placement: Placement): for holder in self.__synapse_info.pre_run_connection_holders: holder.add_connections(conns) - def __read_connections( - self, placement: Placement, - synapses_address: int) -> List[NDArray]: + def get_connections(self, placement: Placement) -> List[NDArray]: """ Read connections from an address on the machine. :param ~pacman.model.placements.Placement placement: Where the matrix is on the machine - :param int synapses_address: - The base address of the synaptic matrix region :return: A list of arrays of connections, each with dtype :py:const:`~.NUMPY_CONNECTORS_DTYPE` :rtype: list(~numpy.ndarray) """ connections = list() + synapses_address = None + buffers = None + if (self.__download_index is None and + self.__download_delay_index is None): + synapses_address: int = locate_memory_region_for_placement( + placement, self.__synaptic_matrix_region) + else: + buffers: BufferManager = SpynnakerDataView().get_buffer_manager() + splitter = self.__app_edge.post_vertex.splitter vertex_slice = placement.vertex.vertex_slice if self.__syn_mat_offset is not None: + if self.__download_index is not None: + block = buffers.get_data_by_placement( + placement, self.__download_index)[0] + else: + block = self.__get_block(placement, synapses_address) connections.append(convert_to_connections( self.__synapse_info, vertex_slice, self.__app_edge.pre_vertex.n_atoms, self.__max_row_info.undelayed_max_words, - self.__n_synapse_types, self.__weight_scales, - self.__get_block(placement, synapses_address), False, + self.__n_synapse_types, self.__weight_scales, block, False, splitter.max_support_delay(), self.__max_atoms_per_core)) if self.__delay_syn_mat_offset is not None: + if self.__download_delay_index is not None: + block = buffers.get_data_by_placement( + placement, self.__download_delay_index)[0] + else: + block = self.__get_delayed_block(placement, synapses_address) connections.append(convert_to_connections( self.__synapse_info, vertex_slice, self.__app_edge.pre_vertex.n_atoms, - self.__max_row_info.delayed_max_words, self.__n_synapse_types, - self.__weight_scales, - self.__get_delayed_block(placement, synapses_address), True, + self.__max_row_info.delayed_max_words, + self.__n_synapse_types, self.__weight_scales, block, True, splitter.max_support_delay(), self.__max_atoms_per_core)) return connections @@ -488,3 +495,35 @@ def get_index(self) -> int: if self.__index is None: raise RuntimeError("master pop table space not yet reserved") return self.__index + + def get_download_regions( + self, placement: Placement, + start_index: int) -> List[Tuple[int, int, int]]: + """ + Get the data regions that should be downloaded when the simulation + pauses. + + :param ~pacman.model.placements.Placement placement: + The placement of the vertex + :param int start_index: + The index to use for the first region to download + + :return: A list of tuples of (index, address, size) to download + """ + if not self.__synapse_info.download_on_pause: + return [] + synapses_address = locate_memory_region_for_placement( + placement, self.__synaptic_matrix_region) + regions = list() + if self.__syn_mat_offset is not None: + regions.append((start_index, + synapses_address + self.__syn_mat_offset, + self.__matrix_size)) + self.__download_index = start_index + start_index += 1 + if self.__delay_syn_mat_offset is not None: + regions.append((start_index, + synapses_address + self.__delay_syn_mat_offset, + self.__delay_matrix_size)) + self.__download_delay_index = start_index + return regions diff --git a/spynnaker/pyNN/models/projection.py b/spynnaker/pyNN/models/projection.py index 638462423b8..3a49943a838 100644 --- a/spynnaker/pyNN/models/projection.py +++ b/spynnaker/pyNN/models/projection.py @@ -86,7 +86,8 @@ def __init__( synapse_type: Optional[AbstractSynapseDynamics] = None, source: None = None, receptor_type: str = "excitatory", space: Optional[PyNNSpace] = None, - label: Optional[str] = None): + label: Optional[str] = None, + download_synapses: bool = False): """ :param ~spynnaker.pyNN.models.populations.PopulationBase \ pre_synaptic_population: @@ -98,6 +99,7 @@ def __init__( :param str receptor_type: :param ~pyNN.space.Space space: :param str label: + :param bool download_synapses: """ # pylint: disable=too-many-arguments if source is not None: @@ -167,7 +169,8 @@ def __init__( connector, pre_synaptic_population, post_synaptic_population, pre_is_view, post_is_view, synapse_dynamics, synapse_id, receptor_type, synapse_id_from_dynamics, - synapse_dynamics.weight, synapse_dynamics.delay) + synapse_dynamics.weight, synapse_dynamics.delay, + download_synapses) # Set projection information in connector connector.set_projection_information(self.__synapse_information) @@ -539,3 +542,11 @@ def size(self, gather=True): # @UnusedVariable """ # TODO _we_dont_do_this_now() + + def set_download_synapses(self, download_synapses): + """ + Set whether synapses should be downloaded when the simulation pauses. + + :param bool download_synapses: Whether to download synapses or not + """ + self.__synapse_information.download_synapses = download_synapses diff --git a/spynnaker_integration_tests/test_split_various/test_split_struct_stdp.py b/spynnaker_integration_tests/test_split_various/test_split_struct_stdp.py index 82a34aae0f1..27a57380ea8 100644 --- a/spynnaker_integration_tests/test_split_various/test_split_struct_stdp.py +++ b/spynnaker_integration_tests/test_split_various/test_split_struct_stdp.py @@ -58,7 +58,8 @@ def split_structural_with_stdp(): tau_plus, tau_minus, A_plus, A_minus), weight_dependence=p.AdditiveWeightDependence(w_min, w_max), f_rew=1000, initial_weight=w_init_1, initial_delay=delay_1, - s_max=1, seed=0, weight=0.0, delay=1.0)) + s_max=1, seed=0, weight=0.0, delay=1.0), + download_synapses=True) proj_2 = p.Projection( stim, pop_2, p.FromListConnector([]), p.StructuralMechanismSTDP( partner_selection=p.RandomSelection(), @@ -68,7 +69,8 @@ def split_structural_with_stdp(): tau_plus, tau_minus, A_plus, A_minus), weight_dependence=p.AdditiveWeightDependence(w_min, w_max), f_rew=1000, initial_weight=w_init_2, initial_delay=delay_2, - s_max=1, seed=0, weight=0.0, delay=1.0)) + s_max=1, seed=0, weight=0.0, delay=1.0), + download_synapses=True) proj_3 = p.Projection( stim, pop_3, p.FromListConnector([(0, 0)]), p.StructuralMechanismSTDP( diff --git a/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_mad_pair_additive.py b/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_mad_pair_additive.py index b2f6e8cc239..8c710ec571c 100644 --- a/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_mad_pair_additive.py +++ b/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_mad_pair_additive.py @@ -78,7 +78,8 @@ def potentiation_and_depression(self): plastic_synapse = p.Projection(pre_pop, post_pop, p.OneToOneConnector(), synapse_type=syn_plas, - receptor_type='excitatory') + receptor_type='excitatory', + download_synapses=True) # Record the spikes post_pop.record("spikes") diff --git a/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_nearest_pair_additive.py b/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_nearest_pair_additive.py index 61fffe73c16..f3c103b7027 100644 --- a/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_nearest_pair_additive.py +++ b/spynnaker_integration_tests/test_stdp/test_IF_cond_exp_stdp_nearest_pair_additive.py @@ -76,7 +76,8 @@ def potentiation_and_depression(self): plastic_synapse = p.Projection(pre_pop, post_pop, p.OneToOneConnector(), synapse_type=syn_plas, - receptor_type='excitatory') + receptor_type='excitatory', + download_synapses=True) # Record the spikes post_pop.record("spikes") diff --git a/spynnaker_integration_tests/test_stdp/test_IF_curr_delta_stdp.py b/spynnaker_integration_tests/test_stdp/test_IF_curr_delta_stdp.py index 0ce6fb41c8a..6bf01294652 100644 --- a/spynnaker_integration_tests/test_stdp/test_IF_curr_delta_stdp.py +++ b/spynnaker_integration_tests/test_stdp/test_IF_curr_delta_stdp.py @@ -194,11 +194,11 @@ def nearest_pair_additive_delta(self): injector_proj_exc = sim.Projection( injector_neurons_exc, output_neuron, sim.AllToAllConnector(allow_self_connections=True), - stdp_model, receptor_type='excitatory') + stdp_model, receptor_type='excitatory', download_synapses=True) injector_proj_inh = sim.Projection( injector_neurons_inh, output_neuron, sim.AllToAllConnector(allow_self_connections=True), - stdp_model2, receptor_type='inhibitory') + stdp_model2, receptor_type='inhibitory', download_synapses=True) sim.run(runtime) diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py index 5c08035e0cd..387f20e7ea9 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_additive.py @@ -76,7 +76,8 @@ def potentiation_and_depression(self): plastic_synapse = p.Projection(pre_pop, post_pop, p.OneToOneConnector(), synapse_type=syn_plas, - receptor_type='excitatory') + receptor_type='excitatory', + download_synapses=True) # Record the spikes post_pop.record("spikes") diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py index aa6af556da4..60a643fde4c 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_nearest_pair_multiplicative.py @@ -76,7 +76,8 @@ def potentiation_and_depression(self): plastic_synapse = p.Projection(pre_pop, post_pop, p.OneToOneConnector(), synapse_type=syn_plas, - receptor_type='excitatory') + receptor_type='excitatory', + download_synapses=True) # Record the spikes post_pop.record("spikes") diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py b/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py index 43ac5410e7b..48803f51c8f 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_neuromodulation.py @@ -106,7 +106,8 @@ def neuromodulation(self): pre_pop, post_pop, sim.AllToAllConnector(), synapse_type=synapse_dynamics, - receptor_type='excitatory', label='Pre-post projection') + receptor_type='excitatory', label='Pre-post projection', + download_synapses=True) # Create dopaminergic connection sim.Projection( diff --git a/spynnaker_integration_tests/test_stdp/test_STDP_pair_additive.py b/spynnaker_integration_tests/test_stdp/test_STDP_pair_additive.py index ca09bf8e981..d93634521a4 100644 --- a/spynnaker_integration_tests/test_stdp/test_STDP_pair_additive.py +++ b/spynnaker_integration_tests/test_stdp/test_STDP_pair_additive.py @@ -136,7 +136,8 @@ def potentiation_and_depression(): plastic_synapse = p.Projection(pre_pop, post_pop, p.OneToOneConnector(), synapse_type=syn_plas, - receptor_type='excitatory') + receptor_type='excitatory', + download_synapses=True) # Record the spikes post_pop.record("spikes") diff --git a/spynnaker_integration_tests/test_struct_pl/test_structural_elimination_to_empty.py b/spynnaker_integration_tests/test_struct_pl/test_structural_elimination_to_empty.py index ac200f19861..ce98b83738c 100644 --- a/spynnaker_integration_tests/test_struct_pl/test_structural_elimination_to_empty.py +++ b/spynnaker_integration_tests/test_struct_pl/test_structural_elimination_to_empty.py @@ -33,7 +33,8 @@ def structural_eliminate_to_empty(): formation=p.DistanceDependentFormation([3, 3], 0.0), elimination=p.RandomByWeightElimination(4.0, 1.0, 1.0), f_rew=1000, initial_weight=4.0, initial_delay=3.0, - s_max=9, seed=0, weight=0.0, delay=1.0)) + s_max=9, seed=0, weight=0.0, delay=1.0), + download_synapses=True) pop.record("rewiring") diff --git a/spynnaker_integration_tests/test_struct_pl/test_structural_formation_to_full.py b/spynnaker_integration_tests/test_struct_pl/test_structural_formation_to_full.py index b01f5d6e934..024ab742826 100644 --- a/spynnaker_integration_tests/test_struct_pl/test_structural_formation_to_full.py +++ b/spynnaker_integration_tests/test_struct_pl/test_structural_formation_to_full.py @@ -30,7 +30,8 @@ def structural_formation_to_full(): formation=p.DistanceDependentFormation([2, 2], 1.0), elimination=p.RandomByWeightElimination(4.0, 0, 0), f_rew=1000, initial_weight=4.0, initial_delay=3.0, - s_max=4, seed=0, weight=0.0, delay=1.0, with_replacement=False)) + s_max=4, seed=0, weight=0.0, delay=1.0, with_replacement=False), + download_synapses=True) pop.record("rewiring") diff --git a/spynnaker_integration_tests/test_struct_pl/test_structural_shared.py b/spynnaker_integration_tests/test_struct_pl/test_structural_shared.py index c9ecde57a98..42fa5a2c43a 100644 --- a/spynnaker_integration_tests/test_struct_pl/test_structural_shared.py +++ b/spynnaker_integration_tests/test_struct_pl/test_structural_shared.py @@ -50,9 +50,11 @@ def structural_shared(): f_rew=1000, initial_weight=2.0, initial_delay=5.0, s_max=1, seed=0, weight=0.0, delay=1.0) proj = p.Projection( - stim, pop, p.FromListConnector([]), struct_pl_static) + stim, pop, p.FromListConnector([]), struct_pl_static, + download_synapses=True) proj_2 = p.Projection( - stim, pop_2, p.FromListConnector([]), struct_pl_static) + stim, pop_2, p.FromListConnector([]), struct_pl_static, + download_synapses=True) proj_3 = p.Projection( stim, pop_3, p.FromListConnector([(0, 0)]), struct_pl_stdp) proj_4 = p.Projection( diff --git a/spynnaker_integration_tests/test_struct_pl/test_structural_with_stdp.py b/spynnaker_integration_tests/test_struct_pl/test_structural_with_stdp.py index 8ec9d459bba..8e171bc4cfc 100644 --- a/spynnaker_integration_tests/test_struct_pl/test_structural_with_stdp.py +++ b/spynnaker_integration_tests/test_struct_pl/test_structural_with_stdp.py @@ -48,7 +48,8 @@ def structural_with_stdp(): tau_plus, tau_minus, A_plus, A_minus), weight_dependence=p.AdditiveWeightDependence(w_min, w_max), f_rew=1000, initial_weight=w_init_1, initial_delay=delay_1, - s_max=1, seed=0, weight=0.0, delay=1.0)) + s_max=1, seed=0, weight=0.0, delay=1.0), + download_synapses=True) proj_2 = p.Projection( stim, pop_2, p.FromListConnector([]), p.StructuralMechanismSTDP( partner_selection=p.RandomSelection(), @@ -58,7 +59,8 @@ def structural_with_stdp(): tau_plus, tau_minus, A_plus, A_minus), weight_dependence=p.AdditiveWeightDependence(w_min, w_max), f_rew=1000, initial_weight=w_init_2, initial_delay=delay_2, - s_max=1, seed=0, weight=0.0, delay=1.0)) + s_max=1, seed=0, weight=0.0, delay=1.0), + download_synapses=True) proj_3 = p.Projection( stim, pop_3, p.FromListConnector([(0, 0)]), p.StructuralMechanismSTDP( From 73b140890492a604a3710142cf118b2af886d78f Mon Sep 17 00:00:00 2001 From: Andrew Rowley Date: Thu, 1 Aug 2024 10:40:31 +0100 Subject: [PATCH 2/9] Get the name of the property right! --- spynnaker/pyNN/models/projection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spynnaker/pyNN/models/projection.py b/spynnaker/pyNN/models/projection.py index 3a49943a838..77cbcacff38 100644 --- a/spynnaker/pyNN/models/projection.py +++ b/spynnaker/pyNN/models/projection.py @@ -549,4 +549,4 @@ def set_download_synapses(self, download_synapses): :param bool download_synapses: Whether to download synapses or not """ - self.__synapse_information.download_synapses = download_synapses + self.__synapse_information.download_on_pause = download_synapses From 576237fc683ba350b985dccbd175533b4036a7fb Mon Sep 17 00:00:00 2001 From: Andrew Rowley Date: Thu, 1 Aug 2024 10:58:01 +0100 Subject: [PATCH 3/9] Fix types --- .../models/neuron/population_machine_synapses.py | 2 +- .../pyNN/models/neuron/synaptic_matrices.py | 2 +- .../pyNN/models/neuron/synaptic_matrix_app.py | 16 ++++++++++------ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/spynnaker/pyNN/models/neuron/population_machine_synapses.py b/spynnaker/pyNN/models/neuron/population_machine_synapses.py index 904a64aaf34..0dfaeccda3e 100644 --- a/spynnaker/pyNN/models/neuron/population_machine_synapses.py +++ b/spynnaker/pyNN/models/neuron/population_machine_synapses.py @@ -271,7 +271,7 @@ def bit_field_size(self) -> int: @overrides(AbstractReceiveRegionsToHost.get_download_regions) def get_download_regions( - self, placement: Placement) -> Sequence[Tuple[int, int]]: + self, placement: Placement) -> Sequence[Tuple[int, int, int]]: # Make sure not to overwrite the recording regions start_index = len(self._pop_vertex.get_recording_variables()) diff --git a/spynnaker/pyNN/models/neuron/synaptic_matrices.py b/spynnaker/pyNN/models/neuron/synaptic_matrices.py index 6e995c32c67..b562876332c 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_matrices.py +++ b/spynnaker/pyNN/models/neuron/synaptic_matrices.py @@ -569,7 +569,7 @@ def get_index(self, app_edge: ProjectionApplicationEdge, def get_download_regions( self, placement: Placement, - start_index: int) -> Tuple[int, int, int]: + start_index: int) -> List[Tuple[int, int, int]]: """ Get the regions that need to be downloaded. diff --git a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py index 0c4be666baa..cbfb87f68b0 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py +++ b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py @@ -142,8 +142,8 @@ def __init__( self.__index: Optional[int] = None self.__delay_index: Optional[int] = None - self.__download_index = None - self.__download_delay_index = None + self.__download_index: Optional[int] = None + self.__download_delay_index: Optional[int] = None @property def gen_size(self) -> int: @@ -412,22 +412,24 @@ def get_connections(self, placement: Placement) -> List[NDArray]: """ connections = list() - synapses_address = None - buffers = None + synapses_address: Optional[int] = None + buffers: Optional[BufferManager] = None if (self.__download_index is None and self.__download_delay_index is None): - synapses_address: int = locate_memory_region_for_placement( + synapses_address = locate_memory_region_for_placement( placement, self.__synaptic_matrix_region) else: - buffers: BufferManager = SpynnakerDataView().get_buffer_manager() + buffers = SpynnakerDataView().get_buffer_manager() splitter = self.__app_edge.post_vertex.splitter vertex_slice = placement.vertex.vertex_slice if self.__syn_mat_offset is not None: if self.__download_index is not None: + assert buffers is not None block = buffers.get_data_by_placement( placement, self.__download_index)[0] else: + assert synapses_address is not None block = self.__get_block(placement, synapses_address) connections.append(convert_to_connections( self.__synapse_info, vertex_slice, @@ -438,9 +440,11 @@ def get_connections(self, placement: Placement) -> List[NDArray]: if self.__delay_syn_mat_offset is not None: if self.__download_delay_index is not None: + assert buffers is not None block = buffers.get_data_by_placement( placement, self.__download_delay_index)[0] else: + assert synapses_address is not None block = self.__get_delayed_block(placement, synapses_address) connections.append(convert_to_connections( self.__synapse_info, vertex_slice, From de2aec074b74bded9f2e09f2058684ab326abfcb Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 16 Aug 2024 08:41:29 +0100 Subject: [PATCH 4/9] double download synapse test --- .../test_double_download_synapses.py | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 spynnaker_integration_tests/test_various/test_double_download_synapses.py diff --git a/spynnaker_integration_tests/test_various/test_double_download_synapses.py b/spynnaker_integration_tests/test_various/test_double_download_synapses.py new file mode 100644 index 00000000000..eca47faabad --- /dev/null +++ b/spynnaker_integration_tests/test_various/test_double_download_synapses.py @@ -0,0 +1,46 @@ +#!/usr/bin/python + +# 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 spinnaker_testbase import BaseTestCase +import pyNN.spiNNaker as sim + + +class TestDoubleDownloadSynapses(BaseTestCase): + + + def do_run(self): + sim.setup(timestep=1.0, n_boards_required=1) + pop = sim.Population(1, sim.IF_curr_exp(), label="pop_1") + input_pop = sim.Population( + 1, sim.SpikeSourceArray(spike_times=[0]), label="input") + sim.Projection(input_pop, pop, sim.OneToOneConnector(), + synapse_type=sim.StaticSynapse(weight=5, delay=1), + download_synapses=True) + sim.Projection(input_pop, pop, sim.OneToOneConnector(), + synapse_type=sim.StaticSynapse(weight=5, delay=1), + download_synapses=True) + pop.record(["spikes", "v"]) + sim.run(10) + + neo = pop.get_data(variables=["spikes", "v"]) + spikes = neo.segments[0].spiketrains + print(spikes) + v = neo.segments[0].filter(name='v')[0] + print(v) + sim.end() + + def test_run(self): + self.runsafe(self.do_run) From 54ad987c2c3bd2eeac1652bfd5c9958dff36ba23 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 16 Aug 2024 08:43:47 +0100 Subject: [PATCH 5/9] flake8 --- .../test_various/test_double_download_synapses.py | 1 - 1 file changed, 1 deletion(-) diff --git a/spynnaker_integration_tests/test_various/test_double_download_synapses.py b/spynnaker_integration_tests/test_various/test_double_download_synapses.py index eca47faabad..6e3b2338b27 100644 --- a/spynnaker_integration_tests/test_various/test_double_download_synapses.py +++ b/spynnaker_integration_tests/test_various/test_double_download_synapses.py @@ -20,7 +20,6 @@ class TestDoubleDownloadSynapses(BaseTestCase): - def do_run(self): sim.setup(timestep=1.0, n_boards_required=1) pop = sim.Population(1, sim.IF_curr_exp(), label="pop_1") From b885fcb753ad04fdea44ff2e7edf9a3c69fe1634 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Fri, 16 Aug 2024 15:47:58 +0100 Subject: [PATCH 6/9] download_regions after all recordable regions --- spynnaker/pyNN/models/neuron/population_machine_synapses.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spynnaker/pyNN/models/neuron/population_machine_synapses.py b/spynnaker/pyNN/models/neuron/population_machine_synapses.py index 0dfaeccda3e..467bcbe0eb1 100644 --- a/spynnaker/pyNN/models/neuron/population_machine_synapses.py +++ b/spynnaker/pyNN/models/neuron/population_machine_synapses.py @@ -274,6 +274,6 @@ def get_download_regions( self, placement: Placement) -> Sequence[Tuple[int, int, int]]: # Make sure not to overwrite the recording regions - start_index = len(self._pop_vertex.get_recording_variables()) + start_index = len(self._pop_vertex.get_recordable_variables()) return self._synaptic_matrices.get_download_regions( placement, start_index) From e5c954ce7f4d71165311c1340bd78b3992bbfab5 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 20 Aug 2024 11:06:16 +0100 Subject: [PATCH 7/9] remove unneeded test --- .../test_double_download_synapses.py | 45 ------------------- 1 file changed, 45 deletions(-) delete mode 100644 spynnaker_integration_tests/test_various/test_double_download_synapses.py diff --git a/spynnaker_integration_tests/test_various/test_double_download_synapses.py b/spynnaker_integration_tests/test_various/test_double_download_synapses.py deleted file mode 100644 index 6e3b2338b27..00000000000 --- a/spynnaker_integration_tests/test_various/test_double_download_synapses.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/python - -# 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 spinnaker_testbase import BaseTestCase -import pyNN.spiNNaker as sim - - -class TestDoubleDownloadSynapses(BaseTestCase): - - def do_run(self): - sim.setup(timestep=1.0, n_boards_required=1) - pop = sim.Population(1, sim.IF_curr_exp(), label="pop_1") - input_pop = sim.Population( - 1, sim.SpikeSourceArray(spike_times=[0]), label="input") - sim.Projection(input_pop, pop, sim.OneToOneConnector(), - synapse_type=sim.StaticSynapse(weight=5, delay=1), - download_synapses=True) - sim.Projection(input_pop, pop, sim.OneToOneConnector(), - synapse_type=sim.StaticSynapse(weight=5, delay=1), - download_synapses=True) - pop.record(["spikes", "v"]) - sim.run(10) - - neo = pop.get_data(variables=["spikes", "v"]) - spikes = neo.segments[0].spiketrains - print(spikes) - v = neo.segments[0].filter(name='v')[0] - print(v) - sim.end() - - def test_run(self): - self.runsafe(self.do_run) From 5eb7da842c232a5158f22f513057948a3546e5b8 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Tue, 20 Aug 2024 15:02:58 +0100 Subject: [PATCH 8/9] use only the lack block --- .../pyNN/models/neuron/synaptic_matrix_app.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py index cbfb87f68b0..2a1eaffae37 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py +++ b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py @@ -428,6 +428,13 @@ def get_connections(self, placement: Placement) -> List[NDArray]: assert buffers is not None block = buffers.get_data_by_placement( placement, self.__download_index)[0] + if len(block) > self.__matrix_size: + if len(block) % self.__matrix_size == 0: + block = block[-self.__matrix_size:] + else: + raise NotImplementedError( + f"Unexpected block size {len(block)} " + f"Expected a multiple of {self.__matrix_size}") else: assert synapses_address is not None block = self.__get_block(placement, synapses_address) @@ -437,12 +444,18 @@ def get_connections(self, placement: Placement) -> List[NDArray]: self.__max_row_info.undelayed_max_words, self.__n_synapse_types, self.__weight_scales, block, False, splitter.max_support_delay(), self.__max_atoms_per_core)) - if self.__delay_syn_mat_offset is not None: if self.__download_delay_index is not None: assert buffers is not None block = buffers.get_data_by_placement( placement, self.__download_delay_index)[0] + if len(block) > self.__delay_matrix_size: + if len(block) % self.__delay_matrix_size == 0: + block = block[-self.__delay_matrix_size:] + else: + raise NotImplementedError( + f"Unexpected block size {len(block)} " + f"Expected a multiple of {self.__delay_matrix_size}") else: assert synapses_address is not None block = self.__get_delayed_block(placement, synapses_address) From 816288bfe581d9745bf98f4bdde109dd254b92b4 Mon Sep 17 00:00:00 2001 From: "Christian Y. Brenninkmeijer" Date: Mon, 9 Sep 2024 07:50:15 +0100 Subject: [PATCH 9/9] buffers.get_last_data_by_placement --- .../pyNN/models/neuron/synaptic_matrix_app.py | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py index 2a1eaffae37..d3ad082a9f2 100644 --- a/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py +++ b/spynnaker/pyNN/models/neuron/synaptic_matrix_app.py @@ -426,15 +426,8 @@ def get_connections(self, placement: Placement) -> List[NDArray]: if self.__syn_mat_offset is not None: if self.__download_index is not None: assert buffers is not None - block = buffers.get_data_by_placement( - placement, self.__download_index)[0] - if len(block) > self.__matrix_size: - if len(block) % self.__matrix_size == 0: - block = block[-self.__matrix_size:] - else: - raise NotImplementedError( - f"Unexpected block size {len(block)} " - f"Expected a multiple of {self.__matrix_size}") + block, _ = buffers.get_last_data_by_placement( + placement, self.__download_index) else: assert synapses_address is not None block = self.__get_block(placement, synapses_address) @@ -447,15 +440,8 @@ def get_connections(self, placement: Placement) -> List[NDArray]: if self.__delay_syn_mat_offset is not None: if self.__download_delay_index is not None: assert buffers is not None - block = buffers.get_data_by_placement( - placement, self.__download_delay_index)[0] - if len(block) > self.__delay_matrix_size: - if len(block) % self.__delay_matrix_size == 0: - block = block[-self.__delay_matrix_size:] - else: - raise NotImplementedError( - f"Unexpected block size {len(block)} " - f"Expected a multiple of {self.__delay_matrix_size}") + block, _ = buffers.get_last_data_by_placement( + placement, self.__download_delay_index) else: assert synapses_address is not None block = self.__get_delayed_block(placement, synapses_address)