spike_generator#
- class brainpy.state.spike_generator(in_size=1, spike_times=(), spike_weights=(), start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), name=None)#
Spike generator – NEST-compatible stimulation device.
Emit deterministic spike-like outputs at prescribed times with optional per-event amplitudes, while respecting a half-open activity window.
1. Model equations
Let \(\{t_i\}_{i=1}^{K}\) be configured spike times in ms (non-descending after conversion), and \(\{w_i\}_{i=1}^{K}\) optional spike weights. At simulation time \(t\) with step \(\Delta t\) (both in ms), define the matching indicator
\[m_i(t) = \mathbf{1}\!\left[|t - t_i| < \frac{\Delta t}{2}\right].\]The active-window gate is
\[g(t) = \mathbf{1}\!\left[t \ge t_0 + t_{\mathrm{start,rel}}\right] \cdot \mathbf{1}\!\left[t < t_0 + t_{\mathrm{stop,rel}}\right],\]where the second indicator is omitted when
stop is None.This implementation computes a scalar amplitude \(a(t)\) as follows:
no
spike_weights: \(a(t)=1\) if any \(m_i(t)=1\), else \(a(t)=0\);with
spike_weights: \(a(t)\) equals the weight associated with the last matching index (iteration order throughspike_times).
The returned output is broadcast to
self.varshape:\[y(t) = g(t)\,a(t)\,\mathbf{1}_{\mathrm{varshape}}.\]2. Timing semantics, assumptions, and constraints
A configured spike at \(t_s\) is intended for the step satisfying \(t_s-\Delta t < t \le t_s\) under grid-aligned simulation. The implementation uses \(|t-t_s| < \Delta t/2\) for robust floating-point matching, which is equivalent to \(t - \Delta t/2 < t_s < t + \Delta t/2\).
Enforced constraints:
spike_timesmust be sorted in non-descending order after conversion.spike_weightsmust be empty or have exactlylen(spike_times)elements.
Accepted but not additionally constrained:
Unitless
spike_timesare interpreted as ms.Duplicate spike times are allowed. Without weights, duplicates remain binary output. With weights, the last duplicate’s weight is used.
3. Computational implications
Each
update()call usesu.math.searchsorted()to locate the spike-time range matching the current step, then selects the last matching weight withu.math.clip()andu.math.where(). Per-call complexity is \(O(\log K + \prod\mathrm{varshape})\), where \(K\) is the number of configured spike times.- Parameters:
in_size (
Size, optional) – Output size/shape specification consumed bybrainstate.nn.Dynamics. The emitted array has shapeself.varshapederived fromin_size. Default is1.spike_times (
Sequence, optional) – Sequence of spike times with lengthK. Entries may be unitful times (typically ms) or unitless numerics interpreted as ms. Passed directly tou.math.asarray(), which validates unit consistency across all entries. Must be non-descending. Default is().spike_weights (
Sequence, optional) – Optional sequence of per-spike amplitudes with lengthKmatchingspike_timesexactly, or empty to use binary spikes. Entries are passed tou.math.asarray()(dimensionless). Default is().start (
ArrayLike, optional) – Relative activation time \(t_{\mathrm{start,rel}}\) (typically ms), initialized throughbraintools.init.param(). Effective lower bound isorigin + start(inclusive). Default is0. * u.ms.stop (
ArrayLikeorNone, optional) – Relative deactivation time \(t_{\mathrm{stop,rel}}\) (typically ms), initialized throughbraintools.init.param()when provided. Effective upper bound isorigin + stop(exclusive).Nonemeans no upper bound. Default isNone.origin (
ArrayLike, optional) – Global time origin \(t_0\) (typically ms) added tostartandstop, broadcast toself.varshape. Default is0. * u.ms.name (
strorNone, optional) – Optional node name passed tobrainstate.nn.Dynamics.
Parameter Mapping
Table 28 Parameter mapping to model symbols# Parameter
Default
Math symbol
Semantics
spike_times()\(t_i\)
Scheduled spike times in ms, checked by
|t - t_i| < dt/2.spike_weights()\(w_i\)
Per-spike amplitude; when multiple indices match, the last wins.
start0. * u.ms\(t_{\mathrm{start,rel}}\)
Relative inclusive lower bound of active window.
stopNone\(t_{\mathrm{stop,rel}}\)
Relative exclusive upper bound of active window.
origin0. * u.ms\(t_0\)
Global offset applied to
startandstop.- Raises:
ValueError – If
spike_timesis not non-descending, or iflen(spike_weights)is non-zero and differs fromlen(spike_times).TypeError – If
u.math.asarray()detects unit inconsistency across entries, or if unitful/unitless arithmetic is invalid during time-window comparisons.KeyError – At update time, if simulation context lacks
't'ordtinbrainstate.environ.
Notes
Unlike current generators (
dc_generator,step_current_generator),spike_generatoremits dimensionless impulses (or weighted real values) rather than physical current quantities. The output is intended to be consumed directly as pre-synaptic spike events or injected into a synapse model that scales by connection weight.NEST’s
spike_generatoruses multiplicity to allow multiple spikes per time step; this implementation preserves that semantics — the last matching weight wins when duplicates exist. Theupdate()method is fully compatible withjax.jit: both the spike-time lookup and the activity-window check use purely functional operations with no Python control flow over traced values.Spike times should ideally be aligned to the simulation grid (multiples of
dt) to avoid off-by-one steps due to floating-point comparison. The half-open tolerancedt/2generally covers one-ULP rounding errors for grid-aligned times.See also
dc_generatorConstant-current stimulation device.
ac_generatorSinusoidal current stimulation device.
step_current_generatorPiecewise-constant current stimulation device.
spike_train_injectorInject pre-recorded spike trains into the network.
References
Examples
>>> import brainpy >>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... sg = brainpy.state.spike_generator( ... spike_times=[5.0 * u.ms, 10.0 * u.ms, 15.0 * u.ms], ... ) ... with brainstate.environ.context(t=10.0 * u.ms): ... spk = sg.update() ... _ = spk.shape
>>> import brainpy >>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... sg = brainpy.state.spike_generator( ... spike_times=[5.0 * u.ms, 5.0 * u.ms, 10.0 * u.ms], ... spike_weights=[0.25, 0.5, 2.0], ... ) ... with brainstate.environ.context(t=5.0 * u.ms): ... spk = sg.update() ... _ = spk.shape
- update()[source]#
Compute spike output for the current simulation step.
The implementation is fully compatible with
jax.jit: spike-time matching usesu.math.searchsorted()on the staticspike_timesarray whiletanddtremain traced values throughout. The activity-window check usesu.math.logical_and()andu.math.where()with no Python branching over traced values.- Returns:
out – Float-valued JAX array with shape
self.varshape. Output semantics:0when outside[origin + start, origin + stop)(or[origin + start, +inf)ifstop is None),0when active but no configured spike matches|t - t_i| < dt/2,1at a matching spike time without weights,last matching weight when
spike_weightsis configured.
- Return type:
jax.Array- Raises:
KeyError – If required simulation context values are missing from
brainstate.environ(e.g.'t'ordt).
Notes
Both
spike_timesandtare divided byu.msto obtain dimensionless arrays before callingu.math.searchsorted(). The matching condition|t - t_s| < dt/2is rewritten as the open interval(t - dt/2, t + dt/2)and located with twosearchsortedcalls:idx_lo = searchsorted(times, t - dt/2, side='right')— first index strictly greater than the lower bound.idx_hi = searchsorted(times, t + dt/2, side='left')— first index at or above the upper bound.
Any spike exists when
idx_hi > idx_lo; the last matching spike index isidx_hi - 1, clamped to a valid range for the gather. Start is inclusive and stop is exclusive, matching NEST semantics.See also
spike_generatorClass-level parameter definitions and model equations.
dc_generator.updateWindowed constant-current update rule.
step_current_generator.updateWindowed piecewise-constant update rule.