Skip to content

Commit

Permalink
Merge pull request nest#118 from flinz/parrot-multiport
Browse files Browse the repository at this point in the history
Extended parrot_neuron to allow connections onto port 0 and 1, not repeating spikes on port 1
  • Loading branch information
abigailm committed Nov 3, 2015
2 parents 5c6535f + 9c4c2f3 commit ce63c55
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 5 deletions.
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()

0 comments on commit ce63c55

Please sign in to comment.