weight_recorder#

class brainpy.state.weight_recorder(in_size=1, senders=(), targets=(), start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), time_in_steps=False, frozen=False, name=None)#

NEST-compatible recorder for synaptic weights.

weight_recorder accumulates transmitted synaptic events in memory, storing weight, sender/target IDs, receptor/port metadata, and event time with NEST-compatible activity-window and filtering semantics. This implementation follows NEST weight_recorder behavior (models/weight_recorder.{h,cpp} and nestkernel/{recording_device.*,connector_base_impl.h}) while exposing a direct batch API.

1. Event Payload Model on the Simulation Grid

Let \(dt > 0\) be simulation resolution in ms, and \(n = \mathrm{round}(t/dt)\) the current step when update() is called at simulation time \(t\). For each incoming item \(j \in \{1,\dots,N\}\), define the payload tuple \((w_j, s_j, q_j, r_j, p_j, \delta_j)\):

  • \(w_j\) – synaptic weight (unitless/implementation-specific value),

  • \(s_j\) – sender node ID,

  • \(q_j\) – target node ID,

  • \(r_j\) – receptor port (rport),

  • \(p_j\) – connection port metadata,

  • \(\delta_j\) – sub-step offset in ms.

If stamp_steps is omitted, \(s^{(\mathrm{step})}_j = n + 1\) for all items, matching NEST’s event stamp convention for events generated during \((t, t+dt]\). Otherwise, user-provided integer stamp steps are used.

With time_in_steps=False, stored physical time for item \(j\) is

\[t_j = s^{(\mathrm{step})}_j \cdot dt - \delta_j.\]

With time_in_steps=True, time is represented as (events['times'], events['offsets']) = (s^{(\mathrm{step})}_j, \delta_j), preserving sub-step precision.

2. Activity-Window Gate and Sender/Target Filtering

Define recorder bounds on the step lattice:

\[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}).\]

An event is recordable iff

\[s^{(\mathrm{step})}_j > s_{\min} \;\land\; s^{(\mathrm{step})}_j \le s_{\max}.\]

Therefore start is exclusive and stop is inclusive, exactly as in NEST recording devices. Optional filter sets \(\mathcal{S}\) (senders) and \(\mathcal{T}\) (targets) further constrain recording:

\[s_j \in \mathcal{S} \text{ if } \mathcal{S}\neq\varnothing,\qquad q_j \in \mathcal{T} \text{ if } \mathcal{T}\neq\varnothing.\]

Events failing any gate/filter condition are discarded.

3. Input Normalization and Broadcast Rules

All update payload inputs are flattened to one-dimensional arrays. weights defines the batch size \(N\). Optional payload arrays are interpreted as:

  • None — default scalar broadcast to (N,),

  • scalar-like — broadcast to (N,),

  • length-\(N\) vector — used directly.

This matches NEST-like behavior where missing sender/target/receptor/port fields receive default metadata and each accepted item contributes exactly one stored event.

4. Assumptions, Constraints, and Computational Implications

start, stop (if finite), origin, current t, and dt must be scalar-convertible and aligned to the simulation grid (checked by round-trip integer conversion with 1e-12 tolerance). stop must satisfy stop >= start when finite. Sender/target filters must contain strictly positive integer node IDs.

Per update(), normalization and masking are \(O(N)\), and appends are \(O(E_{\mathrm{new}})\) where \(E_{\mathrm{new}}\) is the number of accepted events. Persistent storage cost is linear in the total number of accumulated events across calls.

