Skip to content

Commit

Permalink
Merge pull request #494 from appukuttan-shailesh/issue_491_490
Browse files Browse the repository at this point in the history
Fixes #449, #490, #491: Corrects simulator and population recording
  • Loading branch information
apdavison committed Jun 30, 2017
2 parents de3ab37 + 046489c commit 656d35a
Show file tree
Hide file tree
Showing 4 changed files with 213 additions and 11 deletions.
13 changes: 8 additions & 5 deletions pyNN/common/populations.py
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,9 @@ def initialize(self, **initial_values):
def find_units(self, variable):
return self.celltype.units[variable]

def annotate(self, **annotations):
self.annotations.update(annotations)

def can_record(self, variable):
"""Determine whether `variable` can be recorded from this population."""
return self.celltype.can_record(variable)
Expand All @@ -420,7 +423,7 @@ def record(self, variables, to_file=None, sampling_interval=None):
names. For a given celltype class, `celltype.recordable` contains a list of
variables that can be recorded for that celltype.
If specified, `to_file` should be a Neo IO instance and `write_data()`
If specified, `to_file` should be either a filename or a Neo IO instance and `write_data()`
will be automatically called when `end()` is called.
`sampling_interval` should be a value in milliseconds, and an integer
Expand All @@ -438,6 +441,7 @@ def record(self, variables, to_file=None, sampling_interval=None):
self.recorder.record(variables, self._record_filter, sampling_interval)
if isinstance(to_file, basestring):
self.recorder.file = to_file
self._simulator.state.write_on_end.append((self, variables, self.recorder.file))

@deprecated("record('v')")
def record_v(self, to_file=True):
Expand Down Expand Up @@ -735,9 +739,6 @@ def _set_positions(self, pos_array):
giving the x,y,z coordinates of all the neurons (soma, in the
case of non-point models).""")

def annotate(self, **annotations):
self.annotations.update(annotations)

def describe(self, template='population_default.txt', engine='default'):
"""
Returns a human-readable description of the population.
Expand Down Expand Up @@ -828,6 +829,7 @@ def __init__(self, parent, selector, label=None):
self.local_cells = self.all_cells[self._mask_local]
self.first_id = numpy.min(self.all_cells) # only works if we assume all_cells is sorted, otherwise could use min()
self.last_id = numpy.max(self.all_cells)
self.annotations = {}
self.recorder = self.parent.recorder
self._record_filter = self.all_cells

Expand Down Expand Up @@ -923,6 +925,7 @@ def describe(self, template='populationview_default.txt', engine='default'):
"parent": self.parent.label,
"mask": self.mask,
"size": self.size}
context.update(self.annotations)
return descriptions.render(engine, template, context)


