diff --git a/brian2/codegen/runtime/numpy_rt/templates/spikegenerator.py_ b/brian2/codegen/runtime/numpy_rt/templates/spikegenerator.py_ index 63c16767c..ca9d17755 100644 --- a/brian2/codegen/runtime/numpy_rt/templates/spikegenerator.py_ +++ b/brian2/codegen/runtime/numpy_rt/templates/spikegenerator.py_ @@ -1,25 +1,48 @@ -{# USES_VARIABLES { _spikespace, neuron_index, _timebins, _period_bins, _lastindex, t_in_timesteps } #} -{% extends 'common_group.py_' %} +{% extends 'common_group.py.jinja2' %} + +{% block before_code %} +# Copy of the SpikeGeneratorGroup.before_run code +dt = {{dt.item()}} # Always copy dt +period = {{period_}} # Always copy period + +# Always recalculate timesteps +from brian2 import defaultclock # Use Brian 2's clock instead of 't' +current_t = defaultclock.t +timesteps = ({{_spike_time}} / dt).astype(np.int32) +current_step = int(current_t / dt) + +# Always update _lastindex +in_the_past = np.nonzero(timesteps < current_step)[0] +if len(in_the_past): + {{_lastindex}}[0] = in_the_past[-1] + 1 +else: + {{_lastindex}}[0] = 0 + +# Always recalculate _timebins +shift = 1e-3 * dt +timebins = np.asarray(({{_spike_time}} + shift) / dt, dtype=np.int32) + +{{_timebins}}[:] = timebins + +# Always recalculate period_bins (ignore limit checks) +period_bins = int(round(period / dt)) +{{_period_bins}}[0] = period_bins +{% endblock %} {% block maincode %} _the_period = {{_period_bins}} -_timebin = {{t_in_timesteps}} +_timebin = int(defaultclock.t / {{dt.item()}}) # Use Brian 2's clock instead of 't_in_timesteps' _n_spikes = 0 _lastindex_before = {{_lastindex}} -if _the_period > 0: - _timebin %= _the_period - # If there is a periodicity in the SpikeGenerator, we need to reset the - # lastindex when the period has passed - if _lastindex_before > 0 and {{_timebins}}[_lastindex_before - 1] >= _timebin: - _lastindex_before = 0 +# Always reset _lastindex if period is applied +_timebin %= _the_period +_lastindex_before = 0 -_n_spikes = _numpy.searchsorted({{_timebins}}[_lastindex_before:], - _timebin, side='right') +_n_spikes = _numpy.searchsorted({{_timebins}}, _timebin, side='right') {{_lastindex}} = _lastindex_before + _n_spikes - _indices = {{neuron_index}}[_lastindex_before:_lastindex_before+_n_spikes] {{_spikespace}}[:_n_spikes] = _indices diff --git a/brian2/devices/cpp_standalone/templates/spikegenerator.cpp b/brian2/devices/cpp_standalone/templates/spikegenerator.cpp index 691a55805..eeaba2580 100644 --- a/brian2/devices/cpp_standalone/templates/spikegenerator.cpp +++ b/brian2/devices/cpp_standalone/templates/spikegenerator.cpp @@ -1,23 +1,56 @@ -{# USES_VARIABLES { _spikespace, neuron_index, _timebins, _period_bins, _lastindex, t_in_timesteps, N } #} +{# USES_VARIABLES { _spikespace, neuron_index, _timebins, _period_bins, _lastindex, N, _spike_time, dt, period } #} {% extends 'common_group.cpp' %} + +{% block before_code %} + // Copy of the SpikeGeneratorGroup.before_run code + const double _dt = {{dt.item()}}; // Always copy dt + const double _period = {{period_}}; // Always copy period + + // Always recalculate _timesteps + std::vector _timesteps({{_spike_time}}.size()); + for (size_t i = 0; i < _timesteps.size(); i++) { + _timesteps[i] = static_cast({{_spike_time}}[i] / _dt); + } + + // Get current simulation time from Brian 2's clock instead of 't' + extern double defaultclock_t; + const int32_t _current_step = static_cast(defaultclock_t / _dt); + + // Always update _lastindex + int32_t _last_idx = 0; + for (size_t i = 0; i < _timesteps.size(); i++) { + if (_timesteps[i] < _current_step) { + _last_idx = i + 1; + } else { + break; + } + } + {{_lastindex}} = _last_idx; + + // Always recalculate _timebins + const double _shift = 1e-3 * _dt; + std::vector _timebins({{_spike_time}}.size()); + for (size_t i = 0; i < _timebins.size(); i++) { + _timebins[i] = static_cast({{_spike_time}}[i] + _shift) / _dt; + } + {{_timebins}} = _timebins; + + // Always recalculate _period_bins + {{_period_bins}} = static_cast(std::round(_period / _dt)); +{% endblock %} + {% block maincode %} const int32_t _the_period = {{_period_bins}}; - int32_t _timebin = {{t_in_timesteps}}; - const int32_t _n_spikes = 0; - - if (_the_period > 0) { - _timebin %= _the_period; - // If there is a periodicity in the SpikeGenerator, we need to reset the - // lastindex when the period has passed - if ({{_lastindex}} > 0 && {{_timebins}}[{{_lastindex}} - 1] >= _timebin) - {{_lastindex}} = 0; - } + int32_t _timebin = static_cast(defaultclock_t / {{dt.item()}}); // Use Brian 2's clock instead of 't_in_timesteps' + + // Always recalculate timebin with period + _timebin %= _the_period; + {{_lastindex}} = 0; int32_t _cpp_numspikes = 0; - for(size_t _idx={{_lastindex}}; _idx < _num_timebins; _idx++) - { + for (size_t _idx = {{_lastindex}}; _idx < _num_timebins; _idx++) { if ({{_timebins}}[_idx] > _timebin) break; @@ -28,5 +61,4 @@ {{_lastindex}} += _cpp_numspikes; - -{% endblock %} \ No newline at end of file +{% endblock %}