glif_psc_double_alpha#

class brainpy.state.glif_psc_double_alpha(in_size, g=Quantity(9.43, 'nS'), E_L=Quantity(-78.85, 'mV'), V_th=Quantity(-51.68, 'mV'), C_m=Quantity(58.72, 'pF'), t_ref=Quantity(3.75, 'ms'), V_reset=Quantity(-78.85, 'mV'), th_spike_add=0.37, th_spike_decay=0.009, voltage_reset_fraction=0.2, voltage_reset_add=18.51, th_voltage_index=0.005, th_voltage_decay=0.09, asc_init=(0.0, 0.0), asc_decay=(0.003, 0.1), asc_amps=(-9.18, -198.94), asc_r=(1.0, 1.0), tau_syn_fast=(2.0,), tau_syn_slow=(6.0,), amp_slow=(0.3,), spike_dependent_threshold=False, after_spike_currents=False, adapting_threshold=False, I_e=Quantity(0., 'pA'), gsl_error_tol=1e-06, V_initializer=None, spk_fun=ReluGrad(alpha=0.3, width=1.0), spk_reset='hard', name=None)#

Current-based generalized leaky integrate-and-fire (GLIF) neuron model with double alpha-function shaped synaptic currents.

Implements the NEST glif_psc_double_alpha model, which extends the basic GLIF framework [1] with dual-component (fast + slow) alpha-function shaped postsynaptic currents [2]. This allows flexible control over synaptic waveform shape, including realistic biphasic or long-tailed currents observed experimentally. The model provides five GLIF variants (Models 1-5) selectable via boolean flags, ranging from simple LIF to adaptive threshold models with after-spike currents.

Model Family Overview

The five GLIF models are hierarchical, each adding biological mechanisms:

  • GLIF Model 1 (LIF) — Traditional leaky integrate-and-fire

  • GLIF Model 2 (LIF_R) — LIF with biologically defined reset rules

  • GLIF Model 3 (LIF_ASC) — LIF with after-spike currents

  • GLIF Model 4 (LIF_R_ASC) — LIF with reset rules and after-spike currents

  • GLIF Model 5 (LIF_R_ASC_A) — LIF with reset rules, after-spike currents, and a voltage-dependent threshold

Model selection is determined by three boolean parameters:

Model

spike_dependent_threshold

after_spike_currents

adapting_threshold

GLIF1

False

False

False

GLIF2

True

False

False

GLIF3

False

True

False

GLIF4

True

True

False

GLIF5

True

True

True

Double Alpha-Function Synaptic Currents

Each synaptic receptor port receives inputs shaped by a sum of two alpha functions (fast and slow components) [2]:

\[I_\mathrm{syn,k}(t) = \alpha_\mathrm{fast}(t; \tau_{\mathrm{syn,fast},k}) + \mathrm{amp\_slow}_k \cdot \alpha_\mathrm{slow}(t; \tau_{\mathrm{syn,slow},k})\]

Normalization: A spike of weight 1.0 produces a peak current of 1 pA for the fast component at \(t = \tau_\mathrm{syn,fast}\). The slow component peaks at \(\mathrm{amp\_slow}_k\) pA at \(t = \tau_\mathrm{syn,slow}\).

Multiple receptor ports are supported by passing arrays to tau_syn_fast, tau_syn_slow, and amp_slow. By default, one receptor port is created. Projections specify receptor ports via receptor_<k> labels (0-based indexing).

Detailed Mathematical Description

1. Membrane Dynamics

The membrane potential \(U\) (tracked relative to \(E_L\)) evolves via exact integration using propagator matrices:

\[U(t+dt) = U(t) \cdot P_{33} + (I_e + I_\mathrm{stim} + I_\mathrm{ASC,sum}) \cdot P_{30} + \sum_k \left( P_{31,k}^\mathrm{fast} \cdot y_{1,k}^\mathrm{fast} + P_{32,k}^\mathrm{fast} \cdot y_{2,k}^\mathrm{fast} \right) + \sum_k \left( P_{31,k}^\mathrm{slow} \cdot y_{1,k}^\mathrm{slow} + P_{32,k}^\mathrm{slow} \cdot y_{2,k}^\mathrm{slow} \right)\]