Expand Down Expand Up @@ -1240,7 +1243,7 @@ def record(self, variables, to_file=None, sampling_interval=None):
names. For a given celltype class, `celltype.recordable` contains a list of
variables that can be recorded for that celltype.
If specified, `to_file` should be a Neo IO instance and `write_data()`
If specified, `to_file` should be either a filename or a Neo IO instance and `write_data()`
will be automatically called when `end()` is called.
"""
for p in self.populations:
Expand Down
4 changes: 2 additions & 2 deletions pyNN/common/procedural_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ def record(variables, source, filename, sampling_interval=None, annotations=None
# would actually like to be able to record to an array and choose later
# whether to write to a file.
if not isinstance(source, (BasePopulation, Assembly)):
source = source.parent
if isinstance(source, (IDMixin)):
source = source.as_view()
source.record(variables, to_file=filename, sampling_interval=sampling_interval)
if annotations:
source.annotate(**annotations)
simulator.state.write_on_end.append((source, variables, filename))
return record


Expand Down
201 changes: 199 additions & 2 deletions test/system/scenarios/test_recording.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@
import os
import numpy
import quantities as pq
from nose.tools import assert_equal
from nose.tools import assert_equal, assert_true
from numpy.testing import assert_array_equal, assert_array_almost_equal
from neo.io import get_io
from pyNN.utility import assert_arrays_equal, assert_arrays_almost_equal, init_logging
from pyNN.utility import assert_arrays_equal, assert_arrays_almost_equal, init_logging, normalized_filename
from .registry import register
import pickle


@register(exclude=['nemo'])
Expand Down Expand Up @@ -165,6 +166,201 @@ def test_mix_procedural_and_oo(sim):
test_mix_procedural_and_oo.__test__ = False


@register()
def issue_449_490_491(sim):
"""
Test to ensure that Simulator and Population recording work properly
The following 12 scenarios are explored:
Note: var1 = "spikes", var2 = "v"
1) sim.record()
i) cell[0]
a) 2 parameters (2vars) (scenario 1)
b) parameter1 (var1) (scenario 2)
c) parameter2 (var2) (scenario 3)
ii) cell[1]
a) 2 parameters (2vars) (scenario 4)
b) parameter1 (var1) (scenario 5)
c) parameter2 (var2) (scenario 6)
iii) population
a) 2 parameters (2vars) (scenario 7)
b) parameter1 (var1) (scenario 8)
c) parameter2 (var2) (scenario 9)
2) pop.record() - always records for a population; not a single cell
a) 2 parameters (2vars) (scenario 10)
b) parameter1 (var1) (scenario 11)
c) parameter2 (var2) (scenario 12)
"""
# START ***** defining methods needed for test *****

def get_file_data(filename):
# method to access pickled file and retrieve data
data = []
with (open(filename, "rb")) as openfile:
while True:
try:
data.append(pickle.load(openfile))
except EOFError:
break
return data

def eval_num_cells(data):
# scan data object to evaluate number of cells; returns 4 values
# nCells : # of cells in analogsignals (if "v" recorded)
# nspikes1: # of spikes in first recorded cell
# nspikes2: # of spikes in second recorded cell (if exists)
# -- if any parameter absent, return -1 as its value
# annot_bool # true if specified annotation exists; false otherwise

try:
nCells = data[0].segments[0].analogsignals[0].shape[1]
except:
nCells = -1

try:
nspikes1 = data[0].segments[0].spiketrains[0].shape[0]
except:
nspikes1 = -1

try:
nspikes2 = data[0].segments[0].spiketrains[1].shape[0]
except:
nspikes2 = -1

if 'script_name' in data[0].annotations.keys():
annot_bool = True
else:
annot_bool = False

return (nCells, nspikes1, nspikes2, annot_bool)

# END ***** defining methods needed for test *****

sim_dt = 0.1
sim.setup(min_delay=1.0, timestep = sim_dt)

# creating a population of two cells; only cell[0] gets stimulus
# hence only cell[0] will have entries for spiketrains
cells = sim.Population(2, sim.IF_curr_exp(v_thresh=-55.0, tau_refrac=5.0))
steady = sim.DCSource(amplitude=2.5, start=25.0, stop=75.0)
cells[0].inject(steady)

# specify appropriate filenames for output files
filename_sim_cell1_2vars = normalized_filename("Results", "sim_cell1_2vars", "pkl", sim)
filename_sim_cell1_var1 = normalized_filename("Results", "sim_cell1_var1", "pkl", sim)
filename_sim_cell1_var2 = normalized_filename("Results", "sim_cell1_var2", "pkl", sim)
filename_sim_cell2_2vars = normalized_filename("Results", "sim_cell2_2vars", "pkl", sim)
filename_sim_cell2_var1 = normalized_filename("Results", "sim_cell2_var1", "pkl", sim)
filename_sim_cell2_var2 = normalized_filename("Results", "sim_cell2_var2", "pkl", sim)
filename_sim_popl_2vars = normalized_filename("Results", "sim_popl_2vars", "pkl", sim)
filename_sim_popl_var1 = normalized_filename("Results", "sim_popl_var1", "pkl", sim)
filename_sim_popl_var2 = normalized_filename("Results", "sim_popl_var2", "pkl", sim)
filename_rec_2vars = normalized_filename("Results", "rec_2vars", "pkl", sim)
filename_rec_var1 = normalized_filename("Results", "rec_var1", "pkl", sim)
filename_rec_var2 = normalized_filename("Results", "rec_var2", "pkl", sim)

# instruct pynn to record as per above scenarios
sim.record(["spikes", "v"], cells[0], filename_sim_cell1_2vars, annotations={'script_name': __file__})
sim.record(["spikes"], cells[0], filename_sim_cell1_var1, annotations={'script_name': __file__})
sim.record(["v"], cells[0], filename_sim_cell1_var2, annotations={'script_name': __file__})
sim.record(["spikes", "v"], cells[1], filename_sim_cell2_2vars, annotations={'script_name': __file__})
sim.record(["spikes"], cells[1], filename_sim_cell2_var1, annotations={'script_name': __file__})
sim.record(["v"], cells[1], filename_sim_cell2_var2, annotations={'script_name': __file__})
sim.record(["spikes", "v"], cells, filename_sim_popl_2vars, annotations={'script_name': __file__})
sim.record(["spikes"], cells, filename_sim_popl_var1, annotations={'script_name': __file__})
sim.record(["v"], cells, filename_sim_popl_var2, annotations={'script_name': __file__})
cells.record(["spikes", "v"], to_file=filename_rec_2vars)
cells.record(["spikes"], to_file=filename_rec_var1)
cells.record(["v"], to_file=filename_rec_var2)

sim.run(100.0)
sim.end()

# retrieve data from the created files, and perform appropriate checks
# scenario 1
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_cell1_2vars))
assert_true (nCells == 1)
assert_true (nspikes1 > 0)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 2
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_cell1_var1))
assert_true (nCells == -1)
assert_true (nspikes1 > 0)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 3
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_cell1_var2))
assert_true (nCells == 1)
assert_true (nspikes1 == -1)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 4
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_cell2_2vars))
assert_true (nCells == 1)
assert_true (nspikes1 == 0)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 5
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_cell2_var1))
assert_true (nCells == -1)
assert_true (nspikes1 == 0)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 6
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_cell2_var2))
assert_true (nCells == 1)
assert_true (nspikes1 == -1)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 7
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_popl_2vars))
assert_true (nCells == 2)
assert_true (nspikes1 > 0)
assert_true (nspikes2 == 0)
assert_true (annot_bool)

# scenario 8
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_popl_var1))
assert_true (nCells == -1)
assert_true (nspikes1 > 0)
assert_true (nspikes2 == 0)
assert_true (annot_bool)

# scenario 9
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_sim_popl_var2))
assert_true (nCells == 2)
assert_true (nspikes1 == -1)
assert_true (nspikes2 == -1)
assert_true (annot_bool)

# scenario 10
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_rec_2vars))
assert_true (nCells == 2)
assert_true (nspikes1 > 0)
assert_true (nspikes2 == 0)
assert_true (annot_bool)

# scenario 11
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_rec_var1))
assert_true (nCells == -1)
assert_true (nspikes1 > 0)
assert_true (nspikes2 == 0)
assert_true (annot_bool)

# scenario 12
nCells, nspikes1, nspikes2, annot_bool = eval_num_cells(get_file_data(filename_rec_var2))
assert_true (nCells == 2)
assert_true (nspikes1 == -1)
assert_true (nspikes2 == -1)
assert_true (annot_bool)


if __name__ == '__main__':
from pyNN.utility import get_simulator
sim, args = get_simulator()
Expand All @@ -173,3 +369,4 @@ def test_mix_procedural_and_oo(sim):
issue259(sim)
test_sampling_interval(sim)
test_mix_procedural_and_oo(sim)
issue_449_490_491(sim)
6 changes: 4 additions & 2 deletions test/unittests/test_lowlevelapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ def test_build_record():
source.record = Mock()
record_function(('v', 'spikes'), source, "filename")
source.record.assert_called_with(('v', 'spikes'), to_file="filename", sampling_interval=None)
assert_equal(simulator.state.write_on_end, [(source, ('v', 'spikes'), "filename")])
# below check needs to be re-implmented with pyNN.mock
# assert_equal(simulator.state.write_on_end, [(source, ('v', 'spikes'), "filename")])


def test_build_record_with_assembly():
Expand All @@ -71,4 +72,5 @@ def test_build_record_with_assembly():
source.record = Mock()
record_function('foo', source, "filename")
source.record.assert_called_with('foo', to_file="filename", sampling_interval=None)
assert_equal(simulator.state.write_on_end, [(source, 'foo', "filename")]) # not sure this is what we want - won't file get over-written?
# below check needs to be re-implmented with pyNN.mock
# assert_equal(simulator.state.write_on_end, [(source, 'foo', "filename")]) # not sure this is what we want - won't file get over-written?

0 comments on commit 656d35a

Please sign in to comment.