iaf_psc_delta_ps#

class brainpy.state.iaf_psc_delta_ps(in_size, E_L=Quantity(-70., "mV"), C_m=Quantity(250., "pF"), tau_m=Quantity(10., "ms"), t_ref=Quantity(2., "ms"), V_th=Quantity(-55., "mV"), V_reset=Quantity(-70., "mV"), I_e=Quantity(0., "pA"), V_min=None, V_initializer=Constant(value=-70. mV), spk_fun=ReluGrad(alpha=0.3, width=1.0), spk_reset='hard', refractory_input=False, ref_var=False, name=None)#

NEST-compatible iaf_psc_delta_ps with precise spike timing.

Description

iaf_psc_delta_ps is a current-based leaky integrate-and-fire neuron with delta-shaped synaptic jumps (weights in mV), exact linear subthreshold integration, and precise off-grid spike timing inside each global simulation step. The implementation follows NEST models/iaf_psc_delta_ps.{h,cpp} semantics, including event ordering by within-step offsets, analytic threshold-crossing localization for current-driven spikes, and optional accumulation of refractory-time inputs.

1. Linear Membrane Dynamics and Exact Closed-Form Propagator

The subthreshold membrane potential dynamics are

\[\frac{dV_m}{dt} = -\frac{V_m - E_L}{\tau_m} + \frac{I_\mathrm{ext}(t) + I_e}{C_m},\]

with piecewise-constant \(I_\mathrm{ext}\) over each simulation step.

Defining \(U = V_m - E_L\), \(R = \tau_m / C_m\), and a constant current over an interval \(\Delta t\), exact integration gives

\[U(t + \Delta t) = U(t)e^{-\Delta t/\tau_m} + R(I_\mathrm{ext}+I_e)\left(1 - e^{-\Delta t/\tau_m}\right).\]

The code evaluates this update with expm1-based algebra for numerical stability when \(\Delta t/\tau_m\) is small, which reduces cancellation error in fine-step simulations.

2. Spike Generation Mechanisms and Precise Spike-Time Derivation

Two spike mechanisms are implemented:

  • Instantaneous event-driven spikes: if an incoming delta event at offset \(\delta\) pushes \(U \ge U_{th}\), spike time is the event time exactly.

  • Current-driven spikes: if propagation yields \(U \ge U_{th}\), spike offset is solved analytically from the exact trajectory:

    \[\Delta t_\mathrm{cross} = -\tau_m \log\frac{V_\infty - U}{V_\infty - U_{th}}, \quad V_\infty = R(I_\mathrm{ext}+I_e).\]

The model stores:

  • last_spike_time: absolute spike time in ms,

  • last_spike_offset: off-grid offset relative to the right border of the current grid step (NEST semantics),

  • last_spike_step: on-grid step index used internally for refractory logic.

3. Refractory Handling and Deferred Refractory-Input Accumulation

After a spike, membrane potential is reset to V_reset and clamped during the absolute refractory period.

In NEST iaf_psc_delta_ps, refractory duration in steps is derived as floor(t_ref / dt) (via Time(...).get_steps()) and must be at least one simulation step. This implementation enforces the same runtime constraint.

By default, spikes arriving during refractory are discarded. If refractory_input=True, they are accumulated and exponentially damped until end of refractoriness, then applied once at refractory release, matching NEST.

4. Event Ordering, Assumptions, Constraints, and Computational Implications

For each simulation step the update proceeds as follows:

  1. Optional immediate spike if state starts super-threshold.

  2. Process within-step events in offset order (start to end of step):

    • propagate to event time (if non-refractory),

    • check current-driven crossing,

    • apply event jump and check instant crossing.

  3. Propagate remaining interval (if any).

  4. Store new external current input buffer for next step.

Assumptions and constraints used by the implementation:

  • Parameter tensors are scalar or broadcastable to self.varshape.

  • Required physical inequalities are validated at construction: V_reset < V_th, C_m > 0, tau_m > 0, t_ref >= 0, and if V_min is provided then V_reset >= V_min.

  • Runtime requires floor(t_ref / dt) >= 1 and dt > 0.

  • Every precise event offset must satisfy 0 <= offset <= dt.

Computationally, the update iterates scalar-wise over np.ndindex across the full state shape and processes all local events in each cell, so cost is \(O(|\mathrm{state}| \cdot K)\) per step for K events (excluding input aggregation).

