Skip to content

Commit

Permalink
Merge pull request #1474 from SpiNNakerManchester/region_downloading
Browse files Browse the repository at this point in the history
Region downloading
  • Loading branch information
Christian-B authored Sep 9, 2024
2 parents 73eafca + 816288b commit 6f5420e
Show file tree
Hide file tree
Showing 18 changed files with 173 additions and 49 deletions.
6 changes: 4 additions & 2 deletions spynnaker/pyNN/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
"""
Expand All @@ -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):
Expand Down
28 changes: 26 additions & 2 deletions spynnaker/pyNN/models/neural_projections/synapse_information.py
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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()
Expand Down Expand Up @@ -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
13 changes: 12 additions & 1 deletion spynnaker/pyNN/models/neuron/population_machine_synapses.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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.
"""
Expand Down Expand Up @@ -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)
20 changes: 20 additions & 0 deletions spynnaker/pyNN/models/neuron/synaptic_matrices.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
94 changes: 68 additions & 26 deletions spynnaker/pyNN/models/neuron/synaptic_matrix_app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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:
"""
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
15 changes: 13 additions & 2 deletions spynnaker/pyNN/models/projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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(),
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
Loading

0 comments on commit 6f5420e

Please sign in to comment.