inhomogeneous_poisson_generator#

class brainpy.state.inhomogeneous_poisson_generator(in_size=1, rate_times=None, rate_values=None, allow_offgrid_times=False, start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), rng_seed=0, name=None)#

Inhomogeneous Poisson spike generator with NEST-compatible scheduling.

Emit Poisson-distributed spike multiplicities from a piecewise-constant rate schedule and replicate NEST update ordering for future rate changes.

1. Stochastic model and one-step-ahead schedule semantics

Let \(\Delta t\) be the simulation resolution in ms and \(n \in \mathbb{N}\) the current step index with \(t_n = n \Delta t\). The generator maintains an internal rate \(r_n\) in spikes/s. For each configured pair \((t_k, v_k) =\) (rate_times[k], rate_values[k]), the requested time is aligned to a grid step \(s_k\):

\[\begin{split}s_k = \begin{cases} \mathrm{round}(t_k / \Delta t), & \text{if representable on grid}, \\ \left\lceil t_k / \Delta t \right\rceil, & \text{if off-grid and ``allow\_offgrid\_times`` is True}. \end{cases}\end{split}\]

During update(), entries with \(s_k \le n\) are skipped as past events. The next unapplied entry is consumed exactly when \(s_k = n + 1\), i.e., one simulation step ahead of delivery. This one-step-ahead convention reproduces NEST device ordering and avoids retroactive rate jumps.

For active steps with \(r_n > 0\), per-output spike multiplicities are sampled independently as

\[K_n \sim \mathrm{Poisson}(\lambda_n), \quad \lambda_n = \frac{r_n \,\Delta t}{1000},\]

where the factor of 1000 converts Hz × ms to a dimensionless Poisson mean. Returned values are non-negative integers and may exceed 1 for high firing rates or large time steps.

2. Activity window, assumptions, and constraints

Activity is gated by the NEST spike-device convention using a half-open-on-the-left interval:

\[t_{\min} < t_n \le t_{\max}, \quad t_{\min} = t_0 + t_{\mathrm{start,rel}},\; t_{\max} = t_0 + t_{\mathrm{stop,rel}}.\]

Therefore, start is an exclusive lower bound and stop is an inclusive upper bound in timestamp space. If stop is None, \(t_{\max} = +\infty\) and no upper cutoff is applied.

The following schedule constraints are enforced at set() call time:

  • rate_times and rate_values must always be provided together.

  • Flattened lengths of both arrays must match after conversion.

  • Aligned schedule steps \(s_k\) must form a strictly increasing sequence; duplicate grid positions are rejected.

  • Each configured rate time must lie strictly in the future relative to the environment time reported by brainstate.environ at the moment set() is called.

3. Computational implications

Schedule preprocessing in set() is \(O(K)\), where \(K\) is the number of configured change points. The per-step update() cost is \(O(M + \prod \mathrm{varshape})\), where \(M\) is the number of outdated entries skipped in that call (amortized \(O(1)\) over a full simulation). Poisson sampling is vectorized over self.varshape via jax.random.poisson, yielding statistically independent output trains for each element in the output array.

Parameters:
  • in_size (Size, optional) – Output size/shape specification for brainstate.nn.Dynamics. self.varshape derived from in_size gives the shape of the sampled multiplicity array returned by each update() call. Default is 1.

  • rate_times (Sequence[ArrayLike] or ArrayLike or None, optional) – Scheduled rate-change times with logical shape (K,). Entries are interpreted as milliseconds and stored internally as a flattened np.ndarray with dtype float64 after grid alignment. None means no schedule is configured at construction time. Must be provided together with rate_values. Default is None.

  • rate_values (Sequence[ArrayLike] or ArrayLike or None, optional) – Scheduled firing rates in spikes/s (Hz) paired one-to-one with rate_times, logical shape (K,). Stored as a flattened np.ndarray with dtype float64. Must be provided together with rate_times. Default is None.

  • allow_offgrid_times (bool, optional) – Grid-alignment policy for rate_times entries that do not fall exactly on a simulation time step. If False, any off-grid time raises ValueError. If True, off-grid times are aligned upward (ceiling) to the nearest representable grid step, subject to a small absolute tolerance of 1e-12 to absorb floating-point round- off. Default is False.

  • start (ArrayLike, optional) – Scalar relative start time \(t_{\mathrm{start,rel}}\) in ms. Added to origin to form the exclusive lower bound of the active interval. Unitless scalars are treated as ms; saiunit.Quantity values are converted automatically. Default is 0. * u.ms.

  • stop (ArrayLike or None, optional) – Scalar relative stop time \(t_{\mathrm{stop,rel}}\) in ms. Added to origin to form the inclusive upper bound of the active interval. None disables the upper bound (\(t_{\max} = +\infty\)). Default is None.

  • origin (ArrayLike, optional) – Scalar time offset \(t_0\) in ms applied to both start and stop. Allows shifting the activity window without modifying the relative start/stop values. Default is 0. * u.ms.

  • rng_seed (int, optional) – Integer seed used to initialize the jax.random.PRNGKey for Poisson sampling. Changing the seed produces a statistically independent output spike train for otherwise identical parameters. Default is 0.

  • name (str or None, optional) – Optional human-readable name for the dynamics node passed to brainstate.nn.Dynamics. Default is None.