Parameters:
  • in_size (Size, optional) – Shape/size passed to brainstate.nn.Dynamics. This recorder emits dictionary outputs instead of dense tensors; in_size is retained for API compatibility only. Default is 1.

  • senders (ArrayLike, optional) – Sender-node filter whitelist. Interpreted as a 1-D integer array of shape (K_s,) with strictly positive entries. An empty sequence disables sender filtering entirely. Default is ().

  • targets (ArrayLike, optional) – Target-node filter whitelist. Interpreted as a 1-D integer array of shape (K_t,) with strictly positive entries. An empty sequence disables target filtering entirely. Default is ().

  • start (saiunit.Quantity or float, optional) – Scalar relative exclusive lower bound of the recording window, convertible to ms. Effective gate is strict: stamp_step > (origin + start) / dt. Must be finite and an integer multiple of dt. Default is 0.0 * u.ms.

  • stop (saiunit.Quantity, float, or None, optional) – Scalar relative inclusive upper bound of the recording window, convertible to ms. Gate is inclusive: stamp_step <= (origin + stop) / dt. None means no upper bound (\(s_{\max} = +\infty\)). Finite values must be dt-aligned and satisfy stop >= start. Default is None.

  • origin (saiunit.Quantity or float, optional) – Scalar global origin shift added to both start and stop before window evaluation, convertible to ms. Shifting the origin displaces the entire recording window without changing its duration. Must be finite and dt-aligned. Default is 0.0 * u.ms.

  • time_in_steps (bool, optional) – Time output representation. If False, events['times'] stores float64 milliseconds computed as \(s \cdot dt - \delta_j\). If True, events['times'] stores int64 step stamps and events['offsets'] stores float64 offsets in ms. Becomes immutable after the first update() call. Default is False.

  • frozen (bool, optional) – NEST-compatibility flag. True is unconditionally rejected because this recorder cannot be frozen in this backend. Default is False.

  • name (str or None, optional) – Optional node name passed to brainstate.nn.Dynamics. Default is None.

Parameter Mapping

Table 42 Mapping of constructor parameters to model symbols#

Parameter

Default

Math symbol

Semantics

start

0.0 * u.ms

\(t_{\mathrm{start,rel}}\)

Relative exclusive lower bound of the active stamp-step interval.

stop

None

\(t_{\mathrm{stop,rel}}\)

Relative inclusive upper bound of the active stamp-step interval.

origin

0.0 * u.ms

\(t_0\)

Origin shift added to both relative bounds before discretization.

senders

()

\(\mathcal{S}\)

Optional sender-ID whitelist; empty disables sender filtering.

targets

()

\(\mathcal{T}\)

Optional target-ID whitelist; empty disables target filtering.

time_in_steps

False

\(\mathrm{repr}_t\)

Time representation: physical ms or integer step-stamp + offset.

Raises:
  • ValueError – If frozen=True; if filter IDs are non-positive; if timing parameters are non-scalar, non-finite (where required), off-grid with respect to dt, or violate stop >= start; if time_in_steps is modified after the first update() call; if n_events is set to a value other than 0; if event payload array sizes mismatch weights length after broadcasting; if weights or offsets contain non-finite values; or if the runtime simulation time t is not grid-aligned.

  • TypeError – If unit conversion or numeric casting of any input array or time parameter fails.

  • KeyError – If get() is called with an unsupported key, or if simulation context values needed by update() (such as 't' or dt) are unavailable via brainstate.environ.

Notes

  • This recorder stores only events explicitly passed to update(); it does not introspect synapse objects or connection containers.

  • Filter checks are evaluated before event insertion, following NEST’s handler ordering for weight_recorder.

  • n_events is write-restricted to 0 to support explicit buffer reset while preventing partial truncation.

  • time_in_steps becomes immutable after the first update() call that accesses simulation context, matching NEST backend constraints.

  • weights=None in update() is treated as a no-op that returns the current events without writing any new events.

References

Examples

Record weights from a filtered subset of senders over a 1 ms window, then inspect the accepted event weights:

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> import numpy as np
>>> with brainstate.environ.context(dt=0.1 * u.ms):
ditype = brainstate.environ.ditype()
...     wr = brainpy.state.weight_recorder(
...         senders=np.array([10, 11], dtype=ditype),
...         start=0.0 * u.ms,
...         stop=1.0 * u.ms,
...     )
...     with brainstate.environ.context(t=0.0 * u.ms):
dftype = brainstate.environ.dftype()
...         _ = wr.update(
...             weights=np.array([0.5, 0.7], dtype=dftype),
...             senders=np.array([10, 12], dtype=ditype),
...             targets=np.array([3, 4], dtype=ditype),
...         )
...     ev = wr.flush()
...     _ = ev['weights'].shape

Record a single event 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):
...     wr = brainpy.state.weight_recorder(time_in_steps=True)
...     with brainstate.environ.context(t=1.0 * u.ms):
...         _ = wr.update(
...             weights=np.array([1.2], dtype=dftype),
...             senders=np.array([5], dtype=ditype),
...             targets=np.array([6], dtype=ditype),
...             offsets=np.array([0.03], dtype=dftype) * u.ms,
...             stamp_steps=np.array([12], dtype=ditype),
...         )
...     ev = wr.events
...     _ = (ev['times'][0], ev['offsets'][0])
clear_events()[source]#

Clear all stored event buffers in-place.

Returns:

outNone. Internal Python lists for all event fields are reset to empty lists.

Return type:

Any

connect()[source]#

Compatibility no-op for device connection phase.

flush()[source]#

Return a snapshot of all recorded events.

get(key='events')[source]#

Return a recorder property by key.

Parameters:

key ({'events', 'n_events', 'time_in_steps', 'senders', 'targets'}, optional) – Property selector. Default is 'events'.

Returns:

out – Selected value:

  • 'events' -> dict[str, np.ndarray] with event arrays,

  • 'n_events' -> int,

  • 'time_in_steps' -> bool,

  • 'senders' or 'targets' -> np.ndarray of int64.

Return type:

dict

Raises:

KeyError – If key is unsupported.

init_state(batch_size=None, **kwargs)[source]#

Initialize dynamic state by clearing recorded events.

Parameters:
  • batch_size (int or None, optional) – Unused compatibility argument accepted by brainstate.nn.Dynamics.

  • **kwargs – Unused extra keyword arguments for framework compatibility.

update(weights=None, senders=None, targets=None, receptors=None, ports=None, offsets=None, stamp_steps=None)[source]#

Record a batch of transmitted synaptic events for the current step.

Parameters:
  • weights (ArrayLike or None, optional) – Event weights. Flattened to shape (N,) with dtype float64. Unitless/implementation-specific weight values. If None, this call is a no-op and current events are returned.

  • senders (ArrayLike or None, optional) – Sender node IDs for each item, shape (N,) after broadcast, dtype int64. None defaults to 1 for all items.

  • targets (ArrayLike or None, optional) – Target node IDs for each item, shape (N,) after broadcast, dtype int64. None defaults to 1 for all items.

  • receptors (ArrayLike or None, optional) – Receptor IDs (rport) per item, shape (N,) after broadcast, dtype int64. None defaults to 0.

  • ports (ArrayLike or None, optional) – Port metadata per item, shape (N,) after broadcast, dtype int64. None defaults to -1.

  • offsets (ArrayLike or None, optional) – Per-item timing offsets \(\delta_j\) in milliseconds. Flattened to shape (N,) with dtype float64; Quantity values are converted from ms. None defaults to 0.0 ms.

  • stamp_steps (ArrayLike or None, optional) – Integer event stamp steps, shape (N,) after broadcast, dtype int64. If None, all items use current step +1.

Returns:

out – Updated events dictionary dict[str, np.ndarray]. If no events are accepted by window/filter gates, returns current unchanged buffers.

Return type:

jax.Array

Raises:
  • ValueError – If simulation dt is non-positive; if t, start, stop, or origin are not scalar/grid-aligned; if stop < start; if payload arrays do not match weights length after broadcasting; or if weights/offsets contain non-finite values.

  • TypeError – If payload values cannot be cast to required numeric types.

  • KeyError – If simulation context values (for example t or dt) are not available from brainstate.environ.