spike_recorder#
- class brainpy.state.spike_recorder(in_size=1, start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), time_in_steps=False, frozen=False, name=None)#
NEST-compatible spike recording device.
spike_recorderaccumulates spike events into an in-memoryeventsdictionary, with timestamping and activity-window semantics matching NESTspike_recorder. The NEST recording-device timing model is reproduced while exposing a Python batch API:Incoming spike arrays are timestamped at step \(n + 1\) where \(n = \mathrm{round}(t / dt)\) is the current simulation step.
Recording is gated by a window \((\mathrm{origin} + \mathrm{start},\;\mathrm{origin} + \mathrm{stop}]\) (start exclusive, stop inclusive) evaluated in simulation steps.
Event writes are immediate — there is no one-step delivery lag, unlike the request/reply mechanism of
multimeter.
1. Step-Stamp and Physical-Time Model
Let \(dt > 0\) be the simulation resolution (ms), and let \(n = \mathrm{round}(t / dt)\) be the current step index when
update()is called at simulation time \(t\). Incoming events are stamped at\[s = n + 1,\]i.e., spikes are interpreted as generated during \((t,\, t + dt]\). If per-event offsets \(\delta_j\) (ms) are provided, the stored physical event time for item \(j\) is
\[t_j = s \cdot dt - \delta_j.\]With
time_in_steps=True, storage is split into integer stampsevents['times'](step index \(s\)) and continuous offsetsevents['offsets'](\(\delta_j\), ms), preserving sub-step timing.2. Activity-Window Gate on the Step Lattice
Define step bounds
\[s_{\min} = \frac{\mathrm{origin} + \mathrm{start}}{dt}, \qquad s_{\max} = \frac{\mathrm{origin} + \mathrm{stop}}{dt} \quad (\text{or } +\infty \text{ if stop is None}).\]The recorder is active for stamp step \(s\) iff
\[s > s_{\min} \;\land\; s \le s_{\max}.\]Therefore,
startis exclusive andstopis inclusive, exactly as in NEST recording devices.3. Multiplicity Inference and Payload Normalization
Incoming arrays are flattened to one-dimensional vectors of length \(N\). Scalars are broadcast to \((N,)\) for
sendersandoffsets. Let \(x_j\) denotespikes[j]:If
multiplicities is Noneand allspikesare integer-like (within1e-12tolerance), event counts are \(c_j = \max(\mathrm{round}(x_j),\, 0)\).If
multiplicities is Noneandspikescontains non-integer values, \(c_j = \mathbf{1}[x_j > 0]\).If
multiplicitiesis provided with non-negative integers \(m_j\), then \(c_j = m_j \,\mathbf{1}[x_j > 0]\).
Each item contributes exactly \(c_j\) stored events by repetition.
4. Constraints and Computational Implications
start,stop(when notNone),origin, currentt, anddtmust be scalar-convertible and aligned to the simulation grid. Alignment is enforced by round-trip integer checks with1e-12tolerance. Perupdate()call, normalization is \(O(N)\) and event expansion is \(O(E_{\mathrm{new}})\) where \(E_{\mathrm{new}} = \sum_j c_j\). Persistent memory usage is linear in the total number of stored events.- Parameters:
in_size (
Size, optional) – Shape/size argument consumed bybrainstate.nn.Dynamics. The recorder returns event dictionaries rather than dense tensors;in_sizeis retained for API compatibility only. Default is1.start (
saiunit.Quantityorfloat, optional) – Scalar relative exclusive lower bound of the recording window, convertible to ms. Must be finite and an integer multiple ofdt. The effective gate isstamp_step > (origin + start) / dt. Default is0.0 * u.ms.stop (
saiunit.Quantity,float, orNone, optional) – Scalar relative inclusive upper bound of the recording window, convertible to ms. Must beNoneor finite and aligned todt. Must satisfystop >= startwhen notNone. The effective gate isstamp_step <= (origin + stop) / dt.Nonemeans no upper bound (\(s_{\max} = +\infty\)). Default isNone.origin (
saiunit.Quantityorfloat, optional) – Scalar global time-origin shift added to bothstartandstopwhen constructing the active window, convertible to ms. Shifting the origin displaces the entire recording window without changing its duration. Must be finite and aligned todt. Default is0.0 * u.ms.time_in_steps (
bool, optional) – Controls the time representation inevents. IfFalse,events['times']storesfloat64milliseconds computed as \(s \cdot dt - \delta_j\). IfTrue,events['times']stores integer step stamps (int64) andevents['offsets']stores the correspondingfloat64offsets in ms. Becomes immutable after the firstupdate()call. Default isFalse.frozen (
bool, optional) – NEST-compatibility flag.Trueis unconditionally rejected because this recorder cannot be frozen. Default isFalse.name (
strorNone, optional) – Optional node name forwarded tobrainstate.nn.Dynamics. Default isNone.
Parameter Mapping
Table 41 Mapping of constructor parameters to model symbols# Parameter
Default
Math symbol
Semantics
start0.0 * u.ms\(t_{\mathrm{start,rel}}\)
Relative exclusive lower bound of the active window.
stopNone\(t_{\mathrm{stop,rel}}\)
Relative inclusive upper bound of the active window.
origin0.0 * u.ms\(t_0\)
Global origin shift applied before window gating.
time_in_stepsFalse\(\mathrm{repr}_t\)
Time storage mode: physical ms or integer
(step, offset)pair.- Raises:
ValueError – If
frozen=True; if any time parameter (start,stop,origin,dt, or currentt) is non-scalar, non-finite when required, not aligned todt, or violatesstop >= start; iftime_in_stepsis modified afterupdate()has been called; ifn_eventsis assigned a value other than0; if payload array sizes are incompatible withspikeslength; or if explicitmultiplicitiescontain negative entries.TypeError – If unit conversion or numeric casting of any payload or time parameter fails.
KeyError – If
get()is called with an unsupported key, or if required simulation context entries ('t'ordt) are not available viabrainstate.environ.
Notes
Event writes are immediate (no one-step delivery lag), unlike the request/reply mechanism of
multimeter.time_in_stepsbecomes immutable after the firstupdate()call that accesses simulation context, matching NEST backend constraints.spikes=Noneis treated as a no-op update that returns the currenteventswithout writing any new events.init_state()clears all accumulated events; it can be used to reset the recorder between simulation segments without reconstructing the object.
References
Examples
Record spikes from a three-neuron population over a 1 ms window at 0.1 ms resolution, using integer-like spike counts:
>>> import brainpy >>> import brainstate >>> import saiunit as u >>> import numpy as np >>> with brainstate.environ.context(dt=0.1 * u.ms): ... sr = brainpy.state.spike_recorder(start=0.0 * u.ms, stop=1.0 * u.ms) ... with brainstate.environ.context(t=0.0 * u.ms): dftype = brainstate.environ.dftype() ditype = brainstate.environ.ditype() ... _ = sr.update( ... spikes=np.array([1.0, 0.0, 2.0], dtype=dftype), ... senders=np.array([3, 4, 5], dtype=ditype), ... ) ... ev = sr.flush() ... _ = ev['times'].shape
Record a single spike with a sub-step offset using
time_in_steps=True, which splits the timestamp into an integer step index and a float offset:>>> import brainpy >>> import brainstate >>> import saiunit as u >>> import numpy as np >>> with brainstate.environ.context(dt=0.1 * u.ms): ... sr = brainpy.state.spike_recorder(time_in_steps=True) ... with brainstate.environ.context(t=0.0 * u.ms): ... _ = sr.update( ... spikes=np.array([1.0], dtype=dftype), ... senders=np.array([9], dtype=ditype), ... offsets=np.array([0.03], dtype=dftype) * u.ms, ... ) ... ev = sr.events ... _ = (ev['times'][0], ev['offsets'][0])
- update(spikes=None, senders=None, offsets=None, multiplicities=None)[source]#
Record spike events for the current simulation step.
Reads the current simulation time
tand resolutiondtfrombrainstate.environ, computes the stamp step \(s = n + 1\) where \(n = \mathrm{round}(t / dt)\), applies the activity-window gate, expands the spike payload into individual events, and appends them to the internal buffers.- Parameters:
spikes (
ArrayLikeorNone, optional) –Input spike payload, flattened to shape
(N,). Accepted dtypes include boolean, integer, and floating-point values.None: no new events are written; currenteventsdict is returned immediately.Integer-like values (all within
1e-12of an integer) withmultiplicities is None: each element \(j\) contributes \(c_j = \max(\mathrm{round}(x_j),\, 0)\) events.Non-integer floating values with
multiplicities is None: each element contributes \(c_j = \mathbf{1}[x_j > 0]\) events (binary threshold).
senders (
ArrayLikeorNone, optional) – Sender node IDs cast toint64, shape(N,)or scalar broadcastable to(N,). Default sender ID is1for all entries whenNone.offsets (
ArrayLikeorNone, optional) – Per-event sub-step timing offsets \(\delta_j\) in ms, shape(N,)or scalar broadcastable to(N,). Values may carry asaiunittime unit and are converted to ms. Must contain only finite values. Default is0.0 * u.msfor all entries.multiplicities (
ArrayLikeorNone, optional) – Explicit non-negative integer event multiplicities cast toint64, shape(N,)or scalar broadcastable to(N,). When provided, the integer-like inference path fromspikesis disabled; the count rule becomes \(c_j = m_j \,\mathbf{1}[x_j > 0]\). Negative values raiseValueError. Default isNone.
- Returns:
events – Current accumulated events dictionary after processing this step. All arrays are one-dimensional with length \(E\) equal to the total number of stored events:
'senders'—int64, shape(E,).'times'—float64ms whentime_in_steps=False;int64step stamps whentime_in_steps=True.'offsets'—float64ms, shape(E,)(only present whentime_in_steps=True).
- Return type:
dict[str,np.ndarray]- Raises:
ValueError – If
tis not grid-aligned todt; ifstart,stop, ororiginare invalid with respect todt; ifdt <= 0; if provided payload array sizes are incompatible with theNinferred fromspikes; ifoffsetscontain non-finite values; or if explicitmultiplicitiescontain negative entries.TypeError – If numeric or unit conversion of any payload or time parameter fails.
KeyError – If required simulation context entries (
't'ordt) are not available viabrainstate.environ.
Notes
Events are written at stamp step \(s = \mathrm{round}(t / dt) + 1\) and then gated by the active window \((s_{\min},\, s_{\max}]\) in step space. If the current stamp step falls outside the window, the method returns the unchanged
eventsdict without writing any new data.