Skip to content

Commit

Permalink
Merge pull request #1179 from SpiNNakerManchester/spinnaker_view
Browse files Browse the repository at this point in the history
spinnaker_get_data
  • Loading branch information
andrewgait authored May 19, 2022
2 parents 77b94ea + a822612 commit 40adc33
Show file tree
Hide file tree
Showing 5 changed files with 191 additions and 14 deletions.
23 changes: 14 additions & 9 deletions spynnaker/pyNN/models/populations/population.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,30 +402,35 @@ def get_data_by_indexes(
return self._recorder.extract_neo_block(
variables, indexes, clear, annotations)

def spinnaker_get_data(self, variable):
def spinnaker_get_data(self, variable, as_matrix=False, view_indexes=None):
""" Public accessor for getting data as a numpy array, instead of\
the neo based object
:param variable:
either a single variable name or a list of variable names.
Variables must have been previously recorded, otherwise an
Exception will be raised.
:param str variable: a single variable name.
:type variable: str or list(str)
:param bool as_matrix: If set True the data is returned as a 2d matrix
:param view_indexes: The indexes for which data should be returned.
If ``None``, all data (view_index = data_indexes)
:return: array of the data
:rtype: ~numpy.ndarray
"""
warn_once(
logger, "spinnaker_get_data is non-standard PyNN and therefore "
"may not be portable to other simulators. Nor do we guarantee "
"that this function will exist in future releases.")
"will not be portable to other simulators.")
if isinstance(variable, list):
if len(variable) != 1:
raise ConfigurationException(
"Only one type of data at a time is supported")
variable = variable[0]
if variable == SPIKES:
return self._recorder.get_spikes()
return self._recorder.get_recorded_pynn7(variable)
if as_matrix:
logger.warning(f"Ignoring as matrix for {SPIKES}")
spikes = self._recorder.get_spikes()
if view_indexes is None:
return spikes
return spikes[numpy.isin(spikes[:, 0], view_indexes)]
return self._recorder.get_recorded_pynn7(
variable, as_matrix, view_indexes)