Parameters:
  • in_size (Size) – Population shape specification used to derive self.varshape. Scalar integer for 1D populations or tuple for multi-dimensional.

  • E_L (ArrayLike, optional) – Resting membrane potential \(E_L\) in mV. Scalar or array-like broadcastable to self.varshape. Default is -70. * u.mV.

  • C_m (ArrayLike, optional) – Membrane capacitance \(C_m\) in pF, broadcastable to self.varshape. Must be strictly positive elementwise. Default is 250. * u.pF.

  • tau_m (ArrayLike, optional) – Membrane time constant \(\tau_m\) in ms, broadcastable to self.varshape. Must be strictly positive elementwise. Default is 10. * u.ms.

  • t_ref (ArrayLike, optional) – Absolute refractory duration \(t_{ref}\) in ms, broadcastable to self.varshape. At runtime converted to steps by floor(t_ref / dt) and must produce at least one step. Default is 2. * u.ms.

  • V_th (ArrayLike, optional) – Spike threshold \(V_{th}\) in mV, broadcastable to self.varshape. Default is -55. * u.mV.

  • V_reset (ArrayLike, optional) – Reset potential \(V_{reset}\) in mV, broadcastable to self.varshape. Must satisfy V_reset < V_th elementwise. Default is -70. * u.mV.

  • I_e (ArrayLike, optional) – Constant external current \(I_e\) in pA, broadcastable to self.varshape. Added to buffered current each propagation segment. Default is 0. * u.pA.

  • V_min (ArrayLike or None, optional) – Optional lower membrane bound \(V_{min}\) in mV, broadcastable to self.varshape. None disables lower clipping (uses -inf). Default is None.

  • V_initializer (Callable, optional) – Initializer used by init_state() to create membrane state V. Must return values unit-compatible with mV and shape-compatible with self.varshape (and optional batch prefix). Default is braintools.init.Constant(-70. * u.mV).

  • spk_fun (Callable, optional) – Surrogate spike nonlinearity used by get_spike() and returned by update(). Receives normalized threshold distance tensor. Default is braintools.surrogate.ReluGrad().

  • spk_reset (str, optional) – Reset mode forwarded to Neuron. 'hard' matches NEST hard-reset behavior. Default is 'hard'.

  • refractory_input (bool, optional) – If False, delta events received during refractory are ignored. If True, they are exponentially weighted into refractory_spike_buffer and applied at refractory release. Default is False.

  • ref_var (bool, optional) – If True, exposes additional state self.refractory mirroring self.is_refractory for introspection. Default is False.

  • name (str or None, optional) – Optional node name.

Parameter Mapping

Table 2 Parameter mapping to model symbols#

Parameter

Type / shape / unit

Default

Math symbol

Semantics

in_size

Size; scalar/tuple

required

Defines self.varshape for all state/parameter broadcasts.

E_L

ArrayLike, broadcastable to self.varshape (mV)

-70. * u.mV

\(E_L\)

Resting membrane potential and origin of transformed state U.

C_m

ArrayLike, broadcastable (pF), > 0

250. * u.pF

\(C_m\)

Converts current to membrane-rate contribution.

tau_m

ArrayLike, broadcastable (ms), > 0

10. * u.ms

\(\tau_m\)

Leak/relaxation time constant in exact propagator.

t_ref

ArrayLike, broadcastable (ms), runtime floor(t_ref/dt) >= 1

2. * u.ms

\(t_{ref}\)

Absolute refractory duration.

V_th and V_reset

ArrayLike, broadcastable (mV), with V_reset < V_th

-55. * u.mV, -70. * u.mV

\(V_{th}\), \(V_{reset}\)

Threshold and post-spike reset levels.

I_e

ArrayLike, broadcastable (pA)

0. * u.pA

\(I_e\)

Constant injected current term.

V_min

ArrayLike broadcastable (mV) or None

None

\(V_{min}\)

Optional lower clip on membrane potential.

V_initializer

Callable returning mV-compatible values

Constant(-70. * u.mV)

Initializes membrane state V.

spk_fun

Callable

ReluGrad()

Surrogate spike output function.

spk_reset

str

'hard'

Reset policy inherited from base neuron class.

refractory_input

bool

False

Controls treatment of refractory-time delta events.

ref_var

bool

False

Exposes persistent refractory state variable.

name

str | None

None

Optional node identifier.

Raises:
  • ValueError – If validated construction/runtime constraints fail, including invalid parameter inequalities (for example V_reset >= V_th), non-positive time constants/capacitance, dt <= 0, invalid event offsets, or floor(t_ref / dt) < 1.

  • TypeError – If provided arguments are incompatible with expected unit arithmetic (mV, pA, pF, ms) or callable interfaces.

  • KeyError – If required simulation context entries (t and/or dt) are missing when update() is called.

  • AttributeError – If update() is called before init_state() creates required state variables.

V#

Membrane potential state in mV, shape self.varshape (or with leading batch dimension when batch_size is specified).

Type:

brainstate.HiddenState

I_stim#

One-step buffered continuous current input in pA. Applied in the next update call (NEST ring-buffer semantics).

