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

Fix issue #82: parrot neuron now emits multiple spikes via spike multiplicity instead of a loop #146

Merged
merged 5 commits into from
Nov 5, 2015
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 5 additions & 7 deletions models/parrot_neuron.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,16 @@ parrot_neuron::update( Time const& origin, const long_t from, const long_t to )
assert( to >= 0 && ( delay ) from < Scheduler::get_min_delay() );
assert( from < to );

SpikeEvent se;

for ( long_t lag = from; lag < to; ++lag )
{
const ulong_t current_spikes_n = static_cast< ulong_t >( B_.n_spikes_.get_value( lag ) );

if ( current_spikes_n > 0 )
{
for ( ulong_t i_spike = 0; i_spike < current_spikes_n; i_spike++ )
{
network()->send( *this, se, lag );
}
// create a new SpikeEvent and set its multiplicity
SpikeEvent se;
se.set_multiplicity( current_spikes_n );

network()->send( *this, se, lag );
set_spiketime( Time::step( origin.get_steps() + lag + 1 ) );
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I need to double-check whether set_spiketime() needs to be called once per outgoing spike, i.e., as many times as multiplicity indicates.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#77 (comment) seems to suggest that this is indeed the case.

this would result in

for (ulong_t i=0;  i<current_spikes_n; i++ )
    set_spiketime( Time::step( origin.get_steps() + lag + 1 ) );

with which we're almost back to the old form -- although it's a little cleaner with the handling of multiplicity.

It should be noted that the original model did not set the spike_times multiply -- but the parrot probably wasn't used for plasticity much yet.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that you need this. Can you please add this change?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@flinz I agree with @jougs and after more meditation over the spike archiving and plasticity (see #77) think that we do have to call set_spiketime() once for every spike. One this PR is merged, I will extend set_spiketime() to accept multiplicity as argument in a separate PR, but I don't want to delay this PR further.

}
}
Expand Down
39 changes: 14 additions & 25 deletions models/parrot_neuron.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,18 @@ Name: parrot_neuron - Neuron that repeats incoming spikes.
Description:

The parrot neuron simply emits one spike for every incoming spike.
One possible application for this is to create different channels
for the output of generator devices such as the poisson_generator
or the mip_generator.
An important application is to provide identical poisson spike
trains to a group of neurons. The poisson_generator sends a different
spike train to each of its target neurons. By connecting one
poisson_generator to a parrot_neuron and then that parrot_neuron to
a group of neurons, all target neurons will receive the same poisson
spike train.

Remarks:

Network-wise the parrot neuron behaves like other neuron models
regarding connections and communication. While the number of
outgoing spikes equals that of incoming ones, the weigth of the
outgoing spikes solely depends on the weigth of outgoing connections.

A Poisson generator that would send multiple spikes during a single
time step due to a high rate will send single spikes with
multiple synaptic strength instead, for effiacy reasons.
This can be realized because of the way devices are implemented
in the threaded environment. A parrot neuron on the other
hand always emits single spikes. Hence, in a situation where for
example a poisson generator with a high firing rate is connected
to a parrot neuron, the communication cost associated with outgoing
spikes is much bigger for the latter.
- Weights on connection to the parrot_neuron are ignored.
- Weights on connections from the parrot_neuron are handled as usual.
- Delays are honored on incoming and outgoing connections.

Only spikes arriving on connections to port 0 will be repeated.
Connections onto port 1 will be accepted, but spikes incoming
Expand All @@ -60,22 +52,19 @@ Receives: SpikeEvent
Sends: SpikeEvent

Parameters:

No parameters to be set in the status dictionary.

References:
No references

Author: May 2006, Reichert, Morrison
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should now be

Author: David Reichert, Abigail Morrison, Alexander Seeholzer, Hans Ekkehard Plesser
FirstVersion: May 2006

*/


/**
* The parrot neuron emits one spike for every incoming spike.
* It is a (strongly) simplified version of the iaf_neuron class,
* stripped of the dynamics and unneeded features.
* Instead of the accumulated weigths of the incoming spikes the
* The parrot neuron emits one spike for every incoming spike,
* but may use multiplicity to indicate number of spikes in a single
* time step.
* Instead of the accumulated weigths of the incoming spikes, the
* number of the spikes is stored within a ring buffer.
*
* \author David Reichert
* \date may 2006
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please remove the date and author lines here.

*/
Expand Down
91 changes: 85 additions & 6 deletions pynest/nest/tests/test_parrot_neuron.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import nest
import unittest
import math


@nest.check_stack
Expand All @@ -36,7 +37,8 @@ def setUp(self):
# set up source spike generator, as well as parrot neurons
self.spike_time = 1.
self.delay = .2
self.source = nest.Create("spike_generator", 1, {"spike_times": [self.spike_time]})
self.source = nest.Create("spike_generator", 1,
{"spike_times": [self.spike_time]})
self.parrot = nest.Create('parrot_neuron')
self.spikes = nest.Create("spike_detector")

