iaf_psc_exp_multisynapse#
- class brainpy.state.iaf_psc_exp_multisynapse(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=Quantity([2.], "ms"), I_e=Quantity(0., "pA"), 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_multisynapseneuron model.Current-based leaky integrate-and-fire neuron with an arbitrary number of receptor-indexed exponential postsynaptic current channels.
Description
iaf_psc_exp_multisynapsemirrors NESTmodels/iaf_psc_exp_multisynapse.{h,cpp}and generalizesiaf_psc_expfrom two fixed excitatory/inhibitory channels ton_receptorsindependently parameterized current ports, each with its own exponential decay time constant.Each receptor
k(1-based, NEST convention) carries its own decay constanttau_syn[k-1]. Synaptic weights are signed currents in pA; positive values are depolarizing and negative values are hyperpolarizing.1. Continuous-Time Dynamics and Receptor States
Define \(V_{\mathrm{rel}} = V_m - E_L\). For receptor \(k\), the synaptic current decays exponentially:
\[\frac{dI_k}{dt} = -\frac{I_k}{\tau_{\mathrm{syn},k}}.\]The membrane equation couples all receptor currents additively:
\[\frac{dV_{\mathrm{rel}}}{dt} = -\frac{V_{\mathrm{rel}}}{\tau_m} + \frac{\sum_k I_k + I_e + I_0}{C_m},\]where \(I_0\) is the one-step delayed continuous-current buffer (NEST ring-buffer semantics). Assumptions match NEST’s current-based model: additive receptor currents, constant parameters within one simulation step, and fixed
dtfor exact propagator coefficients.2. Exact Discrete Propagator, Derivation Constraints, and Stability
For step size \(h = dt\) (ms), receptor currents are integrated exactly:
\[I_{k,n+1} = P_{11,k}\, I_{k,n} + w_{k,n}, \qquad P_{11,k} = e^{-h/\tau_{\mathrm{syn},k}},\]where \(w_{k,n}\) is the total weight arriving at receptor \(k\) during step \(n\).
The membrane update uses the exact propagator:
\[V_{\mathrm{rel},n+1} = P_{22}\, V_{\mathrm{rel},n} + P_{20}(I_e + I_{0,n}) + \sum_k P_{21,k}\, I_{k,n},\]with propagator coefficients
\[P_{22} = e^{-h/\tau_m}, \qquad P_{20} = \frac{\tau_m}{C_m}(1 - P_{22}),\]\[P_{21,k} = \frac{\tau_{\mathrm{syn},k}\,\tau_m} {C_m\,(\tau_m - \tau_{\mathrm{syn},k})} \left(e^{-h/\tau_m} - e^{-h/\tau_{\mathrm{syn},k}}\right).\]propagator_exp()(from_utils) evaluates \(P_{21,k}\) with a singular-limit fallback \((h / C_m)\,e^{-h/\tau_m}\) when \(\tau_{\mathrm{syn},k} \approx \tau_m\), preventing catastrophic cancellation in the denominator \((\tau_m - \tau_{\mathrm{syn},k})\). Construction additionally rejectsnp.isclose(tau_syn, tau_m)to preserve robust conditioning and avoid near-degenerate parameterizations.3. Update Order per Simulation Step (NEST Semantics)
Per-step execution order:
Integrate membrane with exact propagator for neurons not refractory (\(r = 0\)).
Decrement refractory counters for refractory neurons (\(r > 0\)).
Decay all receptor currents \(I_k\) by \(P_{11,k}\).
Inject receptor-specific spike weights \(w_{k,n}\), including default delta input mapped to receptor 1 when
n_receptors > 0.Apply threshold test, hard reset, refractory assignment, record spike time, and store buffered continuous current
xfor step \(n+1\).
4. Assumptions, Constraints, and Computational Implications
C_m > 0,tau_m > 0, alltau_syn > 0,not isclose(tau_syn, tau_m),t_ref >= 0, andV_reset < V_thare enforced at construction.update(x=...)uses one-step delayed current buffering: current provided at stepncontributes throughi_constat stepn+1, matching NEST ring-buffer event semantics.The update path is fully vectorized over
self.varshapeand scales as \(O(\prod \mathrm{varshape} \times n\_receptors)\) per call.Internal propagator arithmetic is performed in NumPy
float64before writing back to BrainUnit-typed states.When
n_receptors == 0, all spike event inputs are silently ignored.
- Parameters:
in_size (
Size) – Population shape specification. Per-neuron parameters and state variables are broadcast/initialized overself.varshapederived fromin_size.E_L (
ArrayLike, optional) – Resting potential \(E_L\) in mV; scalar or array broadcastable toself.varshape. Default is-70. * u.mV.C_m (
ArrayLike, optional) – Membrane capacitance \(C_m\) in pF; broadcastable toself.varshapeand strictly positive. Default is250. * u.pF.tau_m (
ArrayLike, optional) – Membrane time constant \(\tau_m\) in ms; broadcastable and strictly positive. Default is10. * u.ms.t_ref (
ArrayLike, optional) – Absolute refractory period \(t_\mathrm{ref}\) in ms; broadcastable and nonnegative. Converted to integer grid steps viaceil(t_ref / dt). Default is2. * u.ms.V_th (
ArrayLike, optional) – Spike threshold \(V_\mathrm{th}\) in mV; broadcastable toself.varshape. Default is-55. * u.mV.V_reset (
ArrayLike, optional) – Post-spike reset potential \(V_\mathrm{reset}\) in mV; broadcastable and constrained byV_reset < V_thelementwise. Default is-70. * u.mV.tau_syn (
ArrayLike, optional) – Synaptic decay constants in ms for all receptor ports. Converted to a 1-Dfloat64array of shape(n_receptors,)vianp.asarray(...).reshape(-1). Every entry must be strictly positive and must not be numerically equal totau_mundernp.isclose. The number of entries definesn_receptors. Default is(2.0,) * u.ms(one receptor).I_e (
ArrayLike, optional) – Constant injected current \(I_e\) in pA; scalar or array broadcastable toself.varshape. Default is0. * u.pA.V_initializer (
Callable, optional) – Initializer for membrane stateVused byinit_state(). Default isbraintools.init.Constant(-70. * u.mV).spk_fun (
Callable, optional) – Surrogate spike function used byget_spike()and returned byupdate(). Default isbraintools.surrogate.ReluGrad().spk_reset (
str, optional) – Reset policy inherited fromNeuron.'hard'reproduces NEST hard reset behavior. Default is'hard'.ref_var (
bool, optional) – IfTrue, allocates optional boolean stateself.refractoryfor external refractory inspection. Default isFalse.name (
strorNone, optional) – Optional node name passed to the parent module. Default isNone.
Parameter Mapping
Table 7 Parameter mapping to model symbols# Parameter
Type / shape / unit
Default
Math symbol
Semantics
in_sizeSize; scalar or tuplerequired
–
Defines population/state shape
self.varshape.E_LArrayLike, broadcastable to
self.varshape(mV)-70. * u.mV\(E_L\)
Leak reversal (resting) potential.
C_mArrayLike, broadcastable (pF),
> 0250. * u.pF\(C_m\)
Membrane capacitance in subthreshold integration.
tau_mArrayLike, broadcastable (ms),
> 010. * u.ms\(\tau_m\)
Membrane leak time constant.
t_refArrayLike, broadcastable (ms),
>= 02. * u.ms\(t_\mathrm{ref}\)
Absolute refractory duration in physical time.
V_thandV_resetArrayLike, broadcastable (mV), with
V_reset < V_th-55. * u.mV,-70. * u.mV\(V_\mathrm{th}\), \(V_\mathrm{reset}\)
Threshold and post-spike reset levels.
tau_synArrayLike, flattened to
(n_receptors,)(ms), each> 0and notisclosetotau_m(2.0,) * u.ms\(\tau_{\mathrm{syn},k}\)
Receptor-specific exponential PSC decay constants; number of entries defines
n_receptors.I_eArrayLike, broadcastable (pA)
0. * u.pA\(I_e\)
Constant current added each update step.
V_initializerCallable
Constant(-70. * u.mV)–
Initializer for membrane state
V.spk_funCallable
ReluGrad()–
Surrogate nonlinearity used for spike output.
spk_resetstr
'hard'–
Reset mode from
Neuron.ref_varbool
False–
If
True, exposes boolean stateself.refractory.namestr | None
None–
Optional node name.
- Raises:
ValueError – Raised at initialization or update time if any of the following holds: -
V_reset >= V_th. -C_m <= 0,tau_m <= 0, anytau_syn <= 0, ort_ref < 0. - Anytau_synis numerically equal totau_mundernp.isclose. - A spike event receptor index is outside[1, n_receptors].TypeError – If parameters or inputs are not unit-compatible with expected conversions (mV, ms, pF, pA).
KeyError – If simulation context entries (for example
tordt) are missing whenupdate()is called.AttributeError – If
update()is called beforeinit_state()creates required state holders.
- V#
Membrane potential in mV; shape
self.varshape.- Type:
brainstate.HiddenState
- i_syn#
Per-receptor synaptic currents in pA; shape
self.varshape + (n_receptors,).- Type:
brainstate.ShortTermState
- i_const#
Buffered continuous current (pA) applied on the next simulation step. Shape
self.varshape.- Type:
brainstate.ShortTermState
- refractory_step_count#
Integer countdown of remaining refractory steps (
jnp.int32). Shapeself.varshape.- Type:
brainstate.ShortTermState
- last_spike_time#
Simulation time of the most recent spike (ms). Shape
self.varshape.- 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
xis combined withI_eand any additional current sources registered viasum_current_inputs(); the combined value is buffered one step (NEST ring-buffer semantics).Spike weights from
spike_eventsandsum_delta_inputsare signed currents in pA: positive for depolarizing, negative for hyperpolarizing receptors.Default delta input from
sum_delta_inputsis routed to receptor 1 whenn_receptors > 0, replicating NEST default port behavior.If
n_receptors == 0, all spike event inputs are silently ignored andsum_delta_inputsis discarded.
Examples
>>> import brainstate >>> import saiunit as u >>> from brainpy_state._nest.iaf_psc_exp_multisynapse import ( ... iaf_psc_exp_multisynapse, ... ) >>> with brainstate.environ.context(dt=0.1 * u.ms): ... neu = iaf_psc_exp_multisynapse( ... in_size=2, ... tau_syn=(2.0, 8.0) * u.ms, ... I_e=180.0 * u.pA, ... ) ... neu.init_state() ... with brainstate.environ.context(t=0.0 * u.ms): ... spk = neu.update( ... spike_events=[{'receptor_type': 2, 'weight': 35.0 * u.pA}] ... ) ... _ = spk.shape
>>> import brainstate >>> import saiunit as u >>> from brainpy_state._nest.iaf_psc_exp_multisynapse import ( ... iaf_psc_exp_multisynapse, ... ) >>> with brainstate.environ.context(dt=0.1 * u.ms): ... neu = iaf_psc_exp_multisynapse(in_size=1, tau_syn=(2.0,) * u.ms) ... neu.init_state() ... with brainstate.environ.context(t=0.0 * u.ms): ... _ = neu.update(x=250.0 * u.pA) ... with brainstate.environ.context(t=0.1 * u.ms): ... spk_next = neu.update() ... _ = spk_next
References
See also
iaf_psc_expLIF with two fixed exponential PSC channels (exc/inh)
iaf_psc_alpha_multisynapseMultisynapse variant with alpha-shaped PSCs
iaf_psc_deltaLIF neuron with delta-function PSCs (voltage-jump synapses)
LIFLeaky integrate-and-fire (brainpy parameterization)
- 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_\mathrm{th}}{V_\mathrm{th} - V_\mathrm{reset}} \right).\]- Parameters:
V (
ArrayLikeorNone, optional) – Membrane voltage in mV, broadcast-compatible withself.varshape. IfNone,self.V.valueis used.- Returns:
out – Surrogate spike output from
self.spk_funwith the same shape asV(orself.V.valuewhenV is None). Positive values indicate a spike; the argument tospk_funis positive when \(V > V_\mathrm{th}\).- Return type:
- Raises:
TypeError – If
Vcannot 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; accepted for interface consistency with other nodes.- Raises:
ValueError – If
V_initializeroutput cannot be broadcast to the target state shape.TypeError – If initializer values are incompatible with required numeric/unit conversions.
- property n_receptors#
Number of independent synaptic receptor ports.
- Returns:
out – Length of
self.tau_syn; equalslen(tau_syn)as supplied at construction.- Return type:
- update(x=Quantity(0., 'pA'), spike_events=None, w_by_rec=None)[source]#
Advance the neuron state by one simulation step.
Executes the full NEST-compatible per-step update: exact membrane propagation for non-refractory neurons, receptor current decay and spike injection, threshold/reset/refractory logic, and buffered current storage.
- Parameters:
x (
ArrayLike, optional) – Continuous current input in pA for this step.xis accumulated throughsum_current_inputs()(which also adds any registered projection currents) and stored ini_constfor use on the next step, matching NEST ring-buffer semantics. Scalar or array broadcastable toself.varshape. Default is0. * u.pA.spike_events (
iterableorNone, optional) –Receptor-indexed spike weight events to inject this step. Each entry must be either:
A
(receptor_type, weight)tuple wherereceptor_typeis a 1-based integer in[1, n_receptors]andweightis a scalar or array in pA broadcastable toself.varshape.A
dictwith keys'receptor_type'(or'receptor') and'weight'.
Multiple events for the same receptor are accumulated additively.
Noneinjects no receptor spike events. Default isNone. Ignored whenw_by_recis provided.w_by_rec (
array-likeorNone, optional) – Pre-computed per-receptor spike weights in pA (dimensionless), shape broadcastable toself.varshape + (n_receptors,). When provided, bypassesspike_eventsparsing andsum_delta_inputs, making the update JIT-compatible for use insidebrainstate.transform.for_loop. Default isNone.
- Returns:
out – Surrogate spike output from
get_spike()with shapeself.V.value.shape. For neurons that fire this step, the voltage argument toget_spike()is nudged \(\theta + E_L + 10^{-12}\) mV (above threshold) to ensure a positive surrogate activation is returned even after the hard voltage reset.- Return type:
jax.Array- Raises:
ValueError – If any receptor index in
spike_eventsis outside[1, n_receptors].KeyError – If the simulation environment context does not supply
tordt.AttributeError – If state variables are missing because
init_state()has not been called beforeupdate.TypeError – If
xor stored states are not unit-compatible with expected pA / mV arithmetic.ValueError – If provided inputs cannot be broadcast to the internal state shape.