Type:

brainstate.ShortTermState

last_spike_time#

Absolute precise spike time (ms) for the latest emitted spike. Initialized to -1e7 * u.ms (far past) to indicate no prior spike.

Type:

brainstate.ShortTermState

last_spike_step#

Integer (jnp.int32) step index associated with the latest emitted spike. Initialized to -1.

Type:

brainstate.ShortTermState

last_spike_offset#

Precise within-step offset (ms) measured from the step right boundary (NEST convention: 0 at step end, dt at step start).

Type:

brainstate.ShortTermState

is_refractory#

Boolean mask indicating which neurons are currently in the absolute refractory period.

Type:

brainstate.ShortTermState

refractory_spike_buffer#

Deferred refractory-time delta contribution (mV). Non-zero only when refractory_input=True; accumulates exponentially decayed delta events and is released at end of refractoriness.

Type:

brainstate.ShortTermState

refractory#

Mirror of is_refractory exposed for external inspection. Present only when ref_var=True.

Type:

brainstate.ShortTermState

Notes

  • x passed to update(x=...) is buffered into I_stim and applied on the next step, mirroring NEST ring-buffer semantics for current events.

  • Delta inputs from add_delta_input are interpreted as on-grid events at step end (offset 0).

  • Additional within-step precise events can be supplied through update(spike_events=...) where each event is (offset, weight) or {'offset': ..., 'weight': ...} in units of ms and mV.

  • This model uses floor(t_ref / dt) for refractory step conversion (matching NEST iaf_psc_delta_ps), whereas iaf_psc_delta uses ceil(t_ref / dt).

Examples

Basic usage with constant current drive:

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     neu = brainpy.state.iaf_psc_delta_ps(in_size=2, t_ref=2.0 * u.ms)
...     neu.init_state()
...     with brainstate.environ.context(t=0.0 * u.ms):
...         spk = neu.update(x=200.0 * u.pA)
...     _ = spk.shape

Precise within-step spike events with refractory_input=True:

>>> import brainpy
>>> import brainstate
>>> import saiunit as u
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     neu = brainpy.state.iaf_psc_delta_ps(in_size=1, refractory_input=True)
...     neu.init_state()
...     with brainstate.environ.context(t=0.0 * u.ms):
...         _ = neu.update(spike_events=[(0.04 * u.ms, 2.5 * u.mV)])

References

See also

iaf_psc_delta

Current-based LIF with delta synapses (on-grid spike times)

iaf_cond_exp

Conductance-based LIF with exponential synapses

get_spike(V=None)[source]#

Evaluate surrogate spike activation for a voltage tensor.

Parameters:

V (ArrayLike or None, optional) – Voltage values in mV, broadcast-compatible with self.varshape (or current state shape when batched). If None, uses self.V.value.

Returns:

out – Output of self.spk_fun evaluated on normalized threshold distance (V - V_th) / (V_th - V_reset) with same shape as V.

Return type:

dict

Raises:

TypeError – If V cannot participate in unit-compatible arithmetic.

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

Initialize membrane, timing, and refractory runtime states.

Parameters:
  • batch_size (int or None, optional) – Optional batch dimension prepended to self.varshape for all state arrays. None keeps unbatched state. Default is None.

  • **kwargs – Unused compatibility parameters accepted by the base-state API.

Raises:
  • ValueError – If initializer outputs cannot be broadcast to target state shape.

  • TypeError – If initializer values are not unit-compatible with mV/pA/ms states.

update(x=Quantity(0., 'pA'), spike_events=None)[source]#

Advance one simulation step with optional precise within-step events.

Parameters:
  • x (ArrayLike, optional) – External current input in pA. This value is buffered into self.I_stim and applied in the next update call, matching NEST ring-buffer current semantics.

  • spike_events (Sequence or dict or tuple or None, optional) – Optional precise delta events applied in the current step. Accepted formats are (offset, weight), {'offset': ..., 'weight': ...}, or a sequence of such events. offset is in ms measured from the step right boundary with NEST convention (0 at step end, dt at step start). weight is a voltage jump in mV and may be scalar or broadcastable to neuron state shape.

Returns:

out – Surrogate spike output from get_spike() with shape self.V.value.shape. Elements corresponding to neurons that spiked in this step are forced slightly above threshold before surrogate evaluation to encode emitted spikes after hard reset.

Return type:

jax.Array

Raises:
  • ValueError – If dt <= 0, if floor(t_ref / dt) < 1, if event offsets are outside [0, dt], or if event structures are invalid.

  • KeyError – If simulation context does not provide required t/dt.

  • AttributeError – If state variables are unavailable because init_state() was not called before update().

  • TypeError – If inputs or internal values are not unit-compatible with expected pA/mV/ms arithmetic.