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

Extended parrot_neuron to allow connections onto port 0 and 1, not repeating spikes on port 1 #118

Merged
merged 11 commits into from
Nov 3, 2015
Merged
11 changes: 9 additions & 2 deletions models/parrot_neuron.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ parrot_neuron::update( Time const& origin, const long_t from, const long_t to )
if ( current_spikes_n > 0 )
{
for ( ulong_t i_spike = 0; i_spike < current_spikes_n; i_spike++ )
{
network()->send( *this, se, lag );
}
set_spiketime( Time::step( origin.get_steps() + lag + 1 ) );
}
}
Expand All @@ -70,6 +72,7 @@ void
parrot_neuron::get_status( DictionaryDatum& d ) const
{
def< double >( d, names::t_spike, get_spiketime_ms() );
Archiving_Node::get_status( d );
}

void
Expand All @@ -81,8 +84,12 @@ parrot_neuron::set_status( const DictionaryDatum& d )
void
parrot_neuron::handle( SpikeEvent& e )
{
B_.n_spikes_.add_value( e.get_rel_delivery_steps( network()->get_slice_origin() ),
static_cast< double_t >( e.get_multiplicity() ) );
// Repeat only spikes incoming on port 0, port 1 will be ignored
if ( 0 == e.get_rport() )
{
B_.n_spikes_.add_value( e.get_rel_delivery_steps( network()->get_slice_origin() ),
static_cast< double_t >( e.get_multiplicity() ) );
}
}

} // namespace
18 changes: 16 additions & 2 deletions models/parrot_neuron.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,13 @@ 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.

Only spikes arriving on connections to port 0 will be repeated.
Connections onto port 1 will be accepted, but spikes incoming
through port 1 will be ignored. This allows setting exact pre-
and post-synaptic spike times for STDP protocols by connecting
two parrot neurons spiking at desired times by, e.g., a
stdp_synapse onto port 1 on the post-synaptic parrot neuron.

Receives: SpikeEvent

Sends: SpikeEvent
Expand Down Expand Up @@ -145,9 +152,16 @@ parrot_neuron::send_test_event( Node& target, rport receptor_type, synindex, boo
inline port
parrot_neuron::handles_test_event( SpikeEvent&, rport receptor_type )
{
if ( receptor_type != 0 )
// Allow connections to port 0 (spikes to be repeated)
// and port 1 (spikes to be ignored).
if ( receptor_type == 0 or receptor_type == 1 )
{
return receptor_type;
}
else
{
throw UnknownReceptorType( receptor_type, get_name() );
return 0;
}
}

} // namespace
Expand Down
4 changes: 3 additions & 1 deletion pynest/nest/tests/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
from . import test_csa
from . import test_quantal_stp_synapse
from . import test_msp
from . import test_parrot_neuron


def suite():
Expand Down Expand Up @@ -72,7 +73,8 @@ def suite():
suite.addTest(test_csa.suite())
suite.addTest(test_quantal_stp_synapse.suite())
suite.addTest(test_msp.suite())

suite.addTest(test_parrot_neuron.suite())

return suite


Expand Down
172 changes: 172 additions & 0 deletions pynest/nest/tests/test_parrot_neuron.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
#
# test_parrot_neuron.py
#
# This file is part of NEST.
#
# Copyright (C) 2004 The NEST Initiative
#
# NEST 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 2 of the License, or
# (at your option) any later version.
#
# NEST 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 NEST. If not, see <http://www.gnu.org/licenses/>.

# This script tests the parrot_neuron in NEST.

import nest
import unittest


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

def setUp(self):
nest.set_verbosity('M_WARNING')
nest.ResetKernel()

# 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.parrot = nest.Create('parrot_neuron')
self.spikes = nest.Create("spike_detector")

# record source and parrot spikes
nest.Connect(self.source, self.spikes)
nest.Connect(self.parrot, self.spikes)

def test_ParrotNeuronRepeatSpike(self):
"""Check parrot_neuron repeats spikes on port 0"""

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

# get spike from parrot neuron
events = nest.GetStatus(self.spikes)[0]["events"]
post_time = events['times'][events['senders'] == self.parrot[0]]

# 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"

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.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"


@nest.check_stack
class ParrotNeuronSTDPTestCase(unittest.TestCase):
"""
Check STDP protocol between two parrot_neurons connected by a stdp_synapse.
Exact pre- and post-synaptic spike times are set by spike_generators
connected to each parrot neuron. Additional spikes sent through the
stdp_synapse are explicitly ignored in the postsynaptic parrot_neuron
by setting the stdp_synapse to connect to port 1.
"""

def run_protocol(self, dt):

"""Set up a network with pre-post spike pairings with t_post - t_pre = dt"""

nest.set_verbosity("M_WARNING")
nest.ResetKernel()

# set pre and postsynaptic spike times
delay = 1. # delay for connections
dspike = 100. # ISI

# set the correct real spike times for generators (correcting for delays)
pre_times = [100., 100. + dspike]
post_times = [k+dt for k in pre_times]

# create spike_generators with these times
pre_spikes = nest.Create("spike_generator", 1, {"spike_times": pre_times})
post_spikes = nest.Create("spike_generator", 1, {"spike_times": post_times})

# create parrot neurons and connect spike_generators
pre_parrot = nest.Create("parrot_neuron", 1)
post_parrot = nest.Create("parrot_neuron", 1)

nest.Connect(pre_spikes, pre_parrot, syn_spec={"delay": delay})
nest.Connect(post_spikes, post_parrot, syn_spec={"delay": delay})

# create spike detector
spikes = nest.Create("spike_detector")
nest.Connect(pre_parrot, spikes)
nest.Connect(post_parrot, spikes)

# connect both parrot neurons with a stdp synapse onto port 1
# thereby spikes transmitted through the stdp connection are
# not repeated postsynaptically.
syn_spec = {
"model": "stdp_synapse",
"receptor_type": 1, # set receptor 1 postsynaptically, to not generate extra spikes
}
conn_spec = {
"rule": "one_to_one",
}
nest.Connect(pre_parrot, post_parrot, syn_spec=syn_spec, conn_spec=conn_spec)

# get STDP synapse and weight before protocol
syn = nest.GetConnections(source=pre_parrot, synapse_model="stdp_synapse")
syn_status = nest.GetStatus(syn)[0]
w_pre = syn_status['weight']

last_time = max(pre_times[-1], post_times[-1])
nest.Simulate(last_time + 2 * delay)

# get weight post protocol
syn_status = nest.GetStatus(syn)[0]
w_post = syn_status['weight']

return w_pre, w_post

def test_ParrotNeuronSTDPProtocolPotentiation(self):
"""Check pre-post spike pairings between parrot_neurons increments weights."""

dt = 10.
w_pre, w_post = self.run_protocol(dt)
assert w_pre < w_post, "Parrot neuron STDP potentiation protocol failed to elicit positive weight changes."

def test_ParrotNeuronSTDPProtocolDepression(self):
"""Check post-pre spike pairings between parrot_neurons decrement weights."""

dt = -10.
w_pre, w_post = self.run_protocol(dt)
assert w_pre > w_post, "Parrot neuron STDP potentiation protocol failed to elicit negative weight changes."


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])


def run():
runner = unittest.TextTestRunner(verbosity=2)
runner.run(suite())


if __name__ == "__main__":
run()