multimeter#

class brainpy.state.multimeter(in_size=1, record_from=(), interval=Quantity(1., 'ms'), offset=Quantity(0., 'ms'), start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), time_in_steps=False, frozen=False, name=None)#

NEST-compatible analog recorder for neuron/device state variables.

multimeter records analog state samples from connected targets into an in-memory events dictionary compatible with NEST multimeter semantics. The NEST device-level timing model is reproduced while exposing a Python update API:

  • Sampling times are constrained to a step-grid lattice defined by interval and offset.

  • Recording is gated by a window \((\mathrm{origin}+\mathrm{start},\;\mathrm{origin}+\mathrm{stop}]\) (start exclusive, stop inclusive) evaluated in simulation steps.

  • Samples are enqueued at the current step and emitted on the next update() call (or immediately by flush()), reproducing the one-step request/reply lag used by NEST multimeters.

1. Step-Grid Sampling Model

Let \(dt\) be simulation resolution in ms, and let step index \(n = \mathrm{round}(t/dt)\). During update(), sampled values are stamped at

\[s = n + 1.\]

Define integer grid parameters

\[m = \frac{\mathrm{interval}}{dt}, \qquad o = \frac{\mathrm{offset}}{dt}.\]

A sample is enqueued iff \(s\) lies on the lattice:

\[s \equiv 0 \ (\mathrm{mod}\ m) \quad \text{if}\ o = 0, \qquad s \equiv o \ (\mathrm{mod}\ m),\ s \ge o \quad \text{if}\ o > 0.\]

Both interval and offset must be exact integer multiples of dt (verified to within 1e-12 tolerance in floating conversion).

2. Active Window and Delivery Lag

With \(s_\min = (\mathrm{origin}+\mathrm{start})/dt\) and \(s_\max = (\mathrm{origin}+\mathrm{stop})/dt\) (or \(+\infty\) when stop is None), a pending sample is written to events only when

\[s > s_\min \quad \land \quad s \le s_\max.\]

Because pending samples are emitted before new sampling in each update(), values observed at step \(n\) become visible in events at step \(n+1\) unless flush() is called.

3. Payload Normalization and Shape Constraints

For each requested recordable k in record_from, data[k] is converted to np.float64 and flattened to shape (N,). All recordables must share the same N; scalar payloads (size 1) are broadcast to (N,) when another recordable defines N > 1. senders is converted to np.int64 with the same broadcast rule, defaulting to ones when omitted. Stored event arrays are one-dimensional with length equal to the total number of emitted samples across all steps.

4. Computational Implications

Per update() call with payload size N and R = len(record_from), enqueue work is \(O(RN)\). Pending emission is linear in the number of buffered items and the appended event count. Memory usage grows linearly with total emitted events for times, senders, and each requested recordable channel.

Parameters:
  • in_size (Size, optional) – Output size/shape argument consumed by brainstate.nn.Dynamics. This recorder is stateful and returns event dictionaries; in_size is retained for API consistency only. Default is 1.

  • record_from (Sequence[str], optional) – Ordered names of recordable state variables expected as keys in data during update(). If empty, incoming payloads are silently ignored and no values are stored. Default is ().

  • interval (saiunit.Quantity or float, optional) – Scalar sampling interval in time units convertible to ms (typically u.ms). Must satisfy interval >= dt and be an exact integer multiple of dt (checked to within 1e-12 tolerance). Default is 1.0 * u.ms.

  • offset (saiunit.Quantity or float, optional) – Scalar phase offset of the sampling lattice relative to the simulation origin, convertible to ms. Must be 0.0 or a positive integer multiple of dt; non-zero offsets shift the first sample to step \(o\) and every \(m\)-th step thereafter. Default is 0.0 * u.ms.

  • start (saiunit.Quantity or float, optional) – Scalar exclusive lower bound of the recording window relative to origin, convertible to ms. A pending sample at stamp step \(s\) is discarded when \(s \le s_\min\). Default is 0.0 * u.ms.

  • stop (saiunit.Quantity, float, or None, optional) – Scalar inclusive upper bound of the recording window relative to origin, convertible to ms. Must satisfy stop >= start when not None. None means no upper bound (\(s_\max = +\infty\)). Default is None.

  • origin (saiunit.Quantity or float, optional) – Scalar global time-origin shift added to both start and stop when constructing the active window, convertible to ms. Shifting the origin displaces the entire recording window without changing its duration. Default is 0.0 * u.ms.

  • time_in_steps (bool, optional) – Controls the unit of events['times']. If False, timestamps are stored as float milliseconds (stamp_step * dt). If True, timestamps are stored as integer-valued step numbers cast to float64, and an additional events['offsets'] key is emitted as a zero-filled array of matching shape. Default is False.

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

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