Parameter Mapping

Table 33 Parameter mapping to model symbols#

Parameter

Default

Math symbol

Semantics

rate_times

None

\(t_k\)

Scheduled rate-change times, aligned to grid steps \(s_k\).

rate_values

None

\(v_k\)

Scheduled firing rates (spikes/s) applied when \(s_k = n + 1\).

start

0. * u.ms

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

Relative exclusive lower bound of the active interval.

stop

None

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

Relative inclusive upper bound; None means no upper cutoff.

origin

0. * u.ms

\(t_0\)

Global time offset added to both start and stop.

allow_offgrid_times

False

Off-grid policy: strict grid validation or upward ceiling alignment.

rng_seed

0

Seed for the JAX PRNG key used in Poisson sampling.

Raises:
  • ValueError – If stop < start at construction time; if rate_times and rate_values are not provided together; if their flattened lengths differ; if any configured time is not strictly in the future; if aligned grid steps are not strictly increasing; if an off-grid time is supplied while allow_offgrid_times is False; or if any time-like parameter is not scalar-convertible.

  • TypeError – If unit conversion or numeric coercion fails for any time or rate input (e.g., incompatible saiunit.Quantity dimensions).

  • KeyError – At runtime during update(), if the simulation context accessed via brainstate.environ is missing the required dt key.

Notes

  • Output values are spike counts per step (0, 1, 2, ...), not binary spikes. High firing rates or large time steps may produce multiplicities greater than one.

  • Calling set() with a new non-empty schedule atomically resets the internal schedule pointer to index 0, matching NEST setter semantics.

  • Calling update() without a prior init_state() call will lazily initialize state variables on the first invocation.

  • The rng_key state is split (not folded) at each call, so the Poisson samples are statistically independent across time steps and across different elements of self.varshape.

See also

poisson_generator

Homogeneous Poisson stimulation device.

sinusoidal_poisson_generator

Sinusoidally modulated Poisson device.

step_rate_generator

Piecewise-constant deterministic rate generator.

References

Examples

Create a generator that fires at 800 Hz during (5, 20] ms then goes silent, and read out the per-neuron spike counts at step t = 6 ms:

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     gen = brainpy.state.inhomogeneous_poisson_generator(
...         in_size=4,
...         rate_times=[5.0 * u.ms, 20.0 * u.ms],
...         rate_values=[800.0 * u.Hz, 0.0 * u.Hz],
...         start=0.0 * u.ms,
...         stop=30.0 * u.ms,
...         rng_seed=7,
...     )
...     gen.init_state()
...     with brainstate.environ.context(t=6.0 * u.ms):
...         counts = gen.update()
...     _ = counts.shape  # (4,), dtype int64

Allow off-grid rate times and inspect the aligned schedule via get():

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     gen = brainpy.state.inhomogeneous_poisson_generator(
...         allow_offgrid_times=True,
...     )
...     gen.set(
...         rate_times=[1.23 * u.ms, 2.34 * u.ms],
...         rate_values=[10.0 * u.Hz, 20.0 * u.Hz],
...     )
...     params = gen.get()
...     # params['rate_times'] contains ceil-aligned ms values
...     _ = params['allow_offgrid_times']  # True
get()[source]#

