iaf_psc_exp_ps_lossless#
- class brainpy.state.iaf_psc_exp_ps_lossless(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"), V_min=None, 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_ps_losslesswith lossless spike detection.Description
iaf_psc_exp_ps_losslessis a current-based leaky integrate-and-fire neuron with exponential excitatory/inhibitory PSC states and off-grid event/spike timing. The implementation follows NESTmodels/iaf_psc_exp_ps_lossless.{h,cpp}semantics: within-step event ordering by precise offsets, exact closed-form mini-step propagation, lossless spike detection via state-space envelope analysis (Krishnan et al., 2018), sub-step threshold localization by root search, and refractory release modeled as an explicit pseudo-event.Compared with
iaf_psc_exp_ps, this model adds a state-space spike detector that can detect spikes hidden between sampled endpoints of a mini-interval, preventing numerical spike loss in scenarios with fast subthreshold oscillations or large discrete input jumps.1. Continuous-time dynamics and exact integration
Let \(U = V_m - E_L\), \(I_{ex}\) and \(I_{in}\) be excitatory/inhibitory PSC states (pA), and \(y_0\) the one-step buffered continuous input current (pA). Subthreshold dynamics are
\[\frac{dU}{dt} = -\frac{U}{\tau_m} + \frac{I_e + y_0 + I_{ex} + I_{in}}{C_m},\]\[\frac{dI_{ex}}{dt} = -\frac{I_{ex}}{\tau_{syn,ex}}, \qquad \frac{dI_{in}}{dt} = -\frac{I_{in}}{\tau_{syn,in}}.\]Over a mini-interval \(\Delta t\), exact integration gives
\[U(t+\Delta t) = P_{20}(\Delta t)\,(I_e+y_0) + P_{21,ex}(\Delta t)\,I_{ex}(t) + P_{21,in}(\Delta t)\,I_{in}(t) + U(t)e^{-\Delta t/\tau_m},\]where \(P_{20}=-\frac{\tau_m}{C_m}\left(e^{-\Delta t/\tau_m}-1\right)\) and \(P_{21,X}\) are evaluated by
propagator_exp()(from_utils). PSC states decay exactly via \(I_X(t+\Delta t)=I_X(t)e^{-\Delta t/\tau_{syn,X}}\).2. Lossless spike detection criterion
Before propagating each mini-interval \([t, t+\Delta t]\), the algorithm checks whether the trajectory \(U(s)\) crosses threshold at any \(s \in [t, t+\Delta t]\) using an analytical envelope test (Krishnan et al., 2018). This requires \(\tau_{syn,ex} = \tau_{syn,in} = \tau_s\) so that the combined synaptic current \(I(s) = I_{ex}(s) + I_{in}(s)\) decays as a single exponential.
The membrane trajectory within the mini-interval is
\[U(s) = \frac{\tau_m}{C_m} I_e + \frac{\tau_m \tau_s}{C_m(\tau_m - \tau_s)} \left[ I(t) \left(e^{-(s-t)/\tau_m} - e^{-(s-t)/\tau_s}\right) \right] + U(t) e^{-(s-t)/\tau_m}.\]The lossless criterion computes the maximum of \(U(s)\) over \(s \in [t, t+\Delta t]\) analytically and checks if it exceeds \(U_{th}\). If so, bisection root finding localizes the precise crossing time. This guarantees no spike is missed due to discrete sampling, even when the trajectory peaks between grid points.
Implementation note: The envelope test uses algebraic bounds on the trajectory extremum. See
is_spike_lossless()internal function and Krishnan et al. (2018) for derivation details.3. Precise-time event processing
Event offsets use NEST convention:
offset=dtat step start andoffset=0at step end. For each global step:Build local event list from
spike_eventsand on-grid delta input (always added atoffset=0).Sort events in descending offset and split the step into mini-intervals.
Apply lossless spike test to each mini-interval before propagation.
If lossless test indicates a spike, solve \(f(\delta)=U(\delta)-U_{th}=0\) with bounded bisection (64 iterations) to obtain off-grid spike time.
Reset to
V_resetand enter refractory state; release from refractory occurs through a pseudo-event whenstep_idx + 1 - last_spike_step == ceil(t_ref / dt).
4. Assumptions, constraints, and computational complexity
Parameters are scalar or broadcastable to
self.varshape.Construction-time constraints enforce
V_reset < V_th,C_m > 0,tau_m > 0,tau_syn_ex > 0,tau_syn_in > 0,t_ref >= 0, and ``tau_syn_ex == tau_syn_in`` (required for lossless envelope test), andtau_m != tau_syn_ex(to avoid singular propagator).When
V_minis provided:V_reset >= V_min.All precise offsets must satisfy
0 <= offset <= dt.Continuous input
xis buffered (stored intoy0for the next global step), matching NEST current-event timing.Per-step complexity is \(O(|\mathrm{state}| \cdot K)\) for
Klocal events, plus envelope test and root search cost on each mini-interval.
- Parameters:
in_size (
Size) – Population shape specification. Model parameters and states are broadcast toself.varshapederived fromin_size.E_L (
ArrayLike, optional) – Resting potential \(E_L\) in mV, broadcastable toself.varshape. Default is-70. * u.mV.C_m (
ArrayLike, optional) – Membrane capacitance \(C_m\) in pF, broadcastable toself.varshape. Must be strictly positive elementwise. Default is250. * u.pF.tau_m (
ArrayLike, optional) – Membrane time constant \(\tau_m\) in ms, broadcastable toself.varshape. Must be strictly positive elementwise and must differ fromtau_syn_exto avoid singular propagator. Default is10. * u.ms.t_ref (
ArrayLike, optional) – Absolute refractory duration \(t_{ref}\) in ms, broadcastable toself.varshape. Can be zero (no refractory period). Converted at runtime to steps usingceil(t_ref / dt). Default is2. * u.ms.V_th (
ArrayLike, optional) – Threshold voltage \(V_{th}\) in mV, broadcastable toself.varshape. Default is-55. * u.mV.V_reset (
ArrayLike, optional) – Reset voltage \(V_{reset}\) in mV, broadcastable toself.varshape. Must satisfyV_reset < V_thelementwise. Default is-70. * u.mV.tau_syn_ex (
ArrayLike, optional) – Excitatory PSC decay constant \(\tau_{syn,ex}\) in ms, broadcastable toself.varshapeand strictly positive. Must equal ``tau_syn_in`` for lossless spike detection. Default is2. * u.ms.tau_syn_in (
ArrayLike, optional) – Inhibitory PSC decay constant \(\tau_{syn,in}\) in ms, broadcastable toself.varshapeand strictly positive. Must equal ``tau_syn_ex`` for lossless spike detection. Default is2. * u.ms.I_e (
ArrayLike, optional) – Constant external current \(I_e\) in pA, broadcastable toself.varshape. Added in each mini-step propagation. Default is0. * u.pA.V_min (
ArrayLikeorNone, optional) – Optional lower bound \(V_{min}\) in mV, broadcastable toself.varshape. IfNone, no lower clip is applied. Default isNone.V_initializer (
Callable, optional) – Initializer used byinit_state()for membrane stateV. Must return mV-compatible values with shape compatible withself.varshape. Default isbraintools.init.Constant(-70. * u.mV).spk_fun (
Callable, optional) – Surrogate spike function used byget_spike()andupdate(). Receives normalized threshold distance tensor. Default isbraintools.surrogate.ReluGrad().spk_reset (
str, optional) – Reset policy forwarded toNeuron.'hard'matches NEST hard reset. Default is'hard'.ref_var (
bool, optional) – IfTrue, creates exposedself.refractorymirroringself.is_refractoryfor inspection. Default isFalse.
Parameter Mapping
Table 10 Parameter mapping to model symbols# Parameter
Type / shape / unit
Default
Math symbol
Semantics
in_sizeSize; scalar/tuplerequired
–
Defines
self.varshapefor parameter/state broadcasting.E_LArrayLike, broadcastable to
self.varshape(mV)-70. * u.mV\(E_L\)
Resting potential and voltage-offset origin.
C_mArrayLike, broadcastable (pF),
> 0250. * u.pF\(C_m\)
Converts current terms to membrane-rate contribution.
tau_mArrayLike, broadcastable (ms),
> 0,!= tau_syn_ex10. * u.ms\(\tau_m\)
Leak time constant in exact subthreshold propagation.
t_refArrayLike, broadcastable (ms),
>= 0, runtimeceil(t_ref/dt)2. * u.ms\(t_{ref}\)
Absolute refractory duration (can be zero).
V_thandV_resetArrayLike, broadcastable (mV), with
V_reset < V_th-55. * u.mV,-70. * u.mV\(V_{th}\), \(V_{reset}\)
Threshold and post-spike reset levels.
tau_syn_exandtau_syn_inArrayLike, broadcastable (ms), each
> 0, must be equal2. * u.ms\(\tau_{syn,ex}\), \(\tau_{syn,in}\)
Exponential PSC decay constants (equality required).
I_eArrayLike, broadcastable (pA)
0. * u.pA\(I_e\)
Constant injected current added every mini-step.
V_minArrayLike broadcastable (mV) or
NoneNone\(V_{min}\)
Optional lower clamp applied after membrane propagation.
V_initializerCallable returning mV-compatible values
Constant(-70. * u.mV)–
Initializes membrane state
V.spk_funCallable
ReluGrad()–
Surrogate spike output nonlinearity.
spk_resetstr
'hard'–
Reset mode inherited from base
Neuron.ref_varbool
False–
Allocate exposed
refractorymirror state.namestr | None
None–
Optional node name.
- Raises:
ValueError – If validated constraints fail (for example
V_reset >= V_th, non-positive capacitance/time constants,V_reset < V_min,tau_syn_ex != tau_syn_in,tau_m == tau_syn_ex,t_ref < 0, or event offsets outside[0, dt]).TypeError – If provided arguments are incompatible with expected units/callables (mV, pA, pF, ms).
KeyError – If simulation context values
tand/ordtare missing whenupdate()is called.AttributeError – If
update()is called beforeinit_state()creates required runtime states.
- V#
Membrane potential state in mV.
- Type:
HiddenState
- I_syn_ex#
Excitatory PSC state in pA.
- Type:
ShortTermState
- I_syn_in#
Inhibitory PSC state in pA.
- Type:
ShortTermState
- y0#
One-step buffered continuous current in pA.
- Type:
ShortTermState
- is_refractory#
Boolean refractory mask.
- Type:
ShortTermState
- last_spike_step#
Step index of latest emitted spike.
- Type:
ShortTermState
- last_spike_offset#
Precise offset (ms) from right step boundary for latest spike.
- Type:
ShortTermState
- last_spike_time#
Absolute precise spike time in ms.
- Type:
ShortTermState
- refractory#
Optional mirror of
is_refractorywhenref_var=True.- Type:
ShortTermState
Notes
spike_eventsaccepts(offset, weight)tuples or{'offset': ..., 'weight': ...}dicts.Offsets are in ms and measured from the right edge of the current step.
Positive event weights contribute to excitatory PSC state; negative weights contribute to inhibitory PSC state.
Internal propagation and root finding are evaluated in NumPy float64 and written back into BrainUnit states at end of step.
The lossless spike test prevents numerical spike loss but adds computational overhead compared to
iaf_psc_exp_ps.Constraint ``tau_syn_ex == tau_syn_in`` is necessary because the analytical envelope test requires a single combined synaptic time constant. This differs from standard
iaf_psc_exp_pswhich allows independent excitatory/inhibitory time constants.
Examples
>>> import brainpy >>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... neu = brainpy.state.iaf_psc_exp_ps_lossless( ... in_size=2, I_e=200.0 * u.pA ... ) ... neu.init_state() ... with brainstate.environ.context(t=0.0 * u.ms): ... spk = neu.update() ... _ = spk.shape
>>> import brainpy >>> import brainstate >>> import saiunit as u >>> with brainstate.environ.context(dt=0.1 * u.ms): ... neu = brainpy.state.iaf_psc_exp_ps_lossless(in_size=1) ... neu.init_state() ... ev = [{'offset': 0.08 * u.ms, 'weight': 120.0 * u.pA}] ... with brainstate.environ.context(t=0.0 * u.ms): ... _ = neu.update(spike_events=ev)
References
- get_spike(V=None)[source]#
Evaluate surrogate spike output from membrane potential.
Applies the surrogate spike function (typically
braintools.surrogate.ReluGrador similar) to a normalized threshold-distance metric. This enables differentiable spike generation for gradient-based learning while maintaining biological spike semantics.The normalized threshold distance is computed as \((V - V_{th}) / (V_{th} - V_{reset})\), which maps the voltage range between reset and threshold to
[0, 1], with values above threshold producing positive outputs through the surrogate function.- Parameters:
V (
ArrayLikeorNone, optional) – Voltage tensor in mV, broadcast-compatible withself.varshape(or current batched state shape). IfNone, usesself.V.value. Default isNone.- Returns:
out – Output of
self.spk_funapplied to normalized threshold distance(V - V_th) / (V_th - V_reset)with same shape as inputV. Typically float values in[0, 1]or similar range depending on the surrogate function’s output characteristics.- Return type:
- Raises:
TypeError – If
Vis not compatible with unit arithmetic in mV or if unit conversion operations fail.AttributeError – If
self.spk_funis not callable or if required parameters (V_th,V_reset) are not available.
- init_state(**kwargs)[source]#
Initialize membrane, synaptic, and precise-timing runtime states.
This method allocates all internal state variables required for lossless precise spike-time simulation. Membrane potential
Vis initialized usingself.V_initializer, synaptic currents and buffered inputs are initialized to zero, and spike-tracking states are initialized to sentinel values (last_spike_step = -1,last_spike_time = -1e7 ms) indicating no prior spike events.- Parameters:
**kwargs (
Any) – Unused compatibility arguments accepted by the base-state API.- Raises:
ValueError – If initializer outputs cannot be broadcast to state shape or if shapes are incompatible.
TypeError – If initializer outputs are not unit-compatible with expected state units (mV for voltage, pA for currents, ms for time, bool for flags).
AttributeError – If
self.V_initializeris not callable or does not produce valid output for the requested shape.
- update(x=Quantity(0., 'pA'), spike_events=None)[source]#
Advance one global step with lossless precise spike detection.
This method implements the complete NEST-compatible lossless precise-spike-time algorithm for
iaf_psc_exp_ps_lossless. Each global time step is subdivided into mini-intervals determined by spike event offsets. Before propagating each mini-interval, the lossless spike detection criterion (Krishnan et al., 2018) checks whether the membrane trajectory crosses threshold anywhere within the interval, even between sampled grid points. If a crossing is detected, bisection root-finding (64 iterations) localizes the precise sub-step spike time.Update sequence:
Parse and validate
spike_eventsand on-grid delta inputs.Sort events in descending offset (from step start to step end).
For each neuron, process events sequentially:
Apply lossless spike test to each mini-interval using analytical envelope bounds (see internal
is_spike_lossless).If test indicates spike, use bisection to find precise crossing.
Propagate states exactly over the mini-interval (or up to spike).
Apply event weights to PSC states (ex/in channels by sign).
Apply hard reset and enter refractory state on spike.
Release from refractory via pseudo-event at calculated step.
Buffer incoming current
xintoy0for next step.Compute surrogate spike output for gradient-based learning.
Lossless spike detection:
The internal
is_spike_losslessfunction computes analytical bounds on the maximum of the membrane trajectory \(U(s)\) over the mini-interval \(s \in [t, t+\Delta t]\). This requires \(\tau_{syn,ex} = \tau_{syn,in}\) so that the combined synaptic current decays exponentially. The test returns:np.nanif no spike is detected (trajectory stays below threshold).dt_localif the trajectory is above threshold at interval end.A positive time value if the envelope analysis indicates a spike occurs within the interval (followed by bisection refinement).
Implementation notes:
All propagation uses NumPy float64 for numerical stability.
Event offsets follow NEST convention:
offset=dtat step start,offset=0at step end.Refractory neurons clamp membrane potential but allow PSC decay.
The lossless test adds computational overhead but guarantees no spikes are missed due to discrete time sampling.
- Parameters:
x (
ArrayLike, optional) – Continuous current input in pA for the current global step. Aggregated throughsum_current_inputs()and stored iny0for use in the next step (one-step buffering). Scalar or array-like broadcastable toself.V.value.shape. Default is0. * u.pA.spike_events (
Iterable[tuple[Any,Any] | dict[str,Any]]orNone, optional) – Optional off-grid events inside the current step. Each entry is(offset, weight)or{'offset': ..., 'weight': ...}, whereoffsetis in ms measured from the right step boundary andweightis in pA. Offsets must satisfy0 <= offset <= dt. Positive weights update excitatory PSC; negative weights update inhibitory PSC.Nonemeans no extra precise events. On-grid delta inputs are automatically included atoffset=0. Default isNone.
- Returns:
out – Surrogate spike output from
get_spike(), shapeself.V.value.shape. Values correspond toself.spk_fun((V - V_th) / (V_th - V_reset))after lossless spike detection, exact piecewise propagation, event application, refractory logic, and precise spike-time localization. For neurons that spiked, the voltage is clamped slightly above threshold to ensure differentiable spike detection; for non-spiking neurons, voltage is clamped below threshold.- Return type:
jax.Array- Raises:
ValueError – If
t_ref < 0(refractory time is negative), or if any event offset lies outside[0, dt], or if parameter constraints are violated at runtime.KeyError – If simulation context values
t(current time) ordt(time step) are unavailable frombrainstate.environ.TypeError – If
xorspike_eventsentries are not unit-compatible with pA/ms conversions, or if type conversions fail during numerical computation.AttributeError – If required runtime states (
V,I_syn_ex,I_syn_in,y0,is_refractory, etc.) are missing becauseinit_state()has not been called.
See also
iaf_psc_exp_psStandard precise-spike model without lossless detection.