step_current_generator#

class brainpy.state.step_current_generator(in_size=1, amplitude_times=(), amplitude_values=(), start=Quantity(0., 'ms'), stop=None, origin=Quantity(0., 'ms'), name=None)#

Piecewise-constant current generator – NEST-compatible stimulation device.

Generate a deterministic piecewise-constant current trace and gate it with a half-open activity window using NEST-compatible time semantics.

1. Model equations

Let \(\{(t_k, a_k)\}_{k=1}^{K}\) be the configured change-time/current pairs, where \(t_k\) are times (ms) and \(a_k\) are currents (pA). Define the scheduled amplitude

\[\begin{split}A(t) = \begin{cases} 0, & t < t_1, \\ a_k, & t_k \le t < t_{k+1},\ k=1,\dots,K-1, \\ a_K, & t \ge t_K. \end{cases}\end{split}\]

The output is gated by

\[g(t) = \mathbf{1}\!\left[t \ge t_0+t_{\mathrm{start,rel}}\right] \cdot \mathbf{1}\!\left[t < t_0+t_{\mathrm{stop,rel}}\right],\]

with the second indicator omitted when stop is None. Final output:

\[I(t) = g(t)\,A(t).\]

2. Timing semantics, assumptions, and constraints

NEST timing is matched by selecting, at time t, the most recent change point with t_k <= t. In discrete simulation with step dt, this corresponds to applying a change exactly from the step whose environment time equals the configured change time.

Enforced constraints in this implementation:

  • len(amplitude_times) == len(amplitude_values).

  • amplitude_times are strictly increasing.

Inputs accepted but not explicitly constrained:

  • Unitless amplitude_times are interpreted as ms.

  • Unitless amplitude_values are interpreted as pA.

  • Positive-time-only schedules are recommended by NEST, but positivity is not explicitly validated here.

3. Computational implications

Each update() call uses u.math.searchsorted() to find the active plateau, then selects the pre-broadcast current array for self.varshape and applies one boolean mask. Per-call complexity is \(O(\log K + \prod \mathrm{varshape})\), with \(K\) schedule entries.

Parameters:
  • in_size (Size, optional) – Output size/shape specification consumed by brainstate.nn.Dynamics. The emitted current has shape self.varshape derived from in_size. Default is 1.

  • amplitude_times (Sequence, optional) – Ordered sequence of change times with length K. Entries may be unitful times (typically ms) or unitless numerics. Passed directly to u.math.asarray(), which validates unit consistency across all entries. Must be strictly increasing. Default is ().

  • amplitude_values (Sequence, optional) – Sequence of current plateaus with length K matching amplitude_times elementwise. Entries may be unitful currents (typically pA) or unitless numerics. Each entry is converted via u.math.asarray() and expanded to the maximum ndim found across all entries (by prepending size-1 axes); the results are stacked to a shape that is broadcastable to (K, *varshape). Default is ().

  • start (ArrayLike, optional) – Relative start time \(t_{\mathrm{start,rel}}\) (typically ms), broadcast to self.varshape via braintools.init.param(). Effective lower bound is origin + start (inclusive). Default is 0. * u.ms.

  • stop (ArrayLike or None, optional) – Relative stop time \(t_{\mathrm{stop,rel}}\) (typically ms), broadcast to self.varshape when provided. Effective upper bound is origin + stop (exclusive). None means no upper bound. Default is None.

  • origin (ArrayLike, optional) – Time origin \(t_0\) (typically ms) added to start and stop, broadcast to self.varshape. Default is 0. * u.ms.

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

Parameter Mapping

Table 26 Parameter mapping to model symbols#

Parameter

Default

Math symbol

Semantics

amplitude_times

()

\(t_k\)

Change times for piecewise-constant plateaus.

amplitude_values

()

\(a_k\)

Plateau currents selected at and after corresponding t_k.

start

0. * u.ms

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

Relative inclusive lower bound of activity window.

stop

None

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

Relative exclusive upper bound of activity window.

origin

0. * u.ms

\(t_0\)

Global offset added to start and stop.

Raises:
  • ValueError – If amplitude_times and amplitude_values lengths differ, or if amplitude_times is not strictly increasing.

  • TypeError – If u.math.asarray() detects unit inconsistency across entries, or if unitful/unitless arithmetic is invalid during broadcasting or time-window comparisons.

  • KeyError – At update time, if simulation time 't' is missing from brainstate.environ.

Notes

NEST recommends specifying amplitude_times on a grid of simulation resolution dt. Using off-grid change times is allowed but may shift the effective change by up to one dt step depending on floating-point rounding when comparing t >= amp_time. Use dc_generator when only a single constant plateau is needed; step_current_generator is the preferred device when the current must take different values at different intervals within a single simulation run.

See also

dc_generator

Constant current stimulation device.

ac_generator

Sinusoidal current stimulation device.

noise_generator

Gaussian white-noise current stimulation device.

References

Examples

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     stim = brainpy.state.step_current_generator(
...         in_size=1,
...         amplitude_times=[10.0 * u.ms, 50.0 * u.ms, 80.0 * u.ms],
...         amplitude_values=[200.0 * u.pA, -100.0 * u.pA, 500.0 * u.pA],
...         start=5.0 * u.ms,
...         stop=120.0 * u.ms,
...     )
...     with brainstate.environ.context(t=60.0 * u.ms):
...         current = stim.update()
...     _ = current.shape
>>> import brainpy
>>> import saiunit as u
>>> stim1 = brainpy.state.step_current_generator(
...     amplitude_times=[0.0 * u.ms, 100.0 * u.ms, 200.0 * u.ms],
...     amplitude_values=[300.0 * u.pA, 0.0 * u.pA, -150.0 * u.pA],
... )
>>> stim2 = brainpy.state.step_current_generator(
...     in_size=10,
...     amplitude_times=[50.0 * u.ms, 150.0 * u.ms],
...     amplitude_values=[400.0 * u.pA, 100.0 * u.pA],
...     start=40.0 * u.ms,
...     stop=180.0 * u.ms,
...     origin=10.0 * u.ms,
... )
update()[source]#

Compute scheduled current at environment time t.

The implementation is fully compatible with jax.jit: the schedule look-up uses u.math.searchsorted() on the static amplitude_times array, while t remains a traced value throughout.

Returns:

out – Current quantity with shape self.varshape. For each output channel, value equals the latest scheduled plateau whose change time is <= t; channels outside the active window [origin + start, origin + stop) are set to zero (or all active when stop is None).

Return type:

Quantity

Raises:

KeyError – If brainstate.environ has no 't' entry.

Notes

Both amplitude_times and t are divided by u.ms to obtain dimensionless arrays before calling u.math.searchsorted(). u.math.searchsorted(..., side='right') - 1 returns the index of the most-recently-passed change point, or -1 when t precedes all change times (zero current). u.math.clip() keeps the index in bounds for the gather; u.math.where() then suppresses the result when the index is negative. Start is inclusive and stop is exclusive, matching NEST semantics.

See also

step_current_generator

Class-level parameter definitions and model equations.

dc_generator.update

Windowed constant-current update rule.

ac_generator.update

Windowed sinusoidal-current update rule.