poisson_generator_ps#

class brainpy.state.poisson_generator_ps(in_size=1, rate=Quantity(0., 'Hz'), dead_time=Quantity(0., 'ms'), start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), rng_seed=0, name=None)#

Precise-time Poisson spike generator with dead time (NEST-compatible).

Description

poisson_generator_ps re-implements NEST’s precise-time stimulation device poisson_generator_ps and emits off-grid spike times generated by an absolute-refractory renewal process.

1. Renewal process with dead time

Let r be the configured mean rate (spikes/s) and \(t_{\mathrm{dead}}\) be dead time (ms). Inter-spike intervals (ISIs) are sampled as

\[\Delta t = t_{\mathrm{dead}} + \xi \alpha, \qquad \xi \sim \mathrm{Exp}(1), \qquad \alpha = \frac{1000}{r} - t_{\mathrm{dead}}.\]

The mean ISI is

\[\mathbb{E}[\Delta t] = t_{\mathrm{dead}} + \alpha = \frac{1000}{r},\]

so the stationary mean rate is preserved exactly when \(\alpha \ge 0\), which is equivalent to dead_time <= 1000 / rate for rate > 0.

When a stream is (re)initialized at an active step, the first offset from the local active left boundary is sampled from the stationary backward-recurrence distribution used by NEST:

  • uniform branch on [0, dead_time) with probability dead_time * rate / 1000,

  • shifted exponential branch on [dead_time, +inf) otherwise.

This avoids transient rate bias immediately after activation.

2. Activity window and update ordering

For one simulation step with left edge \(t\) and width \(dt\) (both in ms), define

\[t_{\min} = \max(t,\, origin + start), \qquad t_{\max} = \min(t + dt,\, origin + stop).\]

If t_min < t_max and rate > 0, spikes are emitted using t_min < spike_time <= t_max (left-open, right-closed on the active slice). This matches NEST poisson_generator_ps.cpp ordering semantics.

3. Assumptions, constraints, and computational implications

  • rate, dead_time, start, stop, and origin are scalar public parameters converted to float64 (Hz/ms).

  • start and origin must be finite; stop must be finite or +inf; stop >= start.

  • Per-target streams are independent numpy.random.Generator instances spawned from rng_seed via numpy.random.SeedSequence.

  • update() cost is \(O(N + S)\) per step where \(N=\prod \mathrm{varshape}\) and \(S\) is the number of spikes emitted in the step across all streams.

Parameters:
  • in_size (Size, optional) – Output size specification for brainstate.nn.Dynamics. The generated multiplicity tensor from update() has shape self.varshape derived from in_size. Each element corresponds to one independent output train. Default is 1.

  • rate (ArrayLike, optional) – Scalar mean firing rate in spikes/s (Hz). Accepts any ArrayLike with exactly one element, optionally a saiunit.Quantity convertible to u.Hz. Must satisfy rate >= 0. Default is 0.0 * u.Hz.

  • dead_time (ArrayLike, optional) – Scalar absolute dead time in ms. Accepts one-element ArrayLike or quantity convertible to u.ms. Must satisfy dead_time >= 0 and dead_time <= 1000 / rate when rate > 0. Default is 0.0 * u.ms.

  • start (ArrayLike, optional) – Scalar relative activation time in ms. Effective lower activity bound is origin + start. Must be finite after conversion. Default is 0.0 * u.ms.

  • stop (ArrayLike or None, optional) – Scalar relative deactivation time in ms. Effective upper activity bound is origin + stop. None maps to +inf. Must satisfy stop >= start after conversion. Default is None.

  • origin (ArrayLike, optional) – Scalar global time offset in ms added to start and stop. Must be finite after conversion. Default is 0.0 * u.ms.

  • rng_seed (int, optional) – Seed passed to numpy.random.SeedSequence for per-target RNG stream construction in init_state(). Default is 0.

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

Parameter Mapping

Table 32 Parameter mapping to model symbols#

Parameter

Default

Math symbol

Semantics

rate

0.0 * u.Hz

\(r\)

Target stationary rate in spikes/s.

dead_time

0.0 * u.ms

\(t_{\mathrm{dead}}\)

Absolute refractory interval added to every ISI.

start

0.0 * u.ms

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

Relative activity-window lower bound (inclusive in slicing step).

stop

None

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

Relative activity-window upper bound; None maps to +\infty.

origin

0.0 * u.ms

\(t_0\)