where:

\[P_{33} = \exp\left(-\frac{dt}{\tau_m}\right), \quad P_{30} = \frac{\tau_m}{C_m} \left(1 - P_{33}\right), \quad \tau_m = \frac{C_m}{g}\]

The propagators \(P_{31,k}\), \(P_{32,k}\) for each receptor and component (fast/slow) are computed using the IAFPropagatorAlpha algorithm, which handles the singularity when \(\tau_m \approx \tau_{\mathrm{syn},k}\).

2. Synaptic Current Dynamics (Double Alpha Function)

Each receptor port \(k\) maintains four state variables: two for the fast component and two for the slow component. Each pair \((y_1, y_2)\) represents an alpha function.

Fast component:

\[y_{2,k}^\mathrm{fast}(t+dt) = P_{21,k}^\mathrm{fast} \cdot y_{1,k}^\mathrm{fast}(t) + P_{22,k}^\mathrm{fast} \cdot y_{2,k}^\mathrm{fast}(t)\]
\[y_{1,k}^\mathrm{fast}(t+dt) = P_{11,k}^\mathrm{fast} \cdot y_{1,k}^\mathrm{fast}(t)\]

Slow component:

\[y_{2,k}^\mathrm{slow}(t+dt) = P_{21,k}^\mathrm{slow} \cdot y_{1,k}^\mathrm{slow}(t) + P_{22,k}^\mathrm{slow} \cdot y_{2,k}^\mathrm{slow}(t)\]
\[y_{1,k}^\mathrm{slow}(t+dt) = P_{11,k}^\mathrm{slow} \cdot y_{1,k}^\mathrm{slow}(t)\]

where:

\[ \begin{align}\begin{aligned}P_{11,k}^\mathrm{fast} = P_{22,k}^\mathrm{fast} = \exp\left(-\frac{dt}{\tau_{\mathrm{syn,fast},k}}\right)\\P_{21,k}^\mathrm{fast} = dt \cdot P_{11,k}^\mathrm{fast}\\P_{11,k}^\mathrm{slow} = P_{22,k}^\mathrm{slow} = \exp\left(-\frac{dt}{\tau_{\mathrm{syn,slow},k}}\right)\\P_{21,k}^\mathrm{slow} = dt \cdot P_{11,k}^\mathrm{slow}\end{aligned}\end{align} \]

On a presynaptic spike of weight \(w\) to receptor port \(k\):

\[ \begin{align}\begin{aligned}y_{1,k}^\mathrm{fast} \leftarrow y_{1,k}^\mathrm{fast} + w \cdot \frac{e}{\tau_{\mathrm{syn,fast},k}}\\y_{1,k}^\mathrm{slow} \leftarrow y_{1,k}^\mathrm{slow} + w \cdot \frac{e}{\tau_{\mathrm{syn,slow},k}} \cdot \mathrm{amp\_slow}_k\end{aligned}\end{align} \]

The total synaptic current is:

\[I_\mathrm{syn,total} = \sum_k \left( y_{2,k}^\mathrm{fast} + y_{2,k}^\mathrm{slow} \right)\]

3. After-Spike Currents (GLIF3/4/5)

After-spike currents (ASC) model slow adaptation via exponentially decaying currents triggered by spikes. Each ASC component \(I_j\) decays with rate \(k_j\):

\[I_j(t+dt) = I_j(t) \cdot \exp(-k_j \cdot dt)\]

The time-averaged ASC over a step (used for stable integration) is:

\[\bar{I}_j = \frac{1 - \exp(-k_j \cdot dt)}{k_j \cdot dt} \cdot I_j(t)\]

On spike, ASC values are reset:

\[I_j \leftarrow \Delta I_j + I_j \cdot r_j \cdot \exp(-k_j \cdot t_\mathrm{ref})\]

