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..467bcbe0eb1 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, int]]: + + # Make sure not to overwrite the recording regions + start_index = len(self._pop_vertex.get_recordable_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..b562876332c 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) -> List[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..d3ad082a9f2 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: Optional[int] = None + self.__download_delay_index: Optional[int] = 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,56 @@ 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: Optional[int] = None + buffers: Optional[BufferManager] = None + if (self.__download_index is None and + self.__download_delay_index is None): + synapses_address = locate_memory_region_for_placement( + placement, self.__synaptic_matrix_region) + else: + 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_last_data_by_placement( + placement, self.__download_index) + else: + assert synapses_address is not None + 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: + assert buffers is not None + 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) 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 +498,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..77cbcacff38 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_on_pause = 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(