Repository: spinnakermanchester/spynnaker
Issue: Issue with selective recording 

This is a (not really MW)Example of selective recording L182 + L190 that seems to fail at data extraction. In a different example I have but can't currently share here it actually crashes with:
```
Traceback (most recent call last):
  File "cerebellum_experiment.py", line 58, in <module>
    sim.run(args.simtime)  # ms
  File "/localhome/mbax3pb2/py3_venv/spinnaker/sPyNNaker8/spynnaker8/__init__.py", line 667, in run
    return __pynn["run"](simtime, callbacks=callbacks)
  File "/localhome/mbax3pb2/py3_venv/lib/python3.5/site-packages/PyNN-0.9.4-py3.5.egg/pyNN/common/control.py", line 111, in run
    return run_until(simulator.state.t + simtime, callbacks)
  File "/localhome/mbax3pb2/py3_venv/lib/python3.5/site-packages/PyNN-0.9.4-py3.5.egg/pyNN/common/control.py", line 93, in run_until
    simulator.state.run_until(time_point)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/sPyNNaker8/spynnaker8/spinnaker.py", line 128, in run_until
    self._run_wait(tstop - self.t)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/sPyNNaker8/spynnaker8/spinnaker.py", line 171, in _run_wait
    super(SpiNNaker, self).run(duration_ms)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/sPyNNaker/spynnaker/pyNN/abstract_spinnaker_common.py", line 334, in run
    super(AbstractSpiNNakerCommon, self).run(run_time)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/abstract_spinnaker_base.py", line 680, in run
    self._run(run_time)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/abstract_spinnaker_base.py", line 855, in _run
    self._do_run(step, graph_changed, run_until_complete)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/abstract_spinnaker_base.py", line 1777, in _do_run
    reraise(*e_inf)
  File "/localhome/mbax3pb2/py3_venv/lib/python3.5/site-packages/six.py", line 693, in reraise
    raise value
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/abstract_spinnaker_base.py", line 1718, in _do_run
    executor.execute_mapping()
  File "/localhome/mbax3pb2/py3_venv/spinnaker/PACMAN/pacman/executor/pacman_algorithm_executor.py", line 637, in execute_mapping
    self._execute_mapping()
  File "/localhome/mbax3pb2/py3_venv/spinnaker/PACMAN/pacman/executor/pacman_algorithm_executor.py", line 653, in _execute_mapping
    results = algorithm.call(self._internal_type_mapping)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/PACMAN/pacman/executor/algorithm_classes/abstract_python_algorithm.py", line 60, in call
    results = self.call_python(method_inputs)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/PACMAN/pacman/executor/algorithm_classes/python_class_algorithm.py", line 71, in call_python
    return method(**inputs)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/interface_functions/buffer_extractor.py", line 46, in __call__
    recording_placements, progress)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/buffer_management/buffer_manager.py", line 597, in get_data_for_placements
    self.__old_get_data_for_placements(placements, progress)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/buffer_management/buffer_manager.py", line 620, in __old_get_data_for_placements
    self._retreive_by_placement(placement, recording_region_id)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/buffer_management/buffer_manager.py", line 776, in _retreive_by_placement
    address=read_ptr, length=length, placement_y=placement.y)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNFrontEndCommon/spinn_front_end_common/interface/buffer_management/buffer_manager.py", line 207, in _request_data
    placement_x, placement_y, address, length)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNMan/spinnman/transceiver.py", line 1913, in read_memory
    return process.read_memory(x, y, cpu, base_address, length)
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNMan/spinnman/processes/read_memory_process.py", line 39, in read_memory
    functools.partial(ReadMemory, x=x, y=y, cpu=p))
  File "/localhome/mbax3pb2/py3_venv/spinnaker/SpiNNMan/spinnman/processes/read_memory_process.py", line 47, in _read_memory
    data = bytearray(length)
ValueError: negative count
```