Return current schedule and timing parameters in NEST-style format.

Serializes all user-configurable generator parameters into a plain Python dict. This mirrors the nest.GetStatus interface so that parameter introspection and round-tripping via set() / get() work as expected.

Returns:

params – Dictionary with the following keys:

  • 'rate_times' (float or list[float]): Grid-aligned rate-change times in ms. A single-entry schedule is returned as a bare float; a multi-entry schedule as a Python list. An empty schedule returns an empty list.

  • 'rate_values' (float or list[float]): Corresponding firing rates in spikes/s (Hz), same shape convention as 'rate_times'.

  • 'allow_offgrid_times' (bool): Current off-grid alignment policy.

  • 'start' (float): Relative exclusive lower activity bound in ms.

  • 'stop' (float): Inclusive upper activity bound in ms, or float('inf') if no upper bound was set.

  • 'origin' (float): Global time offset in ms.

Return type:

dict

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

Initialize transient schedule pointer and RNG state.

Creates the three brainstate.ShortTermState objects required by update(): the schedule pointer _rate_idx (int64 scalar), the currently active firing rate _rate_hz (float64 scalar, initialized to 0.0), and the JAX PRNG key rng_key seeded from self.rng_seed.

This method is idempotent with respect to the configured schedule: the existing _rate_times_ms, _rate_values_hz, and _rate_steps arrays are left unchanged; only the runtime-mutable state variables are (re-)created.

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

  • **kwargs – Additional keyword arguments accepted for API compatibility and silently ignored.

set(*, rate_times=<object object>, rate_values=<object object>, allow_offgrid_times=<object object>)[source]#

Update the rate schedule and/or off-grid policy with NEST-compatible validation.

Replaces the current piecewise-constant rate schedule with a new one, optionally updating the off-grid alignment policy at the same time. All provided times are validated against the current simulation clock (must be strictly in the future), aligned to the simulation grid, and checked for strict monotonicity.

Passing rate_times=[] and rate_values=[] clears the schedule: internal arrays are set to empty and the schedule pointer is reset to 0.

Parameters:
  • rate_times (Sequence[ArrayLike] or ArrayLike, optional) – New rate-change times in ms. Inputs are flattened to shape (K,) and stored as np.ndarray[float64] after grid alignment. Must be provided together with rate_values; omitting one while supplying the other raises ValueError. If omitted entirely (sentinel _UNSET), the existing schedule is left unchanged.

  • rate_values (Sequence[ArrayLike] or ArrayLike, optional) – New firing rates in spikes/s (Hz) paired one-to-one with rate_times. Stored as np.ndarray[float64]. Must have exactly the same flattened length as rate_times.

  • allow_offgrid_times (bool, optional) – If supplied, updates self.allow_offgrid_times. Changing this flag is only permitted when rate_times is also being set in the same call, or when no schedule has been configured yet. Attempting to change the flag with an existing non-empty schedule and without new times raises ValueError.

Raises:
  • ValueError – If exactly one of rate_times / rate_values is provided (must supply both or neither); if their flattened lengths differ; if allow_offgrid_times is changed while an existing non-empty schedule is in place without also providing new times; if any time value is not strictly greater than the current environment time; if any two adjacent aligned grid steps are not strictly increasing; or if a time is off-grid and allow_offgrid_times is False.

  • TypeError – If unit conversion fails for rate_times or rate_values inputs (e.g., incompatible saiunit.Quantity dimensions).

update()[source]#

Advance one simulation step and emit Poisson spike multiplicities.

Reads the current simulation time from brainstate.environ, advances the schedule pointer past any entries whose grid step \(s_k \le n\), then applies the next scheduled rate change if \(s_k = n + 1\). When the generator is active (current time inside the activity window) and the current rate is positive, samples a Poisson multiplicity array over self.varshape. Otherwise returns a zero array.

Lazy initialization: if init_state() has not been called, this method initializes state variables on the first invocation.

Returns:

spikes – Per-output Poisson spike multiplicity for the current time step. Each element \(K_i \sim \mathrm{Poisson}(\lambda_n)\) where \(\lambda_n = r_n \Delta t / 1000\). Returns all-zero array when the generator is inactive or the current rate is zero.

Return type:

jax.Array, shape ``self.varshape``, dtype ``int64``