where \(\Delta I_j\) is the jump amplitude and \(r_j\) is the fraction coefficient.

4. Spike-Dependent Threshold (GLIF2/4/5)

The spike component of the threshold \(\theta_s\) decays exponentially:

\[\theta_s(t+dt) = \theta_s(t) \cdot \exp(-b_s \cdot dt)\]

On spike, after refractory decay:

\[\theta_s \leftarrow \theta_s \cdot \exp(-b_s \cdot t_\mathrm{ref}) + \Delta\theta_s\]

Voltage reset uses a biologically defined rule:

\[U \leftarrow f_v \cdot U_\mathrm{old} + V_\mathrm{add}\]

where \(f_v\) is the voltage fraction coefficient and \(V_\mathrm{add}\) is the additive constant.

5. Voltage-Dependent Threshold (GLIF5)

The voltage component of the threshold \(\theta_v\) evolves according to:

\[\theta_v(t+dt) = \phi \cdot (U_\mathrm{old} - \beta) \cdot P_\mathrm{decay} + \frac{1}{P_{\theta,v}} \cdot \left(\theta_v(t) - \phi \cdot (U_\mathrm{old} - \beta) - \frac{a_v}{b_v} \cdot \beta \right) + \frac{a_v}{b_v} \cdot \beta\]

where:

\[\phi = \frac{a_v}{b_v - g/C_m}, \quad P_\mathrm{decay} = \exp\left(-\frac{g \cdot dt}{C_m}\right), \quad P_{\theta,v} = \exp(b_v \cdot dt), \quad \beta = \frac{I_e + I_\mathrm{stim} + I_\mathrm{ASC,sum}}{g}\]

6. Overall Threshold and Spike Condition

\[\theta_\mathrm{total} = \theta_\infty + \theta_s + \theta_v\]

Spike condition (checked after voltage update):

\[\begin{split}\text{spike} = \begin{cases} \text{True} & \text{if } U > \theta_\mathrm{total} \\ \text{False} & \text{otherwise} \end{cases}\end{split}\]

7. Numerical Integration and Update Order

The discrete-time update sequence per simulation step is:

  1. Record \(U_\mathrm{old}\) (relative to \(E_L\)).

  2. If not refractory:

    1. Decay spike threshold component \(\theta_s\).

    2. Compute time-averaged ASC \(\bar{I}_j\) and decay ASC values.

    3. Update membrane potential \(U\) (include fast/slow synaptic contributions).

    4. Compute voltage-dependent threshold component \(\theta_v\) (using \(U_\mathrm{old}\)).

    5. Update total threshold \(\theta_\mathrm{total}\).

    6. If \(U > \theta_\mathrm{total}\): emit spike, apply reset rules.

  3. If refractory: decrement refractory counter, hold \(U\) at \(U_\mathrm{old}\).

  4. Update synaptic current state variables for both fast and slow components.

  5. Add incoming spike current jumps (scaled for fast/slow).

  6. Buffer external current input for next step.

  7. Save \(U_\mathrm{old}\) for next step.