The MWE (it's a slightly modified version of `stdp_example_cond.py`):
```
# Copyright (c) 2017-2019 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/>.

"""
Simple test for STDP :

   Reproduces a classical plasticity experiment of plasticity induction by
pre/post synaptic pairing specifically :

 * At the begining of the simulation, "n_stim_test" external stimulations of
   the "pre_pop" (presynaptic) population do not trigger activity in the
   "post_pop" (postsynaptic) population.

 * Then the presynaptic and postsynaptic populations are stimulated together
   "n_stim_pairing" times by an external source so that the "post_pop"
   population spikes 10ms after the "pre_pop" population.

 * Ater that period, only the "pre_pop" population is externally stimulated
   "n_stim_test" times, but now it should trigger activity in the "post_pop"
   population (due to STDP learning)

Run as :

   $ ./stdp_example

This example requires that the NeuroTools package is installed
(http://neuralensemble.org/trac/NeuroTools)

Authors : Catherine Wacongne < catherine.waco@gmail.com >
          Xavier Lagorce < Xavier.Lagorce@crans.org >

April 2013
"""
try:
    import pyNN.spiNNaker as sim
except Exception:
    import spynnaker8 as sim
from pyNN.utility.plotting import Figure, Panel
import matplotlib.pyplot as plt

# SpiNNaker setup
sim.setup(timestep=1.0, min_delay=1.0, max_delay=10.0)

# +-------------------------------------------------------------------+
# | General Parameters                                                |
# +-------------------------------------------------------------------+
sim.set_number_of_neurons_per_core(sim.IF_cond_exp, 5)
# Population parameters
model = sim.IF_cond_exp

cell_params = {
    'cm': 0.25, 'i_offset': 0.0, 'tau_m': 20.0,
    'tau_refrac': 2.0, 'tau_syn_E': 5.0, 'tau_syn_I': 5.0,
    'v_reset': -70.0, 'v_rest': -65.0, 'v_thresh': -50.0}


# Other simulation parameters
e_rate = 80
in_rate = 300

n_stim_test = 5
n_stim_pairing = 20
dur_stim = 20

pop_size = 40

ISI = 90.
start_test_pre_pairing = 200.
start_pairing = 1500.
start_test_post_pairing = 700.

simtime = (start_pairing + start_test_post_pairing
           + ISI * (n_stim_pairing + n_stim_test) + 550.)

# Initialisations of the different types of populations
IAddPre = []
IAddPost = []

# +-------------------------------------------------------------------+
# | Creation of neuron populations                                    |
# +-------------------------------------------------------------------+

# Neuron populations
pre_pop = sim.Population(pop_size, model(**cell_params))
post_pop = sim.Population(pop_size, model(**cell_params))

# Test of the effect of activity of the pre_pop population on the post_pop
# population prior to the "pairing" protocol : only pre_pop is stimulated
for i in range(n_stim_test):
    IAddPre.append(sim.Population(
        pop_size, sim.SpikeSourcePoisson(
            rate=in_rate, start=start_test_pre_pairing + ISI * i,
            duration=dur_stim)))

# Pairing protocol : pre_pop and post_pop are stimulated with a 10 ms
# difference
for i in range(n_stim_pairing):
    IAddPre.append(sim.Population(
        pop_size, sim.SpikeSourcePoisson(
           rate=in_rate, start=start_pairing + ISI * i, duration=dur_stim)))
    IAddPost.append(sim.Population(
        pop_size, sim.SpikeSourcePoisson(
            rate=in_rate, start=start_pairing + ISI * i + 10.,
            duration=dur_stim)))

# Test post pairing : only pre_pop is stimulated (and should trigger activity
# in Post)
for i in range(n_stim_test):
    IAddPre.append(sim.Population(
        pop_size, sim.SpikeSourcePoisson(
            rate=in_rate, start=(
                start_pairing + ISI * n_stim_pairing +
                start_test_post_pairing + ISI * i),
            duration=dur_stim)))

# Noise inputs
INoisePre = sim.Population(
    pop_size, sim.SpikeSourcePoisson(
        rate=e_rate, start=0, duration=simtime), label="expoisson")
INoisePost = sim.Population(
    pop_size, sim.SpikeSourcePoisson(
        rate=e_rate, start=0, duration=simtime), label="expoisson")

# +-------------------------------------------------------------------+
# | Creation of connections                                           |
# +-------------------------------------------------------------------+

# Connection parameters
JEE = 3.

# Connection type between noise poisson generator and excitatory populations
ee_connector = sim.OneToOneConnector()

# Noise projections
sim.Projection(
    INoisePre, pre_pop, ee_connector, receptor_type='excitatory',
    synapse_type=sim.StaticSynapse(weight=JEE * 0.001))
sim.Projection(
    INoisePost, post_pop, ee_connector, receptor_type='excitatory',
    synapse_type=sim.StaticSynapse(weight=JEE * 0.001))

# Additional Inputs projections
for i in range(len(IAddPre)):
    sim.Projection(
        IAddPre[i], pre_pop, ee_connector, receptor_type='excitatory',
        synapse_type=sim.StaticSynapse(weight=JEE * 0.001))
for i in range(len(IAddPost)):
    sim.Projection(
        IAddPost[i], post_pop, ee_connector, receptor_type='excitatory',
        synapse_type=sim.StaticSynapse(weight=JEE * 0.001))

# Plastic Connections between pre_pop and post_pop
stdp_model = sim.STDPMechanism(
    timing_dependence=sim.SpikePairRule(
        tau_plus=20., tau_minus=20.0, A_plus=0.02, A_minus=0.02),
    weight_dependence=sim.AdditiveWeightDependence(w_min=0, w_max=0.01))

plastic_projection = sim.Projection(
    pre_pop, post_pop, sim.FixedProbabilityConnector(p_connect=0.5),
    synapse_type=stdp_model)

# +-------------------------------------------------------------------+
# | Simulation and results                                            |
# +-------------------------------------------------------------------+

# Record neurons' potentials
pre_pop.record(['v', 'spikes'])
post_pop.record(['v', 'spikes'])

post_pop[[0, 2, 3]].record(['gsyn_exc'])
# Run simulation
sim.run(simtime)

print("Weights:{}".format(plastic_projection.get('weight', 'list')))

pre_spikes = pre_pop.get_data('spikes')
post_spikes = post_pop.get_data('spikes')
post_gsyn = post_pop.spinnaker_get_data('gsyn_exc')

Figure(
    # raster plot of the presynaptic neuron spike times
    Panel(pre_spikes.segments[0].spiketrains,
          yticks=True, markersize=0.5, xlim=(0, simtime)),
    Panel(post_spikes.segments[0].spiketrains,
          yticks=True, markersize=0.5, xlim=(0, simtime)),
    title="stdp example cond",
    annotations="Simulated with {}".format(sim.name()))
plt.show()

# End simulation on SpiNNaker
sim.end()
``` 

Changed files:
spynnaker/pyNN/models/neuron/abstract_population_vertex.py, spynnaker/pyNN/models/common/neuron_recorder.py

diff --git a/spynnaker/pyNN/models/common/neuron_recorder.py b/spynnaker/pyNN/models/common/neuron_recorder.py
index aae04172e1..d7b3c4de2a 100644
--- a/spynnaker/pyNN/models/common/neuron_recorder.py
+++ b/spynnaker/pyNN/models/common/neuron_recorder.py
@@ -288,6 +288,22 @@ class NeuronRecorder(object):
                 results.append(_id)
         return results
 
+    def _is_recording(self, variable, vertex_slice):
+        if self.__sampling_rates[variable] == 0:
+            return False
+        if self.__indexes[variable] is None:
+            return True
+        indexes = self.__indexes[variable]
+        for index in xrange(vertex_slice.lo_atom, vertex_slice.hi_atom+1):
+            if index in indexes:
+                return True
+        return False
+
+    def recorded_ids_by_slice(self, vertex_slice):
+        return [_id
+                for _id, variable in enumerate(self.__sampling_rates.keys())
+                if self._is_recording(variable, vertex_slice)]
+
     def _compute_rate(self, sampling_interval):
         """ Convert a sampling interval into a rate. \\
             Remember, machine time step is in nanoseconds
diff --git a/spynnaker/pyNN/models/neuron/abstract_population_vertex.py b/spynnaker/pyNN/models/neuron/abstract_population_vertex.py
index 4f03ab5851..8ae22e48e8 100644
--- a/spynnaker/pyNN/models/neuron/abstract_population_vertex.py
+++ b/spynnaker/pyNN/models/neuron/abstract_population_vertex.py
@@ -249,8 +249,10 @@ class AbstractPopulationVertex(
             constraints=None):
 
         self.__n_subvertices += 1
+        recorded_region_ids = self.__neuron_recorder.recorded_ids_by_slice(
+            vertex_slice)
         return PopulationMachineVertex(
-            resources_required, self.__neuron_recorder.recorded_region_ids,
+            resources_required, recorded_region_ids,
             label, constraints)
 
     def get_cpu_usage_for_atoms(self, vertex_slice):