Skip to content

Commit

Permalink
Merge pull request #1496 from SpiNNakerManchester/allow_different_par…
Browse files Browse the repository at this point in the history
…titions

Allow different partitions
  • Loading branch information
rowleya authored Oct 14, 2024
2 parents d7ca40d + b858cad commit be85530
Show file tree
Hide file tree
Showing 39 changed files with 461 additions and 200 deletions.
8 changes: 6 additions & 2 deletions spynnaker/pyNN/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
from spynnaker.pyNN.random_distribution import RandomDistribution
from spynnaker.pyNN.data import SpynnakerDataView
from spynnaker.pyNN.models.abstract_pynn_model import AbstractPyNNModel
from spynnaker.pyNN.utilities.constants import SPIKE_PARTITION_ID

# connections
# noinspection PyUnresolvedReferences
Expand Down Expand Up @@ -357,7 +358,8 @@ def Projection(
synapse_type: Optional[AbstractStaticSynapseDynamics] = None,
source: None = None, receptor_type: str = "excitatory",
space: Optional[Space] = None, label: Optional[str] = None,
download_synapses: bool = False) -> SpiNNakerProjection:
download_synapses: bool = False,
partition_id: str = SPIKE_PARTITION_ID) -> SpiNNakerProjection:
"""
Used to support PEP 8 spelling correctly.
Expand All @@ -376,6 +378,7 @@ def Projection(
:param label: the label
:type label: str or None
:param bool download_synapses: whether to download synapses
:param str partition_id: the partition id to use for the projection
:return: a projection object for SpiNNaker
:rtype: ~spynnaker.pyNN.models.projection.Projection
"""
Expand All @@ -384,7 +387,8 @@ 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, download_synapses=download_synapses)
space=space, label=label, download_synapses=download_synapses,
partition_id=partition_id)


