Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Region downloading #1474

Merged
merged 14 commits into from
Sep 9, 2024
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_recording_variables())
return self._synaptic_matrices.get_download_regions(
Christian-B marked this conversation as resolved.
Show resolved Hide resolved
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
93 changes: 68 additions & 25 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,57 @@ 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_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,
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_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,
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 +499,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