Parameter Mapping

Table 40 Mapping of constructor parameters to model symbols#

Parameter

Default

Math symbol

Semantics

interval

1.0 * u.ms

\(m \cdot dt\)

Sampling period on the simulation step grid.

offset

0.0 * u.ms

\(o \cdot dt\)

Phase shift of the sampling lattice.

start

0.0 * u.ms

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

Relative exclusive lower bound of the activity window.

stop

None

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

Relative inclusive upper bound of the activity window.

origin

0.0 * u.ms

\(t_0\)

Global time-origin shift for the recording window.

record_from

()

\(\{x_r\}_{r=1}^{R}\)

Ordered recordable channels expected in each payload.

Raises:
  • ValueError – If frozen=True; if any timing parameter (interval, offset, start, stop, origin, dt) is not scalar-convertible, not finite when required, not aligned to dt, or violates ordering constraints (e.g. interval < dt or stop < start); if data passed to update() is not a mapping; if a required recordable key is absent from data; if a recordable payload is empty after conversion; or if recordable/sender lengths are inconsistent after flattening/broadcasting.

  • TypeError – If unit conversion or array casting of any time parameter or payload value to a numeric type fails.

  • KeyError – If get() is called with a key other than 'events' or 'n_events'.

Notes

  • After the first connect() call or the first data-carrying update(), properties interval, offset, and record_from become immutable and further assignments raise ValueError.

  • This recorder does not read neuron states autonomously; the caller is responsible for extracting state values and passing them via data in each update() call after state integration.

  • init_state() clears all accumulated events and the pending buffer; it can be used to reset the recorder between simulation segments without reconstructing the object.

References

Examples

Record membrane potential from a single iaf_psc_delta neuron for 50 steps at 0.1 ms resolution, with the recording window clipped to the first 5 ms:

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> import numpy as np
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     neuron = brainpy.state.iaf_psc_delta(1, I_e=500.0 * u.pA)
...     neuron.init_state()
...     mm = brainpy.state.multimeter(
...         record_from=['V_m'],
...         interval=0.1 * u.ms,
...         start=0.0 * u.ms,
...         stop=5.0 * u.ms,
...     )
...     for k in range(50):
...         with brainstate.environ.context(t=k * 0.1 * u.ms):
...             neuron.update()
...             vm = float(neuron.V.value[0] / u.mV)
dftype = brainstate.environ.dftype()
...             _ = mm.update(
...                 {'V_m': np.array([vm], dtype=dftype)},
...                 senders=np.array([1]),
...             )
...     events = mm.flush()
...     _ = events['V_m'].shape
flush()[source]#

Emit all buffered pending samples and return the current event store.

Reads dt from brainstate.environ.get_dt(), validates timing calibration, converts pending step stamps to output times, and appends all active samples to the internal event arrays. After the call the pending buffer is empty.

Returns:

events – Event dictionary identical to events, reflecting all samples emitted up to and including this call. See the class-level Returns section for the full description of keys and dtypes.

Return type:

dict[str, np.ndarray]

Raises:
  • ValueError – If dt obtained from the simulation environment is non-positive, not scalar-convertible, or incompatible with the configured timing parameters (interval, offset, start, stop, origin).

  • TypeError – If dt cannot be converted to a scalar float ms value.

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

State initialization function.

update(data=None, senders=None)[source]#

Process one simulation step and optionally enqueue a new sample.

Parameters:
  • data (Mapping[str, ArrayLike] or None, optional) – Mapping from each name in record_from to its current analog value payload. Each payload is converted to np.float64 and flattened to shape (N,). Scalars (size 1) are broadcast to (N,) when another recordable defines N > 1. If None, no new sample is enqueued and only pending samples are emitted. Default is None.

  • senders (ArrayLike or None, optional) – Sender IDs associated with the payload. Converted to np.int64 and flattened to shape (N,) using the same scalar-broadcast rule as recordables. If None, all sender IDs default to 1. Default is None.

Returns:

events – Event dictionary identical to events after emitting all pending samples and optionally enqueuing the new payload. See the class-level Returns section for the full description of keys and dtypes.

Return type:

dict[str, np.ndarray]

Raises:
  • ValueError – If current simulation time t is not aligned to the simulation grid; if timing parameters are incompatible with dt; if data is not a Mapping; if a required recordable key is absent from data; if a recordable payload is empty after conversion; or if recordable/sender lengths are inconsistent after the scalar-broadcast rule.

  • TypeError – If conversion of t, dt, or any payload value to a numeric array fails.

  • KeyError – If brainstate.environ does not provide the 't' or dt context keys required for step computation.