@overrides(PopulationBase.get_spike_counts, extend_doc=False)
def get_spike_counts(self, gather=True):
Expand Down
12 changes: 12 additions & 0 deletions spynnaker/pyNN/models/populations/population_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ def get_data(
return self.__population.get_data_by_indexes(
variables, self.__indexes, clear=clear)

def spinnaker_get_data(self, variable, as_matrix=False):
""" Public accessor for getting data as a numpy array, instead of\
the neo based object
:param str variable: a single variable name
:param bool as_matrix: If set True the data is returned as a 2d matrix
:return: array of the data
:rtype: ~numpy.ndarray
"""
return self.__population.spinnaker_get_data(
variable, as_matrix, self.__indexes)

def get_spike_counts(self, gather=True):
""" Returns a dict containing the number of spikes for each neuron.
Expand Down
31 changes: 27 additions & 4 deletions spynnaker/pyNN/models/recorder.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,22 +184,45 @@ def turn_on_record(self, variable, sampling_interval=None, to_file=None,
"conductance from a model which does not use conductance "
"input. You will receive current measurements instead.")

def get_recorded_pynn7(self, variable):
def get_recorded_pynn7(self, variable, as_matrix=False, view_indexes=None):
""" Get recorded data in PyNN 0.7 format. Must not be spikes.
:param str variable:
The name of the variable to get. Supported variable names are:
``gsyn_exc``, ``gsyn_inh``, ``v``
:param bool as_matrix: If set True the data is returned as a 2d matrix
:param view_indexes: The indexes for which data should be returned.
If ``None``, all data (view_index = data_indexes)
:type view_indexes: list(int) or None
:rtype: ~numpy.ndarray
"""
if variable in [SPIKES, REWIRING]:
raise NotImplementedError(f"{variable} not supported")
(data, ids, sampling_interval) = self.get_recorded_matrix(variable)
if view_indexes is None:
if len(ids) != self.__population.size:
warn_once(logger, self._SELECTIVE_RECORDED_MSG)
indexes = ids
elif view_indexes == ids:
indexes = ids
else:
# keep just the view indexes in the data
indexes = [i for i in view_indexes if i in ids]
# keep just data columns in the view
map_indexes = [ids.index(i) for i in indexes]
data = data[:, map_indexes]

if as_matrix:
return data

# Convert to triples as Pynn 0,7 did
n_machine_time_steps = len(data)
n_neurons = len(ids)
n_neurons = len(indexes)
column_length = n_machine_time_steps * n_neurons
times = [i * sampling_interval
for i in range(0, n_machine_time_steps)]
return numpy.column_stack((
numpy.repeat(ids, n_machine_time_steps, 0),
numpy.repeat(indexes, n_machine_time_steps, 0),
numpy.tile(times, n_neurons),
numpy.transpose(data).reshape(column_length)))

Expand Down Expand Up @@ -255,7 +278,7 @@ def get_recorded_matrix(self, variable):

return (data, indexes, sampling_interval)

def get_spikes(self):
def get_spikes(self, view_indexes=None):
""" How to get spikes (of a population's neurons) from the recorder.
:return: the spikes (event times) from the underlying vertex
Expand Down
2 changes: 1 addition & 1 deletion spynnaker_integration_tests/test_views/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ def set_with_views(self):
-65., -63.04931641, -61.19375610, -59.42868042, -57.74966431,
-60., -58.29315186, -56.66952515, -55.12509155, -53.65597534,
-65., -64.02465820, -63.09686279, -62.21432495, -61.37481689]
numpy.allclose(v1[:, 2], expected)
assert(numpy.allclose(v1[:, 2], expected))

def test_set_with_views(self):
self.runsafe(self.set_with_views)
137 changes: 137 additions & 0 deletions spynnaker_integration_tests/test_views/test_views_data.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Copyright (c) 2017-2022 The University of Manchester
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import numpy
import pyNN.spiNNaker as sim
from spinnaker_testbase import BaseTestCase
from spynnaker.pyNN.utilities import neo_convertor


class TestViews(BaseTestCase):

def set_with_views(self):
sim.setup(timestep=1.0)

# create two pops that behave identical
pop_1 = sim.Population(10, sim.IF_curr_exp(), label="pop_1")
pop_2 = sim.Population(10, sim.IF_curr_exp(), label="pop_1")
input = sim.Population(10, sim.SpikeSourceArray(
spike_times=[[0], [1], [2], [3], [4], [5], [6], [7], [8], [9]]),
label="input")
sim.Projection(
input, pop_1, sim.OneToOneConnector(),
synapse_type=sim.StaticSynapse(weight=5, delay=1))
sim.Projection(
input, pop_2, sim.OneToOneConnector(),
synapse_type=sim.StaticSynapse(weight=5, delay=1))

# record all on pop 1
pop_1.record(["spikes", "v"])
# part record on pop 2
pop_2[2:5].record("spikes")
pop_2[4, 8].record("spikes")
pop_2[1:7].record("v")

simtime = 30
sim.run(simtime)

# get all data
neo1 = pop_1.get_data(variables=["spikes", "v"])
v1neo = neo1.segments[0].filter(name='v')[0]
v1convert = neo_convertor.convert_analog_signal(v1neo)
v1matrix = pop_1.spinnaker_get_data("v", as_matrix=True)
v1tuple = pop_1.spinnaker_get_data("v")
spikes1neo = neo1.segments[0].spiketrains
spikes1convert = neo_convertor.convert_spikes(neo1)
spikes1tuple = pop_1.spinnaker_get_data("spikes")

# get everything recorded on pop2
neo2all = pop_2.get_data(variables=["spikes", "v"])
v2allneo = neo2all.segments[0].filter(name='v')[0]
v2allconvert = neo_convertor.convert_analog_signal(v2allneo)
spikes2allneo = neo2all.segments[0].spiketrains
spikes2alltuple = pop_2.spinnaker_get_data("spikes")
v2allmatrix = pop_2.spinnaker_get_data("v", as_matrix=True)
v2alltuple = pop_2.spinnaker_get_data("v")

# get using same ids all record
spikes2viewtuple = pop_2[2, 3, 4, 8].spinnaker_get_data("spikes")
v2viewmatrix = pop_2[1:7].spinnaker_get_data("v", as_matrix=True)
v2viewtuple = pop_2[1:7].spinnaker_get_data("v")

# get view different to recorded
neo2part = pop_2[0:5].get_data(variables=["spikes", "v"])
v2partneo = neo2part.segments[0].filter(name='v')[0]
v2partconvert = neo_convertor.convert_analog_signal(v2partneo)
spikes2partneo = neo2part.segments[0].spiketrains
spikes2parttuple = pop_2[0:5].spinnaker_get_data("spikes")
v2partmatrix = pop_2[0:5].spinnaker_get_data("v", as_matrix=True)
v2parttuple = pop_2[0:5].spinnaker_get_data("v")

sim.end()

# check all
s1e = [[0, 7.], [1, 8.], [2, 9.], [3, 10.], [4, 11.], [5, 12.],
[6, 13.],
[7, 14.], [8, 15.], [9, 16.]]
assert (numpy.array_equal(spikes1tuple, s1e))
assert (numpy.array_equal(spikes1convert, s1e))
assert (numpy.array_equal(v1convert, v1tuple))
for id, time, val in v1tuple:
assert (v1matrix[int(time)][int(id)] == val)

# check pop2 all recorded
for id in [2, 3, 4, 8]:
assert (spikes1neo[id] == spikes2allneo[id])
for id in [0, 1, 5, 6, 7, 9]:
assert (len(spikes2allneo[id]) == 0)
s2e = [[2, 9.], [3, 10.], [4, 11.], [8, 15]]
assert (numpy.array_equal(spikes2alltuple, s2e))
assert (numpy.array_equal(spikes2viewtuple, s2e))

assert (v2allneo.shape == (30, 6))
for i, id in enumerate(range(1, 7)):
a = v1neo[:, id]
b = v2allneo[:, i]
c = v2allmatrix[:, i]
d = v2viewmatrix[:, i]
assert (len(a) == len(b))
for j in range(len(a)):
assert (a[j] == b[j] == c[j] == d[j])

assert (numpy.array_equal(v2allconvert, v2alltuple))
assert (numpy.array_equal(v2allconvert, v2viewtuple))

# check pop2 part recorded
for id in [2, 3, 4]:
assert (spikes1neo[id] == spikes2partneo[id])
for id in [0, 1]:
assert (len(spikes2partneo[id]) == 0)
s2ea = [[2, 9.], [3, 10.], [4, 11.]]
assert (numpy.array_equal(spikes2parttuple, s2ea))

assert (v2partneo.shape == (30, 4))
for i, id in enumerate(range(1, 5)):
a = v1neo[:, id]
b = v2partneo[:, i]
c = v2partmatrix[:, i]
for j in range(len(a)):
assert (a[j] == b[j] == c[j])
for id, time, val in v2parttuple:
assert (v1matrix[int(time)][int(id)] == val)
assert (numpy.array_equal(v2partconvert, v2parttuple))

def test_set_with_views(self):
self.runsafe(self.set_with_views)

0 comments on commit 40adc33

Please sign in to comment.