iaf_psc_exp#

class brainpy.state.iaf_psc_exp(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"), tau_syn_ex=Quantity(2., "ms"), tau_syn_in=Quantity(2., "ms"), I_e=Quantity(0., "pA"), rho=Quantity(0.01, "Hz"), delta=Quantity(0., "mV"), V_initializer=Constant(value=-70. mV), spk_fun=ReluGrad(alpha=0.3, width=1.0), spk_reset='hard', ref_var=False, name=None)#

NEST-compatible iaf_psc_exp neuron model.

Description

iaf_psc_exp is a current-based leaky integrate-and-fire neuron with hard reset, fixed absolute refractory period, and exponential excitatory and inhibitory postsynaptic currents. The implementation follows NEST models/iaf_psc_exp.{h,cpp} update order, including one-step buffered current input and receptor-1 filtered current handling.

1. Continuous-Time Dynamics

The subthreshold membrane equation is

\[\frac{dV_m}{dt} = -\frac{V_m - E_L}{\tau_m} + \frac{I_{\mathrm{syn,ex}} + I_{\mathrm{syn,in}} + I_e + I_0}{C_m}\]

where \(I_0\) is the buffered current from the previous simulation step. Synaptic currents decay exponentially:

\[\frac{dI_{\mathrm{syn,ex}}}{dt} = -\frac{I_{\mathrm{syn,ex}}}{\tau_{\mathrm{syn,ex}}} \qquad \frac{dI_{\mathrm{syn,in}}}{dt} = -\frac{I_{\mathrm{syn,in}}}{\tau_{\mathrm{syn,in}}}.\]

NEST also defines a second current receptor \(I_1\) that is filtered through the excitatory kernel; this is exposed via update(x_filtered=...).

2. Exact Step Propagator and NEST Update Ordering

For time step \(h = dt\) (in ms), exact exponentials are used for all linear sub-systems:

\[P_{11,\mathrm{ex}} = e^{-h/\tau_{\mathrm{syn,ex}}}, \quad P_{11,\mathrm{in}} = e^{-h/\tau_{\mathrm{syn,in}}}, \quad P_{22} = e^{-h/\tau_m},\]
\[P_{20} = \frac{\tau_m}{C_m}(1 - P_{22}),\]
\[P_{21}(\tau_{\mathrm{syn}}) = \frac{\tau_{\mathrm{syn}}\tau_m}{C_m(\tau_m - \tau_{\mathrm{syn}})} \left(e^{-h/\tau_m} - e^{-h/\tau_{\mathrm{syn}}}\right),\]

where \(P_{21}\) is evaluated numerically stably by propagator_exp(). Let \(V_\mathrm{rel} = V_m - E_L\). The candidate membrane update is

\[V_{\mathrm{rel},n+1} = P_{22} V_{\mathrm{rel},n} + P_{21,\mathrm{ex}} I_{\mathrm{syn,ex},n} + P_{21,\mathrm{in}} I_{\mathrm{syn,in},n} + P_{20}(I_e + I_{0,n}).\]

Per-step update order is:

  1. Update membrane potential if not refractory.

  2. Decay synaptic currents.

  3. Add filtered-current contribution to excitatory synaptic current.

  4. Add arriving spikes (positive -> excitatory, negative -> inhibitory).

  5. Threshold test, reset and refractory assignment.

  6. Store buffered currents for next step.

3. Escape-Noise Threshold Dynamics

Deterministic thresholding is used when \(\delta < 10^{-10}\): \(V_{\mathrm{rel}} \ge \theta\), where \(\theta = V_{th} - E_L\).

For \(\delta > 0\), the model uses an exponential hazard:

\[\phi(V) = \rho \exp\!\left(\frac{V_{\mathrm{rel}} - \theta}{\delta}\right),\]

and spikes with step probability \(p = \phi(V)\,h\times10^{-3}\) because \(\phi\) is in 1/s while h is in ms. Stochastic decisions use numpy.random.random.

4. Stability Constraints and Computational Implications

  • Construction enforces V_reset < V_th, C_m > 0, tau_m > 0, tau_syn_ex > 0, tau_syn_in > 0, t_ref >= 0, rho >= 0, and delta >= 0.

  • propagator_exp() uses a singular fallback \((h/C_m)\exp(-h/\tau_m)\) when tau_syn is numerically close to tau_m, avoiding cancellation in \((e^{-h/\tau_m} - e^{-h/\tau_{\mathrm{syn}}})/(\tau_m - \tau_{\mathrm{syn}})\).

  • Per-call cost is \(O(\prod \mathrm{varshape})\) with vectorized NumPy operations in float64 for coefficient evaluation.

  • Buffered current semantics match NEST ring-buffer timing: x/x_filtered supplied at step n are stored and consumed at step n+1.

Parameters:
  • in_size (Size) – Population shape specification. All per-neuron parameters are broadcast to self.varshape derived from in_size.

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

  • C_m (ArrayLike, optional) – Membrane capacitance \(C_m\) in pF; broadcastable and strictly positive. Default is 250. * u.pF.

  • tau_m (ArrayLike, optional) – Membrane time constant \(\tau_m\) in ms; broadcastable and strictly positive. Default is 10. * u.ms.

  • t_ref (ArrayLike, optional) – Absolute refractory period \(t_{ref}\) in ms; broadcastable and nonnegative. Converted to integer steps by ceil(t_ref / dt). 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) – Post-spike reset potential \(V_{reset}\) in mV; broadcastable and must satisfy V_reset < V_th elementwise. Default is -70. * u.mV.

  • tau_syn_ex (ArrayLike, optional) – Excitatory synaptic decay constant \(\tau_{\mathrm{syn,ex}}\) in ms; broadcastable and strictly positive. Default is 2. * u.ms.

  • tau_syn_in (ArrayLike, optional) – Inhibitory synaptic decay constant \(\tau_{\mathrm{syn,in}}\) in ms; broadcastable and strictly positive. Default is 2. * u.ms.

  • I_e (ArrayLike, optional) – Constant external injected current \(I_e\) in pA; scalar or array broadcastable to self.varshape. Default is 0. * u.pA.

  • rho (ArrayLike, optional) – Escape-noise base firing intensity \(\rho\) in 1/s; broadcastable and nonnegative. Used only in stochastic mode (delta > 0). Default is 0.01 / u.second.

  • delta (ArrayLike, optional) – Escape-noise soft-threshold width \(\delta\) in mV; broadcastable and nonnegative. delta == 0 reproduces deterministic thresholding. Default is 0. * u.mV.

  • V_initializer (Callable, optional) – Initializer for membrane state V used by init_state(). Default is braintools.init.Constant(-70. * u.mV).

  • spk_fun (Callable, optional) – Surrogate spike nonlinearity used by get_spike(). Default is braintools.surrogate.ReluGrad().

  • spk_reset (str, optional) – Reset policy inherited from Neuron. 'hard' matches NEST reset behavior. Default is 'hard'.

  • ref_var (bool, optional) – If True, allocates self.refractory (boolean array) for external inspection of the refractory state. Default is False.

  • name (str or None, optional) – Optional node name passed to the parent module. Default is None.