def _create_overloaded_functions(spinnaker_simulator: SpiNNaker):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ def __init__(
# pylint: disable=too-many-arguments
if drop_late_spikes is None:
drop_late_spikes = False
extra_partition_ids = [
dev.device_control_partition_id for dev in devices]
super().__init__(
n_neurons=len(devices),
label=f"ext_dev{devices}" if label is None else label,
Expand All @@ -102,7 +104,7 @@ def __init__(
incoming_spike_buffer_size=incoming_spike_buffer_size,
neuron_impl=neuron_impl, pynn_model=pynn_model,
drop_late_spikes=drop_late_spikes, splitter=splitter, seed=seed,
n_colour_bits=n_colour_bits)
n_colour_bits=n_colour_bits, extra_partitions=extra_partition_ids)

if not devices:
raise ConfigurationException("No devices specified")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
from spinn_front_end_common.utilities.constants import (
SYSTEM_BYTES_REQUIREMENT, SIMULATION_N_BYTES, BYTES_PER_WORD)
from spynnaker.pyNN.data import SpynnakerDataView
from spynnaker.pyNN.exceptions import SpynnakerException


class MachineMunichMotorDevice(
Expand Down Expand Up @@ -154,11 +153,8 @@ def generate_data_specification(

# Get the key
routing_info = SpynnakerDataView.get_routing_infos()
edge_key = routing_info.get_first_key_from_pre_vertex(
edge_key = routing_info.get_key_from(
placement.vertex, self.MOTOR_PARTITION_ID)
if edge_key is None:
raise SpynnakerException(
"This motor should have one outgoing edge to the robot")

# write params to memory
spec.switch_write_focus(region=self._PARAMS_REGION)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ def new_key_command_payload(self):
:rtype: int
"""
routing_info = SpynnakerDataView.get_routing_infos()
key = routing_info.get_first_key_from_pre_vertex(
key = routing_info.get_key_from(
self, SPIKE_PARTITION_ID)
return key

Expand Down
22 changes: 10 additions & 12 deletions spynnaker/pyNN/external_devices_models/spif_output_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def _get_set_key_payload(self, index):
:rtype: int
"""
r_infos = SpynnakerDataView.get_routing_infos()
return r_infos.get_first_key_from_pre_vertex(
return r_infos.get_key_from(
self.__incoming_partitions[index].pre_vertex,
self.__incoming_partitions[index].identifier)

Expand All @@ -182,15 +182,15 @@ def _get_set_mask_payload(self, index):
:rtype: int
"""
r_infos = SpynnakerDataView.get_routing_infos()
return r_infos.get_routing_info_from_pre_vertex(
return r_infos.get_info_from(
self.__incoming_partitions[index].pre_vertex,
self.__incoming_partitions[index].identifier).mask

def _get_set_dist_mask_payload(self, index):
""" Get the payload for the command to set the distiller mask
"""
r_infos = SpynnakerDataView.get_routing_infos()
return ~r_infos.get_routing_info_from_pre_vertex(
return ~r_infos.get_info_from(
self.__incoming_partitions[index].pre_vertex,
self.__incoming_partitions[index].identifier).mask & 0xFFFFFFFF

Expand Down Expand Up @@ -245,15 +245,13 @@ def get_device_output_keys(self) -> Dict[MachineVertex,
atom_keys = m_vertex.app_vertex.get_atom_key_map(
m_vertex, part.identifier, routing_infos)
else:
r_info = routing_infos.get_routing_info_from_pre_vertex(
m_vertex, part.identifier)
# r_info could be None if there are no outgoing edges,
# at which point there is nothing to do here anyway
if r_info is not None:
vertex_slice = m_vertex.vertex_slice
keys = get_keys(r_info.key, vertex_slice)
start = vertex_slice.lo_atom
atom_keys = [(i, k) for i, k in enumerate(keys, start)]
r_info = \
routing_infos.get_info_from(
m_vertex, part.identifier)
vertex_slice = m_vertex.vertex_slice
keys = get_keys(r_info.key, vertex_slice)
start = vertex_slice.lo_atom
atom_keys = [(i, k) for i, k in enumerate(keys, start)]

atom_keys_mapped = list((i, key | ((k & mask) >> shift))
for i, k in atom_keys)
Expand Down
34 changes: 20 additions & 14 deletions spynnaker/pyNN/extra_algorithms/delay_support_adder.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,12 @@


def delay_support_adder() -> Tuple[
Sequence[DelayExtensionVertex], Sequence[ApplicationEdge]]:
Sequence[DelayExtensionVertex], Sequence[Tuple[ApplicationEdge, str]]]:
"""
Adds the delay extensions to the application graph, now that all the
splitter objects have been set.
:return: The delay vertices and delay edges that were added
:rtype: tuple(list(DelayExtensionVertex), list(DelayedApplicationEdge or
DelayAfferentApplicationEdge))
"""
adder = _DelaySupportAdder()
# pylint: disable=protected-access
Expand All @@ -57,18 +55,19 @@ def __init__(self) -> None:
self._app_to_delay_map: Dict[
ApplicationEdgePartition, DelayExtensionVertex] = dict()
self._delay_post_edge_map: Dict[
Tuple[DelayExtensionVertex, AbstractPopulationVertex],
Tuple[DelayExtensionVertex, AbstractPopulationVertex, str],
DelayedApplicationEdge] = dict()
self._new_edges: List[ApplicationEdge] = list()
self._new_edges: List[Tuple[ApplicationEdge, str]] = list()
self._new_vertices: List[DelayExtensionVertex] = list()

def add_delays(self) -> Tuple[
List[DelayExtensionVertex], List[ApplicationEdge]]:
List[DelayExtensionVertex], List[Tuple[ApplicationEdge, str]]]:
"""
Adds the delay extensions to the application graph, now that all the
splitter objects have been set.
:rtype: tuple(list(DelayExtensionVertex), list(DelayedApplicationEdge))
:rtype: tuple(list(DelayExtensionVertex),
list(Tuple(DelayedApplicationEdge, str)))
"""
progress = ProgressBar(1 + SpynnakerDataView.get_n_partitions(),
"Adding delay extensions as required")
Expand All @@ -77,7 +76,9 @@ def add_delays(self) -> Tuple[
DelayExtensionVertex):
self._app_to_delay_map[vertex.partition] = vertex
for edge in vertex.outgoing_edges:
self._delay_post_edge_map[vertex, edge.post_vertex] = edge
self._delay_post_edge_map[
vertex, edge.post_vertex,
vertex.partition.identifier] = edge
progress.update(1)

# go through all partitions.
Expand Down Expand Up @@ -109,22 +110,25 @@ def __examine_edge_for_delays_to_add(
partition, edge, steps_per_stage, n_stages)

# add the edge from the delay extension to the dest vertex
self._create_post_delay_edge(delay_app_vertex, edge)
self._create_post_delay_edge(
delay_app_vertex, edge, partition.identifier)

def _create_post_delay_edge(
self, delay_app_vertex: DelayExtensionVertex,
app_edge: ProjectionApplicationEdge):
app_edge: ProjectionApplicationEdge,
partition_id: str):
"""
Creates the edge between delay extension and post vertex. Stores
for future loading to the application graph when safe to do so.
:param DelayExtensionVertex delay_app_vertex: delay extension vertex
:param ProjectionApplicationEdge app_edge:
the undelayed application edge this is associated with.
:param str partition_id: the partition id of the edge
"""
# check for post edge
delayed_edge = self._delay_post_edge_map.get(
(delay_app_vertex, app_edge.post_vertex), None)
(delay_app_vertex, app_edge.post_vertex, partition_id), None)
if delayed_edge is None:
delay_edge = DelayedApplicationEdge(
delay_app_vertex, app_edge.post_vertex,
Expand All @@ -133,8 +137,9 @@ def _create_post_delay_edge(
f"to_{app_edge.post_vertex.label}"),
undelayed_edge=app_edge)
self._delay_post_edge_map[
(delay_app_vertex, app_edge.post_vertex)] = delay_edge
self._new_edges.append(delay_edge)
delay_app_vertex, app_edge.post_vertex,
partition_id] = delay_edge
self._new_edges.append((delay_edge, partition_id))
app_edge.delay_edge = delay_edge
delay_app_vertex.add_outgoing_edge(delay_edge)

Expand Down Expand Up @@ -175,7 +180,8 @@ def _create_delay_app_vertex_and_pre_edge(
delay_pre_edge = DelayAfferentApplicationEdge(
app_edge.pre_vertex, delay_app_vertex,
label=f"{app_edge.pre_vertex.label}_to_DelayExtension")
self._new_edges.append(delay_pre_edge)
self._new_edges.append(
(delay_pre_edge, app_outgoing_edge_partition.identifier))
else:
delay_app_vertex.set_new_n_delay_stages_and_delay_per_stage(
n_delay_stages, delay_per_stage)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
from spynnaker.pyNN.models.utility_models.delays import DelayExtensionVertex
from spynnaker.pyNN.models.neuron.synaptic_matrices import SynapticMatrices
from spynnaker.pyNN.models.neuron.neuron_data import NeuronData
from spynnaker.pyNN.types import Delay_Types
from spynnaker.pyNN.models.neuron.population_synapses_machine_vertex_common \
import (
SDRAM_PARAMS_SIZE as SYNAPSES_SDRAM_PARAMS_SIZE, KEY_CONFIG_SIZE,
Expand Down Expand Up @@ -506,7 +507,9 @@ def __handle_poisson_sources(self, label: str) -> Dict[Slice, List[Tuple[
pre_vertex = cast(SpikeSourcePoissonVertex, edge.pre_vertex)
conn = proj._synapse_information.connector
dynamics = proj._synapse_information.synapse_dynamics
if self.is_direct_poisson_source(pre_vertex, conn, dynamics):
delay = proj._synapse_information.delays
if self.is_direct_poisson_source(
pre_vertex, conn, dynamics, delay):
# Create the direct Poisson vertices here; the splitter
# for the Poisson will create any others as needed
for vertex_slice in self._get_fixed_slices():
Expand Down Expand Up @@ -534,11 +537,13 @@ def handles_source_vertex(self, projection: Projection) -> bool:
pre_vertex = edge.pre_vertex
connector = projection._synapse_information.connector
dynamics = projection._synapse_information.synapse_dynamics
return self.is_direct_poisson_source(pre_vertex, connector, dynamics)
delay = projection._synapse_information.delays
return self.is_direct_poisson_source(
pre_vertex, connector, dynamics, delay)

def is_direct_poisson_source(
self, pre_vertex: ApplicationVertex, connector: AbstractConnector,
dynamics: AbstractSynapseDynamics) -> bool:
dynamics: AbstractSynapseDynamics, delay: Delay_Types) -> bool:
"""
Determine if a given Poisson source can be created by this splitter.
Expand All @@ -552,14 +557,17 @@ def is_direct_poisson_source(
The synapse dynamics in use in the Projection
:type dynamics:
~spynnaker.pyNN.models.neuron.synapse_dynamics.AbstractSynapseDynamics
:param delay:
The delay in use in the Projection
:rtype: bool
"""
return (isinstance(pre_vertex, SpikeSourcePoissonVertex) and
isinstance(pre_vertex.splitter, SplitterPoissonDelegate) and
len(pre_vertex.outgoing_projections) == 1 and
pre_vertex.n_atoms == self.governed_app_vertex.n_atoms and
isinstance(connector, OneToOneConnector) and
isinstance(dynamics, SynapseDynamicsStatic))
isinstance(dynamics, SynapseDynamicsStatic) and
delay == SpynnakerDataView().get_simulation_time_step_ms())

@overrides(AbstractSplitterCommon.get_in_coming_slices)
def get_in_coming_slices(self) -> Sequence[Slice]:
Expand Down
28 changes: 14 additions & 14 deletions spynnaker/pyNN/models/common/local_only_2d_common.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
from pacman.model.graphs.common.mdslice import MDSlice
from spinn_front_end_common.utilities.constants import BYTES_PER_WORD
from spynnaker.pyNN.data.spynnaker_data_view import SpynnakerDataView
from spynnaker.pyNN.utilities.constants import SPIKE_PARTITION_ID
from spynnaker.pyNN.utilities.utility_calls import get_n_bits

#: The number of bits in a short value
Expand Down Expand Up @@ -59,12 +58,13 @@ def get_delay_for_source(incoming):
along with the delay stage and locally-handled delay value
:param Projection incoming: The incoming projection to get the delay from
:return: The vertex, the local delay, the delay stage
:rtype: ApplicationVertex, int, int
:return: The vertex, the local delay, the delay stage, the partition id
:rtype: ApplicationVertex, int, int, str
"""
# pylint: disable=protected-access
app_edge = incoming._projection_edge
delay = incoming._synapse_information.synapse_dynamics.delay
s_info = incoming._synapse_information
delay = s_info.synapse_dynamics.delay
steps = delay * SpynnakerDataView.get_simulation_time_step_per_ms()
max_delay = app_edge.post_vertex.splitter.max_support_delay()
local_delay = steps % max_delay
Expand All @@ -73,26 +73,26 @@ def get_delay_for_source(incoming):
if steps > max_delay:
delay_stage = (steps // max_delay) - 1
pre_vertex = app_edge.delay_edge.pre_vertex
return pre_vertex, local_delay, delay_stage
return pre_vertex, local_delay, delay_stage, s_info.partition_id


def get_rinfo_for_spike_source(pre_vertex):
def get_rinfo_for_spike_source(pre_vertex, partition_id):
"""
Get the routing information for the source of a projection in the
SPIKE_PARTITION_ID partition.
given partition.
:param ApplicationVertex pre_vertex: The source of incoming data
:param str partition_id: The partition ID to get the routing info from
:return: Routing information, core mask, core mask shift
:rtype: AppVertexRoutingInfo, int, int
"""
routing_info = SpynnakerDataView.get_routing_infos()

# Find the routing information
r_info = routing_info.get_routing_info_from_pre_vertex(
pre_vertex, SPIKE_PARTITION_ID)
r_info = routing_info.get_info_from(
pre_vertex, partition_id)

n_cores = len(r_info.vertex.splitter.get_out_going_vertices(
SPIKE_PARTITION_ID))
n_cores = len(r_info.vertex.splitter.get_out_going_vertices(partition_id))

# If there is 1 core, we don't use the core mask
# If there is a virtual vertex, these also don't use core masks
Expand All @@ -112,14 +112,14 @@ def get_sources_for_target(app_vertex):
:param AbstractPopulationVertex app_vertex: The vertex being targeted
:return:
A dict of source ApplicationVertex to list of source information
:rtype: dict(ApplicationVertex, list(Source))
:rtype: dict(tuple(ApplicationVertex, str), list(Source))
"""
sources = defaultdict(list)
for incoming in app_vertex.incoming_projections:
pre_vertex, local_delay, delay_stage = get_delay_for_source(
pre_vertex, local_delay, delay_stage, part_id = get_delay_for_source(
incoming)
source = Source(incoming, local_delay, delay_stage)
sources[pre_vertex].append(source)
sources[pre_vertex, part_id].append(source)
return sources


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -424,11 +424,8 @@ def n_colour_bits(self) -> int:
def get_atom_key_map(
self, pre_vertex: MachineVertex, partition_id: str,
routing_info: RoutingInfo) -> Iterable[Tuple[int, int]]:
base_key = routing_info.get_first_key_from_pre_vertex(
base_key = routing_info.get_key_from(
pre_vertex, partition_id)
# This might happen if there are no edges
if base_key is None:
base_key = 0
vertex_slice = pre_vertex.vertex_slice
keys = get_keys(base_key, vertex_slice, self.n_colour_bits)
return zip(vertex_slice.get_raster_ids(), keys)
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@
Delay_Types, is_scalar, Weight_Delay_Types, Weight_Types)
from spynnaker.pyNN.utilities import utility_calls
from spynnaker.pyNN.exceptions import SpynnakerException
from spynnaker.pyNN.utilities.constants import SPIKE_PARTITION_ID

if TYPE_CHECKING:
from spynnaker.pyNN.models.neural_projections import (
Expand Down Expand Up @@ -678,7 +677,7 @@ def get_connected_vertices(
# whole source
return [(m_vertex, [source_vertex])
for m_vertex in target_vertex.splitter.get_in_coming_vertices(
SPIKE_PARTITION_ID)]
s_info.partition_id)]

def connect(self, projection: Projection):
"""
Expand Down
Loading

0 comments on commit be85530

Please sign in to comment.