Parameters:
  • in_size (int, tuple of int) – Population shape (number of neurons). Scalars are interpreted as (n,).

  • g (ArrayLike, optional) – Membrane (leak) conductance. Default: 9.43 nS. Must be strictly positive. Shape: scalar or broadcastable to in_size.

  • E_L (ArrayLike, optional) – Resting (leak) membrane potential (absolute). Default: -78.85 mV. Shape: scalar or broadcastable to in_size.

  • V_th (ArrayLike, optional) – Instantaneous spike threshold (absolute). Default: -51.68 mV. Must be greater than V_reset. Shape: scalar or broadcastable to in_size.

  • C_m (ArrayLike, optional) – Membrane capacitance. Default: 58.72 pF. Must be strictly positive. Shape: scalar or broadcastable to in_size.

  • t_ref (ArrayLike, optional) – Absolute refractory period. Default: 3.75 ms. Must be strictly positive. Shape: scalar or broadcastable to in_size.

  • V_reset (ArrayLike, optional) – Reset potential (absolute; used in GLIF1/3). Default: -78.85 mV. Must be less than V_th. Shape: scalar or broadcastable to in_size.

  • th_spike_add (float, optional) – Threshold additive constant after spike (\(\Delta\theta_s\)). Default: 0.37 mV. Used in GLIF2/4/5.

  • th_spike_decay (float, optional) – Spike threshold decay rate (\(b_s\)). Default: 0.009 /ms. Must be strictly positive. Used in GLIF2/4/5.

  • voltage_reset_fraction (float, optional) – Voltage fraction coefficient after spike (\(f_v\)). Default: 0.20. Must be in [0.0, 1.0]. Used in GLIF2/4/5.

  • voltage_reset_add (float, optional) – Voltage additive constant after spike (\(V_\mathrm{add}\)). Default: 18.51 mV. Used in GLIF2/4/5.

  • th_voltage_index (float, optional) – Voltage-dependent threshold leak rate (\(a_v\)). Default: 0.005 /ms. Used in GLIF5.

  • th_voltage_decay (float, optional) – Voltage-dependent threshold decay rate (\(b_v\)). Default: 0.09 /ms. Must be strictly positive. Used in GLIF5.

  • asc_init (Sequence[float], optional) – Initial values of after-spike current components (pA). Default: (0.0, 0.0). Length must match asc_decay, asc_amps, asc_r. Used in GLIF3/4/5.

  • asc_decay (Sequence[float], optional) – After-spike current decay rates (\(k_j\), /ms). Default: (0.003, 0.1). All values must be strictly positive. Used in GLIF3/4/5.

  • asc_amps (Sequence[float], optional) – After-spike current jump amplitudes (\(\Delta I_j\), pA). Default: (-9.18, -198.94). Used in GLIF3/4/5.

  • asc_r (Sequence[float], optional) – After-spike current fraction coefficients (\(r_j\)). Default: (1.0, 1.0). All values must be in [0.0, 1.0]. Used in GLIF3/4/5.

  • tau_syn_fast (Sequence[float], optional) – Fast synaptic alpha-function time constants (ms). Default: (2.0,). All values must be strictly positive. Length determines number of receptor ports.

  • tau_syn_slow (Sequence[float], optional) – Slow synaptic alpha-function time constants (ms). Default: (6.0,). All values must be strictly positive. Length must match tau_syn_fast.

  • amp_slow (Sequence[float], optional) – Relative amplitude of slow component (unitless). Default: (0.3,). All values must be strictly positive. Length must match tau_syn_fast.

  • spike_dependent_threshold (bool, optional) – Enable biologically defined reset rules (GLIF2/4/5). Default: False.

  • after_spike_currents (bool, optional) – Enable after-spike currents (GLIF3/4/5). Default: False.

  • adapting_threshold (bool, optional) – Enable voltage-dependent threshold (GLIF5). Default: False.

  • I_e (ArrayLike, optional) – Constant external current input (pA). Default: 0.0 pA. Shape: scalar or broadcastable to in_size.

  • V_initializer (Callable, optional) – Membrane potential initializer. Default: Constant(E_L). Should return values in mV when called with shape and batch_size.

  • spk_fun (Callable, optional) – Surrogate gradient function for differentiable spike generation. Default: ReluGrad(). Must accept scaled voltage and return spike output.

  • spk_reset (str, optional) – Spike reset mode. Default: 'hard' (stop gradient). Alternative: 'soft'.

  • name (str, optional) – Name of this neuron population.

Parameter Mapping

Parameter

Default

Math equivalent

Description

in_size

(required)

Population shape

g

9.43 nS

\(g\)

Membrane (leak) conductance

E_L

-78.85 mV

\(E_L\)

Resting membrane potential

V_th

-51.68 mV

\(V_\mathrm{th}\)

Instantaneous threshold (absolute)

C_m

58.72 pF

\(C_m\)

Membrane capacitance

t_ref

3.75 ms

\(t_\mathrm{ref}\)

Absolute refractory period

V_reset

-78.85 mV