Parameter Mapping

Table 6 Parameter mapping to model symbols#

Parameter

Type / shape / unit

Default

Math symbol

Semantics

in_size

Size; scalar/tuple

required

Defines neuron population shape self.varshape.

E_L

ArrayLike, broadcastable to self.varshape (mV)

-70. * u.mV

\(E_L\)

Resting membrane potential.

C_m

ArrayLike, broadcastable (pF), > 0

250. * u.pF

\(C_m\)

Membrane capacitance in voltage integration.

tau_m

ArrayLike, broadcastable (ms), > 0

10. * u.ms

\(\tau_m\)

Membrane leak time constant.

t_ref

ArrayLike, broadcastable (ms), >= 0

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 voltages.

tau_syn_ex and tau_syn_in

ArrayLike, broadcastable (ms), each > 0

2. * u.ms

\(\tau_{\mathrm{syn,ex}}\), \(\tau_{\mathrm{syn,in}}\)

Exponential PSC decay constants.

I_e

ArrayLike, broadcastable (pA)

0. * u.pA

\(I_e\)

Constant current injected every step.

rho and delta

ArrayLike, broadcastable; rho in 1/s, delta in mV, both >= 0

0.01 / u.second, 0. * u.mV

\(\rho\), \(\delta\)

Escape-noise hazard parameters.

V_initializer

Callable

Constant(-70. * u.mV)

Initializer for membrane state V.

spk_fun

Callable

ReluGrad()

Surrogate function used for output spikes.

spk_reset

str (typically 'hard')

'hard'

Reset behavior selection in base class.

ref_var

bool

False

Enables explicit boolean refractory state variable.

name

str or None

None

Optional instance name.

Raises:

ValueError – Raised at construction when any validated constraint is violated: V_reset >= V_th, nonpositive C_m/tau_m/synaptic time constants, negative t_ref, negative rho, or negative delta.

V#

Membrane potential in mV; shape self.varshape.

Type:

brainstate.HiddenState

i_syn_ex#

Excitatory synaptic current in pA.

Type:

brainstate.ShortTermState

i_syn_in#

Inhibitory synaptic current in pA.

Type:

brainstate.ShortTermState

i_0#

Buffered receptor-0 current (pA) applied on the next simulation step.

Type:

brainstate.ShortTermState

i_1#

Buffered receptor-1 current (pA) filtered through the excitatory exponential kernel on the next simulation step.