Expand All @@ -57,20 +59,96 @@ def test_ParrotNeuronRepeatSpike(self):

# assert spike was repeated at correct time
assert post_time, "Parrot neuron failed to repeat spike."
assert self.spike_time + self.delay == post_time, "Parrot neuron repeated spike at wrong delay"
assert self.spike_time + self.delay == post_time, \
"Parrot neuron repeated spike at wrong delay"

def test_ParrotNeuronIgnoreSpike(self):
"""Check parrot_neuron ignores spikes on port 1"""

# connect with arbitrary delay to port 1
nest.Connect(self.source, self.parrot, syn_spec={"receptor_type": 1, "delay": self.delay})
nest.Connect(self.source, self.parrot,
syn_spec={"receptor_type": 1, "delay": self.delay})
nest.Simulate(self.spike_time + 2. * self.delay)

# get spike from parrot neuron, assert it was ignored
events = nest.GetStatus(self.spikes)[0]["events"]
post_time = events['times'][events['senders'] == self.parrot[0]]
assert len(post_time) == 0, "Parrot neuron failed to ignore spike arriving on port 1"
assert len(post_time) == 0, \
"Parrot neuron failed to ignore spike arriving on port 1"

def test_ParrotNeuronOutgoingMultiplicity(self):
"""
Check parrot_neuron correctly repeats multiple spikes

The parrot_neuron receives two spikes in a single time step.
We check that both spikes are forwarded to the spike_detector.
"""

# connect twice
nest.Connect(self.source, self.parrot, syn_spec={"delay": self.delay})
nest.Connect(self.source, self.parrot, syn_spec={"delay": self.delay})
nest.Simulate(self.spike_time + 2. * self.delay)

# get spikes from parrot neuron, assert two were transmitted
events = nest.GetStatus(self.spikes)[0]["events"]
post_times = events['times'][events['senders'] == self.parrot[0]]
assert len(post_times) == 2 and post_times[0] == post_times[1], \
"Parrot neuron failed to correctly repeat multiple spikes."

@nest.check_stack
class ParrotNeuronPoissonTestCase(unittest.TestCase):
"""Check parrot_neuron spike repetition properties"""

def test_ParrotNeuronIncomingMultiplicity(self):
"""
Check parrot_neuron heeds multiplicity information in incoming spikes.

This test relies on the fact that poisson_generator transmits
multiple spikes during a time step using multiplicity, and that
these spikes are delivered directly, i.e., without multiplicity-
unrolling in send_remote().

We create a high-rate poisson_generator. If parrot_neuron
ignored multiplicity, it would only transmit one spike per time
step. We chain two parrot_neurons to check against any loss.
"""

# set up source spike generator, as well as parrot neurons
h = 0.1 # ms
rate = 1000000. # spikes / s
delay = 1. # ms
t_base = 1000. # ms
t_sim = t_base + 3 * delay # after t_sim, spikes from t_base arrived
spikes_expected = rate * t_base / 1000.
spikes_std = math.sqrt(spikes_expected)

# if the test is to be meaningful we must expect signficantly more
# spikes than time steps
assert spikes_expected - 3 * spikes_std > 10. * t_sim / h, \
"Internal inconsistency: too few spikes."

nest.set_verbosity('M_WARNING')
nest.ResetKernel()
nest.SetKernelStatus({'resolution': h,
'grng_seed': 123,
'rng_seeds': [456]})

source = nest.Create('poisson_generator', params={'rate': rate})
parrots = nest.Create('parrot_neuron', 2)
detect = nest.Create('spike_detector')

nest.Connect(source, parrots[:1], syn_spec={'delay': delay})
nest.Connect(parrots[:1], parrots[1:], syn_spec={'delay': delay})
nest.Connect(parrots[1:], detect)

nest.Simulate(t_sim)

n_spikes = nest.GetStatus(detect)[0]['n_events']
assert n_spikes > spikes_expected - 3 * spikes_std, \
"parrot_neuron loses spikes."
assert n_spikes < spikes_expected + 3 * spikes_std, \
"parrot_neuron adds spikes."


@nest.check_stack
class ParrotNeuronSTDPTestCase(unittest.TestCase):
Expand Down Expand Up @@ -159,8 +237,9 @@ def suite():
# makeSuite is sort of obsolete http://bugs.python.org/issue2721
# using loadTestsFromTestCase instead.
suite1 = unittest.TestLoader().loadTestsFromTestCase(ParrotNeuronTestCase)
suite2 = unittest.TestLoader().loadTestsFromTestCase(ParrotNeuronSTDPTestCase)
return unittest.TestSuite([suite1, suite2])
suite2 = unittest.TestLoader().loadTestsFromTestCase(ParrotNeuronPoissonTestCase)
suite3 = unittest.TestLoader().loadTestsFromTestCase(ParrotNeuronSTDPTestCase)
return unittest.TestSuite([suite1, suite2, suite3])


def run():
Expand Down