\(V_\mathrm{reset}\)

Reset potential (absolute; GLIF1/3)

th_spike_add

0.37 mV

\(\Delta\theta_s\)

Threshold additive constant after spike

th_spike_decay

0.009 /ms

\(b_s\)

Spike threshold decay rate

voltage_reset_fraction

0.20

\(f_v\)

Voltage fraction after spike

voltage_reset_add

18.51 mV

\(V_\mathrm{add}\)

Voltage additive after spike

th_voltage_index

0.005 /ms

\(a_v\)

Voltage-dependent threshold leak

th_voltage_decay

0.09 /ms

\(b_v\)

Voltage-dependent threshold decay rate

asc_init

(0.0, 0.0) pA

\(I_j(0)\)

Initial values of ASC

asc_decay

(0.003, 0.1) /ms

\(k_j\)

ASC decay rates

asc_amps

(-9.18, -198.94) pA

\(\Delta I_j\)

ASC amplitudes on spike

asc_r

(1.0, 1.0)

\(r_j\)

ASC fraction coefficient

tau_syn_fast

(2.0,) ms

\(\tau_{\mathrm{syn,fast},k}\)

Fast synaptic alpha-function time constants

tau_syn_slow

(6.0,) ms

\(\tau_{\mathrm{syn,slow},k}\)

Slow synaptic alpha-function time constants

amp_slow

(0.3,)

\(\mathrm{amp\_slow}_k\)

Relative amplitude of slow component

spike_dependent_threshold

False

Enable biologically defined reset (GLIF2/4/5)

after_spike_currents

False

Enable after-spike currents (GLIF3/4/5)

adapting_threshold

False

Enable voltage-dependent threshold (GLIF5)

I_e

0.0 pA

\(I_e\)

Constant external current

V_initializer

Constant(E_L)

Membrane potential initializer

spk_fun

ReluGrad()

Surrogate spike function

spk_reset

'hard'

Reset mode ('hard' or 'soft')

Notes

  • Default parameters are from GLIF Model 5 of Cell 490626718 in the Allen Cell Type Database (https://celltypes.brain-map.org).

  • Voltage tracking: V_th and V_reset are specified in absolute mV. Internally, membrane potential is stored relative to E_L (matching NEST).

  • Stability constraint for GLIF2/4/5: The reset condition should satisfy:

    \[E_L + f_v \cdot (V_\mathrm{th} - E_L) + V_\mathrm{add} < V_\mathrm{th} + \Delta\theta_s\]

    Otherwise, the neuron may spike continuously.

  • Numerical integration: Uses exact integration via propagator matrices (matching NEST), unlike glif_cond which uses RKF45 ODE integration.

  • Singularity handling: If \(\tau_m \approx \tau_{\mathrm{syn,fast}}\) or \(\tau_m \approx \tau_{\mathrm{syn,slow}}\), the model automatically applies singularity-safe formulas (see NEST IAF_Integration_Singularity notebook).

  • Synaptic waveform control: The double alpha function provides more flexible control over synaptic current shape compared to single alpha (glif_psc). By tuning tau_syn_fast, tau_syn_slow, and amp_slow, experimentally observed waveforms can be matched.

  • Receptor port indexing: Synaptic inputs are registered via add_delta_input() with labels like 'receptor_0', 'receptor_1', etc. Inputs without explicit receptor labels default to receptor 0.

  • State persistence: After-spike current values (_ASCurrents), threshold components (_threshold_spike, _threshold_voltage), and total threshold (_threshold) are stored as NumPy arrays (not JAX arrays) to match NEST’s state handling and allow in-place updates during the per-neuron loop.

Examples

1. GLIF Model 1 (basic LIF) with single receptor:

>>> import brainpy.state as st
>>> import brainstate as bst
>>> import saiunit as u
>>> bst.environ.set(dt=0.1 * u.ms)
>>> neurons = st.glif_psc_double_alpha(
...     in_size=100,
...     g=10.0 * u.nS,
...     E_L=-70.0 * u.mV,
...     V_th=-55.0 * u.mV,
...     C_m=250.0 * u.pF,
...     t_ref=2.0 * u.ms,
...     V_reset=-70.0 * u.mV,
...     tau_syn_fast=(2.0,) * u.ms,
...     tau_syn_slow=(6.0,) * u.ms,
...     amp_slow=(0.5,),
... )
>>> neurons.init_all_states()
>>> spikes = neurons.update(10.0 * u.pA)

2. GLIF Model 5 (full model) with multiple receptors:

>>> neurons = st.glif_psc_double_alpha(
...     in_size=50,
...     spike_dependent_threshold=True,
...     after_spike_currents=True,
...     adapting_threshold=True,
...     tau_syn_fast=(1.0, 3.0) * u.ms,  # Two receptor ports
...     tau_syn_slow=(5.0, 10.0) * u.ms,
...     amp_slow=(0.3, 0.4),
...     asc_decay=(0.01, 0.05) / u.ms,
...     asc_amps=(-10.0, -100.0) * u.pA,
... )
>>> neurons.init_all_states()
>>> # Synaptic inputs can target different receptors
>>> neurons.add_delta_input('excitatory_receptor_0')
>>> neurons.add_delta_input('inhibitory_receptor_1')

3. Accessing synaptic current components:

>>> I_syn_total = neurons.get_I_syn()
>>> I_syn_fast = neurons.get_I_syn_fast()
>>> I_syn_slow = neurons.get_I_syn_slow()

References

See also

glif_psc

Single alpha-function variant.

glif_cond

Conductance-based GLIF using ODE integration.

gif_psc_exp_multisynapse

Generalized IF with exponential PSCs and multisynapse support.

aeif_psc_alpha

Adaptive exponential IF with alpha PSCs.

get_I_syn()[source]#

Get the total synaptic current summed across all receptor ports and components.

Computes the instantaneous total synaptic current by summing the fast and slow alpha-function current components (y2_fast and y2_slow) across all receptor ports. This represents the total postsynaptic current \(I_\mathrm{syn}\) flowing into the membrane at the current time step.

Returns:

I_syn – Total synaptic current across all receptors (fast + slow). Shape: same as self.V.value.shape (including batch dimension if present). Unit: saiunit.pA (picoamperes).

Return type:

jax.Array

Notes

  • This method reads the current state of y2_fast[k] and y2_slow[k] for all receptor ports k, without modifying state.

  • For a population with \(N\) receptor ports, the total current is:

    \[I_\mathrm{syn,total} = \sum_{k=0}^{N-1} \left( y_{2,k}^\mathrm{fast} + y_{2,k}^\mathrm{slow} \right)\]

See also

get_I_syn_fast

Get only the fast component.

get_I_syn_slow

Get only the slow component.

get_I_syn_fast()[source]#

Get the fast component of synaptic current summed across all receptor ports.

Computes the instantaneous fast synaptic current by summing the fast alpha-function current components (y2_fast) across all receptor ports. The fast component corresponds to synaptic currents with time constant tau_syn_fast.

Returns:

I_syn_fast – Fast synaptic current across all receptors. Shape: same as self.V.value.shape (including batch dimension if present). Unit: saiunit.pA (picoamperes).

Return type:

jax.Array

Notes

  • For a population with \(N\) receptor ports, the fast current is:

    \[I_\mathrm{syn,fast} = \sum_{k=0}^{N-1} y_{2,k}^\mathrm{fast}\]

See also

get_I_syn

Get total synaptic current (fast + slow).

get_I_syn_slow

Get only the slow component.

get_I_syn_slow()[source]#

Get the slow component of synaptic current summed across all receptor ports.

Computes the instantaneous slow synaptic current by summing the slow alpha-function current components (y2_slow) across all receptor ports. The slow component corresponds to synaptic currents with time constant tau_syn_slow, scaled by amplitude factor amp_slow.

Returns:

I_syn_slow – Slow synaptic current across all receptors. Shape: same as self.V.value.shape (including batch dimension if present). Unit: saiunit.pA (picoamperes).

Return type:

jax.Array

Notes

  • For a population with \(N\) receptor ports, the slow current is:

    \[I_\mathrm{syn,slow} = \sum_{k=0}^{N-1} y_{2,k}^\mathrm{slow}\]
  • The slow component typically models NMDA-like or other slow synaptic processes.

See also

get_I_syn

Get total synaptic current (fast + slow).

get_I_syn_fast

Get only the fast component.

get_spike(V=None)[source]#

Compute spike output from membrane potential using surrogate gradient function.

Applies the surrogate gradient function (self.spk_fun) to a scaled version of the membrane potential to produce a differentiable spike signal. This method computes the spike output without updating state, making it useful for inspection or custom integration schemes.

Scaling

The membrane potential is scaled to the range where the surrogate function is most sensitive:

\[v_\mathrm{scaled} = \frac{V - V_\mathrm{th}}{V_\mathrm{th} - V_\mathrm{reset}}\]

This normalization ensures that:

  • When \(V = V_\mathrm{th}\), \(v_\mathrm{scaled} = 0\)

  • When \(V = V_\mathrm{reset}\), \(v_\mathrm{scaled} = -1\)

Parameters:

V (ArrayLike, optional) – Membrane potential (absolute, in mV). If None (default), uses the current state self.V.value. If provided, should have shape compatible with self.varshape (or (batch_size, *self.varshape)). Unit: saiunit.mV or dimensionless (interpreted as mV).

Returns:

spike – Spike output computed via surrogate gradient function. Shape: same as input V. Dtype: same as input V (typically jnp.float32). Values: Continuous in [0, 1] for most surrogate functions (e.g., ReluGrad, SigmoidGrad), though exact range depends on self.spk_fun.

Return type:

jax.Array

Notes

  • This method is used internally by update() to compute spike output after the membrane potential update.

  • The surrogate gradient function ensures gradients can flow through spike events during backpropagation, enabling gradient-based training of spiking neural networks.

  • The scaling factor \((V_\mathrm{th} - V_\mathrm{reset})\) normalizes the input to the surrogate function, improving numerical stability and gradient flow.

See also

update

Main simulation step, which calls this method internally.

braintools.surrogate.ReluGrad

Default surrogate gradient function.

init_state(**kwargs)[source]#

Initialize all state variables for the neuron population.

Creates and initializes all state variables required for GLIF dynamics, including membrane potential, synaptic current states (fast and slow components for each receptor port), threshold components, after-spike current values, refractory counters, and buffered input current.

This method is compatible with brainstate.transform.for_loop: all GLIF-specific state variables are stored as JAX HiddenState arrays, and pre-computed decay constants are stored as Python floats.

Parameters:

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

property n_receptors#

Number of synaptic receptor ports.

Returns the number of distinct receptor ports configured for this neuron population. Each receptor port has independent fast and slow alpha-function current dynamics, allowing modeling of multiple synaptic receptor types (e.g., AMPA, NMDA, GABA_A, GABA_B).

Returns:

Number of receptor ports, determined by the length of tau_syn_fast (which must match the lengths of tau_syn_slow and amp_slow).

Return type:

int

Notes

  • Receptor ports are indexed from 0 to n_receptors - 1.

  • Projections target specific receptors via labels like 'receptor_0', 'receptor_1', etc.

  • By default (single-element arrays for synaptic parameters), n_receptors == 1.

See also

_collect_receptor_delta_inputs

Routes synaptic inputs to receptor ports.

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

Perform a single simulation step using exact propagator matrices.

Implements the NEST glif_psc_double_alpha update using the exact IAFPropagatorAlpha integration scheme. All GLIF-specific discrete updates (threshold decay, ASC, voltage-dependent threshold) are applied as vectorised JAX operations, making this method compatible with brainstate.transform.for_loop.

Parameters:

x (ArrayLike, optional) – External current input (pA), applied with one-step delay. Default: 0.0 pA.

Returns:

spike – Binary spike tensor (float32), shape (*varshape).

Return type:

jax.Array