Type:

brainstate.ShortTermState

refractory_step_count#

Integer countdown of remaining refractory steps (jnp.int32).

Type:

brainstate.ShortTermState

last_spike_time#

Simulation time of the most recent spike (ms).

Type:

brainstate.ShortTermState

refractory#

Boolean refractory mask; only present when ref_var=True.

Type:

brainstate.ShortTermState

Notes

  • This implementation uses exact (analytical) integration of the linear subthreshold ODE via pre-computed propagator coefficients, matching NEST’s update precision for fixed-step simulation.

  • Continuous current input x is combined with I_e and any additional current sources registered via sum_current_inputs(); the combined value is buffered one step (NEST ring-buffer semantics).

  • Delta spike inputs from sum_delta_inputs() are split by sign: positive weights increment i_syn_ex; negative weights increment i_syn_in.

  • The stochastic escape-noise mode (delta > 0) uses numpy.random.random and is therefore not JIT-compilable via JAX. Use delta=0 for fully differentiable, JIT-compatible runs.

Examples

>>> import brainstate
>>> import saiunit as u
>>> from brainpy_state._nest.iaf_psc_exp import iaf_psc_exp
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     neu = iaf_psc_exp(in_size=(3,), I_e=250. * u.pA, delta=0. * u.mV)
...     neu.init_state()
...     with brainstate.environ.context(t=0.0 * u.ms):
...         out = neu.update(x=0. * u.pA, x_filtered=0. * u.pA)
...     _ = out.shape
>>> import brainstate
>>> import saiunit as u
>>> from brainpy_state._nest.iaf_psc_exp import iaf_psc_exp
>>> with brainstate.environ.context(dt=0.1 * u.ms):
...     neu = iaf_psc_exp(
...         in_size=10,
...         tau_syn_ex=2.0 * u.ms,
...         tau_syn_in=5.0 * u.ms,
...         ref_var=True,
...     )
...     neu.init_state()
...     with brainstate.environ.context(t=0.0 * u.ms):
...         spk = neu.update(x=300.0 * u.pA)
...     _ = spk.shape

References

See also

iaf_psc_delta

LIF neuron with delta-function PSCs (voltage-jump synapses)

iaf_cond_exp

LIF neuron with exponential conductance synapses

LIF

Leaky integrate-and-fire (brainpy parameterization)

LIFRef

Leaky integrate-and-fire with explicit refractory tracking

get_spike(V=None)[source]#

Evaluate surrogate spike activation for a voltage tensor.

Scales the voltage relative to threshold and reset to compute a dimensionless argument passed to the surrogate nonlinearity self.spk_fun:

\[\text{out} = \mathrm{spk\_fun}\!\left( \frac{V - V_{th}}{V_{th} - V_{reset}} \right).\]
Parameters:

V (ArrayLike or None, optional) – Membrane voltage in mV, broadcast-compatible with self.varshape. If None, self.V.value is used.

Returns:

out – Surrogate spike output from self.spk_fun with the same shape as V (or self.V.value when V is None).

Return type:

dict

Raises:

TypeError – If V cannot participate in arithmetic with membrane parameters due to incompatible dtype or unit.

init_state(**kwargs)[source]#

Initialize membrane potential and all synaptic/refractory states.

Parameters:

**kwargs (Any) – Unused compatibility arguments.

Raises:
  • ValueError – If V_initializer output cannot be broadcast to the target state shape.

  • TypeError – If initializer values are incompatible with required numeric/unit conversions.

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

Advance the neuron state by one simulation step.

Parameters:
  • x (ArrayLike, optional) – Current input in pA for receptor-0 (standard current port). Scalar or array broadcastable to self.varshape. The value is buffered (stored in self.i_0) and applied in the next step, matching NEST ring-buffer semantics. Default is 0. * u.pA.

  • x_filtered (ArrayLike, optional) – Current input in pA for receptor-1. Buffered in self.i_1 and injected through excitatory exponential filtering at the next update step via (1 - P_{11,\mathrm{ex}}) \times i_1. Scalar or array broadcastable to self.varshape. Default is 0. * u.pA.

Returns:

out – Surrogate spike output from get_spike() with shape self.V.value.shape. For neurons that fire this step, the voltage argument to get_spike() is nudged \(\theta + E_L + 10^{-12}\,\text{mV}\) (above threshold) to ensure a positive surrogate activation is returned even after the hard voltage reset.

Return type:

jax.Array

Raises:
  • KeyError – If the simulation environment context does not supply t or dt.

  • AttributeError – If state variables are missing because init_state() has not been called before update.

  • TypeError – If input/state values are not unit-compatible with expected pA/mV arithmetic.

  • ValueError – If provided inputs cannot be broadcast to the internal state shape.