Global offset added to start and stop.

in_size

1

Defines self.varshape and number of independent trains.

rng_seed

0

Root seed used to spawn independent per-target RNG streams.

Raises:
  • ValueError – If rate < 0; dead_time < 0; stop < start; start or origin is non-finite; stop is neither finite nor +inf; or dead_time > 1000 / rate when rate > 0.

  • TypeError – If supplied ArrayLike values cannot be converted to scalar Hz/ms.

  • KeyError – At update time, if required simulation context entries such as dt are unavailable through brainstate.environ.

Notes

  • Unlike the grid-constrained poisson_generator, this model tracks and emits off-grid spike times with sub-step precision.

  • last_spike_time stores the latest emitted precise time per output.

  • last_spike_offset stores (t + dt) - last_spike_time at the step where that spike was emitted, using ms units.

  • Calling set() with rate=... resets next_spike_time state, mirroring NEST behavior.

Examples

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     gen = brainpy.state.poisson_generator_ps(
...         in_size=(2,),
...         rate=800.0 * u.Hz,
...         dead_time=0.5 * u.ms,
...         start=5.0 * u.ms,
...         stop=30.0 * u.ms,
...         rng_seed=7,
...     )
...     with brainstate.environ.context(t=10.0 * u.ms):
...         counts, times = gen.update(return_precise_times=True)
...     _ = counts.shape, len(times)
>>> import brainpy
>>> import saiunit as u
>>> gen = brainpy.state.poisson_generator_ps(rate=500.0 * u.Hz)
>>> gen.set(dead_time=0.8 * u.ms, stop=None)
>>> params = gen.get()
>>> _ = params["rate"], params["dead_time"], params["stop"]

See also

poisson_generator

Grid-constrained homogeneous Poisson generator.

inhomogeneous_poisson_generator

Time-varying Poisson generator.

References

get()[source]#

Return current public parameters as plain Python scalars.

Returns:

params – Dictionary with five float entries:

  • 'rate' – mean firing rate in spikes/s (Hz).

  • 'dead_time' – absolute refractory dead time in ms.

  • 'start' – relative exclusive-lower activity bound in ms.

  • 'stop' – relative inclusive-upper activity bound in ms; inf when deactivation is disabled (stop=None was passed).

  • 'origin' – global time offset in ms.

Return type:

dict

Notes

Returned values are plain Python float scalars (float64 precision). They mirror the internal scalar attributes set in __init__() or updated by set() and are not bound to any saiunit quantities.

See also

poisson_generator_ps.set

Update one or more parameters in place.

Examples

>>> import brainpy
>>> import saiunit as u
>>> gen = brainpy.state.poisson_generator_ps(
...     rate=800.0 * u.Hz,
...     dead_time=0.5 * u.ms,
...     start=5.0 * u.ms,
...     stop=100.0 * u.ms,
...     origin=2.0 * u.ms,
... )
>>> params = gen.get()
>>> params['rate']
800.0
>>> params['dead_time']
0.5
>>> params['stop']
100.0
init_state(batch_size=None, **kwargs)[source]#

Initialize precise-spike state buffers and per-target RNG streams.

Parameters:
  • batch_size (int or None, optional) – Unused. Included for framework API compatibility with brainstate.nn.Dynamics. Default is None.

  • **kwargs (Any) – Unused keyword arguments accepted for API compatibility.

Notes

Calling init_state() resets all state buffers and re-seeds all RNG streams. Repeated calls therefore restart the stochastic sequence from the beginning. update() calls this method lazily on the first step if init_state() has not been invoked explicitly.

See also

poisson_generator_ps.update

Consumes state buffers populated here.

Examples

>>> import brainstate
>>> import saiunit as u
>>> from brainpy.state import poisson_generator_ps
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     gen = poisson_generator_ps(in_size=4, rate=800.0 * u.Hz, rng_seed=7)
...     gen.init_state()
set(*, rate=<object object>, dead_time=<object object>, start=<object object>, stop=<object object>, origin=<object object>)[source]#

Update public parameters and refresh generator state when required.

Only keyword arguments that are explicitly passed are modified; omitted arguments retain their current values.

Parameters:
  • rate (ArrayLike or object, optional) – New scalar mean firing rate in spikes/s (Hz). Accepts any ArrayLike with exactly one element, or a saiunit.Quantity convertible to u.Hz. Must satisfy rate >= 0 after conversion. Setting this parameter resets next_spike_time state to -inf for all targets, matching NEST behavior. Omit to keep the current value.

  • dead_time (ArrayLike or object, optional) – New scalar absolute dead time in ms. Accepts one-element ArrayLike or quantity convertible to u.ms. Must satisfy dead_time >= 0 and dead_time <= 1000 / rate when rate > 0 after conversion. Omit to keep the current value.

  • start (ArrayLike or object, optional) – New scalar relative activation time in ms. Effective lower activity bound is origin + start. Must be finite after conversion. Omit to keep the current value.

  • stop (ArrayLike or None or object, optional) – New scalar relative deactivation time in ms. None maps to +inf. Must satisfy stop >= start after conversion. Omit to keep the current value.

  • origin (ArrayLike or object, optional) – New scalar global time offset in ms added to start and stop. Must be finite after conversion. Omit to keep the current value.

Raises:
  • ValueError – If updated parameters violate model constraints: negative rate/dead_time, stop < start, non-finite origin/start, or dead_time > 1000 / rate for rate > 0.

  • TypeError – If any supplied parameter cannot be converted to scalar Hz/ms.

See also

poisson_generator_ps.get

Read-back current parameter values.

Examples

>>> import brainpy
>>> import saiunit as u
>>> gen = brainpy.state.poisson_generator_ps(rate=500.0 * u.Hz)
>>> gen.set(rate=1000.0 * u.Hz, dead_time=0.5 * u.ms)
>>> params = gen.get()
>>> _ = params['rate'], params['dead_time']
property step_spike_times_ms#

Precise spike times emitted in the most recent update() call.

Returns:

spike_times – Tuple of length np.prod(self.varshape). Each element is a one-dimensional numpy.ndarray with dtype float64 containing the emitted precise spike times (ms) for that flattened output train in the latest simulation step. Arrays are empty when no spikes were emitted for the train in the most recent step.

Return type:

tuple of numpy.ndarray

Notes

The tuple is replaced atomically at each update() call. Holding a reference to a previous value is safe because each call creates new arrays. Spike times are in the half-open interval (t_min_active, t_max_active] corresponding to the step window.

See also

poisson_generator_ps.update

Produces the spike-time arrays stored here.

update(return_precise_times=False)[source]#

Advance one simulation step and emit precise Poisson events.

Parameters:

return_precise_times (bool, optional) – If False (default), return only per-target spike multiplicities. If True, also return per-target precise spike times emitted in the current step.

Returns:

  • counts (numpy.ndarray) – Integer array with dtype int64 and shape self.varshape containing per-step spike multiplicities. Returned in both modes.

    • Active and rate > 0: each element counts how many spikes fell in (t_min_active, t_max_active] for that output train.

    • Inactive or rate <= 0: all entries are exactly 0.

  • spike_times_tuple (tuple of numpy.ndarray, only when ``return_precise_times=True``) – Tuple of length np.prod(self.varshape). Each element is a one-dimensional float64 array of emitted precise spike times (ms) for the corresponding flattened output train in this step. Arrays are empty when no spikes were emitted. Also accessible via step_spike_times_ms after the call.

Raises:
  • ValueError – If simulation step size dt is non-positive after conversion from the runtime environment.

  • KeyError – If required runtime entries (notably dt) are unavailable in brainstate.environ.

  • TypeError – If environment values cannot be converted to scalar milliseconds.

Notes

The update proceeds as follows each call:

  1. Lazy init – If next_spike_time has not been created by init_state(), it is initialized automatically with self.rng_seed.

  2. Window clipping – Computes t_min_active and t_max_active by intersecting the step interval [t, t + dt] with the configured activity window. Returns zeros immediately if the window is empty or rate <= 0.

  3. Stream initialization – For any target whose next_spike_time is -inf (first call or after a rate reset), the initial spike is placed at t_min_active + _sample_initial_offset_ms(...) using the stationary backward-recurrence distribution.

  4. Spike emission loop – Advances each target stream forward, emitting spikes at next_t <= t_max_active and drawing new ISIs via _sample_isi_ms() until the next spike lies outside the current step.

  5. State update – Writes back next_spike_time, last_spike_time, and last_spike_offset states and caches per-target spike-time arrays in step_spike_times_ms.

See also

poisson_generator_ps.init_state

State buffers consumed here.

poisson_generator_ps.set

Update parameters between runs.

poisson_generator_ps.step_spike_times_ms

Access precise times after update without the overhead of the return value.