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_recorderaccumulates 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 NESTweight_recorderbehavior (models/weight_recorder.{h,cpp}andnestkernel/{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_stepsis 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
startis exclusive andstopis 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.
weightsdefines 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, currentt, anddtmust be scalar-convertible and aligned to the simulation grid (checked by round-trip integer conversion with1e-12tolerance).stopmust satisfystop >= startwhen 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 tobrainstate.nn.Dynamics. This recorder emits dictionary outputs instead of dense tensors;in_sizeis retained for API compatibility only. Default is1.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.Quantityorfloat, 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 ofdt. Default is0.0 * u.ms.stop (
saiunit.Quantity,float, orNone, optional) – Scalar relative inclusive upper bound of the recording window, convertible to ms. Gate is inclusive:stamp_step <= (origin + stop) / dt.Nonemeans no upper bound (\(s_{\max} = +\infty\)). Finite values must bedt-aligned and satisfystop >= start. Default isNone.origin (
saiunit.Quantityorfloat, optional) – Scalar global origin shift added to bothstartandstopbefore window evaluation, convertible to ms. Shifting the origin displaces the entire recording window without changing its duration. Must be finite anddt-aligned. Default is0.0 * u.ms.time_in_steps (
bool, optional) – Time output representation. IfFalse,events['times']storesfloat64milliseconds computed as \(s \cdot dt - \delta_j\). IfTrue,events['times']storesint64step stamps andevents['offsets']storesfloat64offsets 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 in this backend. Default isFalse.name (
strorNone, optional) – Optional node name passed tobrainstate.nn.Dynamics. Default isNone.
Parameter Mapping
Table 42 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 stamp-step interval.
stopNone\(t_{\mathrm{stop,rel}}\)
Relative inclusive upper bound of the active stamp-step interval.
origin0.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_stepsFalse\(\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 todt, or violatestop >= start; iftime_in_stepsis modified after the firstupdate()call; ifn_eventsis set to a value other than0; if event payload array sizes mismatchweightslength after broadcasting; ifweightsoroffsetscontain non-finite values; or if the runtime simulation timetis 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 byupdate()(such as't'ordt) are unavailable viabrainstate.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_eventsis write-restricted to0to support explicit buffer reset while preventing partial truncation.time_in_stepsbecomes immutable after the firstupdate()call that accesses simulation context, matching NEST backend constraints.weights=Noneinupdate()is treated as a no-op that returns the currenteventswithout 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:
out –
None. Internal Python lists for all event fields are reset to empty lists.- Return type:
Any
- 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.ndarrayofint64.
- Return type:
- Raises:
KeyError – If
keyis unsupported.
- init_state(batch_size=None, **kwargs)[source]#
Initialize dynamic state by clearing recorded events.
- 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 (
ArrayLikeorNone, optional) – Event weights. Flattened to shape(N,)with dtypefloat64. Unitless/implementation-specific weight values. IfNone, this call is a no-op and current events are returned.senders (
ArrayLikeorNone, optional) – Sender node IDs for each item, shape(N,)after broadcast, dtypeint64.Nonedefaults to1for all items.targets (
ArrayLikeorNone, optional) – Target node IDs for each item, shape(N,)after broadcast, dtypeint64.Nonedefaults to1for all items.receptors (
ArrayLikeorNone, optional) – Receptor IDs (rport) per item, shape(N,)after broadcast, dtypeint64.Nonedefaults to0.ports (
ArrayLikeorNone, optional) – Port metadata per item, shape(N,)after broadcast, dtypeint64.Nonedefaults to-1.offsets (
ArrayLikeorNone, optional) – Per-item timing offsets \(\delta_j\) in milliseconds. Flattened to shape(N,)with dtypefloat64;Quantityvalues are converted fromms.Nonedefaults to0.0ms.stamp_steps (
ArrayLikeorNone, optional) – Integer event stamp steps, shape(N,)after broadcast, dtypeint64. IfNone, 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
dtis non-positive; ift,start,stop, ororiginare not scalar/grid-aligned; ifstop < start; if payload arrays do not matchweightslength after broadcasting; or ifweights/offsetscontain non-finite values.TypeError – If payload values cannot be cast to required numeric types.
KeyError – If simulation context values (for example
tordt) are not available frombrainstate.environ.