diff --git a/models/pulsepacket_generator.h b/models/pulsepacket_generator.h index ddb6af7acb..dd24824324 100644 --- a/models/pulsepacket_generator.h +++ b/models/pulsepacket_generator.h @@ -48,14 +48,13 @@ Generate sequence of Gaussian pulse packets Description +++++++++++ -The pulsepacket_generator produces a spike train contains Gaussian pulse -packets centered about given times. A Gaussian pulse packet is -a given number of spikes with normal distributed random displacements -from the center time of the pulse. -It resembles the output of synfire groups of neurons. +The ``pulsepacket_generator`` produces a spike train contains Gaussian pulse +packets centered about given times. A Gaussian pulse packet is a given +number of spikes with normal distributed random displacements from the center +time of the pulse. It resembles the output of synfire groups of neurons. - All targets receive identical spike trains. -- New pulse packets are generated when activity or sdev are changed. +- New pulse packets are generated when activity or ``sdev`` are changed. - Gaussian pulse are independently generated for each given pulse-center time. - Both standard deviation and number of spikes may be set at any time. @@ -64,13 +63,13 @@ It resembles the output of synfire groups of neurons. .. include:: ../models/stimulation_device.rst pulse_times - Times of the centers of pulses (ms) + List of times of the centers of pulses in ms. activity - Number of spikes per pulse + Number of spikes per pulse. Default: ``0``. sdev - Standard deviation of spike times in each pulse (ms) + Standard deviation of spike times in each pulse in ms. Default: ``0.0``. Setting parameters from a stimulation backend ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/nestkernel/stimulation_device.h b/nestkernel/stimulation_device.h index ac3b40f6e9..512ebb9cb8 100644 --- a/nestkernel/stimulation_device.h +++ b/nestkernel/stimulation_device.h @@ -48,14 +48,15 @@ global offset that shifts the stimulation period. All three values are set as times in ms. - For spike-emitting devices, only spikes with times `t` that fulfill - `start` < `t` <= `stop` are emitted. Spikes that have timestamp of - `t` = `start` are not emitted. + :math:`\mathrm{start} < t \leq \mathrm{stop}` are emitted. Spikes + that have timestamp of :math:`t = \mathrm{start}` are not emitted. - For current-emitting devices, the current is activated and deactivated such that the current first affects the target dynamics - during the update step (start, start+h], i.e., an effect can be - recorded at the earliest at time start+h. The last interval during - which the current affects the target's dynamics is (stop-h, stop]. + during the update step :math:`(\mathrm{start}, \mathrm{start}+h]`, + that is, an effect can be recorded at the earliest at time + :math:`\mathrm{start}+h`. The last interval during which the current + affects the target's dynamics is :math:`(\textrm{stop}-h, \textrm{stop}]`. The property ``stimulus_source`` defaults to an empty string. It can be set to the name of a stimulation backend, in which case it will @@ -68,28 +69,28 @@ Parameters ++++++++++ label - A string (default: `""`) specifying an arbitrary textual label for - the device. Stimulation backends might use the label to generate - device specific identifiers like filenames and such. + A string specifying an arbitrary textual label for the device. + Stimulation backends might use the label to generate device specific + identifiers like filenames and such. Default: ``""``. origin - A positive floating point number (default : `0.0`) used as the - reference time in ms for `start` and `stop`. + A positive floating point number used as the reference time in ms + for ``start`` and ``stop``. Default: ``0.0``. start - A positive floating point number (default: `0.0`) specifying the - activation time in ms, relative to `origin`. + A positive floating point number specifying the activation time in ms, + relative to ``origin``. Default: ``0.0``. stimulus_source - A string (default: `""`) specifying the name of the stimulation - backend from which to get the data for updating the stimulus - parameters of the device. By default the device uses its - internally stored parameters for updating the stimulus. + A string specifying the name of the stimulation backend from which to + get the data for updating the stimulus parameters of the device. + By default, the device uses its internally stored parameters for updating + the stimulus. Default: ``""``. stop - A floating point number (default: `infinity`) specifying the - deactivation time in ms, relative to `origin`. The value of `stop` - must be greater than or equal to `start`. + A floating point number specifying the deactivation time in ms, relative + to ``origin``. The value of ``stop`` must be greater than or equal to + ``start``. Default: ``infinity``. EndUserDocs */ diff --git a/testsuite/pytests/sli2py_stimulating/test_pulsepacket_generator.py b/testsuite/pytests/sli2py_stimulating/test_pulsepacket_generator.py new file mode 100644 index 0000000000..120d081639 --- /dev/null +++ b/testsuite/pytests/sli2py_stimulating/test_pulsepacket_generator.py @@ -0,0 +1,128 @@ +# -*- coding: utf-8 -*- +# +# test_pulsepacket_generator.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 . + +""" +Test parameter setting and correct number of spikes emitted by `pulsepacket_generator`. +""" + +import numpy as np +import pandas as pd +import pandas.testing as pdtest +import pytest + +import nest + + +@pytest.fixture(autouse=True) +def reset(): + nest.ResetKernel() + + +def test_set_params_on_instance_and_model(): + """ + Test setting `pulsepacket_generator` parameters on instance and model. + """ + + params = {"pulse_times": [1.0, 2.5, 4.6], "activity": 150, "sdev": 0.1234} + + # Set params on instance + ppg1 = nest.Create("pulsepacket_generator") + ppg1.set(params) + + # Set params on model + nest.SetDefaults("pulsepacket_generator", params) + ppg2 = nest.Create("pulsepacket_generator") + + # Verify that both ways to set params give same result + df_pgg1 = pd.DataFrame.from_dict(ppg1.get(params.keys())) + df_pgg2 = pd.DataFrame.from_dict(ppg2.get(params.keys())) + pdtest.assert_frame_equal(df_pgg1, df_pgg2) + + +@pytest.mark.parametrize("params", [{"sdev": -5.0}, {"activity": -5}]) +def test_set_illegal_values(params): + """ + Test that an error is raised if `sdev` or `activity` is set to negative value. + """ + + with pytest.raises(nest.kernel.NESTErrors.BadProperty): + nest.SetDefaults("pulsepacket_generator", params) + + +def test_valid_to_pass_empty_pulse_times(): + """ + Assure that a `pulsepacket_generator` with empty `pulse_times` can be simulated. + """ + + params = {"pulse_times": [], "activity": 0, "sdev": 0.0} + ppg = nest.Create("pulsepacket_generator") + ppg.set(params) + nest.Simulate(1.0) + + +def test_number_of_spikes(): + """ + Test that `pulsepacket_generator` emits expected number of spikes. + + The test builds and simulates a system with `pulsepacket_generator` + connected to a `spike_recorder`. The test checks the number of + recorded spikes against our expectation. + """ + + nest.resolution = 0.1 + + tstart = 75.0 + tstop = 225.0 + nspk = 10 + pulset = np.array([10.0, 125.0, 175.0, 275.0]) + stddev = 5.0 + + # Find number of pulse centers in [tstart, tstop] + npulseff = np.count_nonzero(np.logical_and(pulset >= tstart, pulset <= tstop)) + + # Since tstart, tstop are far from pulse times, it is highly likely that + # only spikes belonging to the pulses with centers in [tstart, tstop] are + # fired and then we get for the total spike number + npsktot = nspk * npulseff + + # Build and simulate system + params = { + "start": tstart, + "stop": tstop, + "pulse_times": pulset, + "activity": nspk, + "sdev": stddev, + } + ppg = nest.Create("pulsepacket_generator", params) + sr = nest.Create("spike_recorder") + + nest.Connect(ppg, sr) + + nest.Simulate(300.0) + + actual_spikes = sr.events["times"] + + # Check that min and max are inside [tstart, tstop] + assert np.min(actual_spikes) >= tstart + assert np.max(actual_spikes) <= tstop + + # Check number of spikes + assert actual_spikes.size == npsktot diff --git a/testsuite/unittests/test_pulsepacket_generator.sli b/testsuite/unittests/test_pulsepacket_generator.sli deleted file mode 100644 index f67c9e50f7..0000000000 --- a/testsuite/unittests/test_pulsepacket_generator.sli +++ /dev/null @@ -1,129 +0,0 @@ -/* - * test_pulsepacket_generator.sli - * - * 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 . - * - */ - - - /** @BeginDocumentation -Name: testsuite::test_pulsepacket_generator - minimal test of pulsepacket_generator - -Synopsis: (test_pulsepacket_generator) run -> dies if assertion fails - -Description: -Tests parameter setting and statistical correctness for one application. - -Author: July 2008, Plesser -SeeAlso: noise_generator, testsuite::test_poisson_ps_min_interval -*/ - -(unittest) run -/unittest using - -% flatten arrays that may contain vectors and numbers -/allflat { - { { cva } stopped { ; errordict begin /newerror false def end } if } Map - Flatten -} def - -% First test: parameter setting on model and instance -{ - ResetKernel - /tdict << /pulse_times [ 1.0 2.5 4.6 ] - /activity 150 - /sdev 0.1234 >> def - - /ng1 /pulsepacket_generator Create def - ng1 tdict SetStatus - - /pulsepacket_generator tdict SetDefaults - /ng2 /pulsepacket_generator Create def - - ng1 [tdict keys] get allflat - ng2 [tdict keys] get allflat - eq -} assert_or_die -clear -ResetKernel - -% Second test: error if sdev negative -{ - ResetKernel - /pulsepacket_generator << /sdev -0.5 >> SetDefaults -} fail_or_die - -% Third test: error if activity negative -{ - ResetKernel - /pulsepacket_generator << /activity -5 >> SetDefaults -} fail_or_die - -% Fourth test: assure empty pulse times and zero otherwise is ok -{ - ResetKernel - /pulsepacket_generator Create - << /pulse_times [] /activity 0 /sdev 0.0 >> SetStatus - 1 Simulate -} pass_or_die - -% Fifth test: feed spike recorder -{ - ResetKernel - << /resolution 0.1 >> SetKernelStatus - /ppg /pulsepacket_generator Create def - /sr /spike_recorder Create def - - /tstart 75.0 def - /tstop 225.0 def - /nspk 10 def - /pulset [ 10.0 125.0 175.0 275.0 ] def - /stddev 5.0 def - % find number of pulse centers in start..stop - /npulseff - 0 pulset { dup tstart gt exch tstop lt and { 1 } { 0 } ifelse add } Fold - def - % since start, stop are far from pulse times, it is highly likely that - % only spikes belonging to the pulses with centers in start..stop are - % fired and then we get for the total spike number - /nspktot nspk npulseff mul def - - ppg << /start tstart - /stop tstop - /pulse_times pulset - /activity nspk - /sdev stddev - >> SetStatus - - ppg sr Connect - - 300 Simulate - - sr [ /events /times ] get cva /spks Set - - % check min and max are inside start .. stop - tstart spks Min leq - spks Max tstop leq and - - % check number of spikes - spks length nspktot eq - and - -} assert_